#!/usr/bin/env node /** * CLI for the §B/§C1 mechanical binding-gate. * * node --import tsx src/cli.ts [--json] * * Exit code: 0 if the gate passes (no BLOCK violations — handoff permitted), * 1 if BLOCKED, 2 on usage error. WARN violations never fail the gate; they are * surfaced for KTG's judgment. The text output is the "kvittering" the writing * contract asks for when a draft is delivered. */ import { runGateFile } from "./gate.js"; import type { GateResult, Violation } from "./types.js"; function printViolation(v: Violation): void { console.log(` [${v.ref}] ${v.ruleId}`); if (v.observed !== undefined) { const t = v.threshold !== undefined ? ` (terskel: ${v.threshold})` : ""; console.log(` observert: ${v.observed}${t}`); } console.log(` → ${v.message}`); const shown = v.matches.slice(0, 5); for (const m of shown) console.log(` · L${m.line}: ${m.excerpt}`); if (v.matches.length > shown.length) { console.log(` · … +${v.matches.length - shown.length} til`); } } function report(r: GateResult): void { console.log("§B/§C1 regelsjekk-gate"); console.log(`Utkast: ${r.draftPath ?? "(stdin)"} · ${r.wordCount} ord\n`); if (r.blocks.length) { console.log(`BLOCK (${r.blocks.length}) — må fikses før handoff:`); r.blocks.forEach(printViolation); console.log(""); } if (r.warns.length) { console.log(`WARN (${r.warns.length}) — krever din dom (blokkerer ikke):`); r.warns.forEach(printViolation); console.log(""); } if (r.passed) { console.log(`✓ Gate passert — 0 block, ${r.warns.length} warn til vurdering.`); } else { console.log( `✗ BLOKKERT — ${r.blocks.length} block, ${r.warns.length} warn. Fiks block-ene og kjør på nytt.`, ); } } function main(): void { const args = process.argv.slice(2); const jsonOut = args.includes("--json"); const path = args.find((a) => !a.startsWith("--")); if (!path) { console.error("Bruk: contract-gate [--json]"); process.exit(2); } const result = runGateFile(path); if (jsonOut) console.log(JSON.stringify(result, null, 2)); else report(result); process.exit(result.passed ? 0 : 1); } main();