scripts/validate-review-actions.mjs
scripts/validate-review-actions.mjsBrowse 8 files
784 tokens
2,994 bytes
Token encoding: o200k_base
Snapshot fac8604
← Back to SKILL.md
1#!/usr/bin/env node2 3import { realpathSync } from 'node:fs';4import { readFile } from 'node:fs/promises';5import { resolve } from 'node:path';6import { fileURLToPath } from 'node:url';7 8import { assertReviewActionsV1, REVIEW_ACTIONS_VERSION } from './review-artifacts.mjs';9 10const EXIT_SUCCESS = 0;11const EXIT_OPERATIONAL = 1;12const EXIT_CLI = 2;13 14function parseCliArgs(argv) {15 const args = argv.slice(2);16 const result = { inPath: null, requireFinal: false, help: false };17 if (args.includes('--help')) {18 result.help = true;19 return result;20 }21 22 for (let index = 0; index < args.length; index += 1) {23 const arg = args[index];24 if (arg === '--require-final') {25 result.requireFinal = true;26 continue;27 }28 if (arg !== '--in') {29 throw { code: EXIT_CLI, message: `error: unknown flag "${arg}"` };30 }31 index += 1;32 if (index >= args.length) {33 throw { code: EXIT_CLI, message: 'error: --in requires a value' };34 }35 result.inPath = args[index];36 }37 if (!result.inPath) {38 throw { code: EXIT_CLI, message: 'error: --in is required' };39 }40 if (!result.inPath.endsWith('.json')) {41 throw { code: EXIT_CLI, message: 'error: --in file path must end with .json' };42 }43 return result;44}45 46function getHelpText() {47 return [48 'Usage:',49 ' validate-review-actions.mjs --in <review-actions.json> [--require-final]',50 '',51 'Purpose:',52 ` Validate canonical review-actions.json schema (v${REVIEW_ACTIONS_VERSION}).`,53 ' --require-final fails if any action.decision is triage_pending.',54 ].join('\n');55}56 57function assertNoTriagePending(payload) {58 const pending = payload.actions.filter((action) => action?.decision === 'triage_pending');59 if (pending.length === 0) {60 return;61 }62 const ids = pending.map((action) => action.actionId).join(', ');63 throw new Error(64 `review-actions contains triage_pending decisions (${pending.length}): ${ids}. Complete triage before finishing this phase.`,65 );66}67 68async function main() {69 const args = parseCliArgs(process.argv);70 if (args.help) {71 process.stdout.write(`${getHelpText()}\n`);72 process.exit(EXIT_SUCCESS);73 }74 75 const raw = await readFile(args.inPath, 'utf8');76 const parsed = JSON.parse(raw);77 assertReviewActionsV1(parsed);78 if (args.requireFinal) {79 assertNoTriagePending(parsed);80 }81 process.stdout.write(`ok: ${args.inPath}\n`);82}83 84const isMain = (() => {85 try {86 const invokedScriptPath = process.argv[1] ? realpathSync(resolve(process.argv[1])) : null;87 const currentModulePath = realpathSync(fileURLToPath(import.meta.url));88 return invokedScriptPath !== null && invokedScriptPath === currentModulePath;89 } catch {90 return false;91 }92})();93 94if (isMain) {95 main().catch((error) => {96 const code = typeof error?.code === 'number' ? error.code : EXIT_OPERATIONAL;97 const message = error?.message ? String(error.message) : String(error);98 process.stderr.write(`${message}\n`);99 process.exit(code);100 });101}102