#!/usr/bin/env node // // audit-dev-actions-alignment / index.ts — Entry point. // // Detects and reports drift between three sources of an action's URL: // - pagespec files at .smartstack/ba///pagespecs/..md // - controller files at /src/.Api/Controllers//sController.cs // - service files at /src/features///services/Service.ts // // Usage (read-only diagnostic — safe to run on any project): // // npx --prefer-offline tsx skills/development/audit-dev-frontend/cli/audit-dev-actions-alignment/index.ts \ // --project-path "" \ // --backend-path "" \ // --module-path ".smartstack/ba/APP/MODULE" \ // --mode report-only // // Exit code: 0 when no `err` findings, 1 otherwise. Always writes // `/_audit/actions-alignment.md` (unless --no-write-report). import { Command } from 'commander' import path from 'node:path' import { mkdir, writeFile } from 'node:fs/promises' import { validateArgs } from './validate.js' import { audit, reportPath } from './audit.js' import { apply } from './apply.js' import type { ActionAlignmentOutput } from './types.js' const program = new Command() program .name('audit-dev-actions-alignment') .description('Detect drift between pagespec / controller / service.ts custom-action URLs') .requiredOption('--project-path ', 'Absolute path to the frontend root (src/features/**)') .requiredOption('--backend-path ', 'Absolute path to the .NET backend root (src/**/*Controller.cs)') .option('--module-path ', 'Absolute path to .smartstack/ba// (otherwise auto-discover under project-path)') .option('--mode ', 'report-only | apply', 'report-only') .option('--source-of-truth ', 'pagespec | controller (used by --mode apply)', 'pagespec') .option('--no-write-report', 'Skip writing _audit/actions-alignment.md (stdout only)') .parse(process.argv) const opts = program.opts() const raw = { projectPath: opts.projectPath as string, backendPath: opts.backendPath as string, modulePath: opts.modulePath as string | undefined, mode: opts.mode as string, sourceOfTruth: opts.sourceOfTruth as string, writeReport: opts.writeReport !== false, } const validation = validateArgs(raw) if (!validation.valid || !validation.args) { const output: ActionAlignmentOutput = { success: false, command: 'audit-dev-actions-alignment', data: { mode: (raw.mode as 'report-only' | 'apply') ?? 'report-only', sourceOfTruth: (raw.sourceOfTruth as 'pagespec' | 'controller') ?? 'pagespec', moduleDir: raw.modulePath ?? null, }, report: null, filesModified: [], errors: validation.errors, warnings: [], nextSteps: ['Fix the CLI arguments and retry.'], } console.log(JSON.stringify(output, null, 2)) process.exit(1) } const args = validation.args async function main(): Promise { try { const result = args.mode === 'apply' ? await apply(args) : { report: await audit(args), filesModified: [], notes: [] as string[] } if (args.writeReport) { const dest = reportPath(args.projectPath) await mkdir(path.dirname(dest), { recursive: true }) await writeFile(dest, result.report.markdown, 'utf8') } const success = result.report.counts.err === 0 const output: ActionAlignmentOutput = { success, command: 'audit-dev-actions-alignment', data: { mode: args.mode, sourceOfTruth: args.sourceOfTruth, moduleDir: args.modulePath ?? null, }, report: result.report, filesModified: result.filesModified, errors: [], warnings: [], nextSteps: result.notes, } console.log(JSON.stringify(output, null, 2)) process.exit(success ? 0 : 1) } catch (err) { const output: ActionAlignmentOutput = { success: false, command: 'audit-dev-actions-alignment', data: { mode: args.mode, sourceOfTruth: args.sourceOfTruth, moduleDir: args.modulePath ?? null, }, report: null, filesModified: [], errors: [err instanceof Error ? err.message : String(err)], warnings: [], nextSteps: ['Review the error and retry.'], } console.log(JSON.stringify(output, null, 2)) process.exit(1) } } void main()