/** * preflight-develop-plan CLI — Entry point. * * Validates that the development plan (from /ba-create-plan-development) is executable: * reads dev-plan.json, checks per-module PRD files and audit readiness — the * module's `_audit/prd.md` AND the project `/ba-audit-pre-dev` aggregate * (`_audit/pre-dev.md`, whose verdict had NO reader before chantier 4.5: * ❌ / « non audité » rows become wave blockers here) — and returns a * PreflightReport as an ExecuteEnvelope. * * Usage: * npx --prefer-offline tsx skills/ba-develop-plan/cli/preflight-develop-plan/index.ts \ * --spec '{"baRoot": ".smartstack/ba", "projectPath": "/path/to/project"}' */ import { parseArgs } from 'node:util'; import { executeEnvelope, failExecute, printEnvelope, } from '../../../lib/output.js'; import { validateInput, checkReadiness } from './validate.js'; import type { PreflightReport } from './types.js'; const COMMAND = 'preflight-develop-plan'; async function main(): Promise { // --- 1. Parse CLI arguments --- const { values } = parseArgs({ options: { spec: { type: 'string' }, }, strict: false, }); if (!values.spec) { printEnvelope(failExecute(COMMAND, ['Missing required --spec argument'])); process.exit(1); } let raw: unknown; try { raw = JSON.parse(String(values.spec)); } catch { printEnvelope(failExecute(COMMAND, ['Invalid JSON in --spec argument'])); process.exit(1); } // --- 2. Validate input + read plan --- const validation = await validateInput(raw); if (!validation.valid || !validation.spec || !validation.plan) { printEnvelope(failExecute(COMMAND, validation.errors)); process.exit(1); } // --- 3. Check per-module readiness --- const report = await checkReadiness(validation.spec, validation.plan); // --- 4. Print report --- const allWarnings = [...validation.warnings]; for (const wave of report.waves) { for (const mod of wave.modules) { allWarnings.push(...mod.warnings); } } const errors: string[] = []; if (!report.canProceed) { errors.push('No waves are ready for development — fix blockers first'); } const nextSteps: string[] = []; if (report.canProceed) { nextSteps.push( `Ready to develop waves ${report.firstReadyWave}${report.lastReadyWave !== report.firstReadyWave ? `–${report.lastReadyWave}` : ''} (${report.readyModules} modules)`, ); } // Blocked modules for (const wave of report.waves) { for (const mod of wave.modules) { if (mod.status === 'not-ready') { nextSteps.push(`Fix ${mod.key}: ${mod.blockers.join('; ')}`); } } } printEnvelope( executeEnvelope(COMMAND, { success: report.canProceed, report, errors, warnings: allWarnings, nextSteps, }), ); if (!report.canProceed) process.exit(1); } main().catch((err) => { printEnvelope( failExecute(COMMAND, [err instanceof Error ? err.message : String(err)]), ); process.exit(1); });