#!/usr/bin/env node /** * cli:derive-pwa-spec — index.ts * * Invocation: * npx --prefer-offline tsx skills/pwa/cli/derive-pwa-spec/index.ts --spec '' * * Report-only by default; pass `"write": true` in the spec (with `updates[]`) * to persist approved `pwa` fields into the pagespec machine blocks. */ import { parseArgs } from 'node:util' import { executeEnvelope, failExecute, printEnvelope } from '../../../lib/output.js' import { readSpecArg } from '../../../lib/spec-arg.js' import { validate } from './validate.js' import { execute } from './execute.js' import { DerivePwaSpecInputSchema, type DerivePwaSpecReport } from './types.js' const COMMAND = 'derive-pwa-spec' function main(): void { // strict + readSpecArg — aligned on the rest of the /pwa chain (a typo'd // flag is a hard parse error, and --spec-file carries oversized specs). const { values } = parseArgs({ options: { spec: { type: 'string' }, 'spec-file': { type: 'string' }, }, strict: true, }) const specSrc = readSpecArg(values) if ('error' in specSrc) { printEnvelope(failExecute(COMMAND, [specSrc.error])) process.exit(1) } let raw: unknown try { raw = JSON.parse(specSrc.raw) } catch (err) { printEnvelope(failExecute(COMMAND, [`--spec is not valid JSON: ${(err as Error).message}`])) process.exit(1) } const validation = validate(raw) if (!validation.valid) { printEnvelope(failExecute(COMMAND, validation.errors)) process.exit(1) } try { const spec = DerivePwaSpecInputSchema.parse(raw) const { report, warnings } = execute(spec) const nextSteps: string[] = [] if (report.requiresVersioned.length > 0) { nextSteps.push( `${report.requiresVersioned.length} offline-write entit${report.requiresVersioned.length === 1 ? 'y' : 'ies'} ` + `require versioned: true (scaffold-entity + scaffold-business + scaffold-api-client + scaffold-component) — ` + `the RowVersion column needs an EF migration via /efcore (governed; NEVER auto-run).`, ) } printEnvelope(executeEnvelope(COMMAND, { report, warnings: [...validation.warnings, ...warnings], nextSteps, })) } catch (err) { printEnvelope(failExecute(COMMAND, [(err as Error).message])) process.exit(1) } } main()