@getarbiter/cli 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (989) hide show
  1. package/CHANGELOG.md +713 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +31 -0
  4. package/PRIVACY.md +111 -0
  5. package/README.md +237 -0
  6. package/THIRD_PARTY_LICENSES.md +472 -0
  7. package/dist/.src-manifest.json +1 -0
  8. package/dist/capabilities/host-probe.js +3 -0
  9. package/dist/cli.d.ts +2 -0
  10. package/dist/cli.js +87 -0
  11. package/dist/commands/adopt-policy.js +3 -0
  12. package/dist/commands/configure-interactive.js +3 -0
  13. package/dist/commands/configure.js +5 -0
  14. package/dist/commands/cross-model-review.js +3 -0
  15. package/dist/commands/diff.js +20 -0
  16. package/dist/commands/doc-set.js +3 -0
  17. package/dist/commands/doctor/clean.js +2 -0
  18. package/dist/commands/doctor/fail-open-census.js +12 -0
  19. package/dist/commands/doctor/health.js +11 -0
  20. package/dist/commands/doctor/prove-gates.js +13 -0
  21. package/dist/commands/doctor/recover-lock.js +12 -0
  22. package/dist/commands/doctor/repair-state.js +6 -0
  23. package/dist/commands/doctor/tool-pin-extract.js +4 -0
  24. package/dist/commands/doctor/tool-pins.js +9 -0
  25. package/dist/commands/doctor-interactive.js +2 -0
  26. package/dist/commands/doctor.js +2 -0
  27. package/dist/commands/explain.js +19 -0
  28. package/dist/commands/gate-exec.js +4 -0
  29. package/dist/commands/gold-audit.js +14 -0
  30. package/dist/commands/graph.js +5 -0
  31. package/dist/commands/init/build-arbiter-config.js +1 -0
  32. package/dist/commands/init/generate.js +42 -0
  33. package/dist/commands/init/github-setup.js +5 -0
  34. package/dist/commands/init/maturity-gates.js +10 -0
  35. package/dist/commands/init/resolve-config.js +3 -0
  36. package/dist/commands/init/toolchain.js +10 -0
  37. package/dist/commands/init/types.js +1 -0
  38. package/dist/commands/init.js +17 -0
  39. package/dist/commands/method-interactive.js +4 -0
  40. package/dist/commands/method.js +11 -0
  41. package/dist/commands/obsidian.js +2 -0
  42. package/dist/commands/plugin.js +8 -0
  43. package/dist/commands/pr-merged.js +2 -0
  44. package/dist/commands/retire-policy.js +3 -0
  45. package/dist/commands/review-diff.js +3 -0
  46. package/dist/commands/settings.js +7 -0
  47. package/dist/commands/ship-config.js +2 -0
  48. package/dist/commands/ship-profile.js +2 -0
  49. package/dist/commands/ship-review.js +1 -0
  50. package/dist/commands/ship-tier.js +2 -0
  51. package/dist/commands/ship-train.js +2 -0
  52. package/dist/commands/task-mark.js +2 -0
  53. package/dist/commands/task-note.js +3 -0
  54. package/dist/commands/task-record-red.js +2 -0
  55. package/dist/commands/task-record-tech-debt.js +4 -0
  56. package/dist/commands/task-ship.js +3 -0
  57. package/dist/commands/task-state.js +6 -0
  58. package/dist/commands/task.js +59 -0
  59. package/dist/commands/update.js +60 -0
  60. package/dist/commands/upgrade-level-interactive.js +2 -0
  61. package/dist/commands/upgrade-level.js +7 -0
  62. package/dist/commands/verify-plan.js +8 -0
  63. package/dist/commands/verify-tdd.js +2 -0
  64. package/dist/commands/verify.js +7 -0
  65. package/dist/commands/wizard/coherence.js +1 -0
  66. package/dist/commands/worktree-prune.js +4 -0
  67. package/dist/commands/worktree.js +39 -0
  68. package/dist/compatibility/cross-language-matrix.json +540 -0
  69. package/dist/compatibility/index.d.ts +4 -0
  70. package/dist/compatibility/index.js +2 -0
  71. package/dist/compatibility/matcher.js +1 -0
  72. package/dist/compatibility/matrix.json +27 -0
  73. package/dist/compatibility/parsers.d.ts +38 -0
  74. package/dist/compatibility/parsers.js +1 -0
  75. package/dist/compatibility/probe.d.ts +41 -0
  76. package/dist/compatibility/probe.js +4 -0
  77. package/dist/compatibility/report.js +2 -0
  78. package/dist/compatibility/schema.d.ts +80 -0
  79. package/dist/compatibility/schema.js +1 -0
  80. package/dist/compatibility/skills-matrix.json +98 -0
  81. package/dist/compatibility/skills-validator.d.ts +30 -0
  82. package/dist/compatibility/skills-validator.js +4 -0
  83. package/dist/compatibility/tooling-proposals.json +5 -0
  84. package/dist/config/arbiter-ignore.js +3 -0
  85. package/dist/config/collaboration-mode-defaults.js +1 -0
  86. package/dist/config/diff.js +1 -0
  87. package/dist/config/env-overrides.js +3 -0
  88. package/dist/config/env-registry.js +2 -0
  89. package/dist/config/levels.js +1 -0
  90. package/dist/config/migrations/index.js +2 -0
  91. package/dist/config/migrations/v0-to-v1.js +2 -0
  92. package/dist/config/migrations/v1-to-v2.js +2 -0
  93. package/dist/config/migrations/v2-to-v3.js +2 -0
  94. package/dist/config/migrations/v3-to-v4.js +2 -0
  95. package/dist/config/override-resolver.js +2 -0
  96. package/dist/config/resolve-project-config.js +2 -0
  97. package/dist/config/resolve-project-name.js +2 -0
  98. package/dist/config/schema.d.ts +480 -0
  99. package/dist/config/schema.js +1 -0
  100. package/dist/config/test-pyramid-profiles.js +1 -0
  101. package/dist/config/thresholds.js +2 -0
  102. package/dist/conformance/dimensions.js +3 -0
  103. package/dist/conformance/engine.js +7 -0
  104. package/dist/conformance/gate-proofs.js +4 -0
  105. package/dist/conformance/shared.js +2 -0
  106. package/dist/detectors/axis.js +1 -0
  107. package/dist/detectors/build.js +2 -0
  108. package/dist/detectors/existing.js +2 -0
  109. package/dist/detectors/external-model.js +2 -0
  110. package/dist/detectors/framework.js +2 -0
  111. package/dist/detectors/git.js +11 -0
  112. package/dist/detectors/github.js +2 -0
  113. package/dist/detectors/lanes.js +5 -0
  114. package/dist/detectors/language-hooks.js +117 -0
  115. package/dist/detectors/language.js +2 -0
  116. package/dist/detectors/package-manager.js +2 -0
  117. package/dist/detectors/package.js +2 -0
  118. package/dist/detectors/scaffold-wiring.js +2 -0
  119. package/dist/detectors/workflow-collision.js +4 -0
  120. package/dist/evidence/gate-binding.js +4 -0
  121. package/dist/evidence/git-checks.js +3 -0
  122. package/dist/evidence/load.js +2 -0
  123. package/dist/evidence/provenance.js +2 -0
  124. package/dist/evidence/summary.js +2 -0
  125. package/dist/evidence/tdd-reexecute.js +2 -0
  126. package/dist/evidence/tdd.js +4 -0
  127. package/dist/experimental/index.js +2 -0
  128. package/dist/experimental/parse-argv.js +2 -0
  129. package/dist/experimental/registry.js +2 -0
  130. package/dist/generators/agents-claude.js +2 -0
  131. package/dist/generators/agents-md.js +2 -0
  132. package/dist/generators/anti-drift-validators.js +3 -0
  133. package/dist/generators/anti-proforma.js +2 -0
  134. package/dist/generators/api-e2e.js +2 -0
  135. package/dist/generators/api-middleware.js +2 -0
  136. package/dist/generators/archunit.js +2 -0
  137. package/dist/generators/audit-toolchain.js +2 -0
  138. package/dist/generators/auth.js +2 -0
  139. package/dist/generators/behavioral-tests.js +2 -0
  140. package/dist/generators/boundaries.js +2 -0
  141. package/dist/generators/check-all.js +2 -0
  142. package/dist/generators/check-stack-conformity.js +2 -0
  143. package/dist/generators/ci-five-lane.js +2 -0
  144. package/dist/generators/ci-tier.js +2 -0
  145. package/dist/generators/claude.js +3 -0
  146. package/dist/generators/codex-hooks.js +2 -0
  147. package/dist/generators/codex-known-limitations.js +2 -0
  148. package/dist/generators/codex.js +15 -0
  149. package/dist/generators/commit-footer.js +2 -0
  150. package/dist/generators/compliance-menu.js +2 -0
  151. package/dist/generators/compliance.js +2 -0
  152. package/dist/generators/conformance.js +2 -0
  153. package/dist/generators/contract-testing.js +2 -0
  154. package/dist/generators/coverage.js +2 -0
  155. package/dist/generators/debt-gates.js +24 -0
  156. package/dist/generators/debt-ratchet.js +2 -0
  157. package/dist/generators/derived-class.js +2 -0
  158. package/dist/generators/doc-set.js +4 -0
  159. package/dist/generators/docs.js +2 -0
  160. package/dist/generators/duplication.js +2 -0
  161. package/dist/generators/e2e-constitution.js +2 -0
  162. package/dist/generators/env-template.js +2 -0
  163. package/dist/generators/evidence-retention.js +2 -0
  164. package/dist/generators/feature-matrix.js +2 -0
  165. package/dist/generators/frontend-governance.js +2 -0
  166. package/dist/generators/frontend-quality.js +2 -0
  167. package/dist/generators/gap.js +2 -0
  168. package/dist/generators/gdpr.js +2 -0
  169. package/dist/generators/githooks.js +2 -0
  170. package/dist/generators/github-setup.js +2 -0
  171. package/dist/generators/github.js +2 -0
  172. package/dist/generators/gitignore.js +2 -0
  173. package/dist/generators/global-invariants.js +3 -0
  174. package/dist/generators/go-boundaries.js +2 -0
  175. package/dist/generators/gold-kit.js +2 -0
  176. package/dist/generators/infra.js +2 -0
  177. package/dist/generators/integration-testing.js +7 -0
  178. package/dist/generators/iso27001.js +2 -0
  179. package/dist/generators/iso9001.js +2 -0
  180. package/dist/generators/labels.js +2 -0
  181. package/dist/generators/local-wrapper.js +2 -0
  182. package/dist/generators/modulith.js +2 -0
  183. package/dist/generators/mutation.js +2 -0
  184. package/dist/generators/observability.js +2 -0
  185. package/dist/generators/operations.js +2 -0
  186. package/dist/generators/perf-k6.js +2 -0
  187. package/dist/generators/pharma.js +2 -0
  188. package/dist/generators/playwright-python.js +2 -0
  189. package/dist/generators/playwright-ts.js +2 -0
  190. package/dist/generators/pr-tooling.js +2 -0
  191. package/dist/generators/python-boundaries.js +2 -0
  192. package/dist/generators/quality.js +2 -0
  193. package/dist/generators/registry.js +2 -0
  194. package/dist/generators/regulated.js +2 -0
  195. package/dist/generators/resilience.js +2 -0
  196. package/dist/generators/risk-register.js +2 -0
  197. package/dist/generators/root.js +2 -0
  198. package/dist/generators/rust-boundaries.js +2 -0
  199. package/dist/generators/safety-class.js +2 -0
  200. package/dist/generators/security.js +2 -0
  201. package/dist/generators/seed.js +2 -0
  202. package/dist/generators/self-validation.js +2 -0
  203. package/dist/generators/ship-driver.js +2 -0
  204. package/dist/generators/skill-names.json +17 -0
  205. package/dist/generators/skills.js +2 -0
  206. package/dist/generators/smoke-journeys.js +2 -0
  207. package/dist/generators/solo-exception.js +2 -0
  208. package/dist/generators/ssot.js +2 -0
  209. package/dist/generators/stride-enforcement.js +2 -0
  210. package/dist/generators/suppressions.js +2 -0
  211. package/dist/generators/test-pyramid-manifest.js +3 -0
  212. package/dist/generators/test-taxonomy.js +2 -0
  213. package/dist/generators/wiki.js +2 -0
  214. package/dist/github/branch-protection.js +2 -0
  215. package/dist/github/classify-gh-error.js +2 -0
  216. package/dist/github/labels.js +2 -0
  217. package/dist/github/project-board.js +2 -0
  218. package/dist/graph/builders/adr.js +4 -0
  219. package/dist/graph/builders/ast.js +3 -0
  220. package/dist/graph/builders/canon.js +2 -0
  221. package/dist/graph/builders/evidence.js +2 -0
  222. package/dist/graph/builders/inv.js +1 -0
  223. package/dist/graph/builders/req.js +3 -0
  224. package/dist/graph/builders/test-nodes.js +3 -0
  225. package/dist/graph/builders/utils.js +2 -0
  226. package/dist/graph/load.js +2 -0
  227. package/dist/graph/model.js +2 -0
  228. package/dist/graph/query.js +3 -0
  229. package/dist/graph/store.js +1 -0
  230. package/dist/i18n/en.json +293 -0
  231. package/dist/i18n/index.js +4 -0
  232. package/dist/i18n/types.js +2 -0
  233. package/dist/integrations/companions.js +2 -0
  234. package/dist/integrations/external-review.js +10 -0
  235. package/dist/integrations/plugin-schema.js +2 -0
  236. package/dist/integrations/skill-detector.js +7 -0
  237. package/dist/integrations/skills-matrix.js +2 -0
  238. package/dist/integrations/types.d.ts +23 -0
  239. package/dist/integrations/types.js +2 -0
  240. package/dist/internal/cli-deprecation-registry.js +2 -0
  241. package/dist/internal/deprecate.js +3 -0
  242. package/dist/internal/experimental-warn.js +3 -0
  243. package/dist/invariants/catalog.d.ts +2 -0
  244. package/dist/invariants/catalog.js +1 -0
  245. package/dist/invariants/filter.d.ts +28 -0
  246. package/dist/invariants/filter.js +1 -0
  247. package/dist/invariants/index.d.ts +4 -0
  248. package/dist/invariants/index.js +2 -0
  249. package/dist/invariants/tiers.js +1 -0
  250. package/dist/invariants/types.d.ts +1 -0
  251. package/dist/invariants/types.js +1 -0
  252. package/dist/kit/applicability.js +3 -0
  253. package/dist/kit/audit-report.js +3 -0
  254. package/dist/kit/brownfield-detect.js +4 -0
  255. package/dist/kit/canonical-mapping.json +1 -0
  256. package/dist/kit/catalog.js +2 -0
  257. package/dist/kit/catalog.json +1 -0
  258. package/dist/kit/derived.json +1 -0
  259. package/dist/kit/emit-issues.js +8 -0
  260. package/dist/kit/measure.js +5 -0
  261. package/dist/kit/redaction.js +3 -0
  262. package/dist/kit/schema.js +2 -0
  263. package/dist/kit/taxonomy.js +2 -0
  264. package/dist/kit/thresholds.d.ts +12 -0
  265. package/dist/kit/thresholds.js +2 -0
  266. package/dist/kit/wave-engine.js +1 -0
  267. package/dist/recipes/loader.js +6 -0
  268. package/dist/recipes/schema.js +2 -0
  269. package/dist/risk/classifier.js +2 -0
  270. package/dist/risk/sha-check.js +2 -0
  271. package/dist/state/backups.js +2 -0
  272. package/dist/state/envelope.js +2 -0
  273. package/dist/state/generated-manifest.js +3 -0
  274. package/dist/state/manifest-key.js +2 -0
  275. package/dist/state/migrations/index.js +1 -0
  276. package/dist/state/migrations/v0-to-v1.js +2 -0
  277. package/dist/state/retired-renders.js +2 -0
  278. package/dist/templates/.nvmrc.ejs +1 -0
  279. package/dist/templates/HANDOFF.template.md +45 -0
  280. package/dist/templates/__tests__/contract/error-shape.contract.test.ts.ejs +47 -0
  281. package/dist/templates/agents-md/AGENTS.md.ejs +517 -0
  282. package/dist/templates/api-e2e/README.md.ejs +33 -0
  283. package/dist/templates/api-e2e/go-httptest.go.ejs +43 -0
  284. package/dist/templates/api-e2e/java-restassured.java.ejs +28 -0
  285. package/dist/templates/api-e2e/kotlin-restassured.kt.ejs +30 -0
  286. package/dist/templates/api-e2e/manifest.json.ejs +8 -0
  287. package/dist/templates/api-e2e/postman.collection.json.ejs +30 -0
  288. package/dist/templates/api-e2e/python-httpx.py.ejs +17 -0
  289. package/dist/templates/api-e2e/run.sh.ejs +61 -0
  290. package/dist/templates/api-e2e/ts-supertest.test.ts.ejs +18 -0
  291. package/dist/templates/archunit/AntiCyclicTest.java.ejs +29 -0
  292. package/dist/templates/archunit/AntiProformaExempt.java.ejs +35 -0
  293. package/dist/templates/archunit/AntiProformaTest.java.ejs +104 -0
  294. package/dist/templates/archunit/ArchitectureTest.java.ejs +87 -0
  295. package/dist/templates/archunit/DependencyFlowTest.java.ejs +55 -0
  296. package/dist/templates/archunit/DomainPurityTest.java.ejs +47 -0
  297. package/dist/templates/archunit/NamingConventionsTest.java.ejs +54 -0
  298. package/dist/templates/archunit/NoH2ArchTest.java.ejs +30 -0
  299. package/dist/templates/archunit/NoMockMvcTest.java.ejs +71 -0
  300. package/dist/templates/archunit/PortsIndependenceTest.java.ejs +55 -0
  301. package/dist/templates/archunit/RestAssuredArchTest.java.ejs +32 -0
  302. package/dist/templates/archunit/RestAssuredBaseIT.java.ejs +66 -0
  303. package/dist/templates/archunit/TestCoverageArchTest.java.ejs +29 -0
  304. package/dist/templates/archunit/arch-test-deps-maven.md.ejs +42 -0
  305. package/dist/templates/archunit/arch-test-deps.gradle.ejs +14 -0
  306. package/dist/templates/audit/gdpr/check-gdpr-controls.mjs.ejs +102 -0
  307. package/dist/templates/audit/gdpr/controls-to-gates.md.ejs +42 -0
  308. package/dist/templates/audit/gdpr/data-flow-map.md.ejs +30 -0
  309. package/dist/templates/audit/gdpr/dpia.md.ejs +45 -0
  310. package/dist/templates/audit/generic/audit-gate-rules.md.ejs +24 -0
  311. package/dist/templates/audit/generic/audit-trail-policy.md.ejs +38 -0
  312. package/dist/templates/audit/iso27001/iso27001-controls-gate-map.md.ejs +60 -0
  313. package/dist/templates/auth/setup.md.ejs +661 -0
  314. package/dist/templates/behavioral-tests/ExampleBehavioralTest.java.ejs +67 -0
  315. package/dist/templates/behavioral-tests/TESTING_POLICY.md.ejs +116 -0
  316. package/dist/templates/behavioral-tests/bdd/ExampleBddIT.java.ejs +19 -0
  317. package/dist/templates/behavioral-tests/bdd/ExampleSteps.java.ejs +61 -0
  318. package/dist/templates/behavioral-tests/bdd/example.feature.ejs +14 -0
  319. package/dist/templates/behavioral-tests/bdd/example.steps.ts.ejs +37 -0
  320. package/dist/templates/behavioral-tests/bdd/example_bdd_test.rs.ejs +64 -0
  321. package/dist/templates/behavioral-tests/bdd/example_test.go.ejs +80 -0
  322. package/dist/templates/behavioral-tests/bdd/test_example_bdd.py.ejs +47 -0
  323. package/dist/templates/behavioral-tests/eslint-playwright.json.ejs +10 -0
  324. package/dist/templates/behavioral-tests/eslint.config.playwright.mjs.ejs +29 -0
  325. package/dist/templates/behavioral-tests/example.behavioral.test.ts.ejs +47 -0
  326. package/dist/templates/behavioral-tests/example_behavioral_test.go.ejs +51 -0
  327. package/dist/templates/behavioral-tests/example_behavioral_test.rs.ejs +47 -0
  328. package/dist/templates/behavioral-tests/test_example_behavioral.py.ejs +40 -0
  329. package/dist/templates/boundaries/.eslintrc-boundaries.cjs.ejs +81 -0
  330. package/dist/templates/boundaries/.eslintrc-frontend-spa.cjs.ejs +79 -0
  331. package/dist/templates/boundaries/.importlinter.ejs +48 -0
  332. package/dist/templates/boundaries/check-boundaries-go.mjs.ejs +94 -0
  333. package/dist/templates/boundaries/check-boundaries-python.mjs.ejs +118 -0
  334. package/dist/templates/boundaries/check-boundaries-rust.mjs.ejs +111 -0
  335. package/dist/templates/boundaries/check-boundaries.mjs.ejs +34 -0
  336. package/dist/templates/boundaries/clippy.toml.ejs +50 -0
  337. package/dist/templates/boundaries/deny.toml.ejs +56 -0
  338. package/dist/templates/boundaries/eslint.config.boundaries.mjs.ejs +112 -0
  339. package/dist/templates/boundaries/eslint.config.frontend-spa.mjs.ejs +93 -0
  340. package/dist/templates/boundaries/golangci-boundaries.yml.ejs +70 -0
  341. package/dist/templates/boundaries/ruff-boundaries.toml.ejs +32 -0
  342. package/dist/templates/claude/CLAUDE.md.ejs +78 -0
  343. package/dist/templates/claude/agents/agent-write-classes.json +30 -0
  344. package/dist/templates/claude/agents/bridge-reviewer.md.ejs +68 -0
  345. package/dist/templates/claude/agents/codebase-scanner.md.ejs +69 -0
  346. package/dist/templates/claude/agents/context-checker.md.ejs +73 -0
  347. package/dist/templates/claude/agents/red-team.md.ejs +134 -0
  348. package/dist/templates/claude/commands/close-gold-gap.md.ejs +43 -0
  349. package/dist/templates/claude/commands/drain.md.ejs +81 -0
  350. package/dist/templates/claude/commands/gold-audit.md.ejs +43 -0
  351. package/dist/templates/claude/commands/impact.md.ejs +39 -0
  352. package/dist/templates/claude/commands/levelup.md.ejs +61 -0
  353. package/dist/templates/claude/commands/ship.md.ejs +902 -0
  354. package/dist/templates/claude/commands/tabletop.md.ejs +46 -0
  355. package/dist/templates/claude/commands/task.md.ejs +87 -0
  356. package/dist/templates/claude/commands/wt-close.md.ejs +58 -0
  357. package/dist/templates/claude/commands/wt-list.md.ejs +48 -0
  358. package/dist/templates/claude/commands/wt-open.md.ejs +61 -0
  359. package/dist/templates/claude/commands/wt-prune.md.ejs +38 -0
  360. package/dist/templates/claude/hooks/check-circular-deps.mjs.ejs +56 -0
  361. package/dist/templates/claude/hooks/check-no-orphan-todo.mjs.ejs +45 -0
  362. package/dist/templates/claude/hooks/check-no-pii.mjs.ejs +121 -0
  363. package/dist/templates/claude/hooks/check-no-placeholders.mjs.ejs +71 -0
  364. package/dist/templates/claude/hooks/check-no-skipped-tests.mjs +80 -0
  365. package/dist/templates/claude/hooks/check-no-unused-exports.mjs +62 -0
  366. package/dist/templates/claude/hooks/closer-mode-guard.mjs.ejs +102 -0
  367. package/dist/templates/claude/hooks/debug-state-on-failure.mjs.ejs +98 -0
  368. package/dist/templates/claude/hooks/enforce-gate-before-pr.mjs +142 -0
  369. package/dist/templates/claude/hooks/enforce-read-only.mjs +28 -0
  370. package/dist/templates/claude/hooks/exitplanmode-banner.mjs.ejs +43 -0
  371. package/dist/templates/claude/hooks/guard-done-evidence.mjs.ejs +85 -0
  372. package/dist/templates/claude/hooks/guard-task-completion.mjs.ejs +56 -0
  373. package/dist/templates/claude/hooks/hooks.mjs.ejs +127 -0
  374. package/dist/templates/claude/hooks/lib.mjs.ejs +523 -0
  375. package/dist/templates/claude/hooks/post-brainstorm-stop.mjs +48 -0
  376. package/dist/templates/claude/hooks/post-commit-check.mjs.ejs +65 -0
  377. package/dist/templates/claude/hooks/post-commit-checklists/go/backend.ejs +4 -0
  378. package/dist/templates/claude/hooks/post-commit-checklists/go/docs.ejs +4 -0
  379. package/dist/templates/claude/hooks/post-commit-checklists/go/frontend.ejs +4 -0
  380. package/dist/templates/claude/hooks/post-commit-checklists/java/backend.ejs +4 -0
  381. package/dist/templates/claude/hooks/post-commit-checklists/java/docs.ejs +4 -0
  382. package/dist/templates/claude/hooks/post-commit-checklists/java/frontend.ejs +4 -0
  383. package/dist/templates/claude/hooks/post-commit-checklists/python/backend.ejs +4 -0
  384. package/dist/templates/claude/hooks/post-commit-checklists/python/docs.ejs +4 -0
  385. package/dist/templates/claude/hooks/post-commit-checklists/python/frontend.ejs +4 -0
  386. package/dist/templates/claude/hooks/post-commit-checklists/rust/backend.ejs +5 -0
  387. package/dist/templates/claude/hooks/post-commit-checklists/rust/docs.ejs +4 -0
  388. package/dist/templates/claude/hooks/post-commit-checklists/rust/frontend.ejs +4 -0
  389. package/dist/templates/claude/hooks/post-commit-checklists/ts/backend.ejs +4 -0
  390. package/dist/templates/claude/hooks/post-commit-checklists/ts/docs.ejs +4 -0
  391. package/dist/templates/claude/hooks/post-commit-checklists/ts/frontend.ejs +5 -0
  392. package/dist/templates/claude/hooks/post-edit-dispatch.mjs.ejs +73 -0
  393. package/dist/templates/claude/hooks/post-subagent-release.mjs +80 -0
  394. package/dist/templates/claude/hooks/pre-compact.mjs.ejs +79 -0
  395. package/dist/templates/claude/hooks/pre-edit-plan-anchor.mjs.ejs +225 -0
  396. package/dist/templates/claude/hooks/pre-edit-ssot-guard.mjs +204 -0
  397. package/dist/templates/claude/hooks/pre-spawn-worktree-guard.mjs +129 -0
  398. package/dist/templates/claude/hooks/skill-forced-eval.mjs.ejs +281 -0
  399. package/dist/templates/claude/hooks/stop-dangerous.mjs +86 -0
  400. package/dist/templates/claude/hooks/stop-evidence-guard.mjs.ejs +245 -0
  401. package/dist/templates/claude/hooks/stop-finding-loss.mjs.ejs +168 -0
  402. package/dist/templates/claude/hooks/wiki-on-commit.mjs.ejs +38 -0
  403. package/dist/templates/claude/knowledge-map.json.ejs +26 -0
  404. package/dist/templates/claude/rules/05-agent-lifecycle.md +7 -0
  405. package/dist/templates/claude/rules/25-todo-folder-policy.md +5 -0
  406. package/dist/templates/claude/rules/40-context-economy.md +32 -0
  407. package/dist/templates/claude/rules/45-mcp-fallback.md +25 -0
  408. package/dist/templates/claude/rules/50-batch-execution.md +75 -0
  409. package/dist/templates/claude/rules/55-brainstorm-terminal-state.md +44 -0
  410. package/dist/templates/claude/rules/60-incidental-capture.md +43 -0
  411. package/dist/templates/claude/rules/75-impact-vault-reading.md +43 -0
  412. package/dist/templates/claude/rules/90-exec-protocol.md.ejs +40 -0
  413. package/dist/templates/claude/rules/95-closer-mode.md +45 -0
  414. package/dist/templates/claude/settings.json.ejs +165 -0
  415. package/dist/templates/claude/skills/architect-review/SKILL.md.ejs +82 -0
  416. package/dist/templates/claude/skills/brainstorming/SKILL.md.ejs +86 -0
  417. package/dist/templates/claude/skills/clean-code/SKILL.md.ejs +39 -0
  418. package/dist/templates/claude/skills/close-gold-gap/SKILL.md.ejs +58 -0
  419. package/dist/templates/claude/skills/codebase-audit/SKILL.md.ejs +88 -0
  420. package/dist/templates/claude/skills/configure/SKILL.md.ejs +72 -0
  421. package/dist/templates/claude/skills/epic-decompose/SKILL.md.ejs +86 -0
  422. package/dist/templates/claude/skills/gold-audit/SKILL.md.ejs +91 -0
  423. package/dist/templates/claude/skills/impact/SKILL.md.ejs +104 -0
  424. package/dist/templates/claude/skills/levelup/SKILL.md.ejs +146 -0
  425. package/dist/templates/claude/skills/tabletop/SKILL.md.ejs +118 -0
  426. package/dist/templates/claude/skills/tdd/SKILL.md.ejs +157 -0
  427. package/dist/templates/claude/skills/understand-code/SKILL.md.ejs +93 -0
  428. package/dist/templates/claude/skills/verification/SKILL.md.ejs +68 -0
  429. package/dist/templates/claude/skills/wave-drain/SKILL.md.ejs +491 -0
  430. package/dist/templates/codex/CODEX.md.ejs +140 -0
  431. package/dist/templates/codex/codex-adapter.mjs +132 -0
  432. package/dist/templates/codex/config.toml.ejs +189 -0
  433. package/dist/templates/compliance/compliance-mapping.md.ejs +124 -0
  434. package/dist/templates/compliance/compliance-menu.md.ejs +89 -0
  435. package/dist/templates/contract-testing/CONTRACTS_POLICY.md.ejs +30 -0
  436. package/dist/templates/contract-testing/api-snapshots/config-keys-baseline.json.ejs +1 -0
  437. package/dist/templates/contract-testing/api-snapshots/config-response-baseline.json.ejs +1 -0
  438. package/dist/templates/contract-testing/api-snapshots/enum-values-baseline.json.ejs +1 -0
  439. package/dist/templates/contract-testing/api-snapshots/error-shape-baseline.json.ejs +6 -0
  440. package/dist/templates/contract-testing/api-snapshots/openapi-baseline.json.ejs +12 -0
  441. package/dist/templates/contract-testing/api-snapshots/openapi-content-types-baseline.json.ejs +3 -0
  442. package/dist/templates/contract-testing/api-snapshots/openapi-paths-baseline.json.ejs +1 -0
  443. package/dist/templates/contract-testing/api-snapshots/openapi-required-fields-baseline.json.ejs +1 -0
  444. package/dist/templates/contract-testing/api-snapshots/openapi-response-status-baseline.json.ejs +3 -0
  445. package/dist/templates/contract-testing/api-snapshots/test-snapshot.json.ejs +4 -0
  446. package/dist/templates/contract-testing/env/.env.pact.ejs +4 -0
  447. package/dist/templates/contract-testing/graphql/GraphqlSchemaTest.java.ejs +29 -0
  448. package/dist/templates/contract-testing/graphql/graphql-inspector.test.ts.ejs +20 -0
  449. package/dist/templates/contract-testing/graphql/graphql_schema_test.go.ejs +27 -0
  450. package/dist/templates/contract-testing/graphql/graphql_schema_test.rs.ejs +27 -0
  451. package/dist/templates/contract-testing/graphql/test_graphql_schema.py.ejs +21 -0
  452. package/dist/templates/contract-testing/grpc/GrpcContractTest.java.ejs +23 -0
  453. package/dist/templates/contract-testing/grpc/buf-breaking.yml.ejs +6 -0
  454. package/dist/templates/contract-testing/grpc/buf.yaml.ejs +11 -0
  455. package/dist/templates/contract-testing/grpc/grpc-contract.test.ts.ejs +16 -0
  456. package/dist/templates/contract-testing/grpc/grpc_contract_test.go.ejs +22 -0
  457. package/dist/templates/contract-testing/grpc/grpc_contract_test.rs.ejs +22 -0
  458. package/dist/templates/contract-testing/grpc/test_grpc_contract.py.ejs +18 -0
  459. package/dist/templates/contract-testing/message-queue/SchemaRegistryCheckIT.java.ejs +44 -0
  460. package/dist/templates/contract-testing/message-queue/schema-registry-check.ts.ejs +42 -0
  461. package/dist/templates/contract-testing/message-queue/schema_registry_test.go.ejs +71 -0
  462. package/dist/templates/contract-testing/message-queue/schema_registry_test.rs.ejs +61 -0
  463. package/dist/templates/contract-testing/message-queue/test_schema_registry.py.ejs +31 -0
  464. package/dist/templates/contract-testing/pact-samples/assignment-response.json.ejs +7 -0
  465. package/dist/templates/contract-testing/pact-samples/availability-response.json.ejs +13 -0
  466. package/dist/templates/contract-testing/pact-samples/availability-rule-response.json.ejs +8 -0
  467. package/dist/templates/contract-testing/pact-samples/capacity-response.json.ejs +11 -0
  468. package/dist/templates/contract-testing/pact-samples/fully-booked-response.json.ejs +6 -0
  469. package/dist/templates/contract-testing/pact-samples/schedule-override-response.json.ejs +7 -0
  470. package/dist/templates/contract-testing/rest-owned/PactVerificationIT.java.ejs +31 -0
  471. package/dist/templates/contract-testing/rest-owned/pact-consumer.test.ts.ejs +36 -0
  472. package/dist/templates/contract-testing/rest-owned/pact-deps.gradle.ejs +15 -0
  473. package/dist/templates/contract-testing/rest-owned/pact_consumer_test.go.ejs +48 -0
  474. package/dist/templates/contract-testing/rest-owned/pact_consumer_test.rs.ejs +23 -0
  475. package/dist/templates/contract-testing/rest-owned/test_pact_consumer.py.ejs +35 -0
  476. package/dist/templates/contract-testing/rest-public/OpenApiDiffIT.java.ejs +44 -0
  477. package/dist/templates/contract-testing/rest-public/export-openapi-java.gradle.ejs +13 -0
  478. package/dist/templates/contract-testing/rest-public/export-openapi.mjs.ejs +21 -0
  479. package/dist/templates/contract-testing/rest-public/export_openapi.go.ejs +30 -0
  480. package/dist/templates/contract-testing/rest-public/export_openapi.py.ejs +27 -0
  481. package/dist/templates/contract-testing/rest-public/export_openapi.rs.ejs +32 -0
  482. package/dist/templates/contract-testing/rest-public/openapi-diff.ts.ejs +42 -0
  483. package/dist/templates/contract-testing/rest-public/openapi_diff_test.go.ejs +35 -0
  484. package/dist/templates/contract-testing/rest-public/openapi_diff_test.rs.ejs +36 -0
  485. package/dist/templates/contract-testing/rest-public/test_openapi_diff.py.ejs +32 -0
  486. package/dist/templates/coverage/.coveragerc.ejs +26 -0
  487. package/dist/templates/coverage/.tarpaulin.toml.ejs +30 -0
  488. package/dist/templates/coverage/Cargo.toml.profile.release.ejs +24 -0
  489. package/dist/templates/coverage/jacoco-maven-setup.md.ejs +72 -0
  490. package/dist/templates/coverage/jacoco.gradle.ejs +49 -0
  491. package/dist/templates/coverage/kover.gradle.ejs +35 -0
  492. package/dist/templates/coverage/vitest.browser.config.ts.ejs +47 -0
  493. package/dist/templates/coverage/vitest.config.ts.ejs +47 -0
  494. package/dist/templates/docs/CODING_STANDARDS.md.ejs +81 -0
  495. package/dist/templates/docs/FEATURE_MATRIX.md.ejs +84 -0
  496. package/dist/templates/docs/GAP.md.ejs +58 -0
  497. package/dist/templates/docs/ISO27001_ANNEX_A.md.ejs +144 -0
  498. package/dist/templates/docs/MASTER_TEST_PLAN.md.ejs +52 -0
  499. package/dist/templates/docs/POST_MERGE_REVIEW_TEMPLATE.md.ejs +63 -0
  500. package/dist/templates/docs/SECURE_CODING_CHECKLIST.md.ejs +61 -0
  501. package/dist/templates/docs/adr/ADR-000_template.md.ejs +28 -0
  502. package/dist/templates/docs/bugs/bug-analysis.md.ejs +47 -0
  503. package/dist/templates/docs/bugs/bug-report.md.ejs +51 -0
  504. package/dist/templates/docs/bugs/bug-verification.md.ejs +42 -0
  505. package/dist/templates/docs/runbooks/deployment.md.ejs +119 -0
  506. package/dist/templates/docs/runbooks/prod-checklist.md.ejs +90 -0
  507. package/dist/templates/docs/runbooks/rollback.md.ejs +124 -0
  508. package/dist/templates/docs/runbooks/troubleshooting.md.ejs +130 -0
  509. package/dist/templates/docs/skeletons/arc42-canvas.md.ejs +46 -0
  510. package/dist/templates/docs/skeletons/arc42-full.md.ejs +59 -0
  511. package/dist/templates/docs/skeletons/decision-registry.md.ejs +38 -0
  512. package/dist/templates/docs/skeletons/er-model.md.ejs +19 -0
  513. package/dist/templates/docs/skeletons/glossary.md.ejs +8 -0
  514. package/dist/templates/docs/skeletons/governance.md.ejs +13 -0
  515. package/dist/templates/docs/skeletons/slo.md.ejs +21 -0
  516. package/dist/templates/docs/skeletons/technical-debt.md.ejs +8 -0
  517. package/dist/templates/docs/skeletons/test-strategy.md.ejs +14 -0
  518. package/dist/templates/docs/skeletons/threat-model-4q.md.ejs +20 -0
  519. package/dist/templates/docs/skeletons/threat-model-stride.md.ejs +22 -0
  520. package/dist/templates/docs/specs/atomic-task-list.md.ejs +57 -0
  521. package/dist/templates/docs/steering/product.md.ejs +44 -0
  522. package/dist/templates/docs/steering/structure.md.ejs +49 -0
  523. package/dist/templates/docs/steering/tech.md.ejs +53 -0
  524. package/dist/templates/documentation/cli-catalog.md.ejs +21 -0
  525. package/dist/templates/e2e/E2E_CONSTITUTION.md.ejs +77 -0
  526. package/dist/templates/e2e/playwright-python/conftest.py.ejs +22 -0
  527. package/dist/templates/e2e/playwright-python/run_axe.py.ejs +99 -0
  528. package/dist/templates/e2e/playwright-python/test_a11y.py.ejs +38 -0
  529. package/dist/templates/e2e/playwright-python/test_smoke.py.ejs +28 -0
  530. package/dist/templates/e2e/playwright-ts/a11y.spec.ts.ejs +20 -0
  531. package/dist/templates/e2e/playwright-ts/render-smoke.spec.ts.ejs +90 -0
  532. package/dist/templates/e2e/playwright-ts/run-axe.ts.ejs +74 -0
  533. package/dist/templates/e2e/smoke-journeys/journeys.spec.ts.ejs +53 -0
  534. package/dist/templates/evidence-files.json.ejs +13 -0
  535. package/dist/templates/frontend/FE_DESIGN_PRINCIPLES.md.ejs +115 -0
  536. package/dist/templates/frontend/FRONTEND_CONSTITUTION.md.ejs +170 -0
  537. package/dist/templates/frontend/design-tokens.json.ejs +107 -0
  538. package/dist/templates/frontend/vrt-setup.md.ejs +83 -0
  539. package/dist/templates/githooks/commit-msg.ejs +60 -0
  540. package/dist/templates/githooks/pre-commit.ejs +95 -0
  541. package/dist/templates/githooks/pre-push.ejs +251 -0
  542. package/dist/templates/githooks/setup-hooks.sh.ejs +8 -0
  543. package/dist/templates/github/PULL_REQUEST_TEMPLATE.md +37 -0
  544. package/dist/templates/github/PULL_REQUEST_TEMPLATE.md.ejs +52 -0
  545. package/dist/templates/github/actions/build-cache/action.yml.ejs +84 -0
  546. package/dist/templates/github/actions/nas-ssh/action.yml.ejs +53 -0
  547. package/dist/templates/github/actions/setup-java-maven/action.yml.ejs +53 -0
  548. package/dist/templates/github/actions/setup-node-pnpm/action.yml.ejs +39 -0
  549. package/dist/templates/github/actions/sign-and-attest/action.yml.ejs +53 -0
  550. package/dist/templates/github/dependabot.yml.ejs +66 -0
  551. package/dist/templates/github/extended-ci-paths.txt.ejs +38 -0
  552. package/dist/templates/github/issue-templates/bug-report.yml +72 -0
  553. package/dist/templates/github/issue-templates/compliance-item.yml.ejs +75 -0
  554. package/dist/templates/github/issue-templates/config.yml +2 -0
  555. package/dist/templates/github/issue-templates/epic.yml +66 -0
  556. package/dist/templates/github/issue-templates/feature-request.yml +24 -0
  557. package/dist/templates/github/issue-templates/task-brief.yml.ejs +125 -0
  558. package/dist/templates/github/labels.yml.ejs +161 -0
  559. package/dist/templates/github/scripts/sticky-failure-issue.sh.ejs +61 -0
  560. package/dist/templates/github/workflows/01-pr-fast.yml.ejs +1089 -0
  561. package/dist/templates/github/workflows/02-pr-extended.yml.ejs +512 -0
  562. package/dist/templates/github/workflows/03-human-approval.yml.ejs +76 -0
  563. package/dist/templates/github/workflows/04-deploy-test.yml.ejs +425 -0
  564. package/dist/templates/github/workflows/05-release.yml.ejs +813 -0
  565. package/dist/templates/github/workflows/06-nightly-lite.yml.ejs +125 -0
  566. package/dist/templates/github/workflows/06-nightly.yml.ejs +25 -0
  567. package/dist/templates/github/workflows/07-weekly-lite.yml.ejs +221 -0
  568. package/dist/templates/github/workflows/07-weekly.yml.ejs +23 -0
  569. package/dist/templates/github/workflows/08-monthly.yml.ejs +23 -0
  570. package/dist/templates/github/workflows/09-heartbeat.yml.ejs +268 -0
  571. package/dist/templates/github/workflows/10-deploy-prod.yml.ejs +144 -0
  572. package/dist/templates/github/workflows/11-k6-on-demand.yml.ejs +43 -0
  573. package/dist/templates/github/workflows/12-mutation-scheduled.yml.ejs +100 -0
  574. package/dist/templates/github/workflows/13-archunit-extended.yml.ejs +33 -0
  575. package/dist/templates/github/workflows/14-license-scan.yml.ejs +45 -0
  576. package/dist/templates/github/workflows/15-codeql.yml.ejs +117 -0
  577. package/dist/templates/github/workflows/16-frontend-quality.yml.ejs +154 -0
  578. package/dist/templates/github/workflows/17-ossf-scorecard.yml.ejs +46 -0
  579. package/dist/templates/github/workflows/18-frontend-lane.yml.ejs +50 -0
  580. package/dist/templates/github/workflows/_ai-draft-check.yml.ejs +44 -0
  581. package/dist/templates/github/workflows/_contract-postman.yml.ejs +93 -0
  582. package/dist/templates/github/workflows/_cosign-copy/aws-ecs.ejs +90 -0
  583. package/dist/templates/github/workflows/_cosign-copy/azure-container-app.ejs +97 -0
  584. package/dist/templates/github/workflows/_cosign-copy/gcp-cloud-run.ejs +102 -0
  585. package/dist/templates/github/workflows/_cosign-copy/ghcr.ejs +76 -0
  586. package/dist/templates/github/workflows/_cosign-copy/nas-compose.ejs +69 -0
  587. package/dist/templates/github/workflows/_deploy/aws-ecs.ejs +78 -0
  588. package/dist/templates/github/workflows/_deploy/azure-container-app.ejs +59 -0
  589. package/dist/templates/github/workflows/_deploy/gcp-cloud-run.ejs +71 -0
  590. package/dist/templates/github/workflows/_deploy/ghcr.ejs +19 -0
  591. package/dist/templates/github/workflows/_deploy/nas-compose.ejs +45 -0
  592. package/dist/templates/github/workflows/_k6-runner.yml.ejs +107 -0
  593. package/dist/templates/github/workflows/_label-on-approve.yml.ejs +44 -0
  594. package/dist/templates/github/workflows/_label-sync.yml.ejs +58 -0
  595. package/dist/templates/github/workflows/_monthly.yml.ejs +343 -0
  596. package/dist/templates/github/workflows/_nightly.yml.ejs +826 -0
  597. package/dist/templates/github/workflows/_notify.yml.ejs +96 -0
  598. package/dist/templates/github/workflows/_partials/macos-keychain-bypass.ejs +6 -0
  599. package/dist/templates/github/workflows/_partials/macos-keychain-restore.ejs +6 -0
  600. package/dist/templates/github/workflows/_partials/scheduled-heavy-jobs.ejs +117 -0
  601. package/dist/templates/github/workflows/_partials/sigstore-preflight.ejs +8 -0
  602. package/dist/templates/github/workflows/_partials/trivy-fs-java.ejs +27 -0
  603. package/dist/templates/github/workflows/_post-merge-notify.yml.ejs +228 -0
  604. package/dist/templates/github/workflows/_pr-staleness.yml.ejs +85 -0
  605. package/dist/templates/github/workflows/_shared-security.yml.ejs +132 -0
  606. package/dist/templates/github/workflows/_sigstore-retry-sign.yml.ejs +102 -0
  607. package/dist/templates/github/workflows/_weekly.yml.ejs +313 -0
  608. package/dist/templates/github/workflows/drift-shadow.yml.ejs +51 -0
  609. package/dist/templates/github/workflows/five-lane/ci.yml.ejs +54 -0
  610. package/dist/templates/github/workflows/five-lane/nightly.yml.ejs +70 -0
  611. package/dist/templates/github/workflows/five-lane/release.yml.ejs +53 -0
  612. package/dist/templates/github/workflows/five-lane/weekly.yml.ejs +72 -0
  613. package/dist/templates/github/workflows/issue-state.yml.ejs +93 -0
  614. package/dist/templates/global-invariants/GLOBAL_INVARIANTS.md.ejs +34 -0
  615. package/dist/templates/governance/RACI.md.ejs +21 -0
  616. package/dist/templates/governance/ci-mental-model.md.ejs +268 -0
  617. package/dist/templates/governance/contract-integrity-policy.md.ejs +116 -0
  618. package/dist/templates/governance/enterprise-compliance-baseline.md.ejs +111 -0
  619. package/dist/templates/governance/evidence-retention.md.ejs +43 -0
  620. package/dist/templates/governance/gdpr-erasure-runbook.md.ejs +165 -0
  621. package/dist/templates/governance/qa-audit-phases.md.ejs +129 -0
  622. package/dist/templates/governance/risk-assessment-template.md.ejs +105 -0
  623. package/dist/templates/governance/risk-register.md.ejs +76 -0
  624. package/dist/templates/governance/solo-dev-exception.md.ejs +96 -0
  625. package/dist/templates/governance/validation-evidence.md.ejs +131 -0
  626. package/dist/templates/infra/azure/containerapp.tpl.yaml.ejs +74 -0
  627. package/dist/templates/infra/nas-compose/deploy.sh.ejs +114 -0
  628. package/dist/templates/integration-testing/AbstractIntegrationTest.java.ejs +36 -0
  629. package/dist/templates/integration-testing/NoH2ArchTest.java.ejs +24 -0
  630. package/dist/templates/integration-testing/conftest.py.ejs +20 -0
  631. package/dist/templates/integration-testing/db_fixture.rs.ejs +48 -0
  632. package/dist/templates/integration-testing/eslint-no-fake-db.json.ejs +19 -0
  633. package/dist/templates/integration-testing/eslint.config.no-fake-db.mjs.ejs +46 -0
  634. package/dist/templates/integration-testing/main_test.go.ejs +53 -0
  635. package/dist/templates/integration-testing/main_test_sqlite.go.ejs +37 -0
  636. package/dist/templates/integration-testing/test-setup.ts.ejs +26 -0
  637. package/dist/templates/integration-testing/testcontainers-deps.gradle.ejs +9 -0
  638. package/dist/templates/java/DeprecationInterceptor.java.ejs +50 -0
  639. package/dist/templates/java/archunit-hexagonal.ejs +69 -0
  640. package/dist/templates/java/modulith/ApplicationModulesTest.java.ejs +21 -0
  641. package/dist/templates/java/modulith/modulith-deps.gradle.ejs +13 -0
  642. package/dist/templates/java/modulith/modulith-maven-setup.md.ejs +21 -0
  643. package/dist/templates/java/pharma/AuditEvent.java.ejs +138 -0
  644. package/dist/templates/java/pharma/AuditMapper.java.ejs +60 -0
  645. package/dist/templates/java/pharma/archunit-pharma.ejs +128 -0
  646. package/dist/templates/kit/GLOBAL_KIT.md.ejs +11 -0
  647. package/dist/templates/kit/dim.md.ejs +33 -0
  648. package/dist/templates/llms-txt.config.json.ejs +22 -0
  649. package/dist/templates/local-wrapper/.env.example.ejs +15 -0
  650. package/dist/templates/local-wrapper/Makefile.ejs +44 -0
  651. package/dist/templates/local-wrapper/run.sh.ejs +5 -0
  652. package/dist/templates/middleware/410-gone-handler.ts.ejs +41 -0
  653. package/dist/templates/middleware/correlation-id.ts.ejs +32 -0
  654. package/dist/templates/middleware/deprecation.ts.ejs +45 -0
  655. package/dist/templates/middleware/error-handler.ts.ejs +73 -0
  656. package/dist/templates/middleware/payload-size-limit.ts.ejs +51 -0
  657. package/dist/templates/mutation/README.md.ejs +67 -0
  658. package/dist/templates/mutation/cargo-mutants.toml.ejs +9 -0
  659. package/dist/templates/mutation/mutmut-config.toml.ejs +7 -0
  660. package/dist/templates/mutation/parse-mutants.mjs.ejs +44 -0
  661. package/dist/templates/mutation/parse-mutmut.py.ejs +44 -0
  662. package/dist/templates/mutation/pitest-l2-setup.md.ejs +66 -0
  663. package/dist/templates/mutation/pitest-maven-setup.md.ejs +64 -0
  664. package/dist/templates/mutation/pitest.gradle.ejs +23 -0
  665. package/dist/templates/mutation/stryker.conf.json.ejs +17 -0
  666. package/dist/templates/observability/setup.md.ejs +495 -0
  667. package/dist/templates/operations/handbook.md.ejs +217 -0
  668. package/dist/templates/perf/bundle-budget.json.ejs +29 -0
  669. package/dist/templates/perf/k6/reports/csv-report.py.ejs +91 -0
  670. package/dist/templates/perf/k6/reports/html-report.py.ejs +114 -0
  671. package/dist/templates/perf/k6/reports/json-report.py.ejs +74 -0
  672. package/dist/templates/perf/k6/scenarios/breakpoint.js.ejs +26 -0
  673. package/dist/templates/perf/k6/scenarios/burst.js.ejs +43 -0
  674. package/dist/templates/perf/k6/scenarios/endurance.js.ejs +36 -0
  675. package/dist/templates/perf/k6/scenarios/load.js.ejs +26 -0
  676. package/dist/templates/perf/k6/scenarios/ramp-down.js.ejs +29 -0
  677. package/dist/templates/perf/k6/scenarios/ramp-up.js.ejs +32 -0
  678. package/dist/templates/perf/k6/scenarios/smoke.js.ejs +23 -0
  679. package/dist/templates/perf/k6/scenarios/soak.js.ejs +26 -0
  680. package/dist/templates/perf/k6/scenarios/spike.js.ejs +29 -0
  681. package/dist/templates/perf/k6/scenarios/steady-state.js.ejs +31 -0
  682. package/dist/templates/perf/k6/scenarios/stress.js.ejs +29 -0
  683. package/dist/templates/perf/k6/scenarios/volume.js.ejs +36 -0
  684. package/dist/templates/perf/k6/seed/test-data.sql.ejs +59 -0
  685. package/dist/templates/perf/lighthouserc.json.ejs +41 -0
  686. package/dist/templates/quality/iso9001/CAPA_LOG.md.ejs +36 -0
  687. package/dist/templates/quality/iso9001/DOCUMENT_CONTROL.md.ejs +36 -0
  688. package/dist/templates/quality/iso9001/REQUIREMENTS_TRACEABILITY.md.ejs +61 -0
  689. package/dist/templates/regulated/overlay.json.ejs +26 -0
  690. package/dist/templates/regulated/regulated-overlay.md.ejs +40 -0
  691. package/dist/templates/resilience/RESILIENCE.md.ejs +310 -0
  692. package/dist/templates/root/.gitignore.ejs +85 -0
  693. package/dist/templates/root/CODEOWNERS.ejs +18 -0
  694. package/dist/templates/root/CONTRIBUTING.md.ejs +62 -0
  695. package/dist/templates/root/SECURITY.md.ejs +19 -0
  696. package/dist/templates/root/TEST_TAXONOMY.md.ejs +100 -0
  697. package/dist/templates/root/commitlint.config.js.ejs +4 -0
  698. package/dist/templates/root/docs/METHOD/CANONICAL_PATHS.md.ejs +24 -0
  699. package/dist/templates/root/docs/METHOD/ENGINEERING_DEFAULTS.md.ejs +136 -0
  700. package/dist/templates/root/docs/METHOD/KNOWLEDGE_MAP.md.ejs +90 -0
  701. package/dist/templates/root/docs/METHOD/PATTERNS_CATALOG.md.ejs +51 -0
  702. package/dist/templates/root/docs/METHOD/REUSE_REGISTRY.md.ejs +26 -0
  703. package/dist/templates/root/docs/METHOD/REUSE_REGISTRY_SPEC.md.ejs +80 -0
  704. package/dist/templates/root/docs/METHOD/SSOT_CORE_SET.md.ejs +62 -0
  705. package/dist/templates/root/docs/METHOD/TRACK_ROUTER.md.ejs +39 -0
  706. package/dist/templates/root/docs/SYSTEM/CANON.md.ejs +359 -0
  707. package/dist/templates/root/docs/SYSTEM/FAIL_CLOSED.md.ejs +171 -0
  708. package/dist/templates/root/editorconfig.ejs +39 -0
  709. package/dist/templates/root/tsconfig.json.ejs +21 -0
  710. package/dist/templates/rust/clippy.toml.ejs +14 -0
  711. package/dist/templates/schemas/milestone.schema.json.ejs +164 -0
  712. package/dist/templates/schemas/rtm-verdict.schema.json.ejs +52 -0
  713. package/dist/templates/schemas/source-record.schema.json.ejs +91 -0
  714. package/dist/templates/schemas/tabletop-evidence.schema.json.ejs +53 -0
  715. package/dist/templates/schemas/use-case.schema.json.ejs +59 -0
  716. package/dist/templates/scripts/apply-branch-protection.mjs.ejs +70 -0
  717. package/dist/templates/scripts/audit-toolchain.mjs.ejs +112 -0
  718. package/dist/templates/scripts/bg-run.sh.ejs +59 -0
  719. package/dist/templates/scripts/capacity-probe.mjs.ejs +147 -0
  720. package/dist/templates/scripts/capture-debt-baseline.mjs.ejs +99 -0
  721. package/dist/templates/scripts/check-acceptance.mjs.ejs +298 -0
  722. package/dist/templates/scripts/check-action-pins.mjs.ejs +215 -0
  723. package/dist/templates/scripts/check-agent-return.mjs.ejs +177 -0
  724. package/dist/templates/scripts/check-all.mjs.ejs +1746 -0
  725. package/dist/templates/scripts/check-anti-fake-green.mjs.ejs +80 -0
  726. package/dist/templates/scripts/check-anti-proforma.mjs.ejs +547 -0
  727. package/dist/templates/scripts/check-api-e2e.mjs.ejs +106 -0
  728. package/dist/templates/scripts/check-arc42-slots.mjs.ejs +25 -0
  729. package/dist/templates/scripts/check-assertion-delta.mjs.ejs +287 -0
  730. package/dist/templates/scripts/check-audit-dry-pass.mjs.ejs +176 -0
  731. package/dist/templates/scripts/check-bundle-size.mjs.ejs +151 -0
  732. package/dist/templates/scripts/check-canonical-paths.mjs.ejs +39 -0
  733. package/dist/templates/scripts/check-ci-tiers.mjs.ejs +111 -0
  734. package/dist/templates/scripts/check-claude-md-lint.mjs.ejs +142 -0
  735. package/dist/templates/scripts/check-collab-mode-wired.mjs.ejs +60 -0
  736. package/dist/templates/scripts/check-commit-footer-rationale.mjs.ejs +170 -0
  737. package/dist/templates/scripts/check-constraint-scan.mjs.ejs +681 -0
  738. package/dist/templates/scripts/check-consumer-audit.mjs.ejs +414 -0
  739. package/dist/templates/scripts/check-continue-on-error.mjs.ejs +227 -0
  740. package/dist/templates/scripts/check-cross-model-review.mjs.ejs +458 -0
  741. package/dist/templates/scripts/check-decision-registry.mjs.ejs +101 -0
  742. package/dist/templates/scripts/check-doc-freshness.mjs.ejs +17 -0
  743. package/dist/templates/scripts/check-doc-links.mjs.ejs +98 -0
  744. package/dist/templates/scripts/check-doc-set.mjs.ejs +18 -0
  745. package/dist/templates/scripts/check-docs.mjs.ejs +71 -0
  746. package/dist/templates/scripts/check-domain-api-surface.mjs.ejs +80 -0
  747. package/dist/templates/scripts/check-drift.mjs.ejs +167 -0
  748. package/dist/templates/scripts/check-duplication.mjs.ejs +45 -0
  749. package/dist/templates/scripts/check-e2e-escalation.mjs.ejs +199 -0
  750. package/dist/templates/scripts/check-e2e-quarantine.mjs.ejs +68 -0
  751. package/dist/templates/scripts/check-emission-parity.mjs.ejs +91 -0
  752. package/dist/templates/scripts/check-exit-code-contract.mjs.ejs +138 -0
  753. package/dist/templates/scripts/check-fail-closed-audit.mjs.ejs +389 -0
  754. package/dist/templates/scripts/check-fe-boundaries.mjs.ejs +184 -0
  755. package/dist/templates/scripts/check-feature-matrix.mjs.ejs +835 -0
  756. package/dist/templates/scripts/check-frontend-lane.mjs.ejs +73 -0
  757. package/dist/templates/scripts/check-grace-window.mjs.ejs +102 -0
  758. package/dist/templates/scripts/check-handoff-doc.mjs.ejs +192 -0
  759. package/dist/templates/scripts/check-hook-routing.mjs.ejs +224 -0
  760. package/dist/templates/scripts/check-image-pins.mjs.ejs +90 -0
  761. package/dist/templates/scripts/check-inline-suppressions.mjs.ejs +153 -0
  762. package/dist/templates/scripts/check-iso9001.mjs.ejs +146 -0
  763. package/dist/templates/scripts/check-knowledge-map.mjs.ejs +49 -0
  764. package/dist/templates/scripts/check-local-ci-parity.mjs.ejs +382 -0
  765. package/dist/templates/scripts/check-m16-handoff.mjs.ejs +112 -0
  766. package/dist/templates/scripts/check-merge-method.mjs.ejs +92 -0
  767. package/dist/templates/scripts/check-milestones.mjs.ejs +678 -0
  768. package/dist/templates/scripts/check-min-test-execution.mjs.ejs +225 -0
  769. package/dist/templates/scripts/check-muted-test.mjs.ejs +270 -0
  770. package/dist/templates/scripts/check-no-stub-redirects.mjs.ejs +154 -0
  771. package/dist/templates/scripts/check-no-tracked-artifacts.mjs.ejs +134 -0
  772. package/dist/templates/scripts/check-oracle-discrimination.mjs.ejs +244 -0
  773. package/dist/templates/scripts/check-pii-scan.mjs.ejs +67 -0
  774. package/dist/templates/scripts/check-pipe-tee-hazard.mjs.ejs +85 -0
  775. package/dist/templates/scripts/check-pr-size-gate.mjs.ejs +85 -0
  776. package/dist/templates/scripts/check-refutation-verdicts.mjs.ejs +256 -0
  777. package/dist/templates/scripts/check-regulated-overlay.mjs.ejs +158 -0
  778. package/dist/templates/scripts/check-render-smoke.mjs.ejs +100 -0
  779. package/dist/templates/scripts/check-reuse-registry.mjs.ejs +44 -0
  780. package/dist/templates/scripts/check-review-completion.mjs.ejs +708 -0
  781. package/dist/templates/scripts/check-safety-adopt-ratchet.mjs.ejs +142 -0
  782. package/dist/templates/scripts/check-secret-presence.mjs.ejs +191 -0
  783. package/dist/templates/scripts/check-secret-scan.mjs.ejs +82 -0
  784. package/dist/templates/scripts/check-skip-critical-e2e.mjs.ejs +142 -0
  785. package/dist/templates/scripts/check-smoke-journeys.mjs.ejs +272 -0
  786. package/dist/templates/scripts/check-solo-reactivation.mjs.ejs +102 -0
  787. package/dist/templates/scripts/check-sources.mjs.ejs +245 -0
  788. package/dist/templates/scripts/check-ssot-core.mjs.ejs +45 -0
  789. package/dist/templates/scripts/check-stack-conformity.mjs.ejs +160 -0
  790. package/dist/templates/scripts/check-stride-traceability.mjs.ejs +101 -0
  791. package/dist/templates/scripts/check-suppression-expiry.mjs.ejs +79 -0
  792. package/dist/templates/scripts/check-suppression-rationale.mjs.ejs +76 -0
  793. package/dist/templates/scripts/check-suppressions.mjs.ejs +150 -0
  794. package/dist/templates/scripts/check-tabletop-evidence.mjs.ejs +499 -0
  795. package/dist/templates/scripts/check-tdd-evidence.mjs.ejs +381 -0
  796. package/dist/templates/scripts/check-test-naming.mjs.ejs +151 -0
  797. package/dist/templates/scripts/check-test-pyramid.mjs.ejs +170 -0
  798. package/dist/templates/scripts/check-test-scope-tier.mjs.ejs +208 -0
  799. package/dist/templates/scripts/check-tier-coverage.mjs.ejs +71 -0
  800. package/dist/templates/scripts/check-todo-max-age.mjs.ejs +205 -0
  801. package/dist/templates/scripts/check-touched-vs-manifest.mjs.ejs +179 -0
  802. package/dist/templates/scripts/check-unwired-guards.mjs.ejs +367 -0
  803. package/dist/templates/scripts/check-use-cases.mjs.ejs +345 -0
  804. package/dist/templates/scripts/check-validator-helptext.mjs.ejs +80 -0
  805. package/dist/templates/scripts/check-wiki-lint.mjs.ejs +162 -0
  806. package/dist/templates/scripts/check-workflow-docs-sync.mjs.ejs +91 -0
  807. package/dist/templates/scripts/check-workflow-job-naming.mjs.ejs +102 -0
  808. package/dist/templates/scripts/check-workflow-perms.mjs.ejs +54 -0
  809. package/dist/templates/scripts/check-workflow-runners.mjs.ejs +87 -0
  810. package/dist/templates/scripts/check-workflow-sha-pinning.mjs.ejs +89 -0
  811. package/dist/templates/scripts/check-workflow-test-integrity.mjs.ejs +235 -0
  812. package/dist/templates/scripts/checks/check-rust-no-unsafe.mjs.ejs +58 -0
  813. package/dist/templates/scripts/checks/check-rust-no-unwrap.mjs.ejs +61 -0
  814. package/dist/templates/scripts/ci-classify-changes.mjs.ejs +119 -0
  815. package/dist/templates/scripts/conformance.mjs.ejs +37 -0
  816. package/dist/templates/scripts/constraint-map.json.ejs +25 -0
  817. package/dist/templates/scripts/debt-lib.mjs.ejs +1015 -0
  818. package/dist/templates/scripts/debt-report.mjs.ejs +199 -0
  819. package/dist/templates/scripts/domain-api-surface.json.ejs +23 -0
  820. package/dist/templates/scripts/done-evidence.mjs.ejs +359 -0
  821. package/dist/templates/scripts/evidence-collect.mjs.ejs +264 -0
  822. package/dist/templates/scripts/evidence-prune.mjs.ejs +96 -0
  823. package/dist/templates/scripts/evidence-rotate.mjs.ejs +53 -0
  824. package/dist/templates/scripts/evidence-writer.sh.ejs +220 -0
  825. package/dist/templates/scripts/gate-registry.yml.ejs +286 -0
  826. package/dist/templates/scripts/gen-doc-index.mjs.ejs +19 -0
  827. package/dist/templates/scripts/gen-gap.mjs.ejs +153 -0
  828. package/dist/templates/scripts/gen-llms-txt.mjs.ejs +18 -0
  829. package/dist/templates/scripts/gen-wiki.mjs.ejs +196 -0
  830. package/dist/templates/scripts/gh-cleanup-expired-artifacts.mjs.ejs +134 -0
  831. package/dist/templates/scripts/gold-audit.mjs.ejs +19 -0
  832. package/dist/templates/scripts/i18n-literal-scanner.mjs.ejs +175 -0
  833. package/dist/templates/scripts/ingest-zap-report.mjs.ejs +107 -0
  834. package/dist/templates/scripts/inject-pact-samples.sh.ejs +96 -0
  835. package/dist/templates/scripts/issue-readiness.mjs.ejs +84 -0
  836. package/dist/templates/scripts/knowledge-map-update.mjs.ejs +32 -0
  837. package/dist/templates/scripts/lib/acceptance-criteria.mjs.ejs +213 -0
  838. package/dist/templates/scripts/lib/agent-return-validate.mjs.ejs +481 -0
  839. package/dist/templates/scripts/lib/coverage-gate.mjs.ejs +54 -0
  840. package/dist/templates/scripts/lib/e2e-reliability.mjs.ejs +207 -0
  841. package/dist/templates/scripts/lib/ephemeral-server.mjs.ejs +148 -0
  842. package/dist/templates/scripts/lib/evidence-binding.mjs.ejs +140 -0
  843. package/dist/templates/scripts/lib/exact-sha-policy.mjs.ejs +233 -0
  844. package/dist/templates/scripts/lib/gate-args.mjs.ejs +20 -0
  845. package/dist/templates/scripts/lib/gate-evidence.mjs.ejs +622 -0
  846. package/dist/templates/scripts/lib/gate-mutex.mjs.ejs +340 -0
  847. package/dist/templates/scripts/lib/glob-walk.mjs.ejs +148 -0
  848. package/dist/templates/scripts/lib/run-helpers.mjs.ejs +416 -0
  849. package/dist/templates/scripts/lib/seed-common.sh.ejs +105 -0
  850. package/dist/templates/scripts/lib/waiter-count.mjs.ejs +62 -0
  851. package/dist/templates/scripts/muted-tests-baseline.json.ejs +4 -0
  852. package/dist/templates/scripts/optional-emissions.json.ejs +117 -0
  853. package/dist/templates/scripts/oracle-discrimination-baseline.json.ejs +6 -0
  854. package/dist/templates/scripts/pid-watch.sh.ejs +78 -0
  855. package/dist/templates/scripts/pii-scan.mjs.ejs +125 -0
  856. package/dist/templates/scripts/pr-merge-watch.mjs.ejs +445 -0
  857. package/dist/templates/scripts/record-agent-return.mjs.ejs +279 -0
  858. package/dist/templates/scripts/record-journey-evidence.mjs.ejs +69 -0
  859. package/dist/templates/scripts/rework-log.mjs.ejs +172 -0
  860. package/dist/templates/scripts/run-postman-tests.sh.ejs +112 -0
  861. package/dist/templates/scripts/schemas/agent-return-external.schema.json +74 -0
  862. package/dist/templates/scripts/schemas/agent-return.schema.json.ejs +181 -0
  863. package/dist/templates/scripts/schemas/cross-model-dispatch.schema.json +95 -0
  864. package/dist/templates/scripts/seed-test-data.sh.ejs +99 -0
  865. package/dist/templates/scripts/self-validation.mjs.ejs +126 -0
  866. package/dist/templates/scripts/setup-repo.sh.ejs +78 -0
  867. package/dist/templates/scripts/spotbugs-baseline.json.ejs +4 -0
  868. package/dist/templates/scripts/test-gate-layering.mjs.ejs +92 -0
  869. package/dist/templates/scripts/validate-api-snapshots.mjs.ejs +86 -0
  870. package/dist/templates/scripts/validate-k6-scenarios.mjs.ejs +96 -0
  871. package/dist/templates/scripts/validate-openapi-field-types.mjs.ejs +124 -0
  872. package/dist/templates/scripts/validate-postman-collection.mjs.ejs +159 -0
  873. package/dist/templates/scripts/verify-fe-coverage.mjs.ejs +138 -0
  874. package/dist/templates/scripts/verify-i18n-parity.mjs.ejs +114 -0
  875. package/dist/templates/scripts/verify-module-coverage.mjs.ejs +207 -0
  876. package/dist/templates/scripts/verify-mutation-baseline.mjs.ejs +209 -0
  877. package/dist/templates/scripts/verify-spotbugs.mjs.ejs +216 -0
  878. package/dist/templates/scripts/verify-tokens.mjs.ejs +177 -0
  879. package/dist/templates/security/RISK_ASSESSMENT.md.ejs +35 -0
  880. package/dist/templates/security/STRIDE.md.ejs +24 -0
  881. package/dist/templates/security/gitleaks.toml.ejs +21 -0
  882. package/dist/templates/security/zap/baseline-auth.context.ejs +58 -0
  883. package/dist/templates/security/zap/rules.tsv.ejs +100 -0
  884. package/dist/templates/ship/TICK_PROMPT.md.ejs +55 -0
  885. package/dist/templates/ship/supervisor.sh.ejs +46 -0
  886. package/dist/templates/smoke-journeys/manifest.json.ejs +1 -0
  887. package/dist/templates/sonar-project.properties.ejs +25 -0
  888. package/dist/templates/standards/doc-profile.ejs +27 -0
  889. package/dist/templates/standards/gold-doc-set.yml.ejs +528 -0
  890. package/dist/templates/standards/gold-registry.java.yml.ejs +266 -0
  891. package/dist/templates/standards/gold-registry.typescript.yml.ejs +104 -0
  892. package/dist/templates/standards/gold-registry.yml.ejs +119 -0
  893. package/dist/templates/standards/thresholds.yml.ejs +86 -0
  894. package/dist/templates/static-analysis/.dependency-cruiser.cjs.ejs +78 -0
  895. package/dist/templates/static-analysis/.golangci.yml.ejs +70 -0
  896. package/dist/templates/static-analysis/.shellcheckrc.ejs +13 -0
  897. package/dist/templates/static-analysis/.yamllint.yml.ejs +41 -0
  898. package/dist/templates/static-analysis/checkstyle.xml.ejs +83 -0
  899. package/dist/templates/static-analysis/detekt.yml.ejs +26 -0
  900. package/dist/templates/static-analysis/eslint.config.mjs.ejs +54 -0
  901. package/dist/templates/static-analysis/eslint.config.static.mjs.ejs +38 -0
  902. package/dist/templates/static-analysis/eslintrc-static.json.ejs +30 -0
  903. package/dist/templates/static-analysis/jscpd.json.ejs +25 -0
  904. package/dist/templates/static-analysis/knip.json.ejs +26 -0
  905. package/dist/templates/static-analysis/pmd-ruleset.xml.ejs +92 -0
  906. package/dist/templates/static-analysis/prettierignore.ejs +59 -0
  907. package/dist/templates/static-analysis/prettierrc.json.ejs +10 -0
  908. package/dist/templates/static-analysis/requirements-dev.txt.ejs +11 -0
  909. package/dist/templates/static-analysis/ruff.toml.ejs +40 -0
  910. package/dist/templates/static-analysis/rustfmt.toml.ejs +3 -0
  911. package/dist/templates/static-analysis/spotbugs-exclude.xml.ejs +120 -0
  912. package/dist/templates/static-analysis/spotbugs.gradle.ejs +18 -0
  913. package/dist/templates/static-analysis/spotless.gradle.ejs +38 -0
  914. package/dist/templates/static-analysis/stylelintrc.json.ejs +16 -0
  915. package/dist/templates/suppressions/archunit-baseline.json.ejs +1 -0
  916. package/dist/templates/suppressions/consumer-audit-allowlist.json.ejs +1 -0
  917. package/dist/templates/suppressions/gitleaksignore.ejs +20 -0
  918. package/dist/templates/suppressions/pii-allowlist.json.ejs +1 -0
  919. package/dist/templates/suppressions/suppressions-schema.json.ejs +39 -0
  920. package/dist/templates/suppressions/trivyignore.ejs +17 -0
  921. package/dist/templates/testing/test-taxonomy.md.ejs +120 -0
  922. package/dist/test/example.behavioral.test.js +2 -0
  923. package/dist/types/memory.d.ts +19 -0
  924. package/dist/types/memory.js +1 -0
  925. package/dist/types/plan.d.ts +102 -0
  926. package/dist/types/plan.js +2 -0
  927. package/dist/types/plugin.d.ts +45 -0
  928. package/dist/types/plugin.js +1 -0
  929. package/dist/utils/canon-loader.js +2 -0
  930. package/dist/utils/channel.js +1 -0
  931. package/dist/utils/config.d.ts +79 -0
  932. package/dist/utils/config.js +5 -0
  933. package/dist/utils/confirm-downgrade.js +5 -0
  934. package/dist/utils/env.js +2 -0
  935. package/dist/utils/error-catalog.js +2 -0
  936. package/dist/utils/errors.js +2 -0
  937. package/dist/utils/evidence-log.js +3 -0
  938. package/dist/utils/file-lock.js +2 -0
  939. package/dist/utils/first-run.js +8 -0
  940. package/dist/utils/fs.js +3 -0
  941. package/dist/utils/github-issue-helper.js +4 -0
  942. package/dist/utils/gradle.js +21 -0
  943. package/dist/utils/json-output.js +2 -0
  944. package/dist/utils/logger-config.js +4 -0
  945. package/dist/utils/logger.js +3 -0
  946. package/dist/utils/maturity-check.js +2 -0
  947. package/dist/utils/pkg.js +3 -0
  948. package/dist/utils/platform.js +2 -0
  949. package/dist/utils/plugin-loader.js +2 -0
  950. package/dist/utils/plugin-worker.js +2 -0
  951. package/dist/utils/prettier-format.js +2 -0
  952. package/dist/utils/profiler.js +2 -0
  953. package/dist/utils/render.js +2 -0
  954. package/dist/utils/replay.js +3 -0
  955. package/dist/utils/run-cli.js +6 -0
  956. package/dist/utils/run-id.js +3 -0
  957. package/dist/utils/safe-read.js +2 -0
  958. package/dist/utils/task-id.js +2 -0
  959. package/dist/utils/today.js +2 -0
  960. package/dist/utils/tty.js +2 -0
  961. package/dist/utils/walk-dir.js +2 -0
  962. package/dist/verify/rules/drive-by-scope.js +1 -0
  963. package/dist/verify/rules/orphan-todos.js +1 -0
  964. package/dist/verify/rules/registry.js +1 -0
  965. package/dist/verify/rules/skip-patterns.js +1 -0
  966. package/dist/verify/rules/types.d.ts +15 -0
  967. package/dist/verify/rules/types.js +1 -0
  968. package/dist/verify/rules/ui-language.js +1 -0
  969. package/dist/verify/run.js +3 -0
  970. package/dist/wizard/archetype-defaults.js +1 -0
  971. package/dist/wizard/presets.js +1 -0
  972. package/dist/wizard/prompts.js +44 -0
  973. package/dist/wizard/types.d.ts +637 -0
  974. package/dist/wizard/types.js +1 -0
  975. package/dist/worktree/harvest.js +2 -0
  976. package/dist/worktree/links.js +2 -0
  977. package/dist/worktree/paths.js +2 -0
  978. package/dist/worktree/validate.js +3 -0
  979. package/package.json +124 -0
  980. package/scripts/check-arc42-slots.mjs +738 -0
  981. package/scripts/check-doc-freshness.mjs +236 -0
  982. package/scripts/check-doc-set.mjs +243 -0
  983. package/scripts/check-doc-style.mjs +228 -0
  984. package/scripts/gold-audit.mjs +319 -0
  985. package/scripts/lib/doc-gate-allowlist.mjs +123 -0
  986. package/scripts/lib/doc-set-resolve.mjs +274 -0
  987. package/scripts/lib/glob-walk.mjs +148 -0
  988. package/scripts/lib/gold-audit-lib.mjs +1260 -0
  989. package/scripts/lib/run-helpers.mjs +413 -0
