#!/usr/bin/env node /** * cli:scaffold-controller * Responsabilité : Generate API controller + permissions class */ import { parseArgsOrFail as parseArgs } from '../../../../../lib/cli-args.js' import { mkdirSync, writeFileSync } from 'node:fs' import { dirname, resolve, join } from 'node:path' import { validate } from './validate.js' import { generate, legacyPaths } from './generate.js' import { generateEnvelope, failGenerate, printEnvelope } from '../../../../../lib/output.js' import { guardedRm } from '../../../../../lib/guarded-rm.js' import { customisedWarnings, writeGeneratedFiles } from '../../../../../lib/generated-writes.js' const COMMAND = 'scaffold-controller' function main(): void { const { values } = parseArgs({ options: { spec: { type: 'string' }, outdir: { type: 'string' }, dry_run: { type: 'boolean', default: false } }, strict: true }) if (!values.spec) { printEnvelope(failGenerate(COMMAND, ['--spec is required'])); process.exit(1) } let raw: unknown try { raw = JSON.parse(values.spec) } catch { printEnvelope(failGenerate(COMMAND, ['Invalid JSON'])); process.exit(1) } const validation = validate(raw) if (!validation.valid || validation.data === undefined) { printEnvelope(failGenerate(COMMAND, validation.errors)); process.exit(1) } // The PARSED spec (Zod defaults applied) — never the raw JSON: `actions`, // `customActions`, `fields[].required`… only exist after parse; the raw // object crashed generate() on the SKILL's own example. const spec = validation.data const files = generate(spec) if (values.dry_run) { printEnvelope(generateEnvelope(COMMAND, { data: { dryRun: true, files: files.map(f => f.path) } })) process.exit(0) } const outdir = values.outdir ?? spec.projectPath // Idempotent relocate: delete the pre-classification copies before writing the // new /-classified files so re-running /ba-develop MOVES them. // Guarded sweep (lib/guarded-rm): @customised files preserved, deletions traced. const legacySweep = guardedRm(legacyPaths(spec), { outdir }) // Guarded write (lib/generated-writes): an `@customised` output is skipped + warned, never overwritten. const writes = writeGeneratedFiles(files, { outdir }) const written = writes.written printEnvelope(generateEnvelope(COMMAND, { data: { entity: spec.name, navRoute: spec.navRoute, fileCount: written.length, filesRemoved: legacySweep.removed, skipped: writes.skipped }, filesCreated: written, warnings: [...legacySweep.preserved.map(p => `legacy path ${p} kept: marked @customised — delete manually if truly superseded.`), ...customisedWarnings(writes)], nextSteps: ['Verify SS001-SS007 conventions', 'Run dotnet build'], })) } main()