diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index e006bf5..5639ed1 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -5,7 +5,7 @@ import { Command } from 'commander'; import { createQualityRailsCli } from '@mosaic/quality-rails'; import { registerAgentCommand } from './commands/agent.js'; import { registerMissionCommand } from './commands/mission.js'; -import { registerPrdyCommand } from './commands/prdy.js'; +// prdy is registered via launch.ts import { registerLaunchCommands } from './commands/launch.js'; const _require = createRequire(import.meta.url); @@ -298,10 +298,6 @@ registerAgentCommand(program); registerMissionCommand(program); -// ─── prdy ────────────────────────────────────────────────────────────── - -registerPrdyCommand(program); - // ─── quality-rails ────────────────────────────────────────────────────── const qrWrapper = createQualityRailsCli(); diff --git a/packages/cli/src/commands/launch.ts b/packages/cli/src/commands/launch.ts index e89a8ce..524e9be 100644 --- a/packages/cli/src/commands/launch.ts +++ b/packages/cli/src/commands/launch.ts @@ -461,23 +461,191 @@ function execRuntime(cmd: string, args: string[]): void { } } -// ─── Framework script delegation (for tools that remain in bash) ───────────── +// ─── Framework script/tool delegation ─────────────────────────────────────── -function delegateToFrameworkScript(script: string, args: string[]): never { - const scriptPath = join(MOSAIC_HOME, 'tools', '_scripts', script); +function delegateToScript(scriptPath: string, args: string[], env?: Record): never { if (!existsSync(scriptPath)) { console.error(`[mosaic] Script not found: ${scriptPath}`); process.exit(1); } try { - execFileSync(scriptPath, args, { stdio: 'inherit' }); + execFileSync('bash', [scriptPath, ...args], { + stdio: 'inherit', + env: { ...process.env, ...env }, + }); process.exit(0); } catch (err) { process.exit((err as { status?: number }).status ?? 1); } } -// ─── Commander registration ────────────────────────────────────────────────── +function fwScript(name: string): string { + return join(MOSAIC_HOME, 'tools', '_scripts', name); +} + +function toolScript(toolDir: string, name: string): string { + return join(MOSAIC_HOME, 'tools', toolDir, name); +} + +// ─── Coord (mission orchestrator) ─────────────────────────────────────────── + +const COORD_SUBCMDS: Record = { + status: 'session-status.sh', + session: 'session-status.sh', + init: 'mission-init.sh', + mission: 'mission-status.sh', + progress: 'mission-status.sh', + continue: 'continue-prompt.sh', + next: 'continue-prompt.sh', + run: 'session-run.sh', + start: 'session-run.sh', + smoke: 'smoke-test.sh', + test: 'smoke-test.sh', + resume: 'session-resume.sh', + recover: 'session-resume.sh', +}; + +function runCoord(args: string[]): never { + checkMosaicHome(); + let runtime = 'claude'; + let yoloFlag = ''; + const coordArgs: string[] = []; + + for (const arg of args) { + if (arg === '--claude' || arg === '--codex' || arg === '--pi') { + runtime = arg.slice(2); + } else if (arg === '--yolo') { + yoloFlag = '--yolo'; + } else { + coordArgs.push(arg); + } + } + + const subcmd = coordArgs[0] ?? 'help'; + const subArgs = coordArgs.slice(1); + const script = COORD_SUBCMDS[subcmd]; + + if (!script) { + console.log(`mosaic coord — mission coordinator tools + +Commands: + init --name [opts] Initialize a new mission + mission [--project ] Show mission progress dashboard + status [--project ] Check agent session health + continue [--project ] Generate continuation prompt + run [--project ] Launch runtime with mission context + smoke Run orchestration smoke checks + resume [--project ] Crash recovery + +Runtime: --claude (default) | --codex | --pi | --yolo`); + process.exit(subcmd === 'help' ? 0 : 1); + } + + if (yoloFlag) subArgs.unshift(yoloFlag); + delegateToScript(toolScript('orchestrator', script), subArgs, { + MOSAIC_COORD_RUNTIME: runtime, + }); +} + +// ─── Prdy (PRD tools via framework scripts) ───────────────────────────────── + +const PRDY_SUBCMDS: Record = { + init: 'prdy-init.sh', + update: 'prdy-update.sh', + validate: 'prdy-validate.sh', + check: 'prdy-validate.sh', + status: 'prdy-status.sh', +}; + +function runPrdyLocal(args: string[]): never { + checkMosaicHome(); + let runtime = 'claude'; + const prdyArgs: string[] = []; + + for (const arg of args) { + if (arg === '--claude' || arg === '--codex' || arg === '--pi') { + runtime = arg.slice(2); + } else { + prdyArgs.push(arg); + } + } + + const subcmd = prdyArgs[0] ?? 'help'; + const subArgs = prdyArgs.slice(1); + const script = PRDY_SUBCMDS[subcmd]; + + if (!script) { + console.log(`mosaic prdy — PRD creation and validation + +Commands: + init [--project ] [--name ] Create docs/PRD.md + update [--project ] Update existing PRD + validate [--project ] Check PRD completeness + status [--project ] Quick PRD health check + +Runtime: --claude (default) | --codex | --pi`); + process.exit(subcmd === 'help' ? 0 : 1); + } + + delegateToScript(toolScript('prdy', script), subArgs, { + MOSAIC_PRDY_RUNTIME: runtime, + }); +} + +// ─── Seq (sequential-thinking MCP) ────────────────────────────────────────── + +function runSeq(args: string[]): never { + checkMosaicHome(); + const action = args[0] ?? 'check'; + const rest = args.slice(1); + const checker = fwScript('mosaic-ensure-sequential-thinking'); + + switch (action) { + case 'check': + delegateToScript(checker, ['--check', ...rest]); + break; // unreachable + case 'fix': + case 'apply': + delegateToScript(checker, rest); + break; + case 'start': { + console.log('[mosaic] Starting sequential-thinking MCP server...'); + try { + execFileSync('npx', ['-y', '@modelcontextprotocol/server-sequential-thinking', ...rest], { + stdio: 'inherit', + }); + process.exit(0); + } catch (err) { + process.exit((err as { status?: number }).status ?? 1); + } + break; + } + default: + console.error(`[mosaic] Unknown seq subcommand '${action}'. Use: check|fix|start`); + process.exit(1); + } +} + +// ─── Upgrade ──────────────────────────────────────────────────────────────── + +function runUpgrade(args: string[]): never { + checkMosaicHome(); + const subcmd = args[0]; + + if (!subcmd || subcmd === 'release') { + delegateToScript(fwScript('mosaic-release-upgrade'), args.slice(subcmd === 'release' ? 1 : 0)); + } else if (subcmd === 'check') { + delegateToScript(fwScript('mosaic-release-upgrade'), ['--dry-run', ...args.slice(1)]); + } else if (subcmd === 'project') { + delegateToScript(fwScript('mosaic-upgrade'), args.slice(1)); + } else if (subcmd.startsWith('-')) { + delegateToScript(fwScript('mosaic-release-upgrade'), args); + } else { + delegateToScript(fwScript('mosaic-upgrade'), args); + } +} + +// ─── Commander registration ───────────────────────────────────────────────── export function registerLaunchCommands(program: Command): void { // Runtime launchers @@ -509,15 +677,58 @@ export function registerLaunchCommands(program: Command): void { launchRuntime(runtime as RuntimeName, cmd.args, true); }); - // Framework management commands (delegate to bash scripts) - const frameworkCommands: Record = { + // Coord (mission orchestrator) + program + .command('coord') + .description('Mission coordinator tools (init, status, run, continue, resume)') + .allowUnknownOption(true) + .allowExcessArguments(true) + .action((_opts: unknown, cmd: Command) => { + runCoord(cmd.args); + }); + + // Prdy (PRD tools via local framework scripts) + program + .command('prdy') + .description('PRD creation and validation (init, update, validate, status)') + .allowUnknownOption(true) + .allowExcessArguments(true) + .action((_opts: unknown, cmd: Command) => { + runPrdyLocal(cmd.args); + }); + + // Seq (sequential-thinking MCP management) + program + .command('seq') + .description('sequential-thinking MCP management (check/fix/start)') + .allowUnknownOption(true) + .allowExcessArguments(true) + .action((_opts: unknown, cmd: Command) => { + runSeq(cmd.args); + }); + + // Upgrade (release + project) + program + .command('upgrade') + .description('Upgrade Mosaic release or project files') + .allowUnknownOption(true) + .allowExcessArguments(true) + .action((_opts: unknown, cmd: Command) => { + runUpgrade(cmd.args); + }); + + // Direct framework script delegates + const directCommands: Record = { init: { desc: 'Generate SOUL.md (agent identity contract)', script: 'mosaic-init' }, doctor: { desc: 'Health audit — detect drift and missing files', script: 'mosaic-doctor' }, sync: { desc: 'Sync skills from canonical source', script: 'mosaic-sync-skills' }, - bootstrap: { desc: 'Bootstrap a repo with Mosaic standards', script: 'mosaic-bootstrap-repo' }, + bootstrap: { + desc: 'Bootstrap a repo with Mosaic standards', + script: 'mosaic-bootstrap-repo', + }, }; - for (const [name, { desc, script }] of Object.entries(frameworkCommands)) { + for (const [name, { desc, script }] of Object.entries(directCommands)) { program .command(name) .description(desc) @@ -525,7 +736,7 @@ export function registerLaunchCommands(program: Command): void { .allowExcessArguments(true) .action((_opts: unknown, cmd: Command) => { checkMosaicHome(); - delegateToFrameworkScript(script, cmd.args); + delegateToScript(fwScript(script), cmd.args); }); } }