#!/usr/bin/env node /** * cli:scaffold-extension-config — Generate ExtensionConfig */ 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 } from './generate.js' import { generateEnvelope, failGenerate, printEnvelope } from '../../../../../lib/output.js' import { readSpecArg } from '../../../../../lib/spec-arg.js' import { customisedWarnings, writeGeneratedFiles } from '../../../../../lib/generated-writes.js' const COMMAND = 'scaffold-extension-config' function main(): void { const { values } = parseArgs({ options: { spec: { type: 'string' }, 'spec-file': { type: 'string' }, outdir: { type: 'string' }, dry_run: { type: 'boolean', default: false }, }, strict: true, }) const specSrc = readSpecArg(values) if ('error' in specSrc) { printEnvelope(failGenerate(COMMAND, [specSrc.error])) process.exit(1) } let raw: unknown try { raw = JSON.parse(specSrc.raw) } 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 — one entry point contract for every scaffolder: what // generate() receives is what Zod validated, never the raw JSON. 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 // Guarded write (lib/generated-writes): the SKILL tells the client to populate tableColumns / // formFields by hand — a `// @customised` config is skipped + warned, never overwritten (B-CRIT-04). const writes = writeGeneratedFiles(files, { outdir }) const written = writes.written printEnvelope(generateEnvelope(COMMAND, { data: { module: spec.module, fileCount: written.length, skipped: writes.skipped }, filesCreated: written, warnings: customisedWarnings(writes), nextSteps: ['Import config in app entry point'], })) } main()