#!/usr/bin/env node /** * cli:cross-validate-stack * Cross-stack validation backend .NET / frontend React — 5 consistency checks. * * Usage: * npx --prefer-offline tsx skills/validation/cross-validate/cli/index.ts --specs --src * * Exit codes: * 0 Success (0 errors) * 1 Errors detected * 2 Internal CLI error / input validation */ import { parseArgs } from 'node:util' import { resolve } from 'node:path' import { validate } from './validate' import { execute } from './execute' import { executeEnvelope, failExecute, printEnvelope } from '../../../lib/output.js' const COMMAND = 'cross-validate-stack' const { values } = parseArgs({ options: { specs: { type: 'string' }, src: { type: 'string' }, help: { type: 'boolean', short: 'h' }, }, strict: true, }) if (values.help) { const paren = String.fromCharCode(41) console.log(`cli:cross-validate-stack — SmartStack Studio Cross-stack validation backend .NET / frontend React (5 checks${paren}. Usage: npx --prefer-offline tsx index.ts --specs --src Options: --specs Directory containing the JSON specs (meta.json, endpoints.json, permissions.json${paren} (required${paren} --src Directory containing the .cs and .ts/.tsx source code (required${paren} -h, --help Show this help Checks: 1. routes-have-endpoints Every spec endpoint has a .NET controller 2. endpoints-are-consumed Every .NET endpoint is called by a React service 3. permissions-aligned RETIRED — see DEV-API-021/033 + DEV-UI-011 (id kept, always skipped) 4. resources-have-screens Every resource has a controller + a React page 5. rbac-consistent RETIRED — see DEV-CORE-011 + DEV-API-021 (id kept, always skipped) Exit codes: 0 Success (0 errors${paren} 1 Errors detected 2 Internal CLI error`) process.exit(0) } if (!values.specs) { printEnvelope(failExecute(COMMAND, ['--specs is required'])) process.exit(2) } if (!values.src) { printEnvelope(failExecute(COMMAND, ['--src is required'])) process.exit(2) } const specsPath = resolve(values.specs) const srcPath = resolve(values.src) const validation = validate({ specs: specsPath, src: srcPath }) if (!validation.valid) { printEnvelope(failExecute(COMMAND, validation.blockers)) process.exit(2) } const report = execute(validation.data!) if (report.errors === 0) { printEnvelope(executeEnvelope(COMMAND, { report, warnings: report.warnings > 0 ? [`${report.warnings} warning(s)`] : [] })) } else { printEnvelope({ ...failExecute(COMMAND, [`${report.errors} error(s) detected`]), report }) } process.exit(report.errors > 0 ? 1 : 0)