@karajan-family/code 4.21.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 (746) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +98 -0
  3. package/bin/karajan-mcp.js +2 -0
  4. package/bin/kj-rag-mcp.js +75 -0
  5. package/bin/kj-tail +361 -0
  6. package/bin/kj.js +2 -0
  7. package/docs/README.es.md +80 -0
  8. package/docs/karajan-code-logo-small.png +0 -0
  9. package/library/logical-clocks-lamport.md +40 -0
  10. package/package.json +140 -0
  11. package/packages/ai-trash/README.md +37 -0
  12. package/packages/ai-trash/bin/kj-trash.js +10 -0
  13. package/packages/ai-trash/package.json +43 -0
  14. package/packages/ai-trash/src/cli.js +200 -0
  15. package/packages/ai-trash/src/destructive-parser.js +74 -0
  16. package/packages/ai-trash/src/git-snapshot.js +128 -0
  17. package/packages/ai-trash/src/hook.js +137 -0
  18. package/packages/ai-trash/src/index.js +14 -0
  19. package/packages/ai-trash/src/install.js +73 -0
  20. package/packages/ai-trash/src/logger.js +60 -0
  21. package/packages/ai-trash/src/manifest.js +91 -0
  22. package/packages/ai-trash/src/permissions.js +26 -0
  23. package/packages/ai-trash/src/snapshot.js +66 -0
  24. package/packages/ai-trash/src/ulid.js +66 -0
  25. package/packages/hu-board/README.md +69 -0
  26. package/packages/hu-board/package.json +28 -0
  27. package/packages/hu-board/public/app.js +259 -0
  28. package/packages/hu-board/public/index.html +77 -0
  29. package/packages/hu-board/public/pipeline.css +132 -0
  30. package/packages/hu-board/public/pipeline.html +48 -0
  31. package/packages/hu-board/public/pipeline.js +200 -0
  32. package/packages/hu-board/public/rag.html +21 -0
  33. package/packages/hu-board/public/rag.js +39 -0
  34. package/packages/hu-board/public/styles.css +973 -0
  35. package/packages/hu-board/public/utils/api.js +139 -0
  36. package/packages/hu-board/public/utils/board-view.js +473 -0
  37. package/packages/hu-board/public/utils/command-launcher.js +213 -0
  38. package/packages/hu-board/public/utils/config-editor.js +181 -0
  39. package/packages/hu-board/public/utils/dashboard-view.js +103 -0
  40. package/packages/hu-board/public/utils/formatters.js +300 -0
  41. package/packages/hu-board/public/utils/graph-view.js +216 -0
  42. package/packages/hu-board/public/utils/hu-actions.js +224 -0
  43. package/packages/hu-board/public/utils/init-listeners.js +180 -0
  44. package/packages/hu-board/public/utils/log-panel.js +222 -0
  45. package/packages/hu-board/public/utils/modals.js +309 -0
  46. package/packages/hu-board/public/utils/plan-rollup-view.js +53 -0
  47. package/packages/hu-board/public/utils/preflight-view.js +89 -0
  48. package/packages/hu-board/public/utils/project-actions.js +143 -0
  49. package/packages/hu-board/public/utils/project-picker-view.js +139 -0
  50. package/packages/hu-board/public/utils/run-launcher.js +169 -0
  51. package/packages/hu-board/public/utils/server-push.js +252 -0
  52. package/packages/hu-board/public/utils/sessions-view.js +199 -0
  53. package/packages/hu-board/public/utils/story-detail-view.js +324 -0
  54. package/packages/hu-board/public/utils/story-edit-form.js +271 -0
  55. package/packages/hu-board/public/wiki.html +32 -0
  56. package/packages/hu-board/public/wiki.js +53 -0
  57. package/packages/hu-board/src/auth.js +92 -0
  58. package/packages/hu-board/src/boot-info.js +16 -0
  59. package/packages/hu-board/src/cleanup-zombies.js +145 -0
  60. package/packages/hu-board/src/command-runner.js +133 -0
  61. package/packages/hu-board/src/config-yaml.js +531 -0
  62. package/packages/hu-board/src/db.js +963 -0
  63. package/packages/hu-board/src/ephemeral-cleaner.js +196 -0
  64. package/packages/hu-board/src/event-bus.js +58 -0
  65. package/packages/hu-board/src/format.js +207 -0
  66. package/packages/hu-board/src/hu-zombie-reaper.js +103 -0
  67. package/packages/hu-board/src/journal-parser.js +180 -0
  68. package/packages/hu-board/src/plan-mutations.js +424 -0
  69. package/packages/hu-board/src/preflight.js +286 -0
  70. package/packages/hu-board/src/routes/api.js +1374 -0
  71. package/packages/hu-board/src/routes/pipeline.js +45 -0
  72. package/packages/hu-board/src/routes/rag.js +42 -0
  73. package/packages/hu-board/src/routes/wiki.js +43 -0
  74. package/packages/hu-board/src/run-tracker.js +121 -0
  75. package/packages/hu-board/src/server.js +438 -0
  76. package/packages/hu-board/src/sync.js +732 -0
  77. package/packages/hu-board/src/token-store.js +56 -0
  78. package/packages/hu-board/src/zombie-reaper.js +206 -0
  79. package/scripts/ai-attribution-guard.yml +84 -0
  80. package/scripts/audit-test-diet.mjs +151 -0
  81. package/scripts/build-sea.mjs +150 -0
  82. package/scripts/diet-baseline.mjs +170 -0
  83. package/scripts/dual-publish.mjs +67 -0
  84. package/scripts/esbuild-sea.config.mjs +273 -0
  85. package/scripts/git-hooks/post-merge +18 -0
  86. package/scripts/install-binary.ps1 +174 -0
  87. package/scripts/install-binary.sh +159 -0
  88. package/scripts/install-guard-all-repos.sh +85 -0
  89. package/scripts/install-kj.sh +193 -0
  90. package/scripts/install.js +901 -0
  91. package/scripts/install.sh +7 -0
  92. package/scripts/postinstall.js +117 -0
  93. package/scripts/protect-all-repos.sh +88 -0
  94. package/scripts/quickstart-gate.mjs +97 -0
  95. package/scripts/regen-arch-stats.sh +71 -0
  96. package/scripts/setup-multi-instance.sh +150 -0
  97. package/scripts/verify-pack.mjs +278 -0
  98. package/src/activity-log.js +59 -0
  99. package/src/agents/agy-agent.js +62 -0
  100. package/src/agents/aider-agent.js +78 -0
  101. package/src/agents/availability.js +52 -0
  102. package/src/agents/base-agent.js +134 -0
  103. package/src/agents/claude-agent.js +413 -0
  104. package/src/agents/codex-agent.js +112 -0
  105. package/src/agents/copilot-agent.js +65 -0
  106. package/src/agents/gemini-agent.js +85 -0
  107. package/src/agents/host-agent.js +38 -0
  108. package/src/agents/index.js +59 -0
  109. package/src/agents/kimi-agent.js +45 -0
  110. package/src/agents/model-registry.js +137 -0
  111. package/src/agents/opencode-agent.js +72 -0
  112. package/src/agents/qwen-agent.js +20 -0
  113. package/src/agents/resolve-bin.js +73 -0
  114. package/src/audit/agent-readiness.js +259 -0
  115. package/src/audit/ai-slop-findings.js +102 -0
  116. package/src/audit/audit-fallback.js +66 -0
  117. package/src/audit/audit-history-display.js +61 -0
  118. package/src/audit/audit-history.js +102 -0
  119. package/src/audit/basal-cost.js +281 -0
  120. package/src/audit/circular-deps.js +144 -0
  121. package/src/audit/dead-exports.js +186 -0
  122. package/src/audit/deterministic-summary.js +327 -0
  123. package/src/audit/harness-scorecard.js +100 -0
  124. package/src/audit/harness-section.js +81 -0
  125. package/src/audit/infra-findings.js +100 -0
  126. package/src/audit/injection-findings.js +114 -0
  127. package/src/audit/issue-filter.js +133 -0
  128. package/src/audit/osv-findings.js +167 -0
  129. package/src/audit/plan-adherence.js +224 -0
  130. package/src/audit/semgrep-findings.js +129 -0
  131. package/src/audit/sonar-findings.js +93 -0
  132. package/src/audit/webperf-input.js +133 -0
  133. package/src/autonomy/arbiter-role.js +71 -0
  134. package/src/autonomy/arbiter.js +22 -0
  135. package/src/autonomy/decider.js +18 -0
  136. package/src/autonomy/decision-resolver.js +58 -0
  137. package/src/autonomy/outcome-report.js +55 -0
  138. package/src/autonomy/policy.js +41 -0
  139. package/src/autonomy/stage-ask.js +30 -0
  140. package/src/bootstrap.js +276 -0
  141. package/src/brain/agent-error-classifier.js +116 -0
  142. package/src/brain/decision-tracker.js +125 -0
  143. package/src/brain/decisor.js +243 -0
  144. package/src/brain/solomon-consult.js +171 -0
  145. package/src/brain/standby-scheduler.js +8 -0
  146. package/src/brain/standby-store.js +12 -0
  147. package/src/brain/with-brain-recovery.js +235 -0
  148. package/src/budget/comparison.js +96 -0
  149. package/src/canvas/parse-md.js +238 -0
  150. package/src/canvas/schema.js +101 -0
  151. package/src/canvas/to-flat.js +85 -0
  152. package/src/canvas/validate.js +163 -0
  153. package/src/checks/ai-surface.js +79 -0
  154. package/src/checks/ai-trash.js +34 -0
  155. package/src/checks/binaries.js +207 -0
  156. package/src/checks/ci.js +105 -0
  157. package/src/checks/config-files.js +217 -0
  158. package/src/checks/dir-setup.js +103 -0
  159. package/src/checks/guarantee-level.js +38 -0
  160. package/src/checks/hardware.js +150 -0
  161. package/src/checks/harness-scorecard.js +41 -0
  162. package/src/checks/mcp-health.js +169 -0
  163. package/src/checks/method.js +73 -0
  164. package/src/checks/native-build.js +67 -0
  165. package/src/checks/node.js +70 -0
  166. package/src/checks/ollama.js +41 -0
  167. package/src/checks/orphans.js +77 -0
  168. package/src/checks/ports.js +188 -0
  169. package/src/checks/project-checks.js +245 -0
  170. package/src/checks/qmd.js +41 -0
  171. package/src/checks/rag-hooks.js +54 -0
  172. package/src/checks/release-check.js +102 -0
  173. package/src/checks/rtk.js +36 -0
  174. package/src/checks/runner.js +326 -0
  175. package/src/checks/skills.js +53 -0
  176. package/src/checks/sonar.js +67 -0
  177. package/src/checks/squeezr.js +36 -0
  178. package/src/checks/system.js +53 -0
  179. package/src/checks/tokens.js +172 -0
  180. package/src/checks/types.js +103 -0
  181. package/src/ci/dispatch.js +99 -0
  182. package/src/ci/index.js +3 -0
  183. package/src/ci/pr-diff.js +26 -0
  184. package/src/ci/repo.js +45 -0
  185. package/src/cli/_shared.js +30 -0
  186. package/src/cli/advanced-commands.js +61 -0
  187. package/src/cli/register-meta.js +772 -0
  188. package/src/cli/register-pipeline.js +335 -0
  189. package/src/cli/register-plan.js +224 -0
  190. package/src/cli/register-roles-skills.js +54 -0
  191. package/src/cli/register-sonar.js +28 -0
  192. package/src/cli/register-standby.js +26 -0
  193. package/src/cli.js +155 -0
  194. package/src/commands/advanced.js +31 -0
  195. package/src/commands/agent-run.js +63 -0
  196. package/src/commands/agents.js +105 -0
  197. package/src/commands/architect.js +123 -0
  198. package/src/commands/audit.js +430 -0
  199. package/src/commands/autorun.js +49 -0
  200. package/src/commands/board.js +458 -0
  201. package/src/commands/check.js +40 -0
  202. package/src/commands/clean.js +145 -0
  203. package/src/commands/code.js +45 -0
  204. package/src/commands/config.js +74 -0
  205. package/src/commands/discover.js +105 -0
  206. package/src/commands/doctor.js +251 -0
  207. package/src/commands/env.js +207 -0
  208. package/src/commands/harden.js +203 -0
  209. package/src/commands/hu.js +210 -0
  210. package/src/commands/identity.js +60 -0
  211. package/src/commands/init.js +950 -0
  212. package/src/commands/install-tools.js +432 -0
  213. package/src/commands/mutate.js +94 -0
  214. package/src/commands/ollama.js +44 -0
  215. package/src/commands/onboard.js +45 -0
  216. package/src/commands/plan/_shared.js +55 -0
  217. package/src/commands/plan/add-hu.js +23 -0
  218. package/src/commands/plan/delete.js +9 -0
  219. package/src/commands/plan/fix.js +99 -0
  220. package/src/commands/plan/generate.js +532 -0
  221. package/src/commands/plan/index.js +13 -0
  222. package/src/commands/plan/list.js +21 -0
  223. package/src/commands/plan/migrate-result.js +41 -0
  224. package/src/commands/plan/ready.js +46 -0
  225. package/src/commands/plan/remove-hu.js +17 -0
  226. package/src/commands/plan/share.js +85 -0
  227. package/src/commands/plan/show.js +81 -0
  228. package/src/commands/plan/unshare.js +41 -0
  229. package/src/commands/plan/validate.js +20 -0
  230. package/src/commands/plan.js +17 -0
  231. package/src/commands/policy.js +164 -0
  232. package/src/commands/privacy.js +45 -0
  233. package/src/commands/qmd.js +86 -0
  234. package/src/commands/rag-mcp.js +18 -0
  235. package/src/commands/rag.js +173 -0
  236. package/src/commands/report-issue.js +77 -0
  237. package/src/commands/report.js +469 -0
  238. package/src/commands/researcher.js +54 -0
  239. package/src/commands/resume.js +68 -0
  240. package/src/commands/review-gate.js +355 -0
  241. package/src/commands/review.js +89 -0
  242. package/src/commands/roles.js +116 -0
  243. package/src/commands/run.js +185 -0
  244. package/src/commands/scan.js +18 -0
  245. package/src/commands/skills.js +86 -0
  246. package/src/commands/sonar.js +53 -0
  247. package/src/commands/standby.js +117 -0
  248. package/src/commands/start.js +121 -0
  249. package/src/commands/status.js +36 -0
  250. package/src/commands/sync.js +103 -0
  251. package/src/commands/telemetry.js +70 -0
  252. package/src/commands/tournament.js +85 -0
  253. package/src/commands/triage.js +78 -0
  254. package/src/commands/undo.js +62 -0
  255. package/src/commands/watch.js +46 -0
  256. package/src/commands/webperf.js +162 -0
  257. package/src/commands/worktree.js +76 -0
  258. package/src/config/defaults.js +270 -0
  259. package/src/config/loader.js +204 -0
  260. package/src/config/overrides.js +220 -0
  261. package/src/config/role-resolver.js +171 -0
  262. package/src/config/schema.js +299 -0
  263. package/src/config/test-harness.js +78 -0
  264. package/src/config.js +34 -0
  265. package/src/domains/domain-loader.js +245 -0
  266. package/src/domains/domain-registry.js +140 -0
  267. package/src/domains/domain-synthesizer.js +129 -0
  268. package/src/environment/adr.js +45 -0
  269. package/src/environment/board-access.js +97 -0
  270. package/src/environment/board-select.js +59 -0
  271. package/src/environment/briefs.js +129 -0
  272. package/src/environment/issue-report.js +72 -0
  273. package/src/environment/mcp-wiring.js +44 -0
  274. package/src/environment/playbook.js +127 -0
  275. package/src/git/automation.js +334 -0
  276. package/src/git/hu-automation.js +171 -0
  277. package/src/git/hu-snapshot.js +10 -0
  278. package/src/golden/asserter.js +92 -0
  279. package/src/golden/loader.js +52 -0
  280. package/src/golden/runner.js +92 -0
  281. package/src/golden/schema.js +41 -0
  282. package/src/guards/intent-guard.js +161 -0
  283. package/src/guards/output-guard.js +149 -0
  284. package/src/guards/perf-guard.js +126 -0
  285. package/src/guards/policy-guard.js +90 -0
  286. package/src/guards/policy-resolver.js +85 -0
  287. package/src/guards/secret-redactor.js +71 -0
  288. package/src/harden/advisory.js +203 -0
  289. package/src/harden/alternatives.js +50 -0
  290. package/src/harden/check.js +81 -0
  291. package/src/harden/config-engine.js +128 -0
  292. package/src/harden/config-templates.js +122 -0
  293. package/src/harden/guidelines-engine.js +48 -0
  294. package/src/harden/guidelines-templates.js +36 -0
  295. package/src/harden/harden-engine.js +80 -0
  296. package/src/harden/harness-hooks.js +107 -0
  297. package/src/harden/hook-commands.js +25 -0
  298. package/src/harden/hook-templates.js +183 -0
  299. package/src/harden/interactive.js +76 -0
  300. package/src/harden/sentinel-hooks.js +721 -0
  301. package/src/harden/stack-roots.js +105 -0
  302. package/src/harden/test-on-push.js +38 -0
  303. package/src/harden/workflow-engine.js +116 -0
  304. package/src/harden/workflow-templates.js +285 -0
  305. package/src/hu/acceptance-runner.js +138 -0
  306. package/src/hu/auto-generator.js +358 -0
  307. package/src/hu/graph.js +71 -0
  308. package/src/hu/lazy-planner.js +109 -0
  309. package/src/hu/model-router.js +108 -0
  310. package/src/hu/parallel-executor.js +94 -0
  311. package/src/hu/split-validator.js +143 -0
  312. package/src/hu/splitting-detector.js +158 -0
  313. package/src/hu/splitting-generator.js +175 -0
  314. package/src/hu/store.js +392 -0
  315. package/src/hu/worktree-bootstrap.js +59 -0
  316. package/src/identity/bootstrap.js +48 -0
  317. package/src/identity/compare.js +38 -0
  318. package/src/identity/detect.js +51 -0
  319. package/src/identity/store.js +66 -0
  320. package/src/infrastructure/command-runner.js +44 -0
  321. package/src/infrastructure/environment.js +38 -0
  322. package/src/infrastructure/file-system-service.js +63 -0
  323. package/src/infrastructure/mocks.js +182 -0
  324. package/src/lang/chunk-go.js +94 -0
  325. package/src/lang/chunk-java.js +90 -0
  326. package/src/lang/chunk-python.js +88 -0
  327. package/src/lang/chunk-rust.js +102 -0
  328. package/src/lang/go.js +14 -0
  329. package/src/lang/java.js +16 -0
  330. package/src/lang/js.js +8 -0
  331. package/src/lang/python.js +14 -0
  332. package/src/lang/registry.js +79 -0
  333. package/src/lang/rust.js +14 -0
  334. package/src/lang/tree-sitter-loader.js +45 -0
  335. package/src/mcp/direct-role-runner.js +112 -0
  336. package/src/mcp/error-classifiers.js +66 -0
  337. package/src/mcp/handlers/direct-handlers.js +486 -0
  338. package/src/mcp/handlers/hu-handlers.js +93 -0
  339. package/src/mcp/handlers/management-handlers.js +265 -0
  340. package/src/mcp/handlers/rag-handler.js +52 -0
  341. package/src/mcp/handlers/run-handler.js +313 -0
  342. package/src/mcp/orphan-guard.js +131 -0
  343. package/src/mcp/preflight.js +47 -0
  344. package/src/mcp/progress.js +149 -0
  345. package/src/mcp/response-compressor.js +81 -0
  346. package/src/mcp/run-kj.js +135 -0
  347. package/src/mcp/server-handlers.js +79 -0
  348. package/src/mcp/server.js +66 -0
  349. package/src/mcp/shared-helpers.js +297 -0
  350. package/src/mcp/sovereignty-guard.js +112 -0
  351. package/src/mcp/suggest-handler.js +92 -0
  352. package/src/mcp/tool-arg-normalizers.js +16 -0
  353. package/src/mcp/tools.js +432 -0
  354. package/src/mutate/diff-scope.js +105 -0
  355. package/src/mutate/reviewer-signal.js +76 -0
  356. package/src/mutate/runner.js +72 -0
  357. package/src/mutate/tool-registry.js +90 -0
  358. package/src/onboarder/cache.js +24 -0
  359. package/src/onboarder/collectors/framework-multilang.js +64 -0
  360. package/src/onboarder/collectors/index.js +136 -0
  361. package/src/orchestrator/agent-fallback.js +81 -0
  362. package/src/orchestrator/brain-coordinator.js +186 -0
  363. package/src/orchestrator/ci-integration.js +139 -0
  364. package/src/orchestrator/config-init.js +510 -0
  365. package/src/orchestrator/direct-actions.js +217 -0
  366. package/src/orchestrator/drivers/error-recovery.js +287 -0
  367. package/src/orchestrator/drivers/init-context.js +227 -0
  368. package/src/orchestrator/drivers/iteration-loop.js +380 -0
  369. package/src/orchestrator/drivers/iteration-phases/coder-and-refactorer.js +69 -0
  370. package/src/orchestrator/drivers/iteration-phases/guards.js +84 -0
  371. package/src/orchestrator/drivers/iteration-phases/handle-approved.js +29 -0
  372. package/src/orchestrator/drivers/iteration-phases/quality-gates.js +114 -0
  373. package/src/orchestrator/drivers/iteration-phases/reviewer-gate.js +60 -0
  374. package/src/orchestrator/drivers/post-loop.js +383 -0
  375. package/src/orchestrator/drivers/pre-loop-phases/auto-hu-batch.js +136 -0
  376. package/src/orchestrator/drivers/pre-loop-phases/config-deprecations.js +47 -0
  377. package/src/orchestrator/drivers/pre-loop-phases/ensure-addyosmani-skills.js +84 -0
  378. package/src/orchestrator/drivers/pre-loop-phases/inject-loaded-plan.js +391 -0
  379. package/src/orchestrator/drivers/pre-loop.js +507 -0
  380. package/src/orchestrator/drivers/run-hu-batch.js +427 -0
  381. package/src/orchestrator/feedback-enrichment.js +140 -0
  382. package/src/orchestrator/feedback-queue.js +151 -0
  383. package/src/orchestrator/flow-control.js +170 -0
  384. package/src/orchestrator/flow-runner.js +364 -0
  385. package/src/orchestrator/fs-leak-detector.js +210 -0
  386. package/src/orchestrator/hu-scheduler.js +114 -0
  387. package/src/orchestrator/hu-sub-pipeline.js +616 -0
  388. package/src/orchestrator/integrations.js +81 -0
  389. package/src/orchestrator/iteration-gate.js +73 -0
  390. package/src/orchestrator/iteration-stages.js +9 -0
  391. package/src/orchestrator/parallel-limiter.js +74 -0
  392. package/src/orchestrator/pipeline-context.js +72 -0
  393. package/src/orchestrator/post-loop-stages.js +468 -0
  394. package/src/orchestrator/pre-loop-stages.js +11 -0
  395. package/src/orchestrator/preflight-checks.js +462 -0
  396. package/src/orchestrator/repair/repair-detector.js +117 -0
  397. package/src/orchestrator/repair/repair-runner.js +142 -0
  398. package/src/orchestrator/reviewer-fallback.js +45 -0
  399. package/src/orchestrator/role-output-compressor.js +189 -0
  400. package/src/orchestrator/session-journal.js +308 -0
  401. package/src/orchestrator/solomon-escalation.js +305 -0
  402. package/src/orchestrator/solomon-rules.js +212 -0
  403. package/src/orchestrator/stages/architect-stage.js +172 -0
  404. package/src/orchestrator/stages/coder-stage.js +480 -0
  405. package/src/orchestrator/stages/domain-curator-stage.js +82 -0
  406. package/src/orchestrator/stages/hu-reviewer-phases/detect-and-apply-splits.js +127 -0
  407. package/src/orchestrator/stages/hu-reviewer-stage.js +394 -0
  408. package/src/orchestrator/stages/perf-stage.js +140 -0
  409. package/src/orchestrator/stages/planner-stage.js +85 -0
  410. package/src/orchestrator/stages/rag-context-stage.js +77 -0
  411. package/src/orchestrator/stages/rag-preload-decisor.js +58 -0
  412. package/src/orchestrator/stages/research-stage.js +69 -0
  413. package/src/orchestrator/stages/reviewer-stage.js +422 -0
  414. package/src/orchestrator/stages/sonar-stage.js +421 -0
  415. package/src/orchestrator/stages/stage-classes.js +84 -0
  416. package/src/orchestrator/stages/stage-executor.js +155 -0
  417. package/src/orchestrator/stages/tdd-discipline-stage.js +56 -0
  418. package/src/orchestrator/stages/tool-judge-stage.js +53 -0
  419. package/src/orchestrator/stages/triage-stage.js +172 -0
  420. package/src/orchestrator/standby.js +71 -0
  421. package/src/orchestrator/verification-gate.js +183 -0
  422. package/src/orchestrator.js +22 -0
  423. package/src/plan/adr-generator.js +170 -0
  424. package/src/plan/adr-loader.js +60 -0
  425. package/src/plan/plan-executor.js +100 -0
  426. package/src/plan/plan-fixer.js +192 -0
  427. package/src/plan/plan-hu-ops.js +16 -0
  428. package/src/plan/plan-id.js +4 -0
  429. package/src/plan/plan-migrate-result.js +24 -0
  430. package/src/plan/plan-reviewer.js +236 -0
  431. package/src/plan/plan-schema.js +334 -0
  432. package/src/plan/plan-store.js +260 -0
  433. package/src/plan/plan-structural-pass.js +122 -0
  434. package/src/plan/plan-validation.js +4 -0
  435. package/src/plan/preflight-hu.js +105 -0
  436. package/src/plan/spec-detector.js +61 -0
  437. package/src/plan/tests-synthesizer.js +131 -0
  438. package/src/planning-game/adapter.js +108 -0
  439. package/src/planning-game/architect-adrs.js +45 -0
  440. package/src/planning-game/client.js +108 -0
  441. package/src/planning-game/decomposition.js +87 -0
  442. package/src/planning-game/pipeline-adapter.js +251 -0
  443. package/src/plugins/loader.js +67 -0
  444. package/src/policy/add.js +111 -0
  445. package/src/policy/decisions.js +42 -0
  446. package/src/policy/engine.js +82 -0
  447. package/src/policy/exceptions.js +60 -0
  448. package/src/privacy/onboarding.js +35 -0
  449. package/src/privacy/scan.js +120 -0
  450. package/src/prompts/arbiter.js +38 -0
  451. package/src/prompts/architect.js +103 -0
  452. package/src/prompts/audit.js +474 -0
  453. package/src/prompts/coder.js +287 -0
  454. package/src/prompts/diff-clip.js +27 -0
  455. package/src/prompts/discover.js +222 -0
  456. package/src/prompts/hu-reviewer.js +219 -0
  457. package/src/prompts/planner.js +312 -0
  458. package/src/prompts/prompt-layout.js +80 -0
  459. package/src/prompts/prompt-resolver.js +120 -0
  460. package/src/prompts/reviewer.js +75 -0
  461. package/src/prompts/rtk-snippet.js +25 -0
  462. package/src/prompts/spec-reviewer.js +66 -0
  463. package/src/prompts/split-signal.js +82 -0
  464. package/src/prompts/start-decision.js +52 -0
  465. package/src/prompts/tool-correctness-judge.js +31 -0
  466. package/src/prompts/triage.js +57 -0
  467. package/src/rag/auto-update.js +72 -0
  468. package/src/rag/chunker-ast.js +56 -0
  469. package/src/rag/chunker.js +132 -0
  470. package/src/rag/embedder.js +2 -0
  471. package/src/rag/embedders/_cloud-base.js +2 -0
  472. package/src/rag/embedders/cohere.js +2 -0
  473. package/src/rag/embedders/factory.js +2 -0
  474. package/src/rag/embedders/mistral.js +2 -0
  475. package/src/rag/embedders/onnx.js +2 -0
  476. package/src/rag/embedders/openai.js +2 -0
  477. package/src/rag/embedders/voyage.js +2 -0
  478. package/src/rag/eval.js +61 -0
  479. package/src/rag/governed-embedder.js +39 -0
  480. package/src/rag/indexer.js +199 -0
  481. package/src/rag/library.js +62 -0
  482. package/src/rag/ollama-capability.js +59 -0
  483. package/src/rag/ollama-manager.js +98 -0
  484. package/src/rag/onnx-fallback.js +69 -0
  485. package/src/rag/rerank.js +4 -0
  486. package/src/rag/retriever.js +2 -0
  487. package/src/rag/vec-store.js +17 -0
  488. package/src/rag/watcher.js +62 -0
  489. package/src/rag/where-parser.js +4 -0
  490. package/src/repeat-detector.js +88 -0
  491. package/src/review/card-first.js +99 -0
  492. package/src/review/card-verify.js +83 -0
  493. package/src/review/diff-generator.js +112 -0
  494. package/src/review/gate-gitignore.js +77 -0
  495. package/src/review/git-stash-helper.js +104 -0
  496. package/src/review/mutation-pregate.js +48 -0
  497. package/src/review/one-shot-review.js +122 -0
  498. package/src/review/parser.js +80 -0
  499. package/src/review/policy-gate.js +26 -0
  500. package/src/review/profiles.js +66 -0
  501. package/src/review/reviewer-fallback.js +125 -0
  502. package/src/review/schema.js +31 -0
  503. package/src/review/scope-filter.js +152 -0
  504. package/src/review/snapshot-diff.js +122 -0
  505. package/src/review/solomon-arbitration.js +113 -0
  506. package/src/review/sonar-pregate.js +63 -0
  507. package/src/review/tdd-discipline.js +90 -0
  508. package/src/review/tdd-policy.js +69 -0
  509. package/src/review/tests-with-code.js +33 -0
  510. package/src/review/verdict-store.js +87 -0
  511. package/src/review/workspace.js +21 -0
  512. package/src/roles/agent-role.js +194 -0
  513. package/src/roles/architect-role.js +77 -0
  514. package/src/roles/audit-role.js +258 -0
  515. package/src/roles/base-role.js +165 -0
  516. package/src/roles/coder-role.js +107 -0
  517. package/src/roles/commiter-role.js +110 -0
  518. package/src/roles/discover-role.js +81 -0
  519. package/src/roles/domain-curator-role.js +141 -0
  520. package/src/roles/hu-reviewer-role.js +64 -0
  521. package/src/roles/impeccable-role.js +85 -0
  522. package/src/roles/index.js +18 -0
  523. package/src/roles/karajan-brain-role.js +134 -0
  524. package/src/roles/onboarder-role.js +61 -0
  525. package/src/roles/perf-role.js +122 -0
  526. package/src/roles/planner-role.js +151 -0
  527. package/src/roles/refactorer-role.js +30 -0
  528. package/src/roles/repairer-role.js +181 -0
  529. package/src/roles/researcher-role.js +70 -0
  530. package/src/roles/reviewer-role.js +104 -0
  531. package/src/roles/security-role.js +73 -0
  532. package/src/roles/solomon-role.js +265 -0
  533. package/src/roles/sonar-role.js +70 -0
  534. package/src/roles/spec-reviewer-role.js +106 -0
  535. package/src/roles/tester-role.js +235 -0
  536. package/src/roles/tool-correctness-judge-role.js +78 -0
  537. package/src/roles/triage-role.js +99 -0
  538. package/src/session/journal/decisions-writer.js +200 -0
  539. package/src/session/journal/iteration-logger.js +194 -0
  540. package/src/session/journal/summary-writer.js +320 -0
  541. package/src/session/journal/tree-writer.js +164 -0
  542. package/src/session/journal/write-all.js +89 -0
  543. package/src/session/mutators.js +339 -0
  544. package/src/session/runtime-overrides.js +59 -0
  545. package/src/session/store.js +106 -0
  546. package/src/session-cleanup.js +116 -0
  547. package/src/skills/addyosmani-catalog.js +315 -0
  548. package/src/skills/addyosmani-role-map.js +139 -0
  549. package/src/skills/openskills-client.js +157 -0
  550. package/src/skills/semantic-detector.js +167 -0
  551. package/src/skills/skill-detector.js +385 -0
  552. package/src/skills/skill-loader.js +219 -0
  553. package/src/skills/skills-cache.js +128 -0
  554. package/src/sonar/api.js +128 -0
  555. package/src/sonar/cloud-scanner.js +78 -0
  556. package/src/sonar/config-resolver.js +88 -0
  557. package/src/sonar/credentials.js +60 -0
  558. package/src/sonar/discovery.js +124 -0
  559. package/src/sonar/enforcer.js +19 -0
  560. package/src/sonar/issue-filter.js +12 -0
  561. package/src/sonar/manager.js +181 -0
  562. package/src/sonar/project-key.js +120 -0
  563. package/src/sonar/scanner.js +307 -0
  564. package/src/sonar/token-bootstrap.js +205 -0
  565. package/src/sonar/token-recovery.js +67 -0
  566. package/src/spec-review/refine-loop.js +58 -0
  567. package/src/spec-review/run-spec-review.js +119 -0
  568. package/src/start/assessment.js +74 -0
  569. package/src/start/maturity.js +73 -0
  570. package/src/start/start-decider-role.js +79 -0
  571. package/src/start/sweep.js +157 -0
  572. package/src/sync/apply-canvas-patch.js +55 -0
  573. package/src/sync/detect-drift.js +113 -0
  574. package/src/tournament/crown.js +88 -0
  575. package/src/tournament/judge.js +150 -0
  576. package/src/tournament/run.js +142 -0
  577. package/src/tournament/scoreboard.js +124 -0
  578. package/src/tracker-bootstrap.js +44 -0
  579. package/src/types/agent.js +55 -0
  580. package/src/types/config.js +114 -0
  581. package/src/types/hu.js +47 -0
  582. package/src/types/index.js +38 -0
  583. package/src/types/policy.js +43 -0
  584. package/src/types/session.js +75 -0
  585. package/src/types/solomon.js +87 -0
  586. package/src/types/stage.js +68 -0
  587. package/src/utils/agent-detect.js +93 -0
  588. package/src/utils/ai-trash.js +48 -0
  589. package/src/utils/async-lock.js +31 -0
  590. package/src/utils/atomic-write.js +6 -0
  591. package/src/utils/banner.js +66 -0
  592. package/src/utils/binary-sources.js +70 -0
  593. package/src/utils/board-prompt-bridge.js +108 -0
  594. package/src/utils/budget.js +229 -0
  595. package/src/utils/cli-ask-question.js +93 -0
  596. package/src/utils/cli-progress.js +220 -0
  597. package/src/utils/cli-run-log.js +83 -0
  598. package/src/utils/cwd-confirm.js +39 -0
  599. package/src/utils/derive-project-name-from-cwd.js +119 -0
  600. package/src/utils/display/event-handlers.js +403 -0
  601. package/src/utils/display/formatters.js +132 -0
  602. package/src/utils/display/header.js +47 -0
  603. package/src/utils/display/resume-hint.js +41 -0
  604. package/src/utils/display/session-summary.js +118 -0
  605. package/src/utils/display/solomon.js +42 -0
  606. package/src/utils/display/spec-findings.js +44 -0
  607. package/src/utils/doc-links.js +29 -0
  608. package/src/utils/docker-install.js +34 -0
  609. package/src/utils/escape-regexp.js +15 -0
  610. package/src/utils/events.js +35 -0
  611. package/src/utils/fs.js +19 -0
  612. package/src/utils/garbage-collector.js +481 -0
  613. package/src/utils/git.js +279 -0
  614. package/src/utils/home-migration.js +87 -0
  615. package/src/utils/injection-guard.js +186 -0
  616. package/src/utils/install-hints.js +180 -0
  617. package/src/utils/json-extract.js +64 -0
  618. package/src/utils/locale.js +59 -0
  619. package/src/utils/logger.js +96 -0
  620. package/src/utils/managed-markers.js +93 -0
  621. package/src/utils/manifest-sniff.js +82 -0
  622. package/src/utils/messages.js +61 -0
  623. package/src/utils/model-selector.js +107 -0
  624. package/src/utils/os-detect.js +79 -0
  625. package/src/utils/paths.js +13 -0
  626. package/src/utils/pending-user-action.js +95 -0
  627. package/src/utils/port-check.js +6 -0
  628. package/src/utils/port-occupant.js +96 -0
  629. package/src/utils/pricing.js +29 -0
  630. package/src/utils/process.js +4 -0
  631. package/src/utils/project-detect.js +160 -0
  632. package/src/utils/prompt-project-name.js +35 -0
  633. package/src/utils/provider-env.js +116 -0
  634. package/src/utils/qmd-collection.js +72 -0
  635. package/src/utils/qmd-detect.js +18 -0
  636. package/src/utils/qmd-install.js +37 -0
  637. package/src/utils/quota-simulator.js +56 -0
  638. package/src/utils/rate-limit-detector.js +181 -0
  639. package/src/utils/repo-cleaner.js +79 -0
  640. package/src/utils/retry.js +88 -0
  641. package/src/utils/role-assigner.js +137 -0
  642. package/src/utils/role-env.js +58 -0
  643. package/src/utils/rtk-detect.js +18 -0
  644. package/src/utils/rtk-install.js +38 -0
  645. package/src/utils/rtk-wrapper.js +87 -0
  646. package/src/utils/run-context.js +75 -0
  647. package/src/utils/run-log.js +217 -0
  648. package/src/utils/run-registry.js +9 -0
  649. package/src/utils/shared-paths.js +12 -0
  650. package/src/utils/squeezr-detect.js +21 -0
  651. package/src/utils/squeezr-install.js +37 -0
  652. package/src/utils/stack-detect.js +135 -0
  653. package/src/utils/stall-detector.js +159 -0
  654. package/src/utils/status-dashboard.js +254 -0
  655. package/src/utils/task-file.js +109 -0
  656. package/src/utils/telemetry.js +124 -0
  657. package/src/utils/templates-root.js +61 -0
  658. package/src/utils/tool-call-extractor.js +56 -0
  659. package/src/utils/tool-governor.js +116 -0
  660. package/src/utils/tool-installer.js +93 -0
  661. package/src/utils/update-check.js +280 -0
  662. package/src/utils/vector-store-orphans.js +89 -0
  663. package/src/utils/welcome.js +47 -0
  664. package/src/utils/with-timeout.js +42 -0
  665. package/src/utils/wizard.js +47 -0
  666. package/src/webperf/cwv-gate.js +94 -0
  667. package/src/webperf/devtools-detect.js +112 -0
  668. package/src/webperf/scanner.js +209 -0
  669. package/src/webperf/webperf-role-integration.js +66 -0
  670. package/templates/coder-rules.md +45 -0
  671. package/templates/docker-compose.sonar.yml +60 -0
  672. package/templates/domains/example-dental.md +57 -0
  673. package/templates/kj.config.yml +128 -0
  674. package/templates/review-rules.md +17 -0
  675. package/templates/roles/architect/anthropic.md +74 -0
  676. package/templates/roles/architect/default.md +72 -0
  677. package/templates/roles/architect/google.md +61 -0
  678. package/templates/roles/architect/openai.md +60 -0
  679. package/templates/roles/architect/opencode.md +59 -0
  680. package/templates/roles/architect.md +76 -0
  681. package/templates/roles/audit.md +68 -0
  682. package/templates/roles/coder/anthropic.md +81 -0
  683. package/templates/roles/coder/default.md +78 -0
  684. package/templates/roles/coder/google.md +87 -0
  685. package/templates/roles/coder/openai.md +68 -0
  686. package/templates/roles/coder/opencode.md +58 -0
  687. package/templates/roles/coder.md +152 -0
  688. package/templates/roles/commiter.md +44 -0
  689. package/templates/roles/discover.md +167 -0
  690. package/templates/roles/domain-curator.md +52 -0
  691. package/templates/roles/hu-reviewer.md +236 -0
  692. package/templates/roles/impeccable-design.md +87 -0
  693. package/templates/roles/impeccable.md +125 -0
  694. package/templates/roles/karajan-brain.md +95 -0
  695. package/templates/roles/onboarder.md +45 -0
  696. package/templates/roles/planner/anthropic.md +59 -0
  697. package/templates/roles/planner/default.md +57 -0
  698. package/templates/roles/planner/google.md +75 -0
  699. package/templates/roles/planner/openai.md +55 -0
  700. package/templates/roles/planner/opencode.md +62 -0
  701. package/templates/roles/planner.md +61 -0
  702. package/templates/roles/refactorer.md +48 -0
  703. package/templates/roles/repairer.md +103 -0
  704. package/templates/roles/researcher.md +41 -0
  705. package/templates/roles/reviewer/anthropic.md +61 -0
  706. package/templates/roles/reviewer/default.md +62 -0
  707. package/templates/roles/reviewer/google.md +63 -0
  708. package/templates/roles/reviewer/openai.md +57 -0
  709. package/templates/roles/reviewer/opencode.md +42 -0
  710. package/templates/roles/reviewer-paranoid.md +38 -0
  711. package/templates/roles/reviewer-relaxed.md +34 -0
  712. package/templates/roles/reviewer-strict.md +37 -0
  713. package/templates/roles/reviewer.md +62 -0
  714. package/templates/roles/security.md +54 -0
  715. package/templates/roles/solomon/anthropic.md +71 -0
  716. package/templates/roles/solomon/default.md +83 -0
  717. package/templates/roles/solomon/google.md +61 -0
  718. package/templates/roles/solomon/openai.md +53 -0
  719. package/templates/roles/solomon/opencode.md +55 -0
  720. package/templates/roles/solomon.md +83 -0
  721. package/templates/roles/sonar.md +49 -0
  722. package/templates/roles/spec-reviewer.md +47 -0
  723. package/templates/roles/tester.md +50 -0
  724. package/templates/roles/triage.md +69 -0
  725. package/templates/skills/kj-architect.md +45 -0
  726. package/templates/skills/kj-audit.md +63 -0
  727. package/templates/skills/kj-board.md +16 -0
  728. package/templates/skills/kj-code.md +51 -0
  729. package/templates/skills/kj-csv-transform.md +40 -0
  730. package/templates/skills/kj-data-report.md +44 -0
  731. package/templates/skills/kj-discover.md +24 -0
  732. package/templates/skills/kj-rag-query.md +50 -0
  733. package/templates/skills/kj-review.md +47 -0
  734. package/templates/skills/kj-run.md +73 -0
  735. package/templates/skills/kj-security.md +49 -0
  736. package/templates/skills/kj-sonar.md +41 -0
  737. package/templates/skills/kj-sql-analysis.md +40 -0
  738. package/templates/skills/kj-test.md +40 -0
  739. package/templates/workflows/automerge.yml +30 -0
  740. package/templates/workflows/houston-override.yml +46 -0
  741. package/templates/workflows/kj-ci-gateway.yml +58 -0
  742. package/vendor/tree-sitter-grammars/README.md +21 -0
  743. package/vendor/tree-sitter-grammars/go.wasm +0 -0
  744. package/vendor/tree-sitter-grammars/java.wasm +0 -0
  745. package/vendor/tree-sitter-grammars/python.wasm +0 -0
  746. package/vendor/tree-sitter-grammars/rust.wasm +0 -0
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Gate trackability (KJC-TSK-0646) — a `.karajan/` dir-exclude in
3
+ * .gitignore silently keeps the v4 contract (review-gate marker, hooks)
4
+ * out of git: git cannot re-include children of an excluded DIRECTORY.
5
+ * Reproduced three times in the field before this existed.
6
+ *
7
+ * `kj review --install-gate` calls this: when the exact `.karajan/`
8
+ * (or `.karajan/*`) exclude is present, it is rewritten to the
9
+ * star-pattern with re-includes for the contract, keeping everything
10
+ * else in the file byte-identical. No mention of .karajan → no-op.
11
+ */
12
+ import fs from "node:fs/promises";
13
+ import path from "node:path";
14
+
15
+ // KJC-BUG-0123 (issue #1268): exported as the SINGLE source of truth — kj
16
+ // init and the orchestrator's autoInit write this same block instead of a
17
+ // bare `.karajan/` exclude (which git cannot re-include children of).
18
+ export const CONTRACT_BLOCK = [
19
+ ".karajan/*",
20
+ "# …except the v4 environment contract, which the whole team inherits:",
21
+ "!.karajan/review-gate",
22
+ "!.karajan/hooks/",
23
+ ".karajan/hooks/*",
24
+ "!.karajan/hooks/pre-commit",
25
+ "!.karajan/hooks/commit-msg",
26
+ "!.karajan/hooks/pre-push",
27
+ "!.karajan/hooks/post-merge",
28
+ "!.karajan/adrs/",
29
+ ];
30
+
31
+ /**
32
+ * KJC-BUG-0123: append the canonical contract block to .gitignore when the
33
+ * file has no `.karajan` mention at all (fresh projects — `kj init` path).
34
+ * Files that already mention .karajan are left to ensureGateTrackable,
35
+ * which rewrites legacy root excludes without touching anything else.
36
+ */
37
+ export async function ensureContractBlockPresent(projectDir) {
38
+ const file = path.join(projectDir, ".gitignore");
39
+ let text = "";
40
+ try { text = (await fs.readFile(file, "utf8")) || ""; } catch { /* no .gitignore yet */ }
41
+ if (text.includes(".karajan")) return { changed: false };
42
+ const sep = text && !text.endsWith("\n") ? "\n" : "";
43
+ await fs.writeFile(file, `${text}${sep}# Karajan environment (verdicts stay local; the contract is tracked)\n${CONTRACT_BLOCK.join("\n")}\n`);
44
+ return { changed: true };
45
+ }
46
+
47
+ export async function ensureGateTrackable(projectDir) {
48
+ const file = path.join(projectDir, ".gitignore");
49
+ let text;
50
+ try { text = await fs.readFile(file, "utf8"); } catch { return { changed: false }; }
51
+
52
+ // Already migrated only when EVERY re-include of the contract is present —
53
+ // a partial state (e.g. a hand-written marker re-include without the hook
54
+ // ones) must still be completed, not silently accepted.
55
+ const reIncludes = CONTRACT_BLOCK.filter((l) => l.startsWith("!"));
56
+ if (reIncludes.every((l) => text.includes(l))) return { changed: false };
57
+
58
+ const isRootExclude = (l) => {
59
+ const t = l.trim();
60
+ return t === ".karajan/" || t === ".karajan" || t === ".karajan/*";
61
+ };
62
+ const lines = text.split("\n");
63
+ const idx = lines.findIndex(isRootExclude);
64
+ if (idx === -1) return { changed: false };
65
+
66
+ // Drop EVERY .karajan root exclude (a later duplicate would override the
67
+ // re-includes — last match wins in gitignore) and every pre-existing
68
+ // partial contract line, then insert the canonical block once at the
69
+ // position of the first exclude. Exactly one coherent block, idempotent.
70
+ const partials = new Set(CONTRACT_BLOCK);
71
+ const marker = "@@kj-contract-block@@";
72
+ const cleaned = lines.map((l, i) => (i === idx ? marker : l))
73
+ .filter((l) => l === marker || (!isRootExclude(l) && !partials.has(l.trim())));
74
+ const result = cleaned.flatMap((l) => (l === marker ? CONTRACT_BLOCK : [l]));
75
+ await fs.writeFile(file, result.join("\n"));
76
+ return { changed: true };
77
+ }
@@ -0,0 +1,104 @@
1
+ // Surgical stash helper for the TDD discipline gate (KJC-TSK-0398 PR2).
2
+ // Stashes only the source files we want to hide for the "red" run and
3
+ // restores them afterwards. Tracked files use `git stash push -- <paths>`;
4
+ // untracked files (not yet known to git) are moved aside to a private
5
+ // `.karajan/tmp/stash-<id>/` tree because `git stash` would ignore them.
6
+
7
+ import { mkdir, rename, rm } from "node:fs/promises";
8
+ import { existsSync } from "node:fs";
9
+ import { dirname, join, resolve } from "node:path";
10
+ import { runCommand } from "../utils/process.js";
11
+
12
+ function defaultRunner(cmd, args, opts) {
13
+ return runCommand(cmd, args, opts);
14
+ }
15
+
16
+ async function isTracked(runner, projectDir, file) {
17
+ const res = await runner("git", ["ls-files", "--error-unmatch", "--", file], { cwd: projectDir });
18
+ return res.exitCode === 0;
19
+ }
20
+
21
+ async function stashRefForToken(runner, projectDir, marker) {
22
+ const res = await runner("git", ["stash", "list"], { cwd: projectDir });
23
+ if (res.exitCode !== 0) return null;
24
+ for (const line of String(res.stdout || "").split("\n")) {
25
+ if (line.includes(marker)) {
26
+ const m = line.match(/^(stash@\{\d+\})/);
27
+ if (m) return m[1];
28
+ }
29
+ }
30
+ return null;
31
+ }
32
+
33
+ // Move `files` aside so a test run sees them as missing. Returns a token to
34
+ // pass to `restoreFiles()`. Throws on failure with the partial token attached.
35
+ export async function stashFilesAside(files, { projectDir = process.cwd(), runner = defaultRunner, tmpRoot } = {}) {
36
+ if (!Array.isArray(files) || files.length === 0) {
37
+ return { trackedStashRef: null, untrackedMoves: [], marker: null };
38
+ }
39
+ const tracked = [];
40
+ const untracked = [];
41
+ for (const f of files) {
42
+ if (await isTracked(runner, projectDir, f)) tracked.push(f);
43
+ else if (existsSync(resolve(projectDir, f))) untracked.push(f);
44
+ }
45
+
46
+ const marker = `kj-tdd-discipline-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
47
+ const stashRoot = tmpRoot || join(projectDir, ".karajan", "tmp", marker);
48
+ const untrackedMoves = [];
49
+ let trackedStashRef = null;
50
+
51
+ if (tracked.length > 0) {
52
+ const args = ["stash", "push", "--keep-index", "-m", marker, "--", ...tracked];
53
+ const res = await runner("git", args, { cwd: projectDir });
54
+ if (res.exitCode !== 0) {
55
+ throw new Error(`git stash push failed: ${res.stderr || res.stdout}`);
56
+ }
57
+ trackedStashRef = await stashRefForToken(runner, projectDir, marker);
58
+ }
59
+
60
+ for (const f of untracked) {
61
+ const src = resolve(projectDir, f);
62
+ const dst = join(stashRoot, f);
63
+ await mkdir(dirname(dst), { recursive: true });
64
+ await rename(src, dst);
65
+ untrackedMoves.push({ src, dst });
66
+ }
67
+
68
+ return { trackedStashRef, untrackedMoves, marker, stashRoot };
69
+ }
70
+
71
+ // Reverse a `stashFilesAside` token. Idempotent: a restored token becomes a no-op.
72
+ export async function restoreFiles(token, { projectDir = process.cwd(), runner = defaultRunner } = {}) {
73
+ if (!token || (token.trackedStashRef == null && token.untrackedMoves.length === 0)) return;
74
+
75
+ for (const { src, dst } of token.untrackedMoves) {
76
+ if (existsSync(dst)) {
77
+ await mkdir(dirname(src), { recursive: true });
78
+ await rename(dst, src);
79
+ }
80
+ }
81
+ if (token.stashRoot && existsSync(token.stashRoot)) {
82
+ await rm(token.stashRoot, { recursive: true, force: true });
83
+ }
84
+
85
+ if (token.trackedStashRef) {
86
+ const res = await runner("git", ["stash", "pop", token.trackedStashRef], { cwd: projectDir });
87
+ if (res.exitCode !== 0) {
88
+ throw new Error(`git stash pop failed: ${res.stderr || res.stdout}`);
89
+ }
90
+ }
91
+ token.trackedStashRef = null;
92
+ token.untrackedMoves = [];
93
+ }
94
+
95
+ // Run `body` with `files` stashed aside, always restoring them — even when
96
+ // `body` throws. The TDD discipline stage wraps its "red" run with this.
97
+ export async function withStashedFiles(files, body, opts = {}) {
98
+ const token = await stashFilesAside(files, opts);
99
+ try {
100
+ return await body(token);
101
+ } finally {
102
+ await restoreFiles(token, opts).catch(() => { /* swallow: surface body error */ });
103
+ }
104
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Mutation pre-gate (MUT-A, KJC-TSK-0716) — audita "tests prove behavior":
3
+ * asserts flojos dejan mutantes vivos con la suite verde. OPT-IN
4
+ * (method_gates.mutation: warn|block — cuesta minutos) y SOLO en --staged:
5
+ * supervivientes como advisory al reviewer; block cierra ANTES con la
6
+ * lista exacta. Indisponible = degrada avisando, nunca en silencio.
7
+ */
8
+ import { runMutation } from "../mutate/runner.js";
9
+ import { getDiffScope } from "../mutate/diff-scope.js";
10
+ import { detectProjectStack } from "../utils/stack-detect.js";
11
+ import { getMutationTool } from "../mutate/tool-registry.js";
12
+
13
+ export const formatSurvivor = (s) =>
14
+ `${s.file}:${s.line} (${s.mutator ?? s.status ?? "?"}) — mátalo con un assert que distinga el cambio`;
15
+
16
+ async function defaultMutate({ projectDir }) {
17
+ const { language } = await detectProjectStack(projectDir);
18
+ const tool = getMutationTool(language);
19
+ if (!tool.supported) throw new Error(`lenguaje no soportado (${language ?? "desconocido"}) — ${tool.reason}`);
20
+ const scope = await getDiffScope({ staged: true, language });
21
+ if (scope.empty) return { result: { score: 100, killed: 0, total: 0, survived: [] } };
22
+ const outcome = await runMutation({ binary: tool.binary, args: scope.args, cwd: projectDir });
23
+ if (!outcome.result) throw new Error(`la herramienta ${tool.id} no produjo un informe legible`);
24
+ return outcome;
25
+ }
26
+
27
+ /**
28
+ * @returns {Promise<{enabled: boolean, ok: boolean, available?: boolean, mode?: string, survived?: object[], score?: number, reason?: string}>}
29
+ */
30
+ export async function runMutationPregate({ config = {}, projectDir = process.cwd(), deps = {} } = {}) {
31
+ const mode = config?.method_gates?.mutation;
32
+ if (mode !== "warn" && mode !== "block") return { enabled: false, ok: true };
33
+ const { mutateFn = defaultMutate } = deps;
34
+ let outcome;
35
+ try {
36
+ outcome = await mutateFn({ projectDir });
37
+ } catch (err) {
38
+ // Sin herramienta no hay medición: se degrada DICIENDO qué red cayó.
39
+ return { enabled: true, ok: true, available: false, mode, reason: err.message };
40
+ }
41
+ // Payload malformado del runner = misma degradación que el error (catch
42
+ // de codex): jamás crashear el review gate por un informe roto.
43
+ if (!outcome?.result || !Array.isArray(outcome.result.survived ?? [])) {
44
+ return { enabled: true, ok: true, available: false, mode, reason: "el runner de mutación devolvió un informe ilegible" };
45
+ }
46
+ const { survived = [], score } = outcome.result;
47
+ return { enabled: true, available: true, mode, survived, score, ok: mode === "warn" || survived.length === 0 };
48
+ }
@@ -0,0 +1,122 @@
1
+ /**
2
+ * One-shot cross-AI review (ENV-B1, KJC-TSK-0637) — the v4 primitive.
3
+ *
4
+ * The HOST agent (Claude Code, Codex) orchestrates the work; Karajan
5
+ * routes the review to a DIFFERENT AI and records the verdict tied to
6
+ * the exact diff (verdict-store). No cross-AI reviewer available is an
7
+ * error, never a silent fallback to the host: without a second pair of
8
+ * eyes there is no verdict, and without a verdict the pre-commit gate
9
+ * (ENV-C) keeps the commit out.
10
+ */
11
+ import { createAgent } from "../agents/index.js";
12
+ import { resolveRole } from "../config/role-resolver.js";
13
+ import { buildReviewerPrompt } from "../prompts/reviewer.js";
14
+ import { resolveReviewProfile } from "./profiles.js";
15
+ import { parseMaybeJsonString } from "./parser.js";
16
+ import { detectAvailableAgents, detectHostAgent } from "../utils/agent-detect.js";
17
+ import { saveVerdict } from "./verdict-store.js";
18
+ import { detectWorkspace } from "./workspace.js";
19
+ import { isQuotaExhausted, candidateStatus, pickQuotaFallback, formatCandidateMenu } from "./reviewer-fallback.js";
20
+
21
+ // Cross-AI preference when the configured reviewer IS the host.
22
+ const CROSS_ORDER = ["codex", "claude", "gemini", "opencode", "aider"];
23
+
24
+ /**
25
+ * Pick a reviewer that is NOT the host agent.
26
+ * @returns {Promise<string|null>} provider name, or null if none exists.
27
+ */
28
+ export async function pickCrossReviewer({ config, hostAgent, detectAgents = detectAvailableAgents }) {
29
+ const configured = resolveRole(config, "reviewer").provider;
30
+ if (configured && configured !== hostAgent) return configured;
31
+
32
+ const agents = await detectAgents();
33
+ const candidates = agents
34
+ .filter((a) => a.available && a.name !== hostAgent)
35
+ .map((a) => a.name);
36
+ return CROSS_ORDER.find((name) => candidates.includes(name)) || candidates[0] || null;
37
+ }
38
+
39
+ /**
40
+ * Review a raw diff with a cross-AI reviewer and persist the verdict.
41
+ * @returns {Promise<object>} the stored verdict record.
42
+ */
43
+ export async function runOneShotReview({
44
+ diff, task, config, logger, projectDir,
45
+ hostAgent = detectHostAgent(),
46
+ createAgentFn = createAgent,
47
+ detectAgents = detectAvailableAgents,
48
+ candidateStatusFn = candidateStatus,
49
+ }) {
50
+ if (!diff || !diff.trim()) {
51
+ throw new Error("nothing to review — the diff is empty (stage your changes or pass --range)");
52
+ }
53
+
54
+ const reviewer = await pickCrossReviewer({ config, hostAgent, detectAgents });
55
+ if (!reviewer) {
56
+ throw new Error(
57
+ `cross-AI review requires an agent other than the host (${hostAgent || "unknown"}) — install codex, claude or another supported CLI`
58
+ );
59
+ }
60
+
61
+ const { rules } = await resolveReviewProfile({ mode: "standard", projectDir });
62
+ const attempt = async (who, cfg = config) => {
63
+ const prompt = await buildReviewerPrompt({
64
+ task: task || "Review the following diff for correctness, security and maintainability.",
65
+ diff, reviewRules: rules, mode: "standard", provider: who, projectDir,
66
+ });
67
+ logger?.info?.(`kj review: host=${hostAgent || "none"} → reviewer=${who} (cross-AI)`);
68
+ return createAgentFn(who, cfg, logger).reviewTask({ prompt, role: "reviewer" });
69
+ };
70
+
71
+ let activeReviewer = reviewer;
72
+ let result = await attempt(activeReviewer);
73
+
74
+ // KJC-TSK-0730 — quota failover: exhausted quota is not a review failure,
75
+ // it is a provider outage. One retry with an authenticated candidate
76
+ // (loud notice, never silent), or an actionable menu. Never the host:
77
+ // the brain does not review itself.
78
+ if (!result?.ok && isQuotaExhausted(result?.error) && (config?.reviewer_options?.auto_fallback ?? true)) {
79
+ const statuses = await candidateStatusFn();
80
+ const fallback = pickQuotaFallback(statuses, { exclude: [activeReviewer, hostAgent].filter(Boolean) });
81
+ if (fallback) {
82
+ logger?.warn?.(
83
+ `⚠ reviewer ${activeReviewer} sin cuota → usando ${fallback} SOLO en esta invocación. ` +
84
+ `Para fijarlo: roles.reviewer.provider: ${fallback} (avisando, nunca en silencio — KJC-TSK-0730).`
85
+ );
86
+ activeReviewer = fallback;
87
+ // The role's model pin belongs to the exhausted provider (a codex
88
+ // model name means nothing to copilot) — the candidate runs on its
89
+ // own default model.
90
+ result = await attempt(activeReviewer, {
91
+ ...config,
92
+ roles: { ...config?.roles, reviewer: { ...config?.roles?.reviewer, model: null } },
93
+ });
94
+ } else {
95
+ throw new Error(
96
+ `reviewer ${activeReviewer} sin cuota y ningún candidato autenticado con adaptador.\n` +
97
+ formatCandidateMenu(statuses, { exclude: [hostAgent].filter(Boolean) })
98
+ );
99
+ }
100
+ }
101
+
102
+ if (!result?.ok) {
103
+ throw new Error(`reviewer ${activeReviewer} failed: ${result?.error || "no output"}`);
104
+ }
105
+
106
+ const parsed = parseMaybeJsonString(result.output);
107
+ if (!parsed || typeof parsed.approved !== "boolean") {
108
+ throw new Error(`reviewer ${activeReviewer} returned no parseable verdict`);
109
+ }
110
+
111
+ return saveVerdict(projectDir, diff, {
112
+ verdict: parsed.approved ? "approved" : "rejected",
113
+ reviewer: activeReviewer,
114
+ host: hostAgent || null,
115
+ // KJC-TSK-0680: where the review ran — makes any isolation claim auditable.
116
+ workspace: await detectWorkspace(projectDir),
117
+ issues: parsed.blocking_issues || [],
118
+ suggestions: parsed.non_blocking_suggestions || [],
119
+ summary: parsed.summary || parsed.raw_summary || "",
120
+ confidence: parsed.confidence ?? null,
121
+ });
122
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Review output parsing helpers.
3
+ * Extracted from orchestrator.js to improve testability and reduce complexity.
4
+ */
5
+
6
+ import { extractFirstJson } from "../utils/json-extract.js";
7
+
8
+ export function parseMaybeJsonString(value) {
9
+ if (typeof value !== "string") return null;
10
+ return extractFirstJson(value);
11
+ }
12
+
13
+ function isReviewPayload(obj) {
14
+ return obj?.approved !== undefined && obj?.blocking_issues !== undefined;
15
+ }
16
+
17
+ function findReviewInArray(arr) {
18
+ for (let i = arr.length - 1; i >= 0; i -= 1) {
19
+ const item = arr[i];
20
+ if (isReviewPayload(item)) return item;
21
+
22
+ const nested = item?.result || item?.message?.content?.[0]?.text;
23
+ if (typeof nested === "string") {
24
+ const parsedNested = parseMaybeJsonString(nested);
25
+ if (parsedNested?.approved !== undefined) return parsedNested;
26
+ }
27
+ }
28
+ return null;
29
+ }
30
+
31
+ export function normalizeReviewPayload(payload) {
32
+ if (!payload) return null;
33
+ if (isReviewPayload(payload)) return payload;
34
+ if (Array.isArray(payload)) return findReviewInArray(payload);
35
+
36
+ if (typeof payload.result === "string") {
37
+ const parsedResult = parseMaybeJsonString(payload.result);
38
+ if (parsedResult?.approved !== undefined) return parsedResult;
39
+ }
40
+
41
+ return null;
42
+ }
43
+
44
+ export function parseJsonOutput(raw) {
45
+ const cleaned = raw.trim();
46
+ if (!cleaned) return null;
47
+ try {
48
+ const parsed = JSON.parse(cleaned);
49
+ return normalizeReviewPayload(parsed);
50
+ } catch { /* not a single JSON blob */
51
+ const lines = cleaned
52
+ .split("\n")
53
+ .map((x) => x.trim())
54
+ .filter(Boolean);
55
+
56
+ const parsedLines = [];
57
+ for (const line of lines) {
58
+ try {
59
+ parsedLines.push(JSON.parse(line));
60
+ } catch { /* line is not JSON */
61
+ continue;
62
+ }
63
+ }
64
+
65
+ const normalizedLines = normalizeReviewPayload(parsedLines);
66
+ if (normalizedLines) {
67
+ return normalizedLines;
68
+ }
69
+
70
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
71
+ try {
72
+ const parsed = JSON.parse(lines[i]);
73
+ return normalizeReviewPayload(parsed);
74
+ } catch { /* line is not JSON */
75
+ continue;
76
+ }
77
+ }
78
+ }
79
+ return null;
80
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Adaptador del gate de policy en el flujo de review de karajan-code
3
+ * (GOV-A, KJC-TSK-0745). La decisión warn/deny/inexcepcionable/excepción
4
+ * vive en el kernel (@karajan-family/governance); aquí solo el dominio: la
5
+ * excepción se pide con KJ_ALLOW_POLICY=1 + KJ_POLICY_REASON, y el
6
+ * artefacto de este dominio es el diff staged — por eso el alcance
7
+ * registrado dice "este diff exacto".
8
+ */
9
+ import { evaluateGate } from "@karajan-family/governance";
10
+
11
+ /**
12
+ * @returns {{ok: boolean, invalid?: boolean, warns: object[], denials: object[], exempted: object[]}}
13
+ */
14
+ export function evaluatePolicyGate({
15
+ policy, errors = [], role = "coder", files = [], netLinesAdded = null,
16
+ diffHashValue = null, env = process.env, recordException = () => {},
17
+ standingExceptions = [], now = new Date(),
18
+ }) {
19
+ return evaluateGate({
20
+ policy, errors, role, files, netLinesAdded,
21
+ artifactHash: diffHashValue,
22
+ exemption: { requested: env.KJ_ALLOW_POLICY === "1", justification: env.KJ_POLICY_REASON, hint: 'KJ_POLICY_REASON="por qué"' },
23
+ recordException: (entry) => recordException({ ...entry, scope: "este diff exacto" }),
24
+ standingExceptions, now,
25
+ });
26
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Review profile resolver.
3
+ * Loads mode-specific reviewer instructions with fallback chain:
4
+ * 1. Project-level / user-level / built-in reviewer-{mode}.md
5
+ * 2. Project-level / user-level / built-in reviewer.md
6
+ * 3. Hardcoded default
7
+ */
8
+
9
+ import path from "node:path";
10
+ import { loadFirstExisting } from "../roles/base-role.js";
11
+ import { getKarajanHome } from "../utils/paths.js";
12
+
13
+ const KNOWN_MODES = new Set(["paranoid", "strict", "standard", "relaxed"]);
14
+
15
+ const DEFAULT_RULES = "Focus on critical issues: security vulnerabilities, logic errors, and broken tests.";
16
+
17
+ function buildCandidates(fileName, projectDir) {
18
+ const candidates = [];
19
+
20
+ if (projectDir) {
21
+ candidates.push(path.join(projectDir, ".karajan", "roles", fileName));
22
+ }
23
+
24
+ candidates.push(path.join(getKarajanHome(), "roles", fileName));
25
+
26
+ const builtIn = path.resolve(
27
+ path.dirname(new URL(import.meta.url).pathname),
28
+ "..",
29
+ "..",
30
+ "templates",
31
+ "roles",
32
+ fileName
33
+ );
34
+ candidates.push(builtIn);
35
+
36
+ return candidates;
37
+ }
38
+
39
+ export async function resolveReviewProfile({ mode = "standard", projectDir } = {}) {
40
+ // Known modes: try mode-specific file first, then base reviewer.md
41
+ if (KNOWN_MODES.has(mode)) {
42
+ const modePaths = buildCandidates(`reviewer-${mode}.md`, projectDir);
43
+ const modeRules = await loadFirstExisting(modePaths);
44
+ if (modeRules) {
45
+ return { mode, rules: modeRules };
46
+ }
47
+
48
+ // Fallback to base reviewer.md
49
+ const basePaths = buildCandidates("reviewer.md", projectDir);
50
+ const baseRules = await loadFirstExisting(basePaths);
51
+ if (baseRules) {
52
+ return { mode, rules: baseRules };
53
+ }
54
+
55
+ return { mode, rules: DEFAULT_RULES };
56
+ }
57
+
58
+ // Custom/unknown modes: only check base reviewer.md
59
+ const basePaths = buildCandidates("reviewer.md", projectDir);
60
+ const baseRules = await loadFirstExisting(basePaths);
61
+ if (baseRules) {
62
+ return { mode, rules: baseRules };
63
+ }
64
+
65
+ return { mode, rules: DEFAULT_RULES };
66
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * reviewer-fallback (KJC-TSK-0730) — running out of reviewer quota must
3
+ * never leave the method without a third party, and must never be silent:
4
+ * either an authenticated candidate takes over WITH a loud notice, or the
5
+ * user gets a menu of candidates with their tier and the exact login
6
+ * command. Born from the real codex weekly-quota outage of 2026-08-06.
7
+ *
8
+ * The registry is declarative. `install` is only present when the command
9
+ * was VERIFIED (never invent package names); auth heuristics are cheap
10
+ * local file checks — informative for the menu, while the actual failover
11
+ * only sticks if the candidate answers.
12
+ */
13
+ import { existsSync } from "node:fs";
14
+ import os from "node:os";
15
+ import path from "node:path";
16
+ import { checkBinary } from "../utils/agent-detect.js";
17
+
18
+ export const REVIEWER_CANDIDATES = [
19
+ {
20
+ name: "codex",
21
+ tier: "suscripción ChatGPT (cuota semanal compartida entre modelos)",
22
+ login: "codex → Sign in with ChatGPT",
23
+ install: "npm i -g @openai/codex",
24
+ authPaths: [".codex/auth.json"],
25
+ },
26
+ {
27
+ name: "copilot",
28
+ tier: "gratis (tier free de GitHub Copilot; más cuota con suscripción)",
29
+ login: "copilot → autenticación GitHub",
30
+ install: "npm i -g @github/copilot",
31
+ authPaths: [".copilot/config.json"],
32
+ },
33
+ {
34
+ name: "agy",
35
+ tier: "suscripción Google AI Pro/Ultra (sucesor del gemini CLI, retirado 06-2026)",
36
+ login: "agy → /login (cuenta Google)",
37
+ install: "curl -fsSL https://antigravity.google/cli/install.sh | bash",
38
+ authPaths: [".gemini/antigravity-cli"],
39
+ },
40
+ {
41
+ name: "kimi",
42
+ tier: "gratis (Kimi Code, Moonshot K2.x)",
43
+ login: "kimi login (device-code; si dice 'No model configured', repítelo)",
44
+ install: null,
45
+ authPaths: [".kimi-code/credentials"],
46
+ },
47
+ {
48
+ name: "qwen",
49
+ tier: "requiere plan o API key (el tier gratis hosted se retiró en 04-2026)",
50
+ login: "qwen → OAuth o API key OpenAI-compatible",
51
+ install: "npm i -g @qwen-code/qwen-code",
52
+ authPaths: [".qwen/oauth_creds.json"],
53
+ },
54
+ {
55
+ name: "aider",
56
+ tier: "API keys propias (pago por token: OpenAI/Anthropic/OpenRouter…)",
57
+ login: "exportar OPENAI_API_KEY / ANTHROPIC_API_KEY (o ~/.aider.conf.yml)",
58
+ install: "pipx install aider-chat || pip3 install aider-chat",
59
+ authPaths: [".aider.conf.yml"],
60
+ authEnv: ["OPENAI_API_KEY", "ANTHROPIC_API_KEY", "OPENROUTER_API_KEY"],
61
+ },
62
+ {
63
+ name: "opencode",
64
+ tier: "gratis con modelos locales (LiteLLM/Ollama) o API keys propias",
65
+ login: "provider en ~/.config/opencode/opencode.json",
66
+ install: null,
67
+ authPaths: [".config/opencode/opencode.json"],
68
+ },
69
+ ];
70
+
71
+ const QUOTA_RE = /usage limit|quota|rate.?limit|credits? (?:exhausted|left: ?0)|hit your .{0,20}limit/i;
72
+
73
+ /** True when the agent error smells like exhausted quota, not a real failure. */
74
+ export function isQuotaExhausted(text) {
75
+ return typeof text === "string" && QUOTA_RE.test(text);
76
+ }
77
+
78
+ /** installed × authenticated for every candidate, from cheap local checks. */
79
+ export async function candidateStatus({ home = os.homedir(), checkBin = checkBinary, env = process.env } = {}) {
80
+ return Promise.all(
81
+ REVIEWER_CANDIDATES.map(async (c) => {
82
+ let installed = false;
83
+ try {
84
+ installed = (await checkBin(c.name)).ok;
85
+ } catch { /* not installed */ }
86
+ // Session-file heuristics, or env keys for CLIs (aider) that carry
87
+ // no session file. Key present ≠ credit left — it is menu signal;
88
+ // the failover only sticks if the candidate actually answers.
89
+ const authenticated =
90
+ c.authPaths.some((p) => existsSync(path.join(home, p))) ||
91
+ (c.authEnv || []).some((name) => Boolean(env[name]));
92
+ return { ...c, installed, authenticated };
93
+ }),
94
+ );
95
+ }
96
+
97
+ /**
98
+ * First candidate that is installed, authenticated, has a kj adapter and is
99
+ * not excluded (the exhausted reviewer and the host — the brain NEVER
100
+ * reviews itself).
101
+ */
102
+ export function pickQuotaFallback(statuses, { exclude = [] } = {}) {
103
+ const found = statuses.find(
104
+ (s) => s.installed && s.authenticated && !s.adapterPending && !exclude.includes(s.name),
105
+ );
106
+ return found ? found.name : null;
107
+ }
108
+
109
+ /** Human/agent-readable menu: tier, state, and the exact command per candidate. */
110
+ export function formatCandidateMenu(statuses, { exclude = [] } = {}) {
111
+ const lines = statuses
112
+ .filter((s) => !exclude.includes(s.name))
113
+ .map((s) => {
114
+ let state = "no instalado";
115
+ if (s.authenticated) state = "logado";
116
+ else if (s.installed) state = "instalado, SIN login";
117
+ let action = "";
118
+ if (!s.authenticated) {
119
+ action = s.installed || !s.install ? ` — login: ${s.login}` : ` — instalar: ${s.install}`;
120
+ }
121
+ const pending = s.adapterPending ? " [adaptador kj pendiente: KJC-TSK-0729]" : "";
122
+ return ` - ${s.name} (${s.tier}) · ${state}${action}${pending}`;
123
+ });
124
+ return `Candidatos a reviewer:\n${lines.join("\n")}\nElige uno, haz su login y fija roles.reviewer.provider — o pasa --reviewer <nombre>.`;
125
+ }
@@ -0,0 +1,31 @@
1
+ export function validateReviewResult(reviewResult) {
2
+ if (!reviewResult || typeof reviewResult !== "object") {
3
+ throw new TypeError("Reviewer output must be a JSON object");
4
+ }
5
+
6
+ if (typeof reviewResult.approved !== "boolean") {
7
+ throw new TypeError("Reviewer output missing boolean field: approved");
8
+ }
9
+
10
+ if (!Array.isArray(reviewResult.blocking_issues)) {
11
+ throw new TypeError("Reviewer output missing array field: blocking_issues");
12
+ }
13
+
14
+ if (!Array.isArray(reviewResult.non_blocking_suggestions)) {
15
+ reviewResult.non_blocking_suggestions = [];
16
+ }
17
+
18
+ if (typeof reviewResult.summary !== "string") {
19
+ reviewResult.summary = "";
20
+ }
21
+
22
+ if (typeof reviewResult.confidence !== "number") {
23
+ reviewResult.confidence = 0.5;
24
+ }
25
+
26
+ if (reviewResult.approved && reviewResult.blocking_issues.length > 0) {
27
+ throw new Error("Invalid reviewer output: approved=true with blocking issues");
28
+ }
29
+
30
+ return reviewResult;
31
+ }