#!/usr/bin/env node /** * cli:compute-page-diff — entry point. * * PURE spec-diff for ba-develop Phase 3a targeted re-runs. Compares the * canonical hash of every `pagespecs/..md` machine block against * the previous `.run-snapshot.json` and reports added / modified / removed / * unchanged. `toRegenerate = added ∪ modified`. * * This CLI performs NO inspection of generated `.tsx` files — the disk-drift * guard (closing the silent-skip class) is the orchestrator's job. See * `references/gates.md` § "Phase 3 pre-entry" and `disk-drift.ts`. * * Invocation: * npx --prefer-offline tsx skills/ba-develop/cli/compute-page-diff/index.ts \ * --spec '{"moduleRoot":".smartstack/ba//"}' * * Exit code: always 0 on a successful diff. An empty `toRegenerate` is a valid * "nothing changed in the specs" answer, NOT a failure — the orchestrator still * runs the disk-drift guard before deciding to skip Phase 3a. */ import { parseArgs } from 'node:util' import { validate } from './validate.js' import { computeDiff } from './compute-diff.js' import { executeEnvelope, failExecute, printEnvelope } from '../../../lib/output.js' import type { ComputePageDiffReport } from './types.js' const COMMAND = 'compute-page-diff' function main(): void { const { values } = parseArgs({ options: { spec: { type: 'string' } }, strict: true, }) if (!values.spec) { printEnvelope(failExecute(COMMAND, ['--spec is required'])) process.exit(1) } let raw: unknown try { raw = JSON.parse(values.spec) } catch { printEnvelope(failExecute(COMMAND, ['Invalid JSON in --spec'])) process.exit(1) } const validation = validate(raw) if (!validation.valid || !validation.spec) { printEnvelope(failExecute(COMMAND, validation.errors)) process.exit(1) } const report = computeDiff(validation.spec) const nextSteps: string[] = [] if (!report.snapshotFound) { nextSteps.push( 'No .run-snapshot.json found (first run): every page is "added" → regenerate all, then write the snapshot via update-snapshot.', ) } nextSteps.push( 'Orchestrator: regenerate = toRegenerate ∪ disk-drift. For each page in diff.unchanged, run validate-page; ' + 'fold in any page whose .tsx is missing or has err violations. Skip Phase 3a ONLY if the union is empty. ' + 'After a successful regeneration, run update-snapshot. See references/gates.md § "Phase 3 pre-entry".', ) printEnvelope( executeEnvelope(COMMAND, { success: true, data: { added: report.diff.added.length, modified: report.diff.modified.length, removed: report.diff.removed.length, unchanged: report.diff.unchanged.length, toRegenerate: report.toRegenerate.length, snapshotFound: report.snapshotFound, }, report, warnings: report.warnings, nextSteps, }), ) process.exit(0) } main()