Merge pull request 'feat: complete CLI command parity — coord, prdy, seq, upgrade' (#352) from fix/complete-cli-parity into main
Reviewed-on: mosaic/mosaic-stack#352
This commit was merged in pull request #352.
This commit is contained in:
@@ -5,7 +5,7 @@ import { Command } from 'commander';
|
|||||||
import { createQualityRailsCli } from '@mosaic/quality-rails';
|
import { createQualityRailsCli } from '@mosaic/quality-rails';
|
||||||
import { registerAgentCommand } from './commands/agent.js';
|
import { registerAgentCommand } from './commands/agent.js';
|
||||||
import { registerMissionCommand } from './commands/mission.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';
|
import { registerLaunchCommands } from './commands/launch.js';
|
||||||
|
|
||||||
const _require = createRequire(import.meta.url);
|
const _require = createRequire(import.meta.url);
|
||||||
@@ -298,10 +298,6 @@ registerAgentCommand(program);
|
|||||||
|
|
||||||
registerMissionCommand(program);
|
registerMissionCommand(program);
|
||||||
|
|
||||||
// ─── prdy ──────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
registerPrdyCommand(program);
|
|
||||||
|
|
||||||
// ─── quality-rails ──────────────────────────────────────────────────────
|
// ─── quality-rails ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
const qrWrapper = createQualityRailsCli();
|
const qrWrapper = createQualityRailsCli();
|
||||||
|
|||||||
@@ -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 {
|
function delegateToScript(scriptPath: string, args: string[], env?: Record<string, string>): never {
|
||||||
const scriptPath = join(MOSAIC_HOME, 'tools', '_scripts', script);
|
|
||||||
if (!existsSync(scriptPath)) {
|
if (!existsSync(scriptPath)) {
|
||||||
console.error(`[mosaic] Script not found: ${scriptPath}`);
|
console.error(`[mosaic] Script not found: ${scriptPath}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
execFileSync(scriptPath, args, { stdio: 'inherit' });
|
execFileSync('bash', [scriptPath, ...args], {
|
||||||
|
stdio: 'inherit',
|
||||||
|
env: { ...process.env, ...env },
|
||||||
|
});
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
process.exit((err as { status?: number }).status ?? 1);
|
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<string, string> = {
|
||||||
|
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 <name> [opts] Initialize a new mission
|
||||||
|
mission [--project <path>] Show mission progress dashboard
|
||||||
|
status [--project <path>] Check agent session health
|
||||||
|
continue [--project <path>] Generate continuation prompt
|
||||||
|
run [--project <path>] Launch runtime with mission context
|
||||||
|
smoke Run orchestration smoke checks
|
||||||
|
resume [--project <path>] 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<string, string> = {
|
||||||
|
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 <path>] [--name <feature>] Create docs/PRD.md
|
||||||
|
update [--project <path>] Update existing PRD
|
||||||
|
validate [--project <path>] Check PRD completeness
|
||||||
|
status [--project <path>] 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 {
|
export function registerLaunchCommands(program: Command): void {
|
||||||
// Runtime launchers
|
// Runtime launchers
|
||||||
@@ -509,15 +677,58 @@ export function registerLaunchCommands(program: Command): void {
|
|||||||
launchRuntime(runtime as RuntimeName, cmd.args, true);
|
launchRuntime(runtime as RuntimeName, cmd.args, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Framework management commands (delegate to bash scripts)
|
// Coord (mission orchestrator)
|
||||||
const frameworkCommands: Record<string, { desc: string; script: string }> = {
|
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<string, { desc: string; script: string }> = {
|
||||||
init: { desc: 'Generate SOUL.md (agent identity contract)', script: 'mosaic-init' },
|
init: { desc: 'Generate SOUL.md (agent identity contract)', script: 'mosaic-init' },
|
||||||
doctor: { desc: 'Health audit — detect drift and missing files', script: 'mosaic-doctor' },
|
doctor: { desc: 'Health audit — detect drift and missing files', script: 'mosaic-doctor' },
|
||||||
sync: { desc: 'Sync skills from canonical source', script: 'mosaic-sync-skills' },
|
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
|
program
|
||||||
.command(name)
|
.command(name)
|
||||||
.description(desc)
|
.description(desc)
|
||||||
@@ -525,7 +736,7 @@ export function registerLaunchCommands(program: Command): void {
|
|||||||
.allowExcessArguments(true)
|
.allowExcessArguments(true)
|
||||||
.action((_opts: unknown, cmd: Command) => {
|
.action((_opts: unknown, cmd: Command) => {
|
||||||
checkMosaicHome();
|
checkMosaicHome();
|
||||||
delegateToFrameworkScript(script, cmd.args);
|
delegateToScript(fwScript(script), cmd.args);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user