feat: monorepo consolidation — forge pipeline, MACP protocol, framework plugin, profiles/guides/skills
Some checks failed
ci/woodpecker/push/ci Pipeline failed
ci/woodpecker/pr/ci Pipeline failed

Work packages completed:
- WP1: packages/forge — pipeline runner, stage adapter, board tasks, brief classifier,
  persona loader with project-level overrides. 89 tests, 95.62% coverage.
- WP2: packages/macp — credential resolver, gate runner, event emitter, protocol types.
  65 tests, 96.24% coverage. Full Python-to-TS port preserving all behavior.
- WP3: plugins/mosaic-framework — OC rails injection plugin (before_agent_start +
  subagent_spawning hooks for Mosaic contract enforcement).
- WP4: profiles/ (domains, tech-stacks, workflows), guides/ (17 docs),
  skills/ (5 universal skills), forge pipeline assets (48 markdown files).

Board deliberation: docs/reviews/consolidation-board-memo.md
Brief: briefs/monorepo-consolidation.md

Consolidates mosaic/stack (forge, MACP, bootstrap framework) into mosaic/mosaic-stack.
154 new tests total. Zero Python — all TypeScript/ESM.
This commit is contained in:
Mos (Agent)
2026-03-30 19:43:24 +00:00
parent 40c068fcbc
commit 10689a30d2
123 changed files with 18166 additions and 11 deletions

View File

@@ -0,0 +1,102 @@
import { STAGE_SEQUENCE, STRATEGIC_KEYWORDS, TECHNICAL_KEYWORDS } from './constants.js';
import type { BriefClass, ClassSource } from './types.js';
const VALID_CLASSES: ReadonlySet<string> = new Set<BriefClass>([
'strategic',
'technical',
'hotfix',
]);
/**
* Auto-classify a brief based on keyword analysis.
* Returns 'strategic' if strategic keywords dominate,
* 'technical' if any technical keywords are found,
* otherwise defaults to 'strategic' (full pipeline).
*/
export function classifyBrief(text: string): BriefClass {
const lower = text.toLowerCase();
let strategicHits = 0;
let technicalHits = 0;
for (const kw of STRATEGIC_KEYWORDS) {
if (lower.includes(kw)) strategicHits++;
}
for (const kw of TECHNICAL_KEYWORDS) {
if (lower.includes(kw)) technicalHits++;
}
if (strategicHits > technicalHits) return 'strategic';
if (technicalHits > 0) return 'technical';
return 'strategic';
}
/**
* Parse YAML frontmatter from a brief.
* Supports simple `key: value` pairs via regex (no YAML dependency).
*/
export function parseBriefFrontmatter(text: string): Record<string, string> {
const match = text.match(/^---\s*\n([\s\S]*?)\n---\s*\n/);
if (!match?.[1]) return {};
const result: Record<string, string> = {};
for (const line of match[1].split('\n')) {
const km = line.trim().match(/^(\w[\w-]*)\s*:\s*(.+)$/);
if (km?.[1] && km[2]) {
result[km[1]] = km[2].trim().replace(/^["']|["']$/g, '');
}
}
return result;
}
/**
* Determine brief class from all sources with priority:
* CLI flag > frontmatter > auto-classify.
*/
export function determineBriefClass(
text: string,
cliClass?: string,
): { briefClass: BriefClass; classSource: ClassSource } {
if (cliClass && VALID_CLASSES.has(cliClass)) {
return { briefClass: cliClass as BriefClass, classSource: 'cli' };
}
const fm = parseBriefFrontmatter(text);
if (fm['class'] && VALID_CLASSES.has(fm['class'])) {
return { briefClass: fm['class'] as BriefClass, classSource: 'frontmatter' };
}
return { briefClass: classifyBrief(text), classSource: 'auto' };
}
/**
* Build the stage list based on brief classification.
* - strategic: full pipeline (all stages)
* - technical: skip board (01-board)
* - hotfix: skip board + brief analyzer
*
* forceBoard re-adds the board stage regardless of class.
*/
export function stagesForClass(briefClass: BriefClass, forceBoard = false): string[] {
const stages = ['00-intake', '00b-discovery'];
if (briefClass === 'strategic' || forceBoard) {
stages.push('01-board');
}
if (briefClass === 'strategic' || briefClass === 'technical' || forceBoard) {
stages.push('01b-brief-analyzer');
}
stages.push(
'02-planning-1',
'03-planning-2',
'04-planning-3',
'05-coding',
'06-review',
'07-remediate',
'08-test',
'09-deploy',
);
// Maintain canonical order
return stages.filter((s) => STAGE_SEQUENCE.includes(s));
}