package/dist/cli.js ADDED
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import{createRequire}from"node:module";import{Command}from"commander";import{runInit}from"./commands/init.js";import{resolvePresetOption}from"./wizard/presets.js";import{runUpdate}from"./commands/update.js";import{runDiff}from"./commands/diff.js";import{runObsidian}from"./commands/obsidian.js";import{runConfigure}from"./commands/configure.js";import{runSettings}from"./commands/settings.js";import{runMethodStatus}from"./commands/method.js";import{runWorktreeOpen,runWorktreeClose,runWorktreeList,runWorktreeRelink}from"./commands/worktree.js";import{runWorktreePrune}from"./commands/worktree-prune.js";import{runGateExec}from"./commands/gate-exec.js";import{runVerify,runVerifyEvidence}from"./commands/verify.js";import{formatProvenance}from"./evidence/provenance.js";import{runGoldAudit}from"./commands/gold-audit.js";import{runDocSet}from"./commands/doc-set.js";import{runDocSetPlanApply}from"./generators/doc-set.js";import{runVerifyPlan}from"./commands/verify-plan.js";import{loadConfig}from"./utils/config.js";import{loadPlugin}from"./utils/plugin-loader.js";import{runPluginAdd,runPluginList}from"./commands/plugin.js";import{runDoctorRepairState,runDoctorHealth,runDoctorRecoverLock,runDoctorClean,runDoctorProveGates,runDoctorToolPins,runDoctorFailOpenCensus}from"./commands/doctor.js";import{jsonOutput}from"./utils/json-output.js";import{runUpgradeLevel}from"./commands/upgrade-level.js";import{runTaskAdvance,runTaskRecover,runTaskResume,runTaskInit,runTaskGet,HandoffRequiredError}from"./commands/task.js";import{runTaskShip,buildShipStepLines,shipStepFor}from"./commands/task-ship.js";import{runCrossModelReview,runShipCrossModelReview}from"./commands/cross-model-review.js";import{buildShipOverrides,resolveShipProfile}from"./commands/ship-profile.js";import{detectExternalModel}from"./detectors/external-model.js";import{runTaskRecordRed}from"./commands/task-record-red.js";import{runTaskRecordTechDebt}from"./commands/task-record-tech-debt.js";import{runTaskNote}from"./commands/task-note.js";import{runTaskMark}from"./commands/task-mark.js";import{isTddPhase,readUnifiedState}from"./commands/task-state.js";import{runVerifyTdd}from"./commands/verify-tdd.js";import{normTier}from"./commands/ship-tier.js";import{splitTrainIds}from"./commands/ship-train.js";import{runGraphBuild,runVerifyGraph}from"./commands/graph.js";import{runReviewDiff,renderMarkdown}from"./commands/review-diff.js";import{confirmChannelDowngrade}from"./utils/confirm-downgrade.js";import{appendEvidenceLine,evidenceLogTarget}from"./utils/evidence-log.js";import{getBoolFlag}from"./config/env-registry.js";import{AUTH_PROVIDERS,OBSERVABILITY_PROVIDERS,DEPLOY_TARGETS,isDeployTarget}from"./config/schema.js";import{runCli}from"./utils/run-cli.js";import{ArbiterError,UserFacingError,FatalError,ConfigError,RecoverableError}from"./utils/errors.js";import{SnapshotChecksumError}from"./state/envelope.js";import{registerCleanupHandlers}from"./utils/fs.js";import{runExplain}from"./commands/explain.js";import{getRunId,formatRunIdFooter}from"./utils/run-id.js";import{parseExperimentalArgv}from"./experimental/index.js";import{applyDeprecatedFlagFilter}from"./internal/deprecate.js";import{CLI_DEPRECATED_FLAGS}from"./internal/cli-deprecation-registry.js";import{warnExperimental}from"./internal/experimental-warn.js";import{setRootLogger,getLogger}from"./utils/logger.js";import{resolveFromProcess}from"./utils/logger-config.js";import{startReplay,rotateReplayLogs}from"./utils/replay.js";import{startProfiler}from"./utils/profiler.js";registerCleanupHandlers(),getRunId();function printCliError(msg){process.stderr.write(` Error: ${msg}${formatRunIdFooter()}
4
+ `)}function refuseUnsupportedArc42Flags(opts){opts.arc42&&(opts.manifest===void 0&&opts.docProfile===void 0||(process.stderr.write(`arbiter doc-set: --manifest/--doc-profile are not supported with --arc42 \u2014 the arc42 engine reads standards/gold-doc-set.yml directly. Re-run without them.
5
+ `),process.exit(2)))}function printDocSetPlanApplyReport(result,applied){const mode=applied?"apply":"plan",tier=result.tierColumn??"n/a (no manifest \u2014 SKIP)";process.stdout.write(`doc-set skeletons [tier: ${tier}] (${mode}):
6
+ `);for(const s of result.scaffolded){const verb=s.action==="created"?applied?"+ created":"+ would-scaffold":s.action==="replaced"||s.action==="backed-up-and-replaced"?applied?"~ replaced (banner upgrade)":"~ would-upgrade (banner)":". skipped (withheld \u2014 hand-edited)";process.stdout.write(` ${verb} ${s.path} [${s.template}]
7
+ `)}for(const p of result.unbound)process.stdout.write(` ? unbound (no skeleton bound \u2014 engine --generate banner only): ${p}
8
+ `);applied&&result.scaffolded.length===0&&result.unbound.length>0&&process.stdout.write(" Remedy: run `node scripts/check-doc-set.mjs --generate` to create banner stubs for unbound rows.\n"),result.scaffolded.length===0&&result.unbound.length===0&&process.stdout.write(` (nothing to scaffold \u2014 all applicable rows present)
9
+ `)}const _rawArgv=process.argv.slice(2),_noEvidenceIdx=_rawArgv.indexOf("--no-evidence"),_noEvidence=_noEvidenceIdx!==-1||getBoolFlag("ARBITER_NO_EVIDENCE");_noEvidenceIdx!==-1&&process.argv.splice(2+_noEvidenceIdx,1);const _verboseIdx=process.argv.indexOf("--verbose"),_verbose=_verboseIdx!==-1;_verbose&&process.argv.splice(_verboseIdx,1);try{const{remaining,flags}=parseExperimentalArgv(process.argv);if(process.argv.length=0,process.argv.push(...remaining),Object.keys(flags).length>0){process.env.ARBITER_EXPERIMENTAL=JSON.stringify(flags);for(const name of Object.keys(flags))warnExperimental(name)}}catch(err){process.stderr.write(`arbiter: ${err instanceof Error?err.message:String(err)}
10
+ `),process.exit(1)}{const result=applyDeprecatedFlagFilter(process.argv,CLI_DEPRECATED_FLAGS);result.exitCode!==void 0&&(process.stderr.write(`${result.errorMessage??"Removed flag used."}
11
+ `),process.exit(result.exitCode)),process.argv.length=0,process.argv.push(...result.remaining)}function consumeFlag(name){const idx=process.argv.indexOf(name);return idx===-1?!1:(process.argv.splice(idx,1),!0)}function consumeFlagValue(name){const idx=process.argv.indexOf(name);if(idx===-1){const eqMatch=process.argv.find(a=>a.startsWith(`${name}=`));return eqMatch!==void 0?(process.argv.splice(process.argv.indexOf(eqMatch),1),eqMatch.slice(name.length+1)):void 0}const value=process.argv[idx+1];return process.argv.splice(idx,2),value}const _debugFlag=consumeFlag("--debug"),_profileFlag=consumeFlag("--profile"),_noReplayFlag=consumeFlag("--no-replay"),_logLevelFlag=consumeFlagValue("--log-level"),_logFormatFlag=consumeFlagValue("--log-format"),_channelFlag=consumeFlagValue("--channel"),_jsonRequested=process.argv.includes("--json");_channelFlag!==void 0&&!["latest","beta","canary"].includes(_channelFlag)&&(process.stderr.write(`[arbiter] error: --channel "${_channelFlag}" is not a valid channel.
12
+ Valid values: latest, beta, canary
13
+ `),process.exit(1));const _resolvedLogger=resolveFromProcess([..._logLevelFlag!==void 0?["--log-level",_logLevelFlag]:[],..._logFormatFlag!==void 0?["--log-format",_logFormatFlag]:[]],process.env),_effectiveLevel=_debugFlag?"debug":_resolvedLogger.level,_runId=getRunId();setRootLogger({level:_effectiveLevel,format:_resolvedLogger.format,runId:_runId});let _replayHandle=null;if(!_noReplayFlag)try{rotateReplayLogs({capN:10}),_replayHandle=startReplay({runId:_runId,argv:process.argv.slice(1),env:process.env,cwd:process.cwd()})}catch(err){process.stderr.write(`[warn] replay capture failed: ${String(err)}
14
+ `)}let _profilerHandle=null;async function _startProfileIfRequested(){if(_profileFlag)try{_profilerHandle=await startProfiler({runId:_runId})}catch(err){getLogger().warn("profiler.start_failed",void 0,String(err))}}function resolveHeadSha(){try{return runCli("git",["rev-parse","--short","HEAD"]).stdout.trim()}catch{return"unknown"}}const _headSha=_noEvidence?"":resolveHeadSha(),_startMs=Date.now();function parseCmdArgs(){const tokens=process.argv.slice(2);if(tokens.length===0)return{cmd:"",args:[]};const nested=new Set(["worktree","wt","task","plugin","work","kit"]),first=tokens[0]??"";if(nested.has(first)&&tokens.length>=2){const sub=tokens[1]??"";if(!sub.startsWith("-"))return{cmd:`${first} ${sub}`,args:tokens.slice(2)}}return{cmd:first,args:tokens.slice(1)}}function validateInitFlags(opts){const VALID_ARCHETYPES=["backend-web-db","cli","library","data-pipeline","frontend-spa","embedded"];if(opts.archetype!==void 0&&!VALID_ARCHETYPES.includes(opts.archetype))throw ArbiterError.fromKey("E_INVALID_ARCHETYPE","errors.E_INVALID_ARCHETYPE",{field:"archetype",value:opts.archetype,valid:VALID_ARCHETYPES.join(", ")},{hint:"Run `arbiter init --help` for the list of valid archetypes."});if(opts.authProvider!==void 0&&!AUTH_PROVIDERS.has(opts.authProvider))throw ArbiterError.fromKey("E_INVALID_ARCHETYPE","errors.E_INVALID_ARCHETYPE",{field:"auth-provider",value:opts.authProvider,valid:[...AUTH_PROVIDERS].join(", ")},{hint:"Run `arbiter init --help` for the list of valid auth providers."});if(opts.observabilityProvider!==void 0&&!OBSERVABILITY_PROVIDERS.has(opts.observabilityProvider))throw ArbiterError.fromKey("E_INVALID_ARCHETYPE","errors.E_INVALID_ARCHETYPE",{field:"observability-provider",value:opts.observabilityProvider,valid:[...OBSERVABILITY_PROVIDERS].join(", ")},{hint:"Run `arbiter init --help` for the list of valid observability providers."});if(opts.deployTarget!==void 0&&!isDeployTarget(opts.deployTarget))throw ArbiterError.fromKey("E_INVALID_ARCHETYPE","errors.E_INVALID_ARCHETYPE",{field:"deploy-target",value:opts.deployTarget,valid:[...DEPLOY_TARGETS].join(", ")},{hint:"Run `arbiter init --help` for the list of valid deploy targets."})}const _parsedCmd=parseCmdArgs();let _evidenceLogged=!1;process.on("exit",code=>{if(_replayHandle!==null)try{_replayHandle.close(code)}catch{}if(_evidenceLogged||_noEvidence)return;_evidenceLogged=!0;const _evidenceDir=evidenceLogTarget(process.cwd(),_parsedCmd.cmd);if(_evidenceDir!==null)try{appendEvidenceLine({ts:new Date().toISOString(),cmd:_parsedCmd.cmd,args:_parsedCmd.args,exit:code,durationMs:Date.now()-_startMs,headSha:_headSha},{dir:_evidenceDir})}catch{process.stderr.write(`[arbiter] warning: evidence log write failed on exit \u2014 log entry lost
15
+ `)}});const program=new Command;function readPackageVersion(){try{return createRequire(import.meta.url)("../package.json").version}catch{return"unknown"}}const packageVersion=readPackageVersion();program.name("arbiter").description("AI development governance framework").version(packageVersion),program.helpCommand(!1),program.addHelpText("after","\nRun `arbiter help --all` for experimental commands.");function externalModelAccessForShip(root,overrides){const config=resolveShipProfile(root,{...Object.keys(overrides).length>0?{overrides}:{}}).crossModelReview;if(!(config===void 0||!config.enabled||!config.diffEgressConsent))return detectExternalModel("codex")}function runConfiguredShipReview(root,result,tier,access){const config=result.profile.crossModelReview;if(result.advanced||result.phase!=="refactor"||!config?.enabled)return null;const taskId=readUnifiedState(root)?.taskId;if(taskId===void 0)throw new Error("crossModelReview is enabled but the active ship task id is missing");return runShipCrossModelReview({dir:root,taskId,tier:result.tier??normTier(tier),phase:result.phase,vertical:result.step.verticals.includes("security")?"security":"bugs",cfg:config,collaborationMode:result.profile.collaborationMode,...access!==void 0?{access}:{}})}function shipOutputAfterConfiguredReview(root,result,tier,access){return runConfiguredShipReview(root,result,tier,access)?.status==="degraded"?{...result,step:shipStepFor(result.phase,result.tier,result.profile)}:result}program.option("--log-level <level>","Log level: error|warn|info|debug|trace (default: info)").option("--log-format <format>","Log format: text|json (default: text)").option("--debug","Enable debug-level logging (implies --log-level=debug)").option("--no-replay","Disable replay log capture for this invocation").option("--profile","Capture a V8 CPU profile to ~/.arbiter/profiles/<runId>.cpuprofile").option("--channel <name>","Channel for this invocation: latest|beta|canary \u2014 gates downgrade warnings and shown in `doctor health` (default: arbiter.json channel or latest)"),program.command("note").description("Capture an out-of-scope finding to the per-agent JSONL spool (#1401)").argument("[note]","Finding text (or use --note)").option("--note <text>","Finding text (alternative to the positional argument)").option("--kind <kind>","Finding class: dup|smell|risk|debt|note (default: note)").option("--severity <sev>","Severity band: low|med|high|info (default: info)").option("--file <path>","Repo-relative file the finding concerns").option("--line <n>","Line number the finding was seen at (excluded from the fingerprint)").option("--dir <path>","Project root (default: cwd)").action((positional,opts)=>{const text=(positional??opts.note??"").trim(),logger=getLogger();if(text.length===0){process.stderr.write(`arbiter note: a finding text is required (positional or --note)
16
+ `),process.exitCode=1;return}const noteOpts={note:text};opts.kind!==void 0&&(noteOpts.kind=opts.kind),opts.severity!==void 0&&(noteOpts.severity=opts.severity),opts.file!==void 0&&(noteOpts.file=opts.file),opts.dir!==void 0&&(noteOpts.dir=opts.dir);const parsedLine=opts.line!==void 0?Number.parseInt(opts.line,10):NaN;Number.isInteger(parsedLine)&&(noteOpts.line=parsedLine);const result=runTaskNote(noteOpts);result.ok?(logger.info("note.captured",{spool:result.spoolPath,fingerprint:result.fingerprint}),process.stdout.write(`noted \u2192 ${result.spoolPath}
17
+ `)):(process.stderr.write(`arbiter note: ${result.reason}
18
+ `),process.exitCode=1)}),program.command("init").description("Initialize AI governance in a project").option("-y, --yes","Skip wizard \u2014 use auto-detected defaults",!1).option("--tools <tools>","Comma-separated list of AI tools (claude,codex)").option("--level <level>","Governance level: L1, L2, L3, or L4","L2").option("--tier <tier>","Progressive-adoption tier: bootstrap (gentlest Day-1: L1 + brownfield) | L1 | L2 | L3 | L4. Overrides --level.").option("--language <lang>","Override detected language (typescript|java|kotlin|rust|python|go|multi)").option("--archetype <archetype>","Override detected archetype (backend-web-db|cli|library|data-pipeline|frontend-spa|embedded)").option("--dir <dir>","Target directory (default: current directory)").option("--dry-run","Preview what would be generated without writing files",!1).option("--brownfield","Auto-capture debt baseline after generation (locks current state as day-0 baseline)",!1).option("--no-verify","Skip toolchain compatibility probes after generation").option("--accept-beta-tools","Allow generation of L3 features backed by beta-maturity tools (audit trail written to arbiter.json)",!1).option("--backend <backend>","Decomposition backend: github or markdown (overrides gh auth detection)").option("--json","Emit machine-readable JSON output (requires --yes)",!1).option("--quiet","Suppress informational banners (e.g. telemetry notice)",!1).option("--force","Override adverse git state check (detached HEAD, rebase, etc.)",!1).option("--preset <preset>","Apply a meta-preset: industrial-grade (governance + compliance + observability + auth bundle) | solo-homelab (compliance off, governance \u2264 L2, mutation off, no prod runbooks)").option("--auth-provider <provider>","Override auth provider (used with --preset or standalone)").option("--observability-provider <provider>","Override observability provider (used with --preset or standalone)").option("--deploy-target <target>","Deploy target: ghcr | azure-container-app | aws-ecs | gcp-cloud-run | none (non-interactive complement to the wizard)").option("--github","Activate GitHub API calls and set permitGitHub:true in stored config",!1).option("--solo","Set collaborationMode=trunk-solo (direct-merge, full local gate \u2261 CI, CI as verification mirror). Shorthand for ADR-051 trunk-solo mode.",!1).option("--recipe <path>","Path or https:// URL to a recipe JSON file for pre-configured init").option("--recipe-sha256 <hash>","Expected SHA-256 hex digest of the recipe file").action(async opts=>{validateInitFlags(opts);const backend=opts.backend==="github"||opts.backend==="markdown"?opts.backend:void 0,preset=resolvePresetOption(opts.preset);await runInit({yes:opts.yes,tools:opts.tools,level:opts.level,...opts.tier!==void 0?{tier:opts.tier}:{},dir:opts.dir,dryRun:opts.dryRun,brownfield:opts.brownfield,noVerify:!opts.verify,acceptBetaTools:opts.acceptBetaTools,github:opts.github,...opts.solo?{solo:!0}:{},...backend!==void 0?{backend}:{},json:opts.json,quiet:opts.quiet,force:opts.force,...preset!==void 0?{preset}:{},...opts.authProvider!==void 0?{authProvider:opts.authProvider}:{},...opts.observabilityProvider!==void 0?{observabilityProvider:opts.observabilityProvider}:{},...opts.recipe!==void 0?{recipe:opts.recipe}:{},...opts.recipeSha256!==void 0?{recipeSha256:opts.recipeSha256}:{},...opts.language!==void 0?{language:opts.language}:{},...opts.archetype!==void 0?{archetype:opts.archetype}:{},...opts.deployTarget!==void 0?{deployTarget:opts.deployTarget}:{}})}),program.command("update").description("Re-generate governance files using stored config (arbiter.json)").option("--dir <dir>","Target directory (default: current directory)").option("--github","Activate live GitHub API calls (opt-in; ARBITER_GITHUB=1 also activates)",!1).option("--json","Emit machine-readable JSON output",!1).option("--force","Override adverse git state check (detached HEAD, rebase, etc.)",!1).option("--adopt","Force-adopt ALL currently-withheld files (not just safety-class), recording a reversible local-override for each. Safety-class files adopt by default already.",!1).option("--no-adopt-safety","Opt OUT of the default-on safety-class adoption (.claude/hooks/*.mjs). Leaves a user-modified safety hook frozen even if the shipped template fixed it \u2014 dangerous.").option("--adopt-gate-spine","Opt IN to force-adopting the gate spine (scripts/check-all.mjs, scripts/lib/*.mjs) over a user-modified copy. DESTRUCTIVE: checks wired into that file by the project are replaced by the template render \u2014 preview with --adopt-plan first (#2119).",!1).option("--adopt-governance","Opt IN to force-adopting governance files (AGENTS.md, .claude/settings.json) over a user-modified copy. DESTRUCTIVE: the template render is not a superset of governed consumer content \u2014 preview with --adopt-plan first (#2141).",!1).option("--no-adopt-gate-spine","DEPRECATED (#2453, removed in 0.8.0): no-op \u2014 withholding a customized gate spine is already the default since #2119. See docs/DEPRECATIONS.md.").option("--no-adopt-governance","DEPRECATED (#2453, removed in 0.8.0): no-op \u2014 withholding a diverged governance file is already the default since #2141. See docs/DEPRECATIONS.md.",(_value,previous)=>previous).option("--adopt-plan","Two-phase preview: print what --adopt/the default safety adoption WOULD change (file list + line diff), without writing anything.",!1).option("--only <globs>","Restrict this run to the managed files matching these globs (comma-separated, repeatable), matched against manifest keys. Every other managed file is skipped and keeps its manifest entry. .arbiterignore wins on conflict (#2353).",(value,previous)=>[...previous,...value.split(",").map(g=>g.trim()).filter(g=>g.length>0)],[]).option("--refresh-derived","Force-refresh the codex-track derived file set (.agents/rules/*, .claude/hooks/* when codex-only, .codex/codex-adapter.mjs) even though these are skipIfExists by default. Combine with --adopt-plan to preview first. A file marked arbiter:preserve is never overwritten (#1983).",!1).action(async opts=>{if(_channelFlag!==void 0){const config=loadConfig(opts.dir??".");await confirmChannelDowngrade(_channelFlag,config?.channel)}await runUpdate({dir:opts.dir,github:opts.github,json:opts.json,force:opts.force,adopt:opts.adopt,noAdoptSafety:!opts.adoptSafety,adoptGateSpine:opts.adoptGateSpine,adoptGovernance:opts.adoptGovernance,adoptPlan:opts.adoptPlan,refreshDerived:opts.refreshDerived,only:opts.only})}),program.command("configure").description("Modify arbiter.json configuration (interactive on TTY, or use --set)").option("--dir <dir>","Target directory (default: current directory)").option("--set <path=value>","Set a config path to a value (repeatable)",(v,acc)=>[...acc,v],[]).option("--json","Emit machine-readable JSON output",!1).action(opts=>{(async()=>{if(opts.set.length===0&&!opts.json&&process.stdin.isTTY){const{runInteractiveConfigure}=await import("./commands/configure-interactive.js");return runInteractiveConfigure(opts.dir)}return runConfigure({dir:opts.dir,sets:opts.set,json:opts.json})})().catch(err=>{const msg=err instanceof Error?err.message:String(err);process.stderr.write(` Error: ${msg}
19
+ `),process.exit(1)})}),program.command("settings",{hidden:!0}).description("List every settable arbiter.json path with its current value (#1121)").option("--dir <dir>","Target directory (default: current directory)").option("--json","Emit machine-readable JSON output",!1).action(opts=>{try{runSettings({...opts.dir!==void 0?{dir:opts.dir}:{},json:opts.json})}catch(err){const msg=err instanceof Error?err.message:String(err);process.stderr.write(` Error: ${msg}
20
+ `),process.exit(1)}});const method=program.command("method",{hidden:!0}).description("Methodology lens: per-feature Config+Emit wiring status over `configure` (#2039)").option("--dir <dir>","Target directory (default: current directory)").option("--json","Emit machine-readable JSON output",!1).action(opts=>{(async()=>{if(!opts.json&&process.stdin.isTTY){const{runInteractiveMethod}=await import("./commands/method-interactive.js");return runInteractiveMethod(opts.dir)}runMethodStatus({...opts.dir!==void 0?{dir:opts.dir}:{},json:opts.json})})().catch(err=>{const msg=err instanceof Error?err.message:String(err);process.stderr.write(` Error: ${msg}
21
+ `),process.exit(1)})});method.command("status").description("Read-only per-feature methodology report (Config + Emit facets)").option("--dir <dir>","Target directory (default: current directory)").option("--json","Emit machine-readable JSON output",!1).action((_opts,cmd)=>{const merged=cmd.optsWithGlobals();try{runMethodStatus({...merged.dir!==void 0?{dir:merged.dir}:{},json:merged.json})}catch(err){const msg=err instanceof Error?err.message:String(err);process.stderr.write(` Error: ${msg}
22
+ `),process.exit(1)}}),program.command("diff").description("Show what arbiter update would change (dry run)").option("--dir <dir>","Target directory (default: current directory)").option("--json","Emit machine-readable JSON output",!1).option("--withheld","Show only template fixes withheld from user-modified files (#1344)",!1).option("--governance","Audit Iron Laws (AGENTS.md) and the permission deny list (.claude/settings.json) for staleness vs the current template; fail-closed (#2040)",!1).action(opts=>{runDiff({dir:opts.dir,json:opts.json,withheld:opts.withheld,governance:opts.governance})}),program.command("obsidian").description("Sync/validate the Obsidian vault via the repo-owned wiki scripts (#1979)").option("--repo <dir>","Target repo directory (default: current directory)").option("--vault-path <dir>","Vault directory relative to the repo root","wiki").option("--sync","Regenerate the vault then re-validate (fail-closed)",!1).option("--validate-only","Validate the existing vault without writing",!1).option("--write","Reserved for a future writer; v1 is read-only (ADR-001)",!1).option("--dry-run","Report only \u2014 writes nothing (default)",!1).option("--json","Emit machine-readable JSON output",!1).action(opts=>{const result=runObsidian({...opts.repo!==void 0?{dir:opts.repo}:{},vaultPath:opts.vaultPath,sync:opts.sync,validateOnly:opts.validateOnly,write:opts.write,dryRun:opts.dryRun,json:opts.json});opts.json?jsonOutput("obsidian",result.status,{...result}):result.reason?process.stdout.write(`obsidian: ${result.reason}
23
+ `):process.stdout.write(`obsidian: ${result.mode} \u2014 ${result.status} (vault: ${result.vaultDir})
24
+ `),process.exit(result.exitCode)});const plugin=program.command("plugin").description("Manage third-party arbiter plugins (arbiter.json `plugins[]`)");plugin.command("add <package>").description("Resolve, install, validate, and register a plugin (local path or npm package)").option("--dir <dir>","Target directory (default: current directory)").option("--no-install","Skip the package-manager install step (local paths never install)").option("--json","Emit machine-readable JSON output",!1).action((pkg,opts)=>{runPluginAdd({dir:opts.dir,pkg,install:opts.install,json:opts.json}).catch(err=>{const msg=err instanceof Error?err.message:String(err);process.stderr.write(` Error: ${msg}
25
+ `),process.exit(1)})}),plugin.command("list").description("List configured plugins with load status").option("--dir <dir>","Target directory (default: current directory)").option("--json","Emit machine-readable JSON output",!1).action(opts=>{runPluginList({dir:opts.dir,json:opts.json}).catch(err=>{const msg=err instanceof Error?err.message:String(err);process.stderr.write(` Error: ${msg}
26
+ `),process.exit(1)})});const worktree=program.command("worktree").alias("wt").description("Manage git worktrees for parallel task development");worktree.command("open <task-id> [slug]").description("Create a sibling worktree with a task branch and symlinked local files").option("--base <branch>","Base branch to branch from","main").option("--sibling [slug]","Place worktree at <repo>.worktrees/<slug> (sibling layout)").option("--with-build-links","Also materialize buildLinks from config",!1).option("--json","Emit machine-readable JSON output",!1).action((taskId,slug,opts)=>{runWorktreeOpen({taskId,...slug!==void 0?{slug}:{},base:opts.base,...opts.sibling!==void 0?{sibling:opts.sibling===!0?"":opts.sibling}:{},withBuildLinks:opts.withBuildLinks,json:opts.json}).catch(err=>{const msg=err instanceof Error?err.message:String(err);process.stderr.write(` Error: ${msg}
27
+ `),process.exit(1)})}),worktree.command("close <task-id>").description("Tear down a task worktree after its branch is merged").option("--force","Close even if branch is unmerged or hook fails",!1).option("--keep-branch","Do not delete the task branch after closing",!1).option("--no-fetch","Skip git fetch before the merge check",!1).option("--harvest","Copy modified/untracked files back to main repo before closing",!1).option("--harvest-all","Harvest all files and skip merge check (implies --force for cleanup)",!1).option("--json","Emit machine-readable JSON output",!1).action((taskId,opts)=>{runWorktreeClose({taskId,force:opts.force,keepBranch:opts.keepBranch,noFetch:!opts.fetch,harvest:opts.harvest,harvestAll:opts.harvestAll,json:opts.json})}),worktree.command("list").description("List open task worktrees").option("--json","Emit machine-readable JSON output",!1).action(opts=>{runWorktreeList({json:opts.json})}),worktree.command("relink <task-id>").description("Re-materialize configured links for an existing task worktree").option("--with-build-links","Also materialize buildLinks from config",!1).option("--json","Emit machine-readable JSON output",!1).action((taskId,opts)=>{try{runWorktreeRelink({taskId,withBuildLinks:opts.withBuildLinks,json:opts.json})}catch(err){const msg=err instanceof Error?err.message:String(err);process.stderr.write(` Error: ${msg}
28
+ `),process.exit(1)}}),worktree.command("prune").description("Reap zombie worktrees (#1873, ADR-103): clean trees that are merged or inactive beyond --stale hours. Dry-run by default; dirty trees are never touched (INV-96); inactive-unmerged candidates keep their branch.").option("--stale <hours>","Inactivity threshold in hours for unmerged worktrees",v=>{const n=parseInt(v,10);if(isNaN(n)||n<=0)throw new Error("--stale must be a positive integer (hours)");return n}).option("--execute","Close the candidates (default: dry-run report)",!1).option("--no-fetch","Skip git fetch before the merge check",!1).option("--json","Emit machine-readable JSON output",!1).action(opts=>{try{runWorktreePrune({...opts.stale!==void 0?{staleHours:opts.stale}:{},execute:opts.execute,noFetch:!opts.fetch,json:opts.json})}catch(err){const msg=err instanceof Error?err.message:String(err);process.stderr.write(` Error: ${msg}
29
+ `),process.exit(1)}}),program.command("gate-exec <cmd...>").description("Run a command under the per-repo gate mutex (#1873, ADR-103): every worktree of the same repo converges on ONE flock(1) lock, the wait is kernel-side (blocking), and flock closes the lock fd before the command and its descendants run. Usage: arbiter gate-exec [--key K] -- <cmd> [args...]. Exit code: passthrough of the command; 2 on gate-exec errors (e.g. flock missing \u2014 fail-closed).").option("--key <key>","Explicit mutex key (overrides per-repo derivation)").option("--dir <dir>","Target directory (default: current directory)").action(async(cmdArgs,opts)=>{try{const code=await runGateExec({cmdArgs,...opts.key!==void 0?{key:opts.key}:{},...opts.dir!==void 0?{dir:opts.dir}:{}});process.exit(code)}catch(err){const msg=err instanceof Error?err.message:String(err);process.stderr.write(` Error: ${msg}
30
+ `),process.exit(2)}});const review=program.command("review").description("Semantic diff between graph snapshots (#262)"),graph=program.command("graph",{hidden:!0}).description("Manage the provenance graph (#259)");graph.command("build").description("Build the provenance graph from invariants and write .arbiter/graph.json").option("--dir <dir>","Target directory (default: current directory)").option("--output <path>","Override output path (default: <dir>/.arbiter/graph.json)").option("--format <fmt>","Output format: json | dot | mermaid (default: json)","json").option("--json","Emit machine-readable JSON output",!1).action(opts=>{const buildOpts={};opts.dir!==void 0&&(buildOpts.dir=opts.dir),opts.output!==void 0&&(buildOpts.output=opts.output),buildOpts.format=opts.format;const result=runGraphBuild(buildOpts);opts.json?jsonOutput("graph build",result.status,{exitCode:result.exitCode,path:result.path,nodes:result.nodes,edges:result.edges},result.reason!==void 0?[result.reason]:void 0):result.status==="ok"?process.stdout.write(`graph build: wrote ${result.path} (${result.nodes} nodes, ${result.edges} edges)
31
+ `):process.stderr.write(`graph build: FAIL \u2014 ${result.reason??"unknown error"}
32
+ `),process.exit(result.exitCode)}),program.command("gold-audit [repo]").description("Deterministic gold-LEVEL band + missing-items report (#1414, wraps the engine)").option("--stack <stack>","Per-stack registry selector (standards/gold-registry.<stack>.yml)").option("--class <class>","Brownfield class for the level band: gold|light|medium|heavy").option("--check","No-regress gate: bootstrap missing baseline (exit 0), fail on regress",!1).option("--require-baseline","With --check, a missing baseline is a HARD FAIL (#1419)",!1).option("--json","Emit machine-readable JSON output",!1).option("--cockpit","Render the rich TTY-gated goldness console (#1475)",!1).option("--ascii","Force pure-ASCII cockpit output (no unicode glyphs/ANSI)",!1).action((repo,opts)=>{const cls=opts.class==="gold"||opts.class==="light"||opts.class==="medium"||opts.class==="heavy"?opts.class:void 0;opts.class!==void 0&&cls===void 0&&(printCliError(`invalid --class "${opts.class}". Valid: gold, light, medium, heavy.`),process.exit(1));const result=runGoldAudit({...repo!==void 0?{repo}:{},...opts.stack!==void 0?{stack:opts.stack}:{},...cls!==void 0?{class:cls}:{},check:opts.check,requireBaseline:opts.requireBaseline,json:opts.json,cockpit:opts.cockpit,ascii:opts.ascii});process.exit(result.exitCode)}),review.command("cross-model").description("Run the configured Codex review seat against a diff from stdin (#2357)").requiredOption("--task <id>","Task issue id, for example #2357").requiredOption("--prompt <text>","Review prompt; the code diff is read from stdin").option("--tier <tier>","Ship tier (XS|S|Standard; default: Standard)").option("--phase <phase>","Task phase (default: refactor)").option("--vertical <vertical>","Review vertical (default: bugs)").option("--dir <dir>","Target directory (default: current directory)").action(opts=>{const result=runCrossModelReview({taskId:opts.task,prompt:opts.prompt,...opts.tier!==void 0?{tier:opts.tier}:{},...opts.phase!==void 0?{phase:opts.phase}:{},...opts.vertical!==void 0?{vertical:opts.vertical}:{},...opts.dir!==void 0?{dir:opts.dir}:{}});process.stdout.write(`${JSON.stringify(result)}
33
+ `)}),program.command("doc-set [repo]",{hidden:!0}).description("Deterministic gold doc-set presence audit (H1, gold-doc-capability: wraps scripts/check-doc-set.mjs). Required-set is tier-parameterized by collaborationMode (solo/small/enterprise) \u2014 see standards/gold-doc-set.yml `tiers{}`.").option("--strict","Exit 1 if any mandatory doc is missing (default: advisory, exit 0)",!1).option("--check","Run the default advisory presence audit (backward-compat alias for the no-flag default; emitted governed-repo runners pass it)",!1).option("--json","Emit the audit as JSON",!1).option("--generate","Scaffold stub files for missing mandatory+recommended .md docs",!1).option("--refresh-stubs","(with --generate) re-render a doc in place only if it is byte-equal to the stub template",!1).option("--manifest <path>","Manifest path override (default standards/gold-doc-set.yml)").option("--doc-profile <path>","Overlay profile path override (default standards/doc-profile)").option("--plan","T3: dry-run the skeleton generator \u2014 report would-scaffold/unbound, write nothing",!1).option("--apply","T3: scaffold real per-doc-type skeletons for missing bound rows (skipIfExists; never overwrites a hand-edited doc; upgrades a byte-equal banner stub)",!1).option("--freshness","T4: run the per-doc freshness audit (scripts/check-doc-freshness.mjs) instead of presence",!1).option("--arc42","INV-144: run the arc42 slot-completeness audit (scripts/check-arc42-slots.mjs) instead of presence \u2014 every slot the tier's arc42 skeleton provides must be present and filled",!1).option("--update-baseline","(with --arc42) re-record the hollow-slot ratchet; refused when a counter rose",!1).action((repo,opts)=>{if(refuseUnsupportedArc42Flags(opts),opts.plan||opts.apply){const result2=runDocSetPlanApply({...repo!==void 0?{repo}:{},apply:opts.apply,...opts.manifest!==void 0?{manifest:opts.manifest}:{},...opts.docProfile!==void 0?{profile:opts.docProfile}:{}});printDocSetPlanApplyReport(result2,opts.apply),process.exit(opts.apply&&result2.scaffolded.length===0&&result2.unbound.length>0?1:0)}const result=runDocSet({...repo!==void 0?{repo}:{},strict:opts.strict,json:opts.json,generate:opts.generate,refreshStubs:opts.refreshStubs,freshness:opts.freshness,arc42:opts.arc42,updateBaseline:opts.updateBaseline,...opts.manifest!==void 0?{manifest:opts.manifest}:{},...opts.docProfile!==void 0?{profile:opts.docProfile}:{}});process.exit(result.exitCode)});function printProvenanceLines(provenance){if(provenance!==void 0)for(const line of formatProvenance(provenance))process.stdout.write(`${line}
34
+ `)}const verify=program.command("validate").alias("verify").description("Probe toolchain compatibility for the detected stack").option("--json","Emit JSON report",!1).option("--dir <dir>","Target directory (default: current directory)").action(opts=>{runVerify({json:opts.json,dir:opts.dir})});verify.command("evidence").description("Verify the .evidence/SUMMARY.json snapshot (SHA + freshness window).").option("--json","Emit machine-readable JSON output",!1).option("--dir <dir>","Target directory (default: current directory)").action((opts,cmd)=>{const json=!!cmd.optsWithGlobals().json,result=runVerifyEvidence({dir:opts.dir});if(json)jsonOutput("verify evidence",result.status,{exitCode:result.exitCode,...result.skipped!==void 0?{skipped:result.skipped}:{},...result.reason!==void 0?{reason:result.reason}:{},...result.provenance!==void 0?{provenance:result.provenance}:{}},result.status==="error"&&result.reason!==void 0?[result.reason]:void 0);else{const label=result.status==="ok"?"OK":result.status.toUpperCase(),tail=result.reason?` \u2014 ${result.reason}`:"";process.stdout.write(`verify evidence: ${label}${tail}
35
+ `),printProvenanceLines(result.provenance)}process.exit(result.exitCode)}),verify.command("plan <file>").description("Validate a PLAN.json against invariant rules and write REVIEW.json (#253)").option("--dir <dir>","Project root (default: current directory)").option("--reviewer <name>","Reviewer name in REVIEW.json").option("--fail-on-warn","Treat WARN violations as REJECTED",!1).option("--json","Emit machine-readable JSON output",!1).action(async(file,opts,cmd)=>{const json=!!cmd.optsWithGlobals().json,{resolve}=await import("node:path"),dir=resolve(opts.dir??"."),stored=loadConfig(dir),pluginNames=stored!=null&&Array.isArray(stored.plugins)?stored.plugins:[],extraRules=[];for(const pkg of pluginNames)try{const plugin2=await loadPlugin(pkg,dir);Array.isArray(plugin2.verifyPlanRules)&&extraRules.push(...plugin2.verifyPlanRules)}catch(err){process.stderr.write(`[arbiter] verify plan: plugin "${pkg}" failed to load \u2014 its rules will not run: ${err instanceof Error?err.message:String(err)}
36
+ `)}const result=runVerifyPlan({file,dir,...opts.reviewer!==void 0?{reviewer:opts.reviewer}:{},failOnWarn:opts.failOnWarn,json,extraRules});process.exit(result.exitCode)}),verify.command("graph").description("Verify the provenance graph (#259) \u2014 fails on orphan invariants (no enforces / no implements)").option("--dir <dir>","Target directory (default: current directory)").option("--input <path>","Override graph snapshot path (default: <dir>/.arbiter/graph.json)").option("--json","Emit machine-readable JSON output",!1).action((opts,cmd)=>{const commandOpts=cmd.optsWithGlobals(),json=!!commandOpts.json,verifyOpts={};commandOpts.dir!==void 0&&(verifyOpts.dir=commandOpts.dir),opts.input!==void 0&&(verifyOpts.input=opts.input);const result=runVerifyGraph(verifyOpts);if(json)jsonOutput("verify graph",result.status,{exitCode:result.exitCode,path:result.path,totalInv:result.totalInv,orphans:result.orphans},result.reason!==void 0?[result.reason]:void 0);else if(result.status==="ok")process.stdout.write(`verify graph: OK (${result.totalInv} invariant(s) checked, 0 orphan)
37
+ `);else{process.stderr.write(`verify graph: FAIL \u2014 ${result.reason??"unknown error"}
38
+ `);for(const orphan of result.orphans)process.stderr.write(` orphan: ${orphan.id} \u2014 ${orphan.reason}
39
+ `)}process.exit(result.exitCode)}),verify.command("tdd <task-id>").description("Verify TDD red-phase evidence for a task \u2014 replayable audit (#553)").option("--dir <dir>","Target directory / repo root (default: current directory)").option("--json","Emit machine-readable JSON output",!1).action((taskId,opts,cmd)=>{const json=!!cmd.optsWithGlobals().json,result=runVerifyTdd({taskId,json,...opts.dir!==void 0?{dir:opts.dir}:{}});json?jsonOutput("verify tdd",result.status==="PASS"?"ok":"error",{exitCode:result.exitCode,taskId:result.taskId,checks:result.checks??[]},result.reason!==void 0?[result.reason]:void 0):result.status==="PASS"?process.stdout.write(`verify tdd: PASS (${result.checks?.length??0} checks)
40
+ `):result.status==="DEGRADED"?process.stderr.write(`verify tdd: DEGRADED \u2014 ${result.reason??"unknown"}
41
+ `):process.stderr.write(`verify tdd: FAIL \u2014 ${result.reason??"unknown"}
42
+ `),process.exit(result.exitCode)}),program.command("upgrade-level",{hidden:!0}).description("Upgrade governance level with a grace period for new gates").option("--target <level>","Target level (L2 or L3)").option("--extend","Extend an existing active grace period by --days (default: 30)",!1).option("--days <n>","Grace period length in days (default: 30)").option("--dir <dir>","Target directory (default: current directory)").option("--interactive","Guided level selection on a TTY (#1168)",!1).option("--json","Emit machine-readable JSON output",!1).action(opts=>{if(opts.interactive&&!opts.json&&process.stdin.isTTY){import("./commands/upgrade-level-interactive.js").then(({runInteractiveUpgradeLevel})=>runInteractiveUpgradeLevel({...opts.dir!==void 0?{dir:opts.dir}:{}})).catch(err=>{process.stderr.write(` Error: ${err instanceof Error?err.message:String(err)}
43
+ `),process.exit(1)});return}const upgradeOpts={extend:opts.extend,json:opts.json};if(opts.target&&(opts.target!=="L2"&&opts.target!=="L3"&&(printCliError(`invalid --target "${opts.target}". Valid values: L2, L3.`),getLogger().error("invalid_target",{value:opts.target??null}),process.exit(1)),upgradeOpts.target=opts.target),opts.days!==void 0){const parsedDays=Number.parseInt(opts.days,10);(!Number.isInteger(parsedDays)||parsedDays<1)&&(printCliError(`invalid --days "${opts.days}". Must be a positive integer (>= 1).`),getLogger().error("invalid_days",{value:opts.days}),process.exit(1)),upgradeOpts.days=parsedDays}opts.dir!==void 0&&(upgradeOpts.dir=opts.dir),runUpgradeLevel(upgradeOpts).catch(err=>{const msg=err instanceof Error?err.message:String(err);process.stderr.write(` Error: ${msg}
44
+ `),process.exit(1)})});function runDoctorHealthAction(_opts,cmd){const opts=cmd.optsWithGlobals();if(opts.proveGates){const result=runDoctorProveGates({json:opts.json});result.exitCode!==0&&process.exit(result.exitCode);return}if(opts.interactive&&!opts.json&&process.stdin.isTTY){import("./commands/doctor-interactive.js").then(({runInteractiveDoctor})=>runInteractiveDoctor({...opts.dir!==void 0?{dir:opts.dir}:{}})).catch(err=>{process.stderr.write(` Error: ${err instanceof Error?err.message:String(err)}
45
+ `),process.exit(1)});return}runDoctorHealth({...opts.dir!==void 0?{dir:opts.dir}:{},json:opts.json,repair:opts.repair,..._channelFlag!==void 0?{channelFlag:_channelFlag}:{}}).then(result=>{result.exitCode!==0&&process.exit(result.exitCode)}).catch(err=>{const msg=err instanceof Error?err.message:String(err);process.stderr.write(` Error: ${msg}
46
+ `),process.exit(1)})}const doctor=program.command("doctor").description("Diagnose and repair arbiter state").option("--dir <dir>","Target directory (default: current directory)").option("--json","Emit machine-readable JSON output",!1).option("--repair","Auto-release stale .arbiter/.lock files detected by the health check (#824)",!1).option("--interactive","Guided health check with one-key repair on a TTY (#1168)",!1).option("--prove-gates","Run negative proofs for every tier-1 conformance gate; report any gate that does not bite (#1817, A5)",!1).action(runDoctorHealthAction);doctor.command("health").description("Run arbiter health checks").option("--dir <dir>","Target directory (default: current directory)").option("--json","Emit machine-readable JSON output",!1).option("--repair","Auto-release stale .arbiter/.lock files detected by the health check (#824)",!1).option("--interactive","Guided health check with one-key repair on a TTY (#1168)",!1).option("--prove-gates","Run negative proofs for every tier-1 conformance gate; report any gate that does not bite (#1817, A5)",!1).action(runDoctorHealthAction),doctor.command("repair-state").description("Re-derive .arbiter-generated.json from arbiter.json (snapshot corruption recovery)").option("--dir <dir>","Target directory (default: current directory)").option("--json","Emit machine-readable JSON output",!1).action((_opts,cmd)=>{const opts=cmd.optsWithGlobals();runDoctorRepairState({...opts.dir!==void 0?{dir:opts.dir}:{},json:opts.json}).then(result=>{result.exitCode!==0&&process.exit(result.exitCode)}).catch(err=>{const msg=err instanceof Error?err.message:String(err);process.stderr.write(` Error: ${msg}
47
+ `),process.exit(1)})}),doctor.command("recover-lock").description("Force-release a stale .arbiter/.lock file left by a crashed process").option("--dir <dir>","Target directory (default: current directory)").option("--force","Release a live or unconfirmed lock deliberately",!1).option("--json","Emit machine-readable JSON output",!1).action((_opts,cmd)=>{const opts=cmd.optsWithGlobals();runDoctorRecoverLock({...opts.dir!==void 0?{dir:opts.dir}:{},force:opts.force,json:opts.json}).catch(err=>{const msg=err instanceof Error?err.message:String(err);process.stderr.write(` Error: ${msg}
48
+ `),process.exit(1)})}),doctor.command("clean").description("Remove arbiter backup files (*.arbiter-backup, .arbiter-generated.json.bak.*)").option("--dir <dir>","Target directory (default: current directory)").option("--dry-run","List files without deleting",!1).option("--json","Emit machine-readable JSON output",!1).action((_opts,cmd)=>{const opts=cmd.optsWithGlobals();try{runDoctorClean({...opts.dir!==void 0?{dir:opts.dir}:{},dryRun:opts.dryRun,json:opts.json})}catch(err){const msg=err instanceof Error?err.message:String(err);process.stderr.write(` Error: ${msg}
49
+ `),process.exit(1)}}),doctor.command("tool-pins").description("Compare local tool versions against CI workflow pins (see `check-ci-tool-parity.mjs` for CI-internal manifest parity \u2014 a different, non-overlapping check)").option("--dir <dir>","Target directory (default: current directory)").option("--json","Emit machine-readable JSON output",!1).action((_opts,cmd)=>{const opts=cmd.optsWithGlobals(),result=runDoctorToolPins({...opts.dir!==void 0?{dir:opts.dir}:{},json:opts.json});result.exitCode!==0&&process.exit(result.exitCode)}),doctor.command("fail-open-census").description("Census `command -v X || <fail-open>` and positive `if command -v X; then ... fi` gate-script presence-gates (see `check-fail-closed-audit.mjs` for a different pattern class \u2014 `|| true` / swallowed catch \u2014 self-scoped to arbiter's own repo)").option("--dir <dir>","Target directory (default: current directory)").option("--json","Emit machine-readable JSON output",!1).option("--allowlist <path>","Override allowlist path (default: .arbiter/fail-open-allowlist.json)").action((_opts,cmd)=>{const opts=cmd.optsWithGlobals(),result=runDoctorFailOpenCensus({...opts.dir!==void 0?{dir:opts.dir}:{},json:opts.json,...opts.allowlist!==void 0?{allowlistPath:opts.allowlist}:{}});result.exitCode!==0&&process.exit(result.exitCode)});const task=program.command("task").description("Manage task lifecycle state");task.command("resume").description("Print recovery instructions for the current task phase").option("--dir <dir>","Target directory (default: current directory)").action(opts=>{runTaskResume({...opts.dir!==void 0?{dir:opts.dir}:{}})});function advanceLandingFlags(opts){return opts.pr===!1?{noPr:!0}:typeof opts.pr=="number"?{pr:opts.pr}:{}}task.command("advance").description("Advance (or reverse) the task lifecycle phase").requiredOption("--to <phase>","Target phase (preflight|plan|red-team-review|red|green|refactor|verification|close|complete|red-team-rework)").option("--reverse","Allow backward phase transitions",!1).option("--dir <dir>","Target directory (default: current directory)").option("--skip-plan-review","Bypass the plan-review gate (writes audit record + WARNING)",!1).option("--post-clear","Signal post-/clear re-entry (equivalent to ARBITER_POST_CLEAR=1)",!1).option("--no-pr","Complete without a merged PR \u2014 this repo lands by direct push (logged)").option("--pr <n>","Verify this PR number when the branch carries more than one",v=>{const n=parseInt(v,10);if(isNaN(n)||n<=0)throw new Error("--pr must be a positive integer");return n}).action(opts=>{try{runTaskAdvance({to:opts.to,reverse:opts.reverse,skipPlanReview:opts.skipPlanReview,postClear:opts.postClear,...advanceLandingFlags(opts),...opts.dir!==void 0?{dir:opts.dir}:{}})}catch(err){throw err instanceof HandoffRequiredError&&(process.stderr.write(err.message+`
50
+ `),process.exit(78)),err}}),task.command("recover").description("Print 3-layer recovery context for the current task (#694)").option("--dir <dir>","Target directory (default: current directory)").option("--task <id>","Task id (default: the active task)").action(opts=>{runTaskRecover({...opts.dir!==void 0?{dir:opts.dir}:{},...opts.task!==void 0?{taskId:opts.task}:{}})}),task.command("record-red").description("Record TDD red-phase evidence: run a failing test and capture evidence (#551)").requiredOption("--test-path <path>","Repo-relative path to the failing test file").option("--dir <dir>","Target directory / repo root (default: current directory)").option("--task <id>","Task id; use for a declared secondary issue on a train (#2336)").option("--test-command <cmd>","Override the test runner binary (e.g. go, pytest, npx). Overrides language-based auto-selection. The command is passed verbatim to the runner (no shell interpolation).").option("--test-arg <arg>","Argument to the test command (repeatable). Combined with --test-command in the order given.",(val,acc)=>acc.concat(val),[]).option("--timeout-ms <ms>","Test-run timeout in ms (default 60000, clamped to 1..600000)").option("--force","Skip the dirty-__tests__ and test-path-in-HEAD refusals (#1988)",!1).action(opts=>{const testCmd=opts.testCommand!==void 0?[opts.testCommand,...opts.testArg??[]]:void 0,timeoutMs=opts.timeoutMs!==void 0?Number.parseInt(opts.timeoutMs,10):void 0,result=runTaskRecordRed({testPath:opts.testPath,...opts.dir!==void 0?{dir:opts.dir}:{},...opts.task!==void 0?{taskId:opts.task}:{},...testCmd!==void 0?{testCmd}:{},...timeoutMs!==void 0?{timeoutMs}:{},force:opts.force});result.ok?process.stdout.write(`record-red: OK (framework=${result.framework})
51
+ evidence: ${result.evidencePath}
52
+ `):(process.stderr.write(`record-red: FAIL \u2014 ${result.reason}
53
+ `),process.exit(1))}),task.command("record-tech-debt").description("File a tech-debt GitHub issue and persist evidence (#702)").requiredOption("--description <text>","Short description of the tech-debt finding").option("--triggered-by <task-id>","Task ID that triggered this debt filing (default: the active task)").option("--dir <dir>","Target directory / repo root (default: current directory)").action(opts=>{const result=runTaskRecordTechDebt({description:opts.description,...opts.triggeredBy!==void 0?{triggeredBy:opts.triggeredBy}:{},...opts.dir!==void 0?{dir:opts.dir}:{}});result.ok?process.stdout.write(`record-tech-debt: OK (issue #${result.issueNumber})
54
+ `):(process.stderr.write(`record-tech-debt: FAIL \u2014 ${result.reason}
55
+ `),process.exit(1))}),task.command("init [ids...]").description("Initialise / update the unified task document (#1206)").option("--id <id>","Task id, e.g. #1206").option("--tier <tier>","Task tier (XS|S|Standard)").option("--plan <path>","Repo-relative path to the plan file").option("--chain <id>","Other issue id batched into this task worktree/gate/PR (repeatable, #2102)",(v,acc)=>[...acc,v],[]).option("--dir <dir>","Target directory (default: current directory)").action((ids,opts)=>{const train=splitTrainIds(ids,opts.id,opts.chain);runTaskInit({...train.taskId!==void 0?{id:train.taskId}:{},...opts.tier!==void 0?{tier:opts.tier}:{},...opts.plan!==void 0?{plan:opts.plan}:{},...train.chainIds.length>0?{chainIds:train.chainIds}:{},...opts.dir!==void 0?{dir:opts.dir}:{}})}),task.command("get").description("Print a single task-state field for shell consumers (#1206)").requiredOption("--field <field>","phase|taskId|tier|plan|tddPhase|lastAction|nextAction").option("--dir <dir>","Target directory (default: current directory)").action(opts=>{runTaskGet({field:opts.field,...opts.dir!==void 0?{dir:opts.dir}:{}})}),program.command("mark",{hidden:!0}).description("Pinpoint: snapshot the step-cursor so a mid-task /clear resumes exactly (#1206)").option("--next <action>","The exact next sub-step to resume on").option("--last <action>","The sub-step just completed").option("--tdd <phase>","TDD sub-phase (RED|GREEN|REFACTOR)").option("--task <id>","Set/override the active task id").option("--digest <line>","One-line progress digest for log.md").option("--dir <dir>","Target directory (default: current directory)").action(opts=>{let tddPhase;if(opts.tdd!==void 0){const upper=opts.tdd.toUpperCase();isTddPhase(upper)||(process.stderr.write(`Invalid --tdd value "${opts.tdd}". Valid: RED, GREEN, REFACTOR
56
+ `),process.exit(2)),tddPhase=upper}runTaskMark({...opts.next!==void 0?{next:opts.next}:{},...opts.last!==void 0?{last:opts.last}:{},...tddPhase!==void 0?{tddPhase}:{},...opts.task!==void 0?{taskId:opts.task}:{},...opts.digest!==void 0?{digest:opts.digest}:{},...opts.dir!==void 0?{dir:opts.dir}:{}})});function shipReviewFlags(opts){return{...opts.reviewRound?{reviewRound:!0}:{},...opts.forceReview?{forceReview:!0}:{}}}program.command("ship [ids...]").description("Orchestrate an issue \u2192 reviewed, merged PR over the existing engine (#1206)").option("--tier <tier>","Task tier (XS|S|Standard)").option("--autonomy <level>","Per-run autonomy override (L0|L1|L2|L3) \u2014 beats arbiter.json automation.autonomy (#1291)",v=>{if(!["L0","L1","L2","L3"].includes(v))throw new Error("--autonomy must be one of L0|L1|L2|L3");return v}).option("--set <path=value>","Per-run override of an overridable config path (repeatable, ADR-094). Gated by OVERRIDABLE_PATHS; persists to the session layer so it survives /clear. e.g. --set automation.autonomy=L3",(v,acc)=>[...acc,v],[]).option("--advance","Advance to the next phase (runs that phase gate; fails if red)",!1).option("--skip-plan-review","Bypass the plan-review gate on advance",!1).option("--post-clear","Signal post-/clear re-entry on advance",!1).option("--units <n>","Implementation unit count from the plan \u2014 drives the size-driven clear decision",v=>{const n=parseInt(v,10);if(isNaN(n)||n<=0)throw new Error("--units must be a positive integer");return n}).option("--chain <id>","Other issue id batched into this ship worktree/gate/PR (repeatable, #2102)",(v,acc)=>[...acc,v],[]).option("--chain-add <id>","Append an issue to the open train, or refuse if it must seal first (repeatable, #2331)",(v,acc)=>[...acc,v],[]).option("--seal","Seal the open train now \u2014 land it before starting another (#2331)",!1).option("--no-pr","Complete without a merged PR \u2014 this repo lands by direct push (logged)").option("--pr <n>","Verify this PR number when the branch carries more than one",v=>{const n=parseInt(v,10);if(isNaN(n)||n<=0)throw new Error("--pr must be a positive integer");return n}).option("--review-round","Record another code-review round on this task (entering refactor records the first)",!1).option("--force-review","Take a review round past ship.review.maxRounds, and record that it was forced",!1).option("--dir <dir>","Target directory (default: current directory)").action((ids,opts)=>{try{const shipRoot=opts.dir??process.cwd(),overrides=buildShipOverrides(shipRoot,{sets:opts.set,...opts.autonomy!==void 0?{autonomy:opts.autonomy}:{}}),externalModelAccess=externalModelAccessForShip(shipRoot,overrides),train=splitTrainIds(ids,void 0,opts.chain),result=runTaskShip({...Object.keys(overrides).length>0?{overrides}:{},...train.taskId!==void 0?{taskId:train.taskId}:{},...opts.tier!==void 0?{tier:opts.tier}:{},...train.chainIds.length>0?{chainIds:train.chainIds}:{},...opts.chainAdd.length>0?{chainAddIds:opts.chainAdd}:{},...opts.seal?{seal:!0}:{},...shipReviewFlags(opts),advance:opts.advance,advanceOpts:{skipPlanReview:opts.skipPlanReview,postClear:opts.postClear,...opts.units!==void 0?{units:opts.units}:{},...advanceLandingFlags(opts)},...opts.dir!==void 0?{dir:opts.dir}:{},...externalModelAccess!==void 0?{externalModelAccess}:{}}),outputResult=shipOutputAfterConfiguredReview(shipRoot,result,opts.tier,externalModelAccess),lines=buildShipStepLines(outputResult);process.stdout.write(lines.join(`
57
+ `)+`
58
+ `)}catch(err){throw err instanceof HandoffRequiredError&&(process.stderr.write(err.message+`
59
+ `),process.exit(78)),err}});function exitReviewDiffFailure(json,exitCode,data,message){json?jsonOutput("review diff","error",data,[message]):process.stderr.write(message+`
60
+ `),process.exit(exitCode)}review.command("diff").description("Semantic diff between two graph snapshots (#262)").option("--base <path>","Base graph snapshot path (default: reads from stdin or .arbiter/graph-base.json)").option("--head <path>","Head graph snapshot path (default: .arbiter/graph.json)").option("--dir <dir>","Target directory (default: current directory)").option("--json","Emit machine-readable JSON output",!1).option("--post-pr <number>","Post result as GitHub PR comment").action(async opts=>{const{existsSync:efs}=await import("node:fs"),{resolve:res,join:pjoin}=await import("node:path"),{loadGraphSnapshot:loadGraph}=await import("./graph/load.js"),dir=res(opts.dir??"."),headPath=opts.head!==void 0?res(opts.head):pjoin(dir,".arbiter","graph.json"),basePath=opts.base!==void 0?res(opts.base):pjoin(dir,".arbiter","graph-base.json");if(!efs(headPath)){const message=`review diff: FAIL \u2014 head graph not found at ${headPath}`;exitReviewDiffFailure(opts.json,2,{headPath},message)}efs(basePath)||process.stderr.write(`review diff: WARN \u2014 base graph not found at ${basePath}, using empty base
61
+ `);const headOutcome=loadGraph(headPath);if(!headOutcome.ok){const message=`review diff: FAIL \u2014 ${headOutcome.reason}`;exitReviewDiffFailure(opts.json,2,{headPath},message)}let base={nodes:[],edges:[]};if(efs(basePath)){const baseOutcome=loadGraph(basePath);if(!baseOutcome.ok){const message=`review diff: FAIL \u2014 ${baseOutcome.reason}`;exitReviewDiffFailure(opts.json,2,{basePath},message)}base=baseOutcome.snapshot}const result=runReviewDiff({base,head:headOutcome.snapshot});if(opts.json?jsonOutput("review diff",result.status,{exitCode:result.exitCode,recommendation:result.recommendation,risk_delta:result.risk_delta,changes:result.changes,summary:result.summary},result.reason!==void 0?[result.reason]:void 0):process.stdout.write(renderMarkdown(result)+`
62
+ `),opts.postPr!==void 0){const{runCli:rc}=await import("./utils/run-cli.js"),markdown=renderMarkdown(result);try{rc("gh",["pr","comment",opts.postPr,"--body",markdown])}catch(err){process.stderr.write(`review diff: failed to post PR comment: ${err instanceof Error?err.message:String(err)}
63
+ `)}}process.exit(result.exitCode)}),program.command("explain [code]").description("Show detailed explanation for an error code, INV-NN invariant, or CANON-NN rule").option("--format <format>","Output format: text (default) or json").option("--list","List all known codes grouped by category").option("--handoff <topic>","Scaffold HANDOFF-<TOPIC>.md from the executable-handoff template (#1817 A7)").option("--out <dir>","Target directory for --handoff (default: cwd)").action((code,opts)=>{const result=runExplain(code??"",opts);result.output&&process.stdout.write(result.output),result.error&&process.stderr.write(result.error),result.exitCode!==0&&process.exit(result.exitCode)}),program.command("help [command]",{hidden:!0}).description("Display help for a command").option("--all","Also list experimental (hidden) commands",!1).action((commandName,opts)=>{if(opts.all){const helpRenderer=program.createHelp(),visibleNames=new Set(helpRenderer.visibleCommands(program).map(c=>c.name())),hidden=program.commands.filter(c=>!visibleNames.has(c.name())&&c.name()!=="help");process.stdout.write(program.helpInformation()),process.stdout.write(`
64
+ Experimental commands:
65
+ `);const width=Math.max(...hidden.map(c=>c.name().length));for(const cmd of hidden.slice().sort((a,b)=>a.name().localeCompare(b.name())))process.stdout.write(` ${cmd.name().padEnd(width+2)}${cmd.summary()||cmd.description()}
66
+ `);return}if(commandName){const sub=program.commands.find(c=>c.name()===commandName||c.aliases().includes(commandName));sub&&sub.help(),process.stderr.write(`error: unknown command '${commandName}'
67
+ `),process.exit(1)}program.help()});function _writeArbiterError(err,prefix="Error"){if(_jsonRequested){jsonOutput("arbiter","error",{},[err.message],{code:err.code,...err instanceof FatalError?{errorClass:"fatal"}:err instanceof ConfigError?{errorClass:"config"}:err instanceof RecoverableError?{errorClass:"recoverable"}:{}});return}process.stderr.write(`
68
+ ${prefix} [${err.code}]: ${err.message}
69
+ `),err.hint&&process.stderr.write(` Hint: ${err.hint}
70
+ `),err.docUrl&&process.stderr.write(` Docs: ${err.docUrl}
71
+ `),process.stderr.write(`
72
+ Run \`arbiter explain ${err.code}\` for more detail.
73
+ `),_verbose&&err.stack&&process.stderr.write(`
74
+ ${err.stack}
75
+ `)}function _writeStackIfVerbose(err){_verbose&&err.stack&&process.stderr.write(`
76
+ ${err.stack}
77
+ `)}function _handleTopLevelError(err){err instanceof FatalError?(!_jsonRequested&&err.recoverableContext?.length&&process.stderr.write(`
78
+ Recoverable errors before fatal:
79
+ ${err.recoverableContext.map(w=>` - ${w}`).join(`
80
+ `)}
81
+ `),_writeArbiterError(err,"Fatal error"),process.exit(2)):err instanceof ConfigError?(_writeArbiterError(err,"Config error"),process.exit(78)):err instanceof RecoverableError?(_writeArbiterError(err),process.exit(1)):err instanceof SnapshotChecksumError?(process.stderr.write(`
82
+ Checksum error: ${err.message}
83
+ `),process.stderr.write(" The .arbiter-generated.json snapshot is corrupt. Delete it and re-run `arbiter update`.\n"),_writeStackIfVerbose(err),process.exit(2)):err instanceof ArbiterError?_writeArbiterError(err):err instanceof UserFacingError?(process.stderr.write(`Error: ${err.message}
84
+ `),_writeStackIfVerbose(err)):err instanceof Error?(process.stderr.write(`Unexpected error: ${err.message}
85
+ `),_verbose&&process.stderr.write(`${err.stack??""}
86
+ `)):process.stderr.write(`Unexpected error: ${String(err)}
87
+ `),process.exit(1)}async function _main(){await _startProfileIfRequested();try{await program.parseAsync()}finally{if(_profilerHandle!==null)try{const path=await _profilerHandle.stop();getLogger().info("profiler.written",{path})}catch(err){getLogger().warn("profiler.stop_failed",void 0,String(err))}}}_main().catch(_handleTopLevelError);
@@ -0,0 +1,3 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import{createHash}from"node:crypto";import{join}from"node:path";import{ensureDir,writeFileTranslated}from"../utils/fs.js";import{isGateSpineKey,isGovernanceClassKey,isSafetyClassKey}from"../generators/safety-class.js";import{isDerivedTrackKey}from"../generators/derived-class.js";function sha256(content){return createHash("sha256").update(content).digest("hex")}export function buildAdoptPredicate(options){const adoptAll=options.adopt===!0,adoptSafety=options.noAdoptSafety!==!0,adoptGateSpine=options.adoptGateSpine===!0,adoptGovernance=options.adoptGovernance===!0,refreshDerived=options.refreshDerived===!0,informative=(key,provenanceKnown)=>provenanceKnown&&(adoptGateSpine&&isGateSpineKey(key)||adoptGovernance&&isGovernanceClassKey(key)||refreshDerived&&isDerivedTrackKey(key));return(key,provenanceKnown)=>adoptAll||adoptSafety&&isSafetyClassKey(key)||informative(key,provenanceKnown)}function localOverrideSlug(key){return key.replace(/^\.+/,"").replace(/[/\\]+/g,"__")}function localOverrideReason(key){return isDerivedTrackKey(key)?"update --refresh-derived: codex-track derived file force-refreshed to the current template render (skipIfExists bypassed for this known set only)":isGovernanceClassKey(key)?"update --adopt-governance: governance file force-adopted over locally-modified content \u2014 AGENTS.md carries the Iron Laws and .claude/settings.json the ARBITER_* deny list; this now happens only under explicit --adopt-governance (#2056, #2120, #2141; mark the file `arbiter:preserve` to freeze it)":isGateSpineKey(key)?"update: gate-spine file force-adopted over user-modified content \u2014 the gate entrypoint and its libs are the delivery vector for every later fix (#2109, reversed by #2119: this now happens only under explicit --adopt-gate-spine)":"update --adopt: template fix force-adopted over user-modified content (safety-class files adopt by default; see --no-adopt-safety)"}export function recordLocalOverride(targetDir,record,now=()=>new Date){const dir=join(targetDir,".arbiter","evidence","local-overrides");ensureDir(dir);const envelope={path:record.key,adoptedAt:now().toISOString(),reason:localOverrideReason(record.key),priorContent:record.priorContent,priorContentSha256:sha256(record.priorContent),newContent:record.newContent,newContentSha256:sha256(record.newContent)},file=join(dir,`${localOverrideSlug(record.key)}.json`);return writeFileTranslated(file,JSON.stringify(envelope,null,2)+`
3
+ `),file}
@@ -0,0 +1,3 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import{resolve}from"node:path";import{intro,outro,select,multiselect,confirm,text,isCancel,cancel,note}from"@clack/prompts";import{loadConfig}from"../utils/config.js";import{t}from"../i18n/index.js";import{runConfigure}from"./configure.js";import{SUPPORTED_AI_TOOLS}from"../wizard/types.js";function diffStr(path,oldVal,newVal){return oldVal===newVal?[]:[`${path}=${newVal}`]}function diffBool(path,oldVal,newVal){return oldVal===newVal?[]:[`${path}=${String(newVal)}`]}function diffArr(path,oldArr,newArr){const a=[...oldArr].sort().join(","),b=[...newArr].sort().join(",");return a===b?[]:[`${path}=${newArr.join(",")}`]}function axisDefaults(config){return{archetype:config.archetype??"backend-web-db",architectureStyle:config.architectureStyle??"none",isMultiTenant:config.isMultiTenant??!1,hasDatabase:config.hasDatabase??!1,hasPublicApi:config.hasPublicApi??!1,contractType:config.contractType??"none"}}async function promptAxisGroup(config){note(t("cli.configure.interactive.axis_header"));const d=axisDefaults(config),archetype=await select({message:"Project archetype",options:[{value:"backend-web-db",label:"backend-web-db"},{value:"cli",label:"cli"},{value:"library",label:"library"},{value:"data-pipeline",label:"data-pipeline"},{value:"frontend-spa",label:"frontend-spa"},{value:"embedded",label:"embedded"}],initialValue:d.archetype});if(isCancel(archetype))return null;const architectureStyle=await select({message:"Architecture style",options:[{value:"hexagonal",label:"hexagonal"},{value:"layered",label:"layered"},{value:"modular-monolith",label:"modular-monolith"},{value:"none",label:"none"}],initialValue:d.architectureStyle});if(isCancel(architectureStyle))return null;const isMultiTenant=await confirm({message:"Multi-tenant deployment?",initialValue:d.isMultiTenant});if(isCancel(isMultiTenant))return null;const hasDatabase=await confirm({message:"Database connected?",initialValue:d.hasDatabase});if(isCancel(hasDatabase))return null;const hasPublicApi=await confirm({message:"Public API exposed?",initialValue:d.hasPublicApi});if(isCancel(hasPublicApi))return null;const contractType=await select({message:"Contract testing strategy",options:[{value:"rest-owned",label:"rest-owned"},{value:"rest-public",label:"rest-public"},{value:"graphql",label:"graphql"},{value:"grpc",label:"grpc"},{value:"message-queue",label:"message-queue"},{value:"none",label:"none"}],initialValue:d.contractType});if(isCancel(contractType))return null;const governanceLevel=await select({message:"Governance level",options:[{value:"L1",label:"L1",hint:"minimal"},{value:"L2",label:"L2",hint:"standard"},{value:"L3",label:"L3",hint:"strict"},{value:"L4",label:"L4",hint:"maximum"}],initialValue:config.governanceLevel});return isCancel(governanceLevel)?null:[...diffStr("archetype",config.archetype,archetype),...diffStr("architectureStyle",config.architectureStyle,architectureStyle),...diffBool("isMultiTenant",config.isMultiTenant,isMultiTenant),...diffBool("hasDatabase",config.hasDatabase,hasDatabase),...diffBool("hasPublicApi",config.hasPublicApi,hasPublicApi),...diffStr("contractType",config.contractType,contractType),...diffStr("governanceLevel",config.governanceLevel,governanceLevel)]}async function promptFeaturesGroup(config){note(t("cli.configure.interactive.features_header"));const f=config.features,fields=[["features.contractTesting",f.contractTesting],["features.mutationTesting",f.mutationTesting],["features.securityScanning",f.securityScanning],["features.evidenceHarness",f.evidenceHarness],["features.acceptanceAnchor",f.acceptanceAnchor??!1],["features.debtGates",f.debtGates],["features.suppressions",f.suppressions],["features.soloDevMode",f.soloDevMode??!1]],assignments=[];for(const[path,oldVal]of fields){const label=path.replace("features.",""),val=await confirm({message:`Enable ${label}?`,initialValue:oldVal});if(isCancel(val))return null;assignments.push(...diffBool(path,oldVal,val))}return assignments}async function promptThresholdsGroup(config){note(t("cli.configure.interactive.thresholds_header"));const th=config.thresholds,numericFields=[["thresholds.lineCoverage",th.lineCoverage,"Line coverage threshold (%)"],["thresholds.branchCoverage",th.branchCoverage,"Branch coverage threshold (%)"],["thresholds.mutationScore",th.mutationScore,"Mutation score threshold (%)"],["thresholds.cyclomaticComplexity",th.cyclomaticComplexity,"Max cyclomatic complexity"],["thresholds.methodLength",th.methodLength,"Max method length (lines)"],["thresholds.maxParams",th.maxParams,"Max parameters per function"]],assignments=[];for(const[path,oldVal,message]of numericFields){const val=await text({message,initialValue:String(oldVal),validate:v=>v===void 0||!/^\d+$/.test(v.trim())?t("cli.configure.interactive.invalid_number"):void 0});if(isCancel(val))return null;const trimmed=val.trim();trimmed!==String(oldVal)&&assignments.push(`${path}=${trimmed}`)}return assignments}async function promptCollaborationGroup(config){note(t("cli.configure.interactive.collaboration_header"));const collaborationMode=await select({message:"Collaboration mode",options:[{value:"trunk-solo",label:"trunk-solo",hint:"one author, direct push"},{value:"peer-review",label:"peer-review",hint:"shared trust, mandatory PR"},{value:"gated-review",label:"gated-review",hint:"CODEOWNERS, merge queue"}],initialValue:config.collaborationMode??"peer-review"});if(isCancel(collaborationMode))return null;const branchingStrategy=await select({message:"Branching strategy",options:[{value:"trunk-direct",label:"trunk-direct"},{value:"github-flow",label:"github-flow"},{value:"github-flow-with-develop",label:"github-flow-with-develop"}],initialValue:config.branchingStrategy??"trunk-direct"});if(isCancel(branchingStrategy))return null;const soloMergeMode=await select({message:"Solo merge method",options:[{value:"direct",label:"direct",hint:"push directly to main"},{value:"pr-ff",label:"pr-ff",hint:"PR with fast-forward merge"}],initialValue:config.solo?.mergeMode??"direct"});return isCancel(soloMergeMode)?null:[...diffStr("collaborationMode",config.collaborationMode,collaborationMode),...diffStr("branchingStrategy",config.branchingStrategy,branchingStrategy),...diffStr("solo.mergeMode",config.solo?.mergeMode,soloMergeMode)]}async function promptAccessGroup(config){note(t("cli.configure.interactive.access_header"));const tools=await multiselect({message:"Active AI tools",options:SUPPORTED_AI_TOOLS.map(v=>({value:v,label:v})),initialValues:[...config.tools],required:!0});if(isCancel(tools))return null;const permitGitHub=await confirm({message:"Permit GitHub integration?",initialValue:config.permitGitHub??!1});return isCancel(permitGitHub)?null:[...diffArr("tools",config.tools,tools),...diffBool("permitGitHub",config.permitGitHub,permitGitHub)]}async function promptAutomationGroup(config){note(t("cli.configure.interactive.automation_header"));const autonomy=await select({message:"Ship autonomy level",options:[{value:"L0",label:"L0",hint:"ask at each ship step (default)"},{value:"L1",label:"L1",hint:"auto-advance + auto-merge on green"},{value:"L2",label:"L2",hint:"+ autonomous fix-on-red attempt"},{value:"L3",label:"L3",hint:"full auto: wave/batch + fix push + sub-agents"}],initialValue:config.automation?.autonomy??"L0"});return isCancel(autonomy)?null:diffStr("automation.autonomy",config.automation?.autonomy,autonomy)}export async function runInteractiveConfigure(dir){const targetDir=resolve(dir??process.cwd()),stored=loadConfig(targetDir);if(!stored){process.stderr.write(`${t("cli.configure.interactive.no_config")}
3
+ `),process.exit(1);return}intro(t("cli.configure.interactive.intro"));const groups=[c=>promptAxisGroup(c),c=>promptFeaturesGroup(c),c=>promptThresholdsGroup(c),c=>promptCollaborationGroup(c),c=>promptAccessGroup(c),c=>promptAutomationGroup(c)],allAssignments=[];for(const group of groups){const result=await group(stored);if(result===null){cancel(t("cli.configure.no_changes"));return}allAssignments.push(...result)}const saveit=await confirm({message:t("cli.configure.interactive.save_confirm"),initialValue:!0});if(isCancel(saveit)||!saveit){cancel(t("cli.configure.no_changes"));return}if(allAssignments.length===0){outro(t("cli.configure.no_changes"));return}await runConfigure({dir:targetDir,sets:allAssignments})}
@@ -0,0 +1,5 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import{resolve,join,relative}from"node:path";import{existsSync}from"node:fs";import{loadConfig,saveConfig}from"../utils/config.js";import{validateConfig,VALID_COLLABORATION_MODES,VALID_SOLO_MERGE_MODES,VALID_BRANCHING_STRATEGIES,AUTONOMY_LEVELS,VALID_GATE_LEVELS,DEFAULT_CROSS_MODEL_REVIEW}from"../config/schema.js";import{acquireLock}from"../utils/file-lock.js";import{jsonOutput}from"../utils/json-output.js";import{deriveAxisDefaults}from"../detectors/axis.js";import{ArbiterError}from"../utils/errors.js";import{SUPPORTED_AI_TOOLS}from"../wizard/types.js";import{t}from"../i18n/index.js";import{ensureDir,readFileTranslated,writeFile}from"../utils/fs.js";import{resolveMaxParallelWorktrees}from"../config/collaboration-mode-defaults.js";export const ALLOWED_PATHS=new Set(["features.contractTesting","features.mutationTesting","features.securityScanning","features.evidenceHarness","features.acceptanceAnchor","features.debtGates","features.suppressions","features.soloDevMode","features.codeownersNotify","features.taxonomy25d","features.perfTesting","features.riskRegister","features.operationsHandbook","features.iso27001Mapping","features.nis2Mapping","features.gdprMapping","collaborationMode","solo.mergeMode","branchingStrategy","thresholds.lineCoverage","thresholds.branchCoverage","thresholds.mutationScore","thresholds.cyclomaticComplexity","thresholds.methodLength","thresholds.maxParams","governanceLevel","permitGitHub","tools","archetype","architectureStyle","isMultiTenant","hasDatabase","hasPublicApi","contractType","automation.autonomy","automation.maxParallelWorktrees","automation.defaultGateLevel","crossModelReview.enabled","crossModelReview.diffEgressConsent","crossModelReview.providers","crossModelReview.slots.codeReview","crossModelReview.slots.redTeamReview","crossModelReview.timeoutMs","crossModelReview.onUnavailable"]),OVERRIDABLE_PATHS=new Set(["automation.autonomy","automation.defaultGateLevel","crossModelReview.enabled"]);export function assertOverridablePath(path){if(!OVERRIDABLE_PATHS.has(path))throw ArbiterError.fromKey("E_UNKNOWN_PATH","errors.E_UNKNOWN_PATH",{path},{hint:`Per-run --set is limited to: ${[...OVERRIDABLE_PATHS].join(", ")}. Persistent settings change via \`arbiter configure\`.`})}const VALID_TOOLS=new Set(SUPPORTED_AI_TOOLS),VALID_ARCHETYPES=new Set(["backend-web-db","cli","library","data-pipeline","frontend-spa","embedded"]),VALID_ARCHITECTURE_STYLES=new Set(["hexagonal","layered","modular-monolith","none"]),VALID_CONTRACT_TYPES=new Set(["rest-owned","rest-public","graphql","grpc","message-queue","none"]),CROSS_MODEL_BOOLEAN_PATHS=new Set(["crossModelReview.enabled","crossModelReview.diffEgressConsent"]),CROSS_MODEL_SLOT_PATHS=new Set(["crossModelReview.slots.codeReview","crossModelReview.slots.redTeamReview"]);function parseAxisValue(path,raw){if(path==="archetype"){if(!VALID_ARCHETYPES.has(raw))throw ArbiterError.fromKey("E_INVALID_ARCHETYPE","errors.E_INVALID_ARCHETYPE",{field:"archetype",value:raw,valid:[...VALID_ARCHETYPES].join(", ")},{hint:"Run `arbiter configure --help` for a list of valid archetypes."});return raw}if(path==="architectureStyle"){if(!VALID_ARCHITECTURE_STYLES.has(raw))throw ArbiterError.fromKey("E_INVALID_ARCHETYPE","errors.E_INVALID_ARCHETYPE",{field:"architectureStyle",value:raw,valid:[...VALID_ARCHITECTURE_STYLES].join(", ")},{hint:"Run `arbiter configure --help` for valid architectureStyle values."});return raw}if(path==="contractType"){if(!VALID_CONTRACT_TYPES.has(raw))throw ArbiterError.fromKey("E_INVALID_ARCHETYPE","errors.E_INVALID_ARCHETYPE",{field:"contractType",value:raw,valid:[...VALID_CONTRACT_TYPES].join(", ")},{hint:"Run `arbiter configure --help` for valid contractType values."});return raw}if(raw==="true")return!0;if(raw==="false")return!1;throw ArbiterError.fromKey("E_INVALID_BOOL","errors.E_INVALID_BOOL",{path,value:raw},{hint:"Use `true` or `false` (lowercase)."})}function parseCrossModelBoolean(path,raw){if(raw==="true")return!0;if(raw==="false")return!1;throw ArbiterError.fromKey("E_INVALID_BOOL","errors.E_INVALID_BOOL",{path,value:raw},{hint:"Use `true` or `false` (lowercase)."})}function parseCrossModelProviders(raw){const providers=raw.split(",").map(provider=>provider.trim());if(providers.length===1&&providers[0]==="codex")return providers;throw ArbiterError.fromKey("E_INVALID_TOOL","errors.E_INVALID_TOOL",{noun:"provider",tool:JSON.stringify(raw),valid:"codex"},{hint:"The only supported cross-model provider is codex."})}function parseCrossModelNumber(path,raw){const value=Number(raw),minimum=CROSS_MODEL_SLOT_PATHS.has(path)?0:1;if(!Number.isInteger(value)||value<minimum)throw ArbiterError.fromKey("E_INVALID_NUMBER","errors.E_INVALID_NUMBER",{path,value:raw},{hint:`Provide an integer >= ${minimum}.`});return value}function parseCrossModelPolicy(path,raw){if(raw==="degrade"||raw==="fail")return raw;throw ArbiterError.fromKey("E_INVALID_ARCHETYPE","errors.E_INVALID_ARCHETYPE",{field:path,value:raw,valid:"degrade, fail"},{hint:"Use `degrade` or `fail`."})}function parseCrossModelValue(path,raw){return CROSS_MODEL_BOOLEAN_PATHS.has(path)?parseCrossModelBoolean(path,raw):path==="crossModelReview.providers"?parseCrossModelProviders(raw):CROSS_MODEL_SLOT_PATHS.has(path)||path==="crossModelReview.timeoutMs"?parseCrossModelNumber(path,raw):path==="crossModelReview.onUnavailable"?parseCrossModelPolicy(path,raw):raw}function parseFeatureValue(path,raw){if(!path.startsWith("features."))return null;if(raw==="true")return!0;if(raw==="false")return!1;throw ArbiterError.fromKey("E_INVALID_BOOL","errors.E_INVALID_BOOL",{path:"features.*",value:raw},{hint:"Use `true` or `false` (lowercase). Example: `arbiter configure --set features.debtGates=true`."})}const AXIS_PATHS=new Set(["archetype","architectureStyle","isMultiTenant","hasDatabase","hasPublicApi","contractType"]);export function parseValue(path,raw){const featureValue=parseFeatureValue(path,raw);if(featureValue!==null)return featureValue;if(path.startsWith("thresholds.")){const n=Number(raw);if(!Number.isFinite(n))throw ArbiterError.fromKey("E_INVALID_NUMBER","errors.E_INVALID_NUMBER",{path,value:raw},{hint:`Provide a numeric value. Example: \`arbiter configure --set ${path}=80\`.`});return n}if(path==="permitGitHub"){if(raw==="true")return!0;if(raw==="false")return!1;throw ArbiterError.fromKey("E_INVALID_BOOL","errors.E_INVALID_BOOL",{path:"permitGitHub",value:raw},{hint:"Use `true` or `false` (lowercase)."})}if(path==="tools"){const toolList=raw.split(",").map(tool=>tool.trim());for(const tool of toolList)if(!VALID_TOOLS.has(tool))throw ArbiterError.fromKey("E_INVALID_TOOL","errors.E_INVALID_TOOL",{noun:"tool",tool:`"${tool}"`,valid:[...VALID_TOOLS].join(", ")},{hint:`Valid tools: ${[...VALID_TOOLS].join(", ")}.`});return toolList}return path.startsWith("crossModelReview.")?parseCrossModelValue(path,raw):AXIS_PATHS.has(path)?parseAxisValue(path,raw):AUTOMATION_SCALAR_PATHS.has(path)?parseAutomationScalar(path,raw):new Set(["collaborationMode","solo.mergeMode","branchingStrategy","automation.autonomy","automation.defaultGateLevel"]).has(path)?parseEnumPathValue(path,raw):raw}const AUTOMATION_SCALAR_PATHS=new Set(["automation.maxParallelWorktrees"]);function parseAutomationScalar(path,raw){const n=Number(raw);if(!Number.isInteger(n)||n<1)throw ArbiterError.fromKey("E_INVALID_NUMBER","errors.E_INVALID_NUMBER",{path,value:raw},{hint:"Provide a positive integer (\u22651). Example: `--set automation.maxParallelWorktrees=3`."});return n}function parseEnumPathValue(path,raw){const spec={collaborationMode:{valid:VALID_COLLABORATION_MODES,hint:"Valid values: trunk-solo, peer-review, gated-review. Example: `arbiter configure --set collaborationMode=peer-review`."},"solo.mergeMode":{valid:VALID_SOLO_MERGE_MODES,hint:"Valid values: direct, pr-ff. Example: `arbiter configure --set solo.mergeMode=pr-ff`."},branchingStrategy:{valid:VALID_BRANCHING_STRATEGIES,hint:"Valid values: trunk-direct, github-flow, github-flow-with-develop."},"automation.autonomy":{valid:new Set(AUTONOMY_LEVELS),hint:"Valid values: L0, L1, L2, L3. L0 = ask each ship step (default). Per-run override: `arbiter ship --autonomy Lx`."},"automation.defaultGateLevel":{valid:new Set(VALID_GATE_LEVELS),hint:"Valid values: L1, L2. The gate level `arbiter verify` runs by default. Per-run override: `arbiter ship --set automation.defaultGateLevel=L2`."}}[path];if(spec!==void 0&&!spec.valid.has(raw))throw ArbiterError.fromKey("E_INVALID_ARCHETYPE","errors.E_INVALID_ARCHETYPE",{field:path,value:raw,valid:[...spec.valid].join(", ")},{hint:spec.hint});return raw}function cascadeAxisDefaults(config,archetype){const defaults=deriveAxisDefaults(config,archetype);return{...config,architectureStyle:config.architectureStyle??defaults.architectureStyle,isMultiTenant:config.isMultiTenant??defaults.isMultiTenant,hasDatabase:config.hasDatabase??defaults.hasDatabase,hasPublicApi:config.hasPublicApi??defaults.hasPublicApi,contractType:config.contractType??defaults.contractType}}function applyAssignment(config,assignment){const eqIdx=assignment.indexOf("=");if(eqIdx<0)throw ArbiterError.fromKey("E_INVALID_FORMAT","errors.E_INVALID_FORMAT",{assignment},{hint:"Example: `arbiter configure --set tools=claude,codex`."});const path=assignment.slice(0,eqIdx),rawValue=assignment.slice(eqIdx+1);if(!ALLOWED_PATHS.has(path))throw ArbiterError.fromKey("E_UNKNOWN_PATH","errors.E_UNKNOWN_PATH",{path},{hint:"Run `arbiter configure --help` to see valid paths."});const value=parseValue(path,rawValue);return{config:applySet(config,path,value),archetypeTouched:path==="archetype"}}function applyTopLevel(config,key,value){return{...config,[key]:value}}function applyNested(config,top,key,value){const parent=config[top]??(top==="crossModelReview"?structuredClone(DEFAULT_CROSS_MODEL_REVIEW):{});return{...config,[top]:{...parent,[key]:value}}}function applyDeep(config,top,middle,leaf,value){const root=config,fallback=top==="crossModelReview"?{...structuredClone(DEFAULT_CROSS_MODEL_REVIEW)}:{},parent=root[top]??fallback,nested=parent[middle]??{};return{...config,[top]:{...parent,[middle]:{...nested,[leaf]:value}}}}function applySet(config,path,value){const parts=path.split("."),[top,middle,leaf]=parts;if(parts.length===1&&top!==void 0)return applyTopLevel(config,top,value);if(parts.length===2&&top!==void 0&&middle!==void 0)return applyNested(config,top,middle,value);if(parts.length===3&&top!==void 0&&middle!==void 0&&leaf!==void 0)return applyDeep(config,top,middle,leaf,value);throw ArbiterError.fromKey("E_UNSUPPORTED_PATH_DEPTH","errors.E_UNSUPPORTED_PATH_DEPTH",{path},{hint:"Run `arbiter configure --help` to see valid paths."})}function syncDrainMaxParallel(targetDir,config){const drainPath=join(targetDir,".claude","commands","drain.md");if(!existsSync(drainPath))return null;const cap=resolveMaxParallelWorktrees({automation:config.automation,collaborationMode:config.collaborationMode,enableSoloDevMode:config.features.soloDevMode}),before=readFileTranslated(drainPath,"utf8"),after=before.replace(/^(\| `--max-parallel N` \|) [^|\r\n]+(\| Max worktree agents;.*)$/m,`$1 ${cap} $2`);return after===before?null:writeFile(drainPath,after).withheld?{drainPath,cap}:null}export async function runConfigure(options){if(options.sets.length===0){if(options.json){jsonOutput("configure","error",{},["--set is required (non-interactive usage)"]),process.exit(1);return}process.stderr.write(`${t("cli.configure.usage_hint")}
3
+ `),process.exit(2)}const targetDir=resolve(options.dir??process.cwd()),stored=loadConfig(targetDir);if(!stored){if(options.json){jsonOutput("configure","error",{},["No arbiter.json found. Run `arbiter init` first."]),process.exit(1);return}throw ArbiterError.fromKey("E_CONFIG_NOT_FOUND","errors.E_CONFIG_NOT_FOUND",{},{hint:"Run `arbiter init` to initialize governance in this directory.",docUrl:"https://arbiter.dev/reference/cli#init"})}let config=stored,archetypeTouched=!1;for(const assignment of options.sets){const next=applyAssignment(config,assignment);config=next.config,next.archetypeTouched&&(archetypeTouched=!0)}archetypeTouched&&config.archetype!==void 0&&(config=cascadeAxisDefaults(config,config.archetype));const result=validateConfig(config);if(!result.ok)throw ArbiterError.fromKey("E_CONFIG_INVALID","errors.E_CONFIGURE_CONFIG_INVALID",{errors:result.errors.join("; ")},{hint:"Fix the errors above, or delete arbiter.json and re-run `arbiter init`."});ensureDir(join(targetDir,".arbiter"));const lock=await acquireLock(join(targetDir,".arbiter",".lock"));let drainWithheld;try{await saveConfig(targetDir,result.config),drainWithheld=syncDrainMaxParallel(targetDir,result.config)}finally{await lock.release()}const drainWarning=drainWithheld?t("cli.configure.drain_sync_withheld",{path:relative(targetDir,drainWithheld.drainPath),cap:drainWithheld.cap}):null;if(options.json){jsonOutput("configure","ok",{updated:options.sets},void 0,drainWarning?{warnings:[drainWarning]}:void 0);return}process.stdout.write(`${t("cli.configure.updated",{keys:options.sets.join(", ")})}
4
+ `),drainWarning&&process.stderr.write(`${drainWarning}
5
+ `)}
@@ -0,0 +1,3 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import{join,resolve}from"node:path";import{detectExternalModel}from"../detectors/external-model.js";import{assertSafeArbiterEvidenceRoot,invokeExternalReview}from"../integrations/external-review.js";import{resolveShipProfile}from"./ship-profile.js";import{normTier}from"./ship-tier.js";import{existsSync,lstatSync,readFileSync}from"node:fs";import{readFileContained,toFsError,writeFileContained}from"../utils/fs.js";import{runCli}from"../utils/run-cli.js";import{currentBranch,headSha}from"../evidence/git-checks.js";const SHIP_CROSS_MODEL_PROMPT="Review this change for bugs, type safety, security, data integrity, and silent failures.";function runCrossModelReview(options){const repoRoot=resolve(options.dir??process.cwd()),tier=normTier(options.tier),profile=resolveShipProfile(repoRoot),cfg=profile.crossModelReview;if(!cfg?.enabled)throw new Error("crossModelReview.enabled must be true to run an external review");if(!cfg.diffEgressConsent)throw new Error("crossModelReview.diffEgressConsent must be true to send the diff");const result=invokeExternalReview({repoRoot,taskId:options.taskId,prompt:options.prompt,diff:options.diff??readStdin(),cfg,access:detectExternalModel("codex"),tier,phase:options.phase??"refactor",vertical:options.vertical??"bugs"});return existsSync(repoRoot)&&writeExternalReviewSidecar(repoRoot,options.taskId,result,tier,profile.collaborationMode),result}function isRecord(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}function assertSafeSidecarFile(path){try{const stat=lstatSync(path);if(stat.isSymbolicLink())throw new Error(`${path} must not be a symbolic link`);if(!stat.isFile())throw new Error(`${path} must be a regular file`)}catch(error){if(error.code==="ENOENT")return;throw toFsError(error,path)}}function readSidecar(repoRoot){const relativePath=join(".arbiter","agents-dispatched.json"),path=join(repoRoot,".arbiter","agents-dispatched.json");try{if(assertSafeArbiterEvidenceRoot(repoRoot),assertSafeSidecarFile(path),!existsSync(path))return null;const parsed=JSON.parse(readFileContained(repoRoot,relativePath));if(!isRecord(parsed))throw new Error(`${path} must contain a JSON object`);return parsed}catch(error){if(error.code==="ENOENT")return null;throw new Error(`cannot read dispatch sidecar ${path}: ${String(error)}`,{cause:error})}}function isCurrentSidecar(existing,branch,sha,taskId){return existing!==null&&existing.branch===branch&&existing.sha===sha&&existing.taskId===taskId}function hasCurrentFulfilledReview(repoRoot,taskId){if(!existsSync(join(repoRoot,".git")))return!1;const branch=currentBranch(repoRoot),sha=headSha(repoRoot);if(branch==="unknown"||sha==="unknown")return!1;const sidecar=readSidecar(repoRoot);if(!(isCurrentSidecar(sidecar,branch,sha,taskId)&&Array.isArray(sidecar.agents)&&sidecar.agents.includes("codex-reviewer")))return!1;try{return runCli("node",[join(repoRoot,"scripts","check-cross-model-review.mjs"),"--require-fulfilled"],{cwd:repoRoot,timeoutMs:5e3,retries:0}).exitCode===0}catch{return!1}}function readSidecarAgents(existing){if(existing.agents===void 0)return null;if(!Array.isArray(existing.agents)||!existing.agents.every(agent=>typeof agent=="string"))throw new Error("existing dispatch sidecar has invalid agent names");const agents=[...existing.agents];if(new Set(agents).size!==agents.length)throw new Error("existing dispatch sidecar has duplicate agents");if(existing.count!==void 0&&(typeof existing.count!="number"||!Number.isInteger(existing.count)||existing.count<0||existing.count>agents.length))throw new Error("existing dispatch sidecar has an invalid count");return agents}function freshReviewPanel(context){return context.collaborationMode!=="trunk-solo"&&context.tier==="Standard"?{count:2,agents:["anthropic-reviewer","codex-reviewer"]}:{count:1,agents:["codex-reviewer"]}}function completeReviewPanel(agents,panelSize){if(panelSize!==2||agents.length!==1)return agents;const first=agents[0]??"anthropic-reviewer";return[first==="codex-reviewer"?"anthropic-reviewer":first,"codex-reviewer"]}function sidecarAgents(existing,branch,sha,taskId,context){const fresh=freshReviewPanel(context);if(!isCurrentSidecar(existing,branch,sha,taskId))return fresh;const agents=readSidecarAgents(existing);if(agents===null||agents.length===0)return fresh;const completed=completeReviewPanel(agents,fresh.count);return completed!==agents?{count:completed.length,agents:completed}:(agents.includes("codex-reviewer")||(agents[agents.length-1]="codex-reviewer"),{count:agents.length,agents})}function writeExternalReviewSidecar(repoRoot,taskId,result,tier="Standard",collaborationMode="peer-review"){if(result.status!=="fulfilled"||!result.recorded||result.envelope===void 0)return;assertSafeArbiterEvidenceRoot(repoRoot);const sidecarPath=join(repoRoot,".arbiter","agents-dispatched.json");assertSafeSidecarFile(sidecarPath);const branch=currentBranch(repoRoot),sha=headSha(repoRoot);if(branch==="unknown"||sha==="unknown")throw new Error("cannot bind dispatch sidecar to Git HEAD");const panel=sidecarAgents(readSidecar(repoRoot),branch,sha,taskId,{tier,collaborationMode});writeFileContained(repoRoot,join(".arbiter","agents-dispatched.json"),`${JSON.stringify({...panel,taskId,branch,sha},null,2)}
3
+ `)}function assertReviewTreeClean(repoRoot){const unreviewed=runCli("git",["status","--porcelain=v1","--untracked-files=all"],{cwd:repoRoot,timeoutMs:15e3}).stdout.split(/\r?\n/).filter(line=>line.length>2).map(line=>line.slice(3)).filter(path=>path.length>0&&!path.startsWith(".arbiter/"));if(unreviewed.length>0)throw new Error(`working tree has uncommitted changes; commit before external review (${unreviewed.join(", ")})`)}function runShipCrossModelReview(options){const repoRoot=resolve(options.dir);let diff="",access=options.cfg.diffEgressConsent?options.access:void 0,preflightError,preflightDegradation;if(options.cfg.diffEgressConsent)try{if(assertReviewTreeClean(repoRoot),options.cfg.enabled&&hasCurrentFulfilledReview(repoRoot,options.taskId))return{provider:"codex",status:"fulfilled",diffBytes:0,diffTruncated:!1,degradationReasons:[],recorded:!0};diff=runCli("git",["diff","--binary","origin/main...HEAD"],{cwd:repoRoot,timeoutMs:15e3}).stdout}catch(error){access=void 0,preflightError=error,preflightDegradation="invocation-failed"}const result=invokeExternalReview({repoRoot,taskId:options.taskId,prompt:SHIP_CROSS_MODEL_PROMPT,diff,cfg:options.cfg,...access!==void 0?{access}:{},...preflightDegradation!==void 0?{preflightDegradation,preflightError}:{},tier:options.tier,phase:options.phase,vertical:options.vertical});return existsSync(repoRoot)&&writeExternalReviewSidecar(repoRoot,options.taskId,result,options.tier,options.collaborationMode??"peer-review"),result}export{runCrossModelReview,runShipCrossModelReview,writeExternalReviewSidecar};function readStdin(){try{return readFileSync(0,"utf8")}catch(error){throw toFsError(error,"stdin")}}
@@ -0,0 +1,20 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import{resolve,relative,join}from"node:path";import{existsSync}from"node:fs";import{loadConfig}from"../utils/config.js";import{slugifyProjectName}from"./init.js";import{buildAdoptPredicate}from"./update.js";import{buildSelectionPredicate,loadIgnorePatterns}from"../config/arbiter-ignore.js";import{resolveProjectName}from"../config/resolve-project-name.js";import{resolveProjectConfig,gitHubPermitted}from"../config/resolve-project-config.js";import{detectInstalledSkills}from"../integrations/skill-detector.js";import{buildRegistry,runGeneratorsFromRegistry}from"../generators/registry.js";import{jsonOutput,statusToExitCode}from"../utils/json-output.js";import{t}from"../i18n/index.js";import{beginGenerationSession,endGenerationSession,readFileTranslated}from"../utils/fs.js";import{loadGeneratedManifest}from"../state/generated-manifest.js";import{renderAgentsMd}from"../generators/agents-md.js";import{buildRenderContext as buildClaudeRenderContext,parseExistingSettings}from"../generators/claude.js";import{renderTemplate}from"../utils/render.js";const IRON_LAWS_HEADING=/^## Iron Laws[ \t]*$/m;function extractIronLawsBlock(content){const match=IRON_LAWS_HEADING.exec(content);if(!match)return null;const rest=content.slice(match.index),nextHeader=rest.indexOf(`
3
+ ## `,1);return(nextHeader===-1?rest:rest.slice(0,nextHeader)).trim()}function normalizeWhitespace(s){return s.replace(/\s+/g," ").trim()}export function checkGovernanceSections(config,targetDir){const results=[],agentsPath=join(targetDir,"AGENTS.md"),currentIronLaws=extractIronLawsBlock(renderAgentsMd(config));if(!existsSync(agentsPath))results.push({file:"AGENTS.md",section:"Iron Laws",stale:!0,detail:"file missing"});else{const materializedIronLaws=extractIronLawsBlock(readFileTranslated(agentsPath,"utf-8")),stale=materializedIronLaws===null||normalizeWhitespace(materializedIronLaws)!==normalizeWhitespace(currentIronLaws??"");results.push({file:"AGENTS.md",section:"Iron Laws",stale,detail:materializedIronLaws===null?"section missing":"content differs from current template"})}const settingsPath=join(targetDir,".claude","settings.json"),currentDeny=JSON.parse(renderTemplate("claude/settings.json.ejs",buildClaudeRenderContext(config))).permissions.deny;if(!existsSync(settingsPath))results.push({file:".claude/settings.json",section:"deny list",stale:!0,detail:"file missing"});else{let materializedDeny=[],malformed=!1;try{const deny=parseExistingSettings(settingsPath).permissions?.deny;Array.isArray(deny)?materializedDeny=deny:malformed=!0}catch(err){const msg=err instanceof Error?err.message:String(err);process.stderr.write(`[diff] .claude/settings.json: ${msg}
4
+ `),malformed=!0}if(malformed)return results.push({file:".claude/settings.json",section:"deny list",stale:!0,detail:"malformed settings.json"}),results;const missing=currentDeny.filter(d=>!materializedDeny.includes(d));results.push({file:".claude/settings.json",section:"deny list",stale:missing.length>0,detail:missing.length>0?`missing entries: ${missing.join(", ")}`:""})}return results}function actionToStatus(action){return action==="created"?"new":action==="skipped"?"unchanged":"changed"}function buildRemoteSideEffects(permitGitHub,githubOwner,githubRepo){if(!permitGitHub||!githubOwner||!githubRepo)return[];const target=`${githubOwner}/${githubRepo}`;return[{op:"provision-labels",target},{op:"apply-branch-protection",target},{op:"create-project-board",target}]}function buildDiffFiles(results,targetDir){return results.map(r=>{const rel=relative(targetDir,r.path),status=r.excluded==="ignored"?"ignored":r.withheld===!0&&r.adopted!==!0?"withheld":actionToStatus(r.action);return{key:rel,status,action:r.action,path:rel}})}function printFileLine(f){f.status==="new"?process.stdout.write(`${t("cli.diff.new_file",{key:f.key})}
5
+ `):f.status==="changed"?process.stdout.write(`${t("cli.diff.changed_file",{key:f.key})}
6
+ `):f.status==="ignored"?process.stdout.write(`${t("cli.diff.ignored_file",{key:f.key})}
7
+ `):process.stdout.write(`${t("cli.diff.unchanged_file",{key:f.key})}
8
+ `)}function printWithheldSection(withheld){if(withheld.length!==0){process.stdout.write(`${t("cli.diff.withheld_header",{count:withheld.length})}
9
+ `);for(const f of withheld)process.stdout.write(`${t("cli.diff.withheld_file",{key:f.key})}
10
+ `);process.stdout.write(`${t("cli.diff.withheld_hint")}
11
+ `)}}function printHuman(files,remote,hasChanges,withheldOnly){const withheld=files.filter(f=>f.status==="withheld");if(withheldOnly){printWithheldSection(withheld);return}for(const f of files)f.status!=="withheld"&&printFileLine(f);if(remote.length>0){process.stdout.write(`${t("cli.diff.remote_header")}
12
+ `);for(const r of remote)process.stdout.write(`${t("cli.diff.remote_effect",{op:r.op,target:r.target})}
13
+ `)}printWithheldSection(withheld),hasChanges?process.stdout.write(`${t("cli.diff.run_update")}
14
+ `):withheld.length===0&&process.stdout.write(`${t("cli.diff.up_to_date")}
15
+ `)}function printGovernanceReport(config,targetDir,json){const sections=checkGovernanceSections(config,targetDir),stale=sections.filter(s=>s.stale);if(json){jsonOutput("diff",stale.length>0?"warning":"ok",{sections}),stale.length>0&&process.exit(statusToExitCode("warning"));return}for(const s of sections)s.stale&&process.stdout.write(`${t("cli.diff.governance_stale_section",{file:s.file,section:s.section,detail:s.detail})}
16
+ `);stale.length>0&&(process.stdout.write(`${t("cli.diff.governance_stale_footer")}
17
+ `),process.exit(1)),process.stdout.write(`${t("cli.diff.governance_up_to_date")}
18
+ `)}export function runDiff(options){const targetDir=resolve(options.dir??process.cwd());options.json||process.stdout.write(`${t("cli.diff.banner")}
19
+ `);const stored=loadConfig(targetDir);if(!stored){options.json?jsonOutput("diff","error",{},["No arbiter.json found. Run `arbiter init` first."]):process.stdout.write(`${t("cli.diff.no_config")}
20
+ `),process.exit(statusToExitCode("error"));return}const projectName=slugifyProjectName(resolveProjectName(targetDir,stored)),{config}=resolveProjectConfig(targetDir,projectName,stored);if(options.governance){printGovernanceReport(config,targetDir,options.json===!0);return}const claudeHome=process.env.HOME?`${process.env.HOME}/.claude`:"",installedSkills=detectInstalledSkills({targetDir,claudeHome}),specs=buildRegistry(config,installedSkills),prevManifest=loadGeneratedManifest(targetDir);beginGenerationSession({targetDir,prevHashes:prevManifest,onWithheld:()=>{},adoptPredicate:buildAdoptPredicate({}),selectPredicate:buildSelectionPredicate({patterns:loadIgnorePatterns(targetDir),only:[]})});let results;try{results=runGeneratorsFromRegistry(specs,[],{dryRun:!0})}finally{endGenerationSession()}const allFiles=buildDiffFiles(results,targetDir),withheldCount=allFiles.filter(f=>f.status==="withheld").length,files=options.withheld?allFiles.filter(f=>f.status==="withheld"):allFiles,remoteSideEffect=buildRemoteSideEffects(gitHubPermitted(stored),config.githubOwner,config.githubRepo),hasChanges=allFiles.some(f=>f.status!=="unchanged"&&f.status!=="withheld"&&f.status!=="ignored");if(options.json){const status=hasChanges||withheldCount>0?"warning":"ok";jsonOutput("diff",status,{hasChanges,files,remoteSideEffect,withheldCount});const code=statusToExitCode(status);code!==0&&process.exit(code);return}printHuman(files,remoteSideEffect,hasChanges,options.withheld===!0)}
@@ -0,0 +1,3 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import{fileURLToPath}from"node:url";import{resolve}from"node:path";import{runCli,CliError}from"../utils/run-cli.js";import{jsonOutput}from"../utils/json-output.js";function packageRoot(){return resolve(fileURLToPath(import.meta.url),"../../..")}function buildEngineArgs(opts){const args=[];return opts.json&&args.push("--json"),opts.arc42?opts.updateBaseline?[...args,"--update-baseline"]:args:(opts.manifest&&args.push("--manifest",opts.manifest),opts.profile&&args.push("--profile",opts.profile),opts.freshness||(opts.strict&&args.push("--strict"),opts.generate&&args.push("--generate"),opts.refreshStubs&&args.push("--refresh-stubs")),args)}function engineFor(opts){return opts.arc42?"scripts/check-arc42-slots.mjs":opts.freshness?"scripts/check-doc-freshness.mjs":"scripts/check-doc-set.mjs"}function runEngine(script,args,repo){try{const r=runCli("node",[script,...args],{cwd:repo});return{stdout:r.stdout,stderr:r.stderr,exitCode:0}}catch(err){return err instanceof CliError?{stdout:err.stdout,stderr:err.stderr,exitCode:err.exitCode>=0?err.exitCode:2}:{stdout:"",stderr:`doc-set: engine failed \u2014 ${err instanceof Error?err.message:String(err)}
3
+ `,exitCode:2}}}function parsePayload(stdout,jsonRequested){if(!jsonRequested)return null;const text=stdout.trim();if(!text.startsWith("{"))return null;try{return JSON.parse(text)}catch{return null}}export function runDocSet(opts={}){const repo=opts.repo?resolve(opts.repo):process.cwd(),script=resolve(packageRoot(),engineFor(opts)),{stdout,stderr,exitCode}=runEngine(script,buildEngineArgs(opts),repo),payload=parsePayload(stdout,!!opts.json);return opts.quiet||(opts.json?jsonOutput("doc-set",exitCode===0?"ok":exitCode===1?"warning":"error",payload===null?{}:{...payload}):stdout&&process.stdout.write(stdout),stderr&&process.stderr.write(stderr)),{exitCode,payload}}
@@ -0,0 +1,2 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import{realpathSync}from"node:fs";import{resolve}from"node:path";import{walkDir}from"../../utils/walk-dir.js";import{jsonOutput}from"../../utils/json-output.js";import{unlinkTranslated}from"../../utils/fs.js";const CLEAN_SKIP_DIRS=new Set(["node_modules",".git","dist"]);function isBackupFile(name){return name.endsWith(".arbiter-backup")||/^\.arbiter-generated\.json\.bak\./.test(name)}function collectBackups(dir,out){out.push(...walkDir(dir,{skipDirs:CLEAN_SKIP_DIRS,filter:name=>isBackupFile(name),errorMode:"fs-soft"}))}export function runDoctorClean(opts={}){const rawDir=resolve(opts.dir??".");let targetDir;try{targetDir=realpathSync(rawDir)}catch{targetDir=rawDir}const found=[];collectBackups(targetDir,found);const deleted=[];if(!opts.dryRun)for(const f of found)try{unlinkTranslated(f),deleted.push(f)}catch{}return opts.json&&jsonOutput("doctor clean","ok",{found,deleted}),{found,deleted}}
@@ -0,0 +1,12 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import{existsSync,readdirSync,statSync}from"node:fs";import{join,relative,resolve}from"node:path";import{jsonOutput}from"../../utils/json-output.js";import{readFileTranslated}from"../../utils/fs.js";const SCAN_SUBDIRS=["scripts",".githooks"],SKIP_DIR_NAMES=new Set(["node_modules",".git","data"]);function listScanFiles(dir){const out=[];for(const sub of SCAN_SUBDIRS)walk(join(dir,sub),out);return out}function walk(current,out){let entries;try{entries=readdirSync(current)}catch{return}for(const entry of entries){const full=join(current,entry);let stat;try{stat=statSync(full)}catch{continue}if(stat.isDirectory()){if(SKIP_DIR_NAMES.has(entry))continue;walk(full,out)}else stat.isFile()&&out.push(full)}}function lineOf(text,index){return text.slice(0,index).split(`
3
+ `).length}const BRACE_FORM_RE=/command\s+-v\s+(\S+)[^\n]*\|\|\s*\{[^}]*\b(?:return|exit)\s+0\b/g,BARE_FORM_RE=/command\s+-v\s+(\S+)[^\n]*\|\|\s*exit\s+0\b/g,IF_GUARD_RE=/if\s*!\s*command\s+-v\s+(\S+)/g,POSITIVE_IF_GUARD_RE=/(?<!el)\bif\s+command\s+-v\s+(\S+)/g;function findMatchingFi(lines,startLine){let depth=1;for(let i=startLine;i<lines.length;i++){const line=lines[i]??"";if(/\bif\b(?!\s*!\s*command)/.test(line)&&/;\s*then\s*$|then\s*$/.test(line)&&depth++,/(?:^|[;\s])fi(?:[;\s]|$)/.test(line)&&(depth--,depth===0))return i}return lines.length-1}function findGuardFindings(text,file,re,isFailOpen){const lines=text.split(`
4
+ `),findings=[];for(const m of text.matchAll(re)){const tool=m[1];if(!tool)continue;const startLine=lineOf(text,m.index),blockStartIdx=startLine,fiIdx=findMatchingFi(lines,blockStartIdx);isFailOpen(lines.slice(blockStartIdx,fiIdx+1))&&findings.push({file,line:startLine,tool})}return findings}function hasZeroExit(blockLines){return/\b(?:return|exit)\s+0\b/.test(blockLines.join(`
5
+ `))}function hasNoBlockingElse(blockLines){let depth=1,elseIdx=-1;for(let i=1;i<blockLines.length;i++){const line=blockLines[i]??"";/\bif\b(?!\s*!\s*command)/.test(line)&&/;\s*then\s*$|then\s*$/.test(line)&&depth++,/^\s*else\b/.test(line)&&depth===1&&(elseIdx=i),/(?:^|[;\s])fi(?:[;\s]|$)/.test(line)&&depth--}if(elseIdx===-1)return!0;const elseLines=blockLines.slice(elseIdx);elseLines[0]=elseLines[0]?.replace(/^\s*else\b/,"")??"";const last=elseLines.length-1;elseLines[last]=elseLines[last]?.replace(/(?:^|;)\s*fi(?:[;\s]|$).*$/,"")??"";const elseBody=elseLines.join(`
6
+ `).trim();return elseBody===""?!0:elseBody.split(/[;\n]/).every(command=>/^(?:#.*|(?:echo|printf)\b.*|:\s*(?:#.*)?)$/.test(command.trim()))}function scanFileText(text,file){const joined=text.replace(/\\\n/g," "),findings=[];for(const m of joined.matchAll(BRACE_FORM_RE)){const tool=m[1];tool&&findings.push({file,line:lineOf(joined,m.index),tool})}for(const m of joined.matchAll(BARE_FORM_RE)){const tool=m[1];tool&&findings.push({file,line:lineOf(joined,m.index),tool})}return findings.push(...findGuardFindings(joined,file,IF_GUARD_RE,hasZeroExit)),findings.push(...findGuardFindings(joined,file,POSITIVE_IF_GUARD_RE,hasNoBlockingElse)),findings}function loadAllowlist(path){if(!existsSync(path))return{entries:[],malformed:!1};const entries=JSON.parse(readFileTranslated(path,"utf-8")).entries??[],malformed=entries.some(e=>!e.reason||e.reason.trim()==="");return{entries,malformed}}function applyAllowlist(findings,allowlist){return findings.map(f=>{const match=allowlist.find(e=>e.file===f.file&&e.line===f.line&&e.reason);return match?.reason?{...f,allowlisted:match.reason}:f})}function emitTextOutput(findings){if(process.stdout.write(`
7
+ `),findings.length===0){process.stdout.write(` no fail-open patterns found
8
+
9
+ `);return}for(const f of findings){const suffix=f.allowlisted?` (allowlisted: ${f.allowlisted})`:"";process.stdout.write(` ${f.file}:${f.line} command -v ${f.tool}${suffix}
10
+ `)}process.stdout.write(`
11
+ `)}export function runDoctorFailOpenCensus(opts={}){const dir=resolve(opts.dir??"."),allowlistPath=opts.allowlistPath??join(dir,".arbiter","fail-open-allowlist.json"),{entries,malformed}=loadAllowlist(allowlistPath);if(malformed)return opts.json?jsonOutput("doctor fail-open-census","error",{findings:[]},["Allowlist entry missing a reason \u2014 every entry must justify its suppression."],{errorClass:"config"}):process.stderr.write(` Error: allowlist entry missing a reason (${relative(dir,allowlistPath)})
12
+ `),{exitCode:2,findings:[]};const rawFindings=listScanFiles(dir).flatMap(path=>scanFileText(readFileTranslated(path,"utf-8"),relative(dir,path))).sort((a,b)=>a.file===b.file?a.line-b.line:a.file.localeCompare(b.file)),findings=applyAllowlist(rawFindings,entries),exitCode=findings.filter(f=>!f.allowlisted).length>0?1:0;return opts.json?jsonOutput("doctor fail-open-census",exitCode===0?"ok":"error",{findings}):emitTextOutput(findings),{exitCode,findings}}
@@ -0,0 +1,11 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import{existsSync,readdirSync}from"node:fs";import{join,resolve}from"node:path";import os from"node:os";import{jsonOutput}from"../../utils/json-output.js";import{loadConfig}from"../../utils/config.js";import{runCli}from"../../utils/run-cli.js";import{forceReleaseLock,isLockStale,readLockInfo}from"../../utils/file-lock.js";import{ArbiterError}from"../../utils/errors.js";import{resolveChannel}from"../../utils/channel.js";import{detectLanguage}from"../../detectors/language.js";import{detectExternalModel}from"../../detectors/external-model.js";import{runProbes}from"../../compatibility/probe.js";import{isArbiterSelf}from"../ship-profile.js";import{diagnoseCompanions}from"../../integrations/companions.js";import{checkScaffoldWiring}from"../../detectors/scaffold-wiring.js";import{isValidPhase}from"../task-state.js";import{validateCollaborationCoherence,validateOverlayCoherence,validateAutonomyCoherence,validateProfileCoherence,validateLanguageArchetypeCoherence,validateTrunkSoloParityCoherence}from"../wizard/coherence.js";import{resolveCollaborationMode}from"../../config/collaboration-mode-defaults.js";import{deriveGateKey,gateLockPath}from"../gate-exec.js";import{readFileTranslated}from"../../utils/fs.js";function readRawCoherenceConfig(dir){try{return JSON.parse(readFileTranslated(join(dir,"arbiter.json"),"utf8"))}catch{return null}}function checkNodeVersion(){const nodeVer=process.versions.node,nodeMajor=parseInt(nodeVer.split(".")[0]??"0",10),check={id:"node-version",label:"Node.js version >= 22",status:nodeMajor>=22?"PASS":"FAIL",detail:`found ${nodeVer}`};return nodeMajor<22&&(check.hint="Upgrade to Node.js >= 22. See https://nodejs.org/en/download."),check}function checkGitAvailable(dir){let gitOk=!1;try{gitOk=runCli("git",["--version"],{cwd:dir,timeoutMs:3e3}).exitCode===0}catch{}const check={id:"git-available",label:"git installed",status:gitOk?"PASS":"FAIL",detail:gitOk?"git found in PATH":"git not found in PATH"};return gitOk||(check.hint="Install git from https://git-scm.com."),[check,gitOk]}function checkArbiterProject(dir,gitOk,claudeHome){const out=[];if(!existsSync(join(dir,"arbiter.json")))return out;const agentsPresent=existsSync(join(dir,"AGENTS.md")),agentsCheck={id:"agents-md",label:"AGENTS.md present",status:agentsPresent?"PASS":"WARN",detail:agentsPresent?"found":"not found"};if(agentsPresent||(agentsCheck.hint="Run `arbiter init` to generate AGENTS.md."),out.push(agentsCheck),out.push({id:"integrations",label:"Skill integrations discoverable",status:"PASS",detail:"advisory \u2014 review the detected and recommended skills in your configured integrations.",hint:"Review your configured integrations and installed skills."}),out.push(checkExternalModelHealth()),gitOk){let hooksPath="";try{hooksPath=runCli("git",["config","core.hooksPath"],{cwd:dir,timeoutMs:3e3}).stdout.trim()}catch{}const hooksCheck={id:"hooks-path",label:"git hooks path configured",status:hooksPath.length>0?"PASS":"WARN",detail:hooksPath.length>0?`core.hooksPath = ${hooksPath}`:"core.hooksPath not set"};hooksPath.length===0&&(hooksCheck.hint="Run `git config core.hooksPath .githooks` to enable hooks."),out.push(hooksCheck)}return out.push(checkGateScript(dir)),out.push(checkGateToolchain(dir)),out.push(checkGateMutex(dir)),out.push(checkScaffoldWiringHealth(dir)),out.push(...checkLockfiles(dir)),out.push(checkCollaborationCoherence(dir)),out.push(checkLanguageArchetypeCoherence(dir)),out.push(checkOverlayCoherence(dir)),out.push(checkAutonomyCoherence(dir)),out.push(checkProfileCoherence(dir)),out.push(checkTrunkSoloParityWiring(dir)),out.push(checkCompanionHealth(dir,claudeHome)),out.push(checkBypassCeremony(dir)),out}function checkExternalModelHealth(){const access=detectExternalModel("codex"),version=access.version?`codex ${access.version}`:"codex";return access.available?access.authenticated?{id:"external-model-codex",label:"External Codex reviewer",status:"PASS",detail:`${version}: authenticated`}:{id:"external-model-codex",label:"External Codex reviewer",status:"WARN",detail:`${version}: not authenticated`,hint:"Authenticate the Codex CLI before enabling cross-model review."}:{id:"external-model-codex",label:"External Codex reviewer",status:"WARN",detail:`${version}: ${access.error??"unavailable"}`,hint:"Install the Codex CLI to enable the optional cross-model review slot."}}function checkCompanionHealth(dir,claudeHome){const base={id:"companions",label:"Companions",status:"PASS"};if(isArbiterSelf(dir))return{...base,detail:"companions never activate on arbiter-self (its own complexity is load-bearing)"};const cfg=readRawCoherenceConfig(dir),rows=diagnoseCompanions({claudeHome:claudeHome??join(os.homedir(),".claude"),language:detectLanguage(dir),...cfg?.companions?{overrides:cfg.companions}:{}});if(rows.length===0)return{...base,detail:"no known companions in the registry"};const detail=rows.map(r=>r.installed?r.disabledByConfig?`${r.label}: detected, disabled by config`:`${r.label}: detected, mode=${r.mode} (${r.modeSource})`:`${r.label}: not installed`).join("; ");return{...base,detail}}function checkGateScript(dir){const present=existsSync(join(dir,"scripts","check-all.mjs")),check={id:"gate-script",label:"gate script (scripts/check-all.mjs) present",status:present?"PASS":"WARN",detail:present?"found":"scripts/check-all.mjs not found"};return present||(check.hint="Run `arbiter init` (or `arbiter update`) to (re)generate the gate."),check}function checkGateToolchain(dir){const check={id:"gate-toolchain",label:"gate toolchain installed (tools check-all.mjs invokes)",status:"PASS",detail:"all required gate tools resolve"};let report;try{report=runProbes(dir)}catch(err){return check.status="WARN",check.detail=`could not probe gate toolchain: ${err instanceof Error?err.message:String(err)}`,check}const failed=report.probes.filter(p=>p.status==="failed").map(p=>p.tool),missing=report.probes.filter(p=>p.status==="skipped"&&p.reason==="toolchain-missing").map(p=>p.tool);if(failed.length===0&&missing.length===0)return check.detail=`all gate tools resolve for ${report.stack}`,check;check.status=failed.length>0?"FAIL":"WARN";const parts=[];return missing.length>0&&parts.push(`not installed: ${missing.join(", ")}`),failed.length>0&&parts.push(`broken/incompatible: ${failed.join(", ")}`),check.detail=`gate would error on first run \u2014 ${parts.join("; ")}`,check.hint="Install the gate toolchain (e.g. `npm install` for the declared devDependencies) before running `node scripts/check-all.mjs L1`.",check}function checkGateMutex(dir){const id="gate-mutex",label="gate-exec mutex";let lockPath;try{lockPath=gateLockPath(deriveGateKey(dir))}catch(err){return{id,label,status:"WARN",detail:`could not derive gate mutex path: ${err instanceof Error?err.message:String(err)}`}}try{runCli("flock",["--version"],{cwd:dir,timeoutMs:3e3})}catch{return{id,label,status:"WARN",detail:`mutex ${lockPath}; flock(1) is unavailable, so holder/waiter count cannot be inspected`,hint:"Install util-linux flock to inspect the gate mutex."}}const helperPath=resolve(dir,"scripts","lib","waiter-count.mjs");if(!existsSync(helperPath))return{id,label,status:"WARN",detail:`mutex ${lockPath}; waiter-count helper is missing at ${helperPath}`,hint:"Run `arbiter update` to restore scripts/lib/waiter-count.mjs."};try{const{stdout}=runCli("node",[helperPath,lockPath],{cwd:dir,timeoutMs:5e3}),count=Number(stdout.trim());return!Number.isInteger(count)||count<0?{id,label,status:"WARN",detail:`mutex ${lockPath}; waiter-count helper returned invalid output: ${JSON.stringify(stdout.trim())}`,hint:"Run `arbiter update` to restore scripts/lib/waiter-count.mjs."}:count>2?{id,label,status:"WARN",detail:`mutex ${lockPath}; ${count} supervisor/holder/waiter process(es) currently have the lock open`,hint:"Inspect the process groups; do not kill only flock while its gated command is running."}:{id,label,status:"PASS",detail:`mutex ${lockPath}; ${count} supervisor/holder/waiter process(es) currently have the lock open (a normal gate-exec uses two: Node holder + detached supervisor)`}}catch{return{id,label,status:"WARN",detail:`mutex ${lockPath}; waiter-count helper could not be run`,hint:"Run `arbiter update` to restore scripts/lib/waiter-count.mjs."}}}function checkScaffoldWiringHealth(dir){const{unwired}=checkScaffoldWiring(dir);if(unwired.length===0)return{id:"scaffold-wiring",label:"gate scripts referenced by check-all.mjs/run.sh/Makefile",status:"PASS",detail:"every scripts/check-*.mjs is referenced or declared optional"};const names=unwired.map(u=>u.path).join(", ");return{id:"scaffold-wiring",label:"gate scripts referenced by check-all.mjs/run.sh/Makefile",status:"WARN",detail:`${unwired.length} script(s) not referenced by check-all.mjs, run.sh, or Makefile: ${names}`,hint:"Wire each into check-all.mjs (or run.sh/Makefile) if it should run, or remove it if dead."}}function checkProfileCoherence(dir){const check={id:"profile-coherence",label:"automation profile (worktrees / gate-level) coherence",status:"PASS",detail:"coherent"},cfg=readRawCoherenceConfig(dir);if(cfg===null)return check.status="WARN",check.detail="could not read arbiter.json for profile-coherence check",check;if(cfg.governanceLevel===void 0)return check.status="WARN",check.detail="governanceLevel missing from arbiter.json \u2014 cannot check profile coherence",check.hint="Run `arbiter update` to repair the governanceLevel field.",check;const mode=resolveCollaborationMode(cfg),r=validateProfileCoherence(cfg.automation?.maxParallelWorktrees,cfg.automation?.defaultGateLevel,mode,cfg.governanceLevel);return r.severity==="OK"?(check.detail=`${mode} @ ${cfg.governanceLevel} \u2014 profile prefs coherent`,check):(check.status=r.severity==="CRITICAL"?"FAIL":"WARN",check.detail=r.message,r.remediation!==void 0&&(check.hint=r.remediation),check)}function checkCollaborationCoherence(dir){const check={id:"collab-coherence",label:"collaborationMode \xD7 governanceLevel coherence",status:"PASS",detail:"coherent"},cfg=readRawCoherenceConfig(dir);if(cfg===null)return check.status="WARN",check.detail="could not read arbiter.json for coherence check",check;if(cfg.collaborationMode===void 0||cfg.governanceLevel===void 0)return check.status="WARN",check.detail="collaborationMode or governanceLevel missing from arbiter.json",check.hint="Run `arbiter update` to migrate the collaborationMode axis.",check;const r=validateCollaborationCoherence(cfg.collaborationMode,cfg.governanceLevel);return r.severity==="OK"?(check.detail=`${cfg.collaborationMode} @ ${cfg.governanceLevel} \u2014 coherent`,check):(check.status=r.severity==="CRITICAL"?"FAIL":"WARN",check.detail=r.message,r.remediation!==void 0&&(check.hint=r.remediation),check)}function hasLocalCiParityCheck(dir){return existsSync(join(dir,"scripts","check-local-ci-parity.mjs"))}function hasPushGatingWired(dir){return existsSync(join(dir,".githooks","pre-push"))}function checkTrunkSoloParityWiring(dir){const check={id:"trunk-solo-parity-wiring",label:"trunk-solo local-ci-parity + push-gating",status:"PASS",detail:"not applicable (collaborationMode is not trunk-solo)"},cfg=readRawCoherenceConfig(dir);if(cfg===null)return check.detail="could not read arbiter.json for trunk-solo-parity-wiring check",check;const mode=resolveCollaborationMode(cfg),hasParityCheck=hasLocalCiParityCheck(dir),hasPushGating=hasPushGatingWired(dir),r=validateTrunkSoloParityCoherence(mode,{hasParityCheck,hasPushGating});return r.severity==="OK"?(mode!=="trunk-solo"||(check.detail="local-ci-parity check + push-gating both wired \u2014 coherent",hasWorkflowFiles(dir)&&(check.detail+=". CI workflow files present \u2014 verify `node scripts/check-local-ci-parity.mjs` is green.")),check):(check.status="FAIL",check.detail=r.message,r.remediation!==void 0&&(check.hint=r.remediation),check)}function checkLanguageArchetypeCoherence(dir){const check={id:"language-archetype-coherence",label:"language \xD7 archetype coherence",status:"PASS",detail:"coherent"},cfg=readRawCoherenceConfig(dir);if(cfg===null)return check.status="WARN",check.detail="could not read arbiter.json for language-archetype check",check;if(cfg.language===void 0||cfg.archetype===void 0)return check.detail="language or archetype absent \u2014 nothing to check",check;const r=validateLanguageArchetypeCoherence(cfg.language,cfg.archetype);return r.severity==="OK"?(check.detail=`${cfg.language} \xD7 ${cfg.archetype} \u2014 coherent`,check):(check.status="WARN",check.detail=r.message,check.hint="Pick an archetype that matches the language, or switch language.",check)}function checkOverlayCoherence(dir){const check={id:"overlay-coherence",label:"industryOverlay \xD7 governanceLevel coherence",status:"PASS",detail:"coherent"},cfg=readRawCoherenceConfig(dir);if(cfg===null)return check.status="WARN",check.detail="could not read arbiter.json for overlay-coherence check",check;const overlay=cfg.industryOverlay??"none";if(overlay==="none")return check.detail="no industry overlay configured \u2014 nothing to check",check;if(cfg.governanceLevel===void 0)return check.status="WARN",check.detail=`industryOverlay='${overlay}' set but governanceLevel missing from arbiter.json`,check.hint="Run `arbiter update` to repair the governanceLevel field.",check;const r=validateOverlayCoherence(overlay,cfg.governanceLevel);return r.severity==="OK"?(check.detail=`${overlay} @ ${cfg.governanceLevel} \u2014 coherent`,check):(check.status="WARN",check.detail=r.message,check.hint="Raise governanceLevel or change industryOverlay so the overlay's controls are gate-backed.",check)}function hasWorkflowFiles(dir){try{return readdirSync(join(dir,".github","workflows")).some(f=>f.endsWith(".yml")||f.endsWith(".yaml"))}catch{return!1}}function checkAutonomyCoherence(dir){const check={id:"autonomy-coherence",label:"automation.autonomy \xD7 governanceLevel \xD7 CI coherence",status:"PASS",detail:"coherent"},cfg=readRawCoherenceConfig(dir);if(cfg===null)return check.status="WARN",check.detail="could not read arbiter.json for autonomy-coherence check",check;const autonomy=cfg.automation?.autonomy;if(autonomy===void 0)return check.detail="no automation block configured \u2014 autonomy defaults to L0 (ask-each-step)",check;const hasCi=hasWorkflowFiles(dir);if(cfg.governanceLevel===void 0)return check.status="WARN",check.detail="automation.autonomy set but governanceLevel missing from arbiter.json \u2014 cannot check autonomy coherence",check.hint="Run `arbiter update` to repair the governanceLevel field.",check;const r=validateAutonomyCoherence(autonomy,cfg.governanceLevel,hasCi);return r.severity!=="OK"?(check.status=r.severity==="CRITICAL"?"FAIL":"WARN",check.detail=r.message,r.remediation!==void 0&&(check.hint=r.remediation),check):!hasCi&&cfg.useGitHub===!0?(check.status="WARN",check.detail="useGitHub is true but .github/workflows/ contains no workflow files \u2014 CI config drift (the autonomy check trusts the filesystem, not the flag)",check.hint="Add a workflow under .github/workflows/ or set useGitHub to false.",check):(check.detail=`autonomy ${autonomy} @ ${cfg.governanceLevel} (CI: ${hasCi?"yes":"no"}) \u2014 coherent`,check)}const LOCK_CHECK_ID="arbiter-lock",DOCTOR_LOCK_STALE_AGE_MS=6*36e5;export const MANAGED_LOCKS=[{rel:join(".arbiter",".lock"),id:LOCK_CHECK_ID},{rel:join(".arbiter","kit.lock"),id:"arbiter-kit-lock"}];function checkLockHealth(dir,rel,id){const lockPath=join(dir,rel);if(!existsSync(lockPath))return{id,label:`${rel} not present`,status:"PASS",detail:"no leftover lock file"};const info=readLockInfo(lockPath);if(info===null)return{id,label:`${rel} unreadable`,status:"WARN",detail:"lock file exists but contents are not valid JSON",hint:"Run `arbiter doctor recover-lock` to remove it."};const ageMs=Date.now()-new Date(info.startedAt).getTime(),ageH=Math.round(ageMs/36e5),sameHost=info.hostname===os.hostname();return isLockStale(info,DOCTOR_LOCK_STALE_AGE_MS)?{id,label:`${rel} stale`,status:"WARN",detail:`pid ${info.pid}, age ${ageH}h, cmd: ${info.cmd}`,hint:"Run `arbiter doctor recover-lock` to clean up."}:{id,label:`${rel} active`,status:"PASS",detail:`pid ${info.pid}, age ${ageH}h${sameHost?"":" (other host)"}`}}function checkLockfiles(dir){return MANAGED_LOCKS.map(({rel,id})=>checkLockHealth(dir,rel,id))}function checkGatePassLog(dir){const logPath=join(dir,".arbiter","gate-pass.jsonl");if(!existsSync(logPath))return{id:"gate-pass-log",label:"gate-pass log",status:"PASS",detail:"not created yet \u2014 run the gate to start logging"};const lines=readFileTranslated(logPath,"utf-8").split(`
3
+ `).filter(l=>l.trim()),parsed=[],parseErrors=[];for(let i=0;i<lines.length;i++){const line=lines[i]??"";try{const entry=JSON.parse(line);parsed.push(`${(entry.sha??"unknown").slice(0,7)} @ ${entry.signedAt??"?"} [${entry.level??"?"}]`)}catch(err){parseErrors.push(`line ${i+1}: ${err instanceof Error?err.message:String(err)}`)}}if(parseErrors.length>0)return{id:"gate-pass-log",label:"gate-pass log",status:"WARN",detail:`${lines.length} entries, ${parseErrors.length} unparseable: ${parseErrors.slice(0,3).join("; ")}`,hint:"Inspect .arbiter/gate-pass.jsonl for corrupt lines."};const recent=parsed.slice(-5);return{id:"gate-pass-log",label:"gate-pass log",status:"PASS",detail:`${lines.length} entries; last ${recent.length}: ${recent.join(", ")}`}}function bypassCeremonyWarn(detail){return{id:"bypass-ceremony",label:"bypass ceremony budget",status:"WARN",detail}}function runBypassCeremonyScript(dir,scriptPath){let result;try{result=runCli("node",[scriptPath,"--json","--root",dir],{cwd:dir,timeoutMs:15e3})}catch(err){return bypassCeremonyWarn(`could not run bypass-ceremony check: ${err instanceof Error?err.message:String(err)}`)}try{return{result,parsed:JSON.parse(result.stdout)}}catch{return bypassCeremonyWarn("bypass-ceremony check produced unparseable output")}}function toBypassCeremonyCheck(result,parsed){const channels=parsed.channels??[],ledgerViolations=parsed.ledgerViolations??[],rateViolations=parsed.rateViolations??[],channelSummary=channels.length>0?channels.map(c=>`${c.env}: ${c.count}/${c.ceiling}`).join(", "):"no bypass-log channels in the trailing 30 days";return result.exitCode!==0||ledgerViolations.length>0||rateViolations.length>0?{id:"bypass-ceremony",label:"bypass ceremony budget",status:"FAIL",detail:[...rateViolations,...ledgerViolations].slice(0,3).join("; ")||`channels: ${channelSummary}`,hint:"Run `node scripts/check-bypass-ceremony.mjs` for the full report."}:{id:"bypass-ceremony",label:"bypass ceremony budget",status:"PASS",detail:`channels: ${channelSummary}`}}function checkBypassCeremony(dir){const scriptPath=join(dir,"scripts","check-bypass-ceremony.mjs");if(!existsSync(scriptPath))return isArbiterSelf(dir)?bypassCeremonyWarn("scripts/check-bypass-ceremony.mjs not found"):{id:"bypass-ceremony",label:"bypass ceremony budget",status:"PASS",detail:"not configured for this project"};const outcome=runBypassCeremonyScript(dir,scriptPath);return"id"in outcome?outcome:toBypassCeremonyCheck(outcome.result,outcome.parsed)}function checkChannelSetting(dir,channelFlag){try{const config=loadConfig(dir),resolved=resolveChannel({...channelFlag!==void 0&&{flag:channelFlag},...config?.channel!==void 0&&{config:config.channel}});return{id:"release-channel",label:"release channel",status:"PASS",detail:`${resolved.value} (${resolved.source})`}}catch(err){return{id:"release-channel",label:"release channel",status:"FAIL",detail:err instanceof Error?err.message:String(err),hint:"Fix or delete arbiter.json and re-run `arbiter init`."}}}function emitHealthOutput(checks,pass,warn,fail){process.stdout.write(`
4
+ `);for(const check of checks){const icon=check.status==="PASS"?"[PASS]":check.status==="WARN"?"[WARN]":"[FAIL]";process.stdout.write(` ${icon} ${check.label} \u2014 ${check.detail}
5
+ `),check.hint&&process.stdout.write(` hint: ${check.hint}
6
+ `)}process.stdout.write(`
7
+ ${pass} passed, ${warn} warnings, ${fail} failed
8
+
9
+ `)}function checkTaskDocument(dir){const statusPath=join(dir,".claude",".task","status.json"),id="task-document",label="unified task document";if(!existsSync(statusPath))return{id,label,status:"PASS",detail:"no active task (.claude/.task/ absent)"};try{const state=JSON.parse(readFileTranslated(statusPath,"utf-8"));return typeof state.phase!="string"||!isValidPhase(state.phase)?{id,label,status:"WARN",detail:`.claude/.task/status.json has an invalid phase: ${JSON.stringify(state.phase)}`,hint:"Re-run `arbiter task init` / `arbiter task advance` to repair task state."}:{id,label,status:"PASS",detail:`active task ${state.taskId||"(unset)"} at phase=${state.phase}`}}catch(err){return{id,label,status:"WARN",detail:`.claude/.task/status.json is not valid JSON \u2014 ${err instanceof Error?err.message:String(err)}`,hint:"Delete .claude/.task/ and re-initialise with `arbiter task init`."}}}export async function runDoctorHealth(opts={}){const dir=resolve(opts.dir??"."),[gitCheck,gitOk]=checkGitAvailable(dir),checks=[checkNodeVersion(),gitCheck,...checkArbiterProject(dir,gitOk,opts.claudeHome),checkChannelSetting(dir,opts.channelFlag),checkTaskDocument(dir),checkGatePassLog(dir)];let repaired;opts.repair&&(repaired=await repairStaleLockInChecks(dir,checks));const pass=checks.filter(c=>c.status==="PASS").length,warn=checks.filter(c=>c.status==="WARN").length,fail=checks.filter(c=>c.status==="FAIL").length;return opts.json?jsonOutput("doctor health",fail>0?"error":"ok",{checks,pass,warn,fail,...repaired?{repaired}:{}}):(emitHealthOutput(checks,pass,warn,fail),repaired&&process.stdout.write(` repaired: released stale lock pid ${repaired.pid}
10
+
11
+ `)),{exitCode:fail>0?1:0,checks,pass,warn,fail,...repaired?{repaired}:{}}}async function repairStaleLockInChecks(dir,checks){let firstRepaired;for(const{rel,id}of MANAGED_LOCKS){const lockCheck=checks.find(c=>c.id===id);if(!lockCheck||lockCheck.status!=="WARN")continue;const lockPath=join(dir,rel),info=readLockInfo(lockPath);if(info){try{await forceReleaseLock(lockPath,info.pid,dir)}catch(err){lockCheck.hint=err instanceof ArbiterError?`auto-repair failed: ${err.message}`:`auto-repair failed: ${err instanceof Error?err.message:String(err)}`;continue}lockCheck.status="PASS",lockCheck.label=`${rel} released (auto-repaired)`,lockCheck.detail=`released stale lock pid ${info.pid}`,delete lockCheck.hint,firstRepaired??={lockPath,pid:info.pid}}}return firstRepaired}
@@ -0,0 +1,13 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import{jsonOutput}from"../../utils/json-output.js";import{runGateProofs}from"../../conformance/gate-proofs.js";function emitProveGatesOutput(results){process.stdout.write(`
3
+ arbiter doctor --prove-gates \u2014 negative proof per tier-1 gate
4
+
5
+ `);for(const r of results){const label=r.bites?"[BITES]":"[NO-BITE]";process.stdout.write(` ${label} ${r.id} \u2014 ${r.title}
6
+ `),process.stdout.write(` violation: ${r.violation}
7
+ `),process.stdout.write(` verdict: ${r.verdict}${r.detail?` (${r.detail})`:""}
8
+ `)}process.stdout.write(`
9
+ `)}export function runDoctorProveGates(opts={}){const results=runGateProofs(),bitingCount=results.filter(r=>r.bites).length,notBitingCount=results.length-bitingCount,exitCode=notBitingCount>0?1:0;return opts.json?jsonOutput("doctor --prove-gates",exitCode===0?"ok":"error",{results,bitingCount,notBitingCount}):(emitProveGatesOutput(results),notBitingCount>0?process.stdout.write(`${notBitingCount} of ${results.length} gate(s) did NOT bite on their negative fixture.
10
+
11
+ `):process.stdout.write(`All ${results.length} tier-1 gates bite on their negative fixture.
12
+
13
+ `)),{exitCode,results,bitingCount,notBitingCount}}
@@ -0,0 +1,12 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import{existsSync}from"node:fs";import{join,resolve}from"node:path";import os from"node:os";import{jsonOutput}from"../../utils/json-output.js";import{t}from"../../i18n/index.js";import{inspectLock,forceReleaseLock,isLockStale}from"../../utils/file-lock.js";import{UserFacingError}from"../../utils/errors.js";import{MANAGED_LOCKS}from"./health.js";const RECOVER_LOCK_STALE_AGE_MS=6*36e5;function formatRefusalHolders(refusals){return refusals.map(({rel,pid,cmd,age})=>`${rel} (pid ${pid}, cmd: ${cmd}, age: ${age}s)`).join("; ")}function emitRefusedRecovery(opts,refusalMessage,summary,refusals){opts.json&&jsonOutput("doctor recover-lock","error",{found:!0,released:summary.released,...summary.firstInfo?{info:summary.firstInfo}:{},...summary.corrupt?{corrupt:!0}:{},refused:refusals},[refusalMessage])}function emitRecoveryResult(opts,targetDir,result){opts.json?jsonOutput("doctor recover-lock","ok",{found:result.found,released:result.released,...result.info?{info:result.info}:{},...result.corrupt?{corrupt:!0}:{}}):result.found||process.stdout.write(` No lock file found in ${join(targetDir,".arbiter")}
3
+ `)}async function recoverManagedLock(lockPath,rel,targetDir,opts){if(!existsSync(lockPath))return{found:!1,released:!1};const info=await inspectLock(lockPath);if(!info)return opts.json||process.stdout.write(` Lock found (${rel}): CORRUPT / unreadable; removing it.
4
+ `),await forceReleaseLock(lockPath,0,targetDir,{allowUnreadable:!0}),opts.json||process.stdout.write(` Corrupt lock released.
5
+ `),{found:!0,released:!0,corrupt:!0};if(!opts.json){const age=Math.round((Date.now()-new Date(info.startedAt).getTime())/1e3),onThisHost=info.hostname===os.hostname()?"yes":"no";process.stdout.write(` Lock found (${rel}):
6
+ `),process.stdout.write(` pid: ${info.pid}
7
+ `),process.stdout.write(` hostname: ${info.hostname}
8
+ `),process.stdout.write(` cmd: ${info.cmd}
9
+ `),process.stdout.write(` age: ${age}s
10
+ `),process.stdout.write(` this host: ${onThisHost}
11
+ `)}if(!opts.force&&!isLockStale(info,RECOVER_LOCK_STALE_AGE_MS)){const age=Math.round((Date.now()-new Date(info.startedAt).getTime())/1e3);return{found:!0,released:!1,info,refusal:{rel,pid:info.pid,cmd:info.cmd,age}}}return await forceReleaseLock(lockPath,info.pid,targetDir),opts.json||process.stdout.write(` Lock released.
12
+ `),{found:!0,released:!0,info}}export async function runDoctorRecoverLock(opts={}){const targetDir=resolve(opts.dir??".");let firstInfo,released=!1,corrupt=!1;const refusals=[];for(const{rel}of MANAGED_LOCKS){const lockPath=join(targetDir,rel),result2=await recoverManagedLock(lockPath,rel,targetDir,opts);released||=result2.released,corrupt||=result2.corrupt===!0,firstInfo??=result2.info,result2.refusal&&refusals.push(result2.refusal)}if(refusals.length>0){const holders=formatRefusalHolders(refusals),refusalMessage=t("cli.doctor.recover_lock.refused",{holders});throw emitRefusedRecovery(opts,refusalMessage,{released,firstInfo,corrupt},refusals),new UserFacingError(t("cli.doctor.recover_lock.refused",{holders}))}if(!firstInfo&&!corrupt){const result2={found:!1,released:!1};return emitRecoveryResult(opts,targetDir,result2),result2}const result={found:!0,released,...firstInfo?{info:firstInfo}:{},...corrupt?{corrupt:!0}:{}};return emitRecoveryResult(opts,targetDir,result),result}
@@ -0,0 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import{existsSync}from"node:fs";import{ensureDir}from"../../utils/fs.js";import{join,resolve}from"node:path";import{acquireLock}from"../../utils/file-lock.js";import{jsonOutput}from"../../utils/json-output.js";import{loadConfig,writeSnapshot}from"../../utils/config.js";const REPAIR_STATE_CMD="doctor repair-state";export async function runDoctorRepairState(opts={}){const dir=resolve(opts.dir??"."),configPath=join(dir,"arbiter.json"),snapshotPath=join(dir,".arbiter-generated.json");if(!existsSync(configPath)){const msg=`arbiter.json not found at ${configPath} \u2014 cannot repair snapshot without source-of-truth config. Run \`arbiter init\` first.`;return opts.json?jsonOutput(REPAIR_STATE_CMD,"error",{dir,configPath},[msg]):process.stderr.write(`Error: ${msg}
3
+ `),{exitCode:2,repaired:!1,snapshotPath}}const config=loadConfig(dir);if(config===null){const msg=`failed to load ${configPath}`;return opts.json?jsonOutput(REPAIR_STATE_CMD,"error",{dir,configPath},[msg]):process.stderr.write(`Error: ${msg}
4
+ `),{exitCode:2,repaired:!1,snapshotPath}}ensureDir(join(dir,".arbiter"));const lock=await acquireLock(join(dir,".arbiter",".lock"));try{writeSnapshot(dir,config)}finally{await lock.release()}const manifestWarning="generated-manifest (.arbiter-generated-manifest.json) is NOT re-derivable from config; if you suspect template-fix drift, re-run `arbiter update` to refresh it.";return opts.json?jsonOutput(REPAIR_STATE_CMD,"ok",{snapshotPath,repaired:!0},void 0,{warnings:[manifestWarning]}):(process.stdout.write(`doctor: snapshot re-derived from arbiter.json \u2192 ${snapshotPath}
5
+ `),process.stderr.write(`doctor: warning \u2014 ${manifestWarning}
6
+ `)),{exitCode:0,repaired:!0,snapshotPath}}
@@ -0,0 +1,4 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ function toSemVer(major,minor,patch){return{major:Number(major),minor:Number(minor),patch:patch?Number(patch):0}}export function compareSemVer(a,b){return a.major-b.major||a.minor-b.minor||a.patch-b.patch}function lineOf(text,index){return text.slice(0,index).split(`
3
+ `).length}const TRUTHY_RE=/continue-on-error:\s*['"]?(\$\{\{\s*true\s*\}\}|true|on|yes|y)\b/i;function buildBlockingLineMap(workflowText){const lines=workflowText.split(`
4
+ `),jobsIdx=lines.findIndex(l=>/^jobs:\s*$/.test(l));if(jobsIdx===-1)return()=>!0;const jobStarts=[];for(let i=jobsIdx+1;i<lines.length;i++)/^ {2}[\w-]+:\s*$/.test(lines[i]??"")&&jobStarts.push(i);const advisoryRanges=jobStarts.map((start,idx)=>({start,end:jobStarts[idx+1]??lines.length})).filter(({start,end})=>lines.slice(start,end).some(l=>TRUTHY_RE.test(l))).map(({start,end})=>({start,end}));return lineNo=>{const idx=lineNo-1;return!advisoryRanges.some(r=>idx>=r.start&&idx<r.end)}}const DOWNLOAD_URL_RE=/https:\/\/github\.com\/[\w.-]+\/([\w.-]+)\/releases\/download\/v?(\d+)\.(\d+)(?:\.(\d+))?\//g,ENV_PIN_RE=/^\s*([A-Z][A-Z0-9_]*)_VERSION\s*:\s*['"]?(\d+)\.(\d+)(?:\.(\d+))?/gm,ACTION_TAG_RE=/uses:\s*[\w.-]+\/([\w.-]+?)(?:-action)?@v?(\d+)\.(\d+)(?:\.(\d+))?/g;function extractDownloadUrlPins(text,file,isBlocking){const pins=[];for(const m of text.matchAll(DOWNLOAD_URL_RE)){const[,repo,major,minor,patch]=m;if(!repo||!major||!minor)continue;const line=lineOf(text,m.index);pins.push({tool:repo,version:toSemVer(major,minor,patch),file,line,blocking:isBlocking(line)})}return pins}function extractEnvPins(text,file,isBlocking){const pins=[];for(const m of text.matchAll(ENV_PIN_RE)){const[,prefix,major,minor,patch]=m;if(!prefix||!major||!minor)continue;const line=lineOf(text,m.index);pins.push({tool:prefix.toLowerCase(),version:toSemVer(major,minor,patch),file,line,blocking:isBlocking(line)})}return pins}function extractActionTagPins(text,file,isBlocking){const pins=[];for(const m of text.matchAll(ACTION_TAG_RE)){const[,repo,major,minor,patch]=m;if(!repo||!major||!minor)continue;const line=lineOf(text,m.index);pins.push({tool:repo,version:toSemVer(major,minor,patch),file,line,blocking:isBlocking(line)})}return pins}export function extractToolPins(workflowText,file){const isBlocking=buildBlockingLineMap(workflowText);return[...extractDownloadUrlPins(workflowText,file,isBlocking),...extractEnvPins(workflowText,file,isBlocking),...extractActionTagPins(workflowText,file,isBlocking)]}
@@ -0,0 +1,9 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import{readdirSync}from"node:fs";import{join,resolve}from"node:path";import{jsonOutput}from"../../utils/json-output.js";import{runCli,CliError}from"../../utils/run-cli.js";import{extractToolPins,compareSemVer}from"./tool-pin-extract.js";import{readFileTranslated}from"../../utils/fs.js";function formatVersion(v){return`${v.major}.${v.minor}.${v.patch}`}function parseSemVerFromVersionOutput(output){const m=/(\d+)\.(\d+)(?:\.(\d+))?/.exec(output);if(!m)return;const[,major,minor,patch]=m;if(!(!major||!minor))return{major:Number(major),minor:Number(minor),patch:patch?Number(patch):0}}function getLocalToolVersion(tool,override){if(override&&Object.prototype.hasOwnProperty.call(override,tool)){const raw=override[tool];return raw?parseSemVerFromVersionOutput(raw)??"unparseable":void 0}try{const result=runCli(tool,["--version"],{timeoutMs:5e3}),combined=`${result.stdout}
3
+ ${result.stderr}`;return parseSemVerFromVersionOutput(combined)??"unparseable"}catch(err){if(err instanceof CliError){if(err.notFound)return;const combined=`${err.stdout}
4
+ ${err.stderr}`;return parseSemVerFromVersionOutput(combined)??"unparseable"}return}}function listWorkflowFiles(dir){const wfDir=join(dir,".github","workflows");let entries;try{entries=readdirSync(wfDir)}catch{return[]}return entries.filter(f=>f.endsWith(".yml")||f.endsWith(".yaml")).map(f=>join(wfDir,f))}function dedupePins(pins){const byTool=new Map;for(const pin of pins){const existing=byTool.get(pin.tool);if(!existing){byTool.set(pin.tool,pin);continue}const merged={...compareSemVer(pin.version,existing.version)>0?pin:existing,blocking:existing.blocking||pin.blocking};byTool.set(pin.tool,merged)}return[...byTool.values()]}function buildCheck(pin,override){const id=`tool-pin-${pin.tool}`,label=`${pin.tool} version matches CI pin`,pinDetail=`pin ${formatVersion(pin.version)} (${pin.file}:${pin.line})`,local=getLocalToolVersion(pin.tool,override);if(local===void 0)return{id,label,status:pin.blocking?"FAIL":"WARN",detail:`${pin.tool} not found locally \u2014 ${pinDetail}`};if(local==="unparseable")return{id,label,status:"WARN",detail:`${pin.tool} local version output unparseable \u2014 ${pinDetail}`};const cmp=compareSemVer(local,pin.version),localDetail=`local ${formatVersion(local)}`;return cmp<0?{id,label,status:"FAIL",detail:`${pin.tool} ${localDetail} < ${pinDetail}`}:{id,label,status:"PASS",detail:`${pin.tool} ${localDetail} >= ${pinDetail}`}}function emitTextOutput(checks,pass,warn,fail){process.stdout.write(`
5
+ `);for(const check of checks){const icon=check.status==="PASS"?"[PASS]":check.status==="WARN"?"[WARN]":"[FAIL]";process.stdout.write(` ${icon} ${check.label} \u2014 ${check.detail}
6
+ `)}process.stdout.write(`
7
+ ${pass} passed, ${warn} warnings, ${fail} failed
8
+
9
+ `)}export function runDoctorToolPins(opts={}){const dir=resolve(opts.dir??"."),allPins=listWorkflowFiles(dir).flatMap(path=>{const text=readFileTranslated(path,"utf-8"),rel=path.slice(dir.length+1);return extractToolPins(text,rel)}),checks=dedupePins(allPins).map(pin=>buildCheck(pin,opts.localVersionOverride)),pass=checks.filter(c=>c.status==="PASS").length,warn=checks.filter(c=>c.status==="WARN").length,fail=checks.filter(c=>c.status==="FAIL").length,exitCode=fail>0?1:0;return opts.json?jsonOutput("doctor tool-pins",fail>0?"error":"ok",{checks,pass,warn,fail}):emitTextOutput(checks,pass,warn,fail),{exitCode,checks,pass,warn,fail}}
@@ -0,0 +1,2 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import{intro,outro,confirm,isCancel,cancel}from"@clack/prompts";import{runDoctorHealth}from"./doctor.js";export async function runInteractiveDoctor(opts={}){const dir=opts.dir;intro("arbiter doctor");const first=await runDoctorHealth({...dir!==void 0?{dir}:{},json:!1,repair:!1});if(!first.checks.some(c=>c.status==="FAIL"&&/lock/i.test(`${c.label} ${c.detail}`))){outro(first.exitCode===0?"Healthy \u2014 nothing to repair.":"No auto-fixable issues found."),first.exitCode!==0&&(process.exitCode=first.exitCode);return}const doRepair=await confirm({message:"Fixable issue detected. Attempt repair now?"});if(isCancel(doRepair)){cancel("Cancelled.");return}if(!doRepair){outro("Skipped repair."),first.exitCode!==0&&(process.exitCode=first.exitCode);return}const repaired=await runDoctorHealth({...dir!==void 0?{dir}:{},json:!1,repair:!0});outro(repaired.exitCode===0?"Repaired \u2014 project healthy.":"Repair ran; some issues remain."),repaired.exitCode!==0&&(process.exitCode=repaired.exitCode)}
@@ -0,0 +1,2 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ export{runDoctorHealth}from"./doctor/health.js";export{runDoctorRepairState}from"./doctor/repair-state.js";export{runDoctorRecoverLock}from"./doctor/recover-lock.js";export{runDoctorClean}from"./doctor/clean.js";export{runDoctorProveGates}from"./doctor/prove-gates.js";export{runDoctorToolPins}from"./doctor/tool-pins.js";export{runDoctorFailOpenCensus}from"./doctor/fail-open-census.js";