@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,220 @@
1
+ /**
2
+ * CLI-flag overrides applied on top of a loaded config.
3
+ *
4
+ * `applyRunOverrides(config, flags)` takes the merged config from the
5
+ * loader plus the CLI flags (also forwarded from MCP tool args) and
6
+ * produces the final config the orchestrator operates on. Overrides are
7
+ * declarative where possible (ROLE_PROVIDER_FLAGS, ROLE_MODEL_FLAGS,
8
+ * PIPELINE_ENABLE_FLAGS, SCALAR_FLAGS, UNDEF_CHECK_FLAGS) and imperative
9
+ * only where the mapping is not 1:1 (methodology, CI, output mode,
10
+ * deprecated flags like `--no-sonar`).
11
+ *
12
+ * Extracted from `src/config.js` in TSK-0332 (Oleada 2 of the v2.7.4
13
+ * audit refactor). Public API: `applyRunOverrides`. The apply* helpers
14
+ * are internal.
15
+ */
16
+
17
+ import { mergeDeep } from "./loader.js";
18
+ import { DEFAULTS } from "./defaults.js";
19
+ import { safeParseConfig, formatConfigIssues } from "./schema.js";
20
+
21
+ const ROLE_PROVIDER_FLAGS = [
22
+ ["planner", "planner"], ["coder", "coder"], ["reviewer", "reviewer"],
23
+ ["refactorer", "refactorer"], ["solomon", "solomon"], ["researcher", "researcher"],
24
+ ["tester", "tester"], ["security", "security"], ["triage", "triage"],
25
+ ["discover", "discover"], ["architect", "architect"]
26
+ ];
27
+
28
+ // Role model flags: [flagName, roleName] — truthy check, String coercion
29
+ const ROLE_MODEL_FLAGS = [
30
+ ["plannerModel", "planner"], ["coderModel", "coder"], ["reviewerModel", "reviewer"],
31
+ ["refactorerModel", "refactorer"], ["solomonModel", "solomon"], ["discoverModel", "discover"],
32
+ ["architectModel", "architect"]
33
+ ];
34
+
35
+ // Pipeline enable flags: [flagName, pipelineKey] — !== undefined check, Boolean coercion
36
+ const PIPELINE_ENABLE_FLAGS = [
37
+ ["enablePlanner", "planner"], ["enableRefactorer", "refactorer"],
38
+ ["enableSolomon", "solomon"], ["enableResearcher", "researcher"],
39
+ ["enableTester", "tester"], ["enableSecurity", "security"], ["enableImpeccable", "impeccable"],
40
+ ["enablePerf", "perf"],
41
+ ["enableTriage", "triage"], ["enableDiscover", "discover"],
42
+ ["enableArchitect", "architect"],
43
+ ["enableHuReviewer", "hu_reviewer"]
44
+ ];
45
+
46
+ const AUTO_SIMPLIFY_FLAG = "autoSimplify";
47
+
48
+ // Scalar flags: [flagName, setter] — truthy check.
49
+ // Note: max_iterations was previously here but `0` was silently ignored
50
+ // (truthy check skipped it). It now lives in UNDEF_CHECK_FLAGS so
51
+ // `--max-iterations 0` reaches the schema validator and errors cleanly
52
+ // instead of falling through to the default (KJC-TSK-0318).
53
+ const SCALAR_FLAGS = [
54
+ ["mode", (out, v) => { out.review_mode = v; }],
55
+ ["maxIterationMinutes", (out, v) => { out.session.max_iteration_minutes = Number(v); }],
56
+ ["maxTotalMinutes", (out, v) => { out.session.max_total_minutes = Number(v); }],
57
+ ["checkpointInterval", (out, v) => { out.session.checkpoint_interval_minutes = Number(v); }],
58
+ ["parallel", (out, v) => { out.session.max_parallel_hus = Number(v); }],
59
+ ["baseBranch", (out, v) => { out.base_branch = v; }],
60
+ ["coderFallback", (out, v) => { out.coder_options.fallback_coder = v; }],
61
+ ["reviewerFallback", (out, v) => { out.reviewer_options.fallback_reviewer = v; }],
62
+ ["taskType", (out, v) => { out.taskType = String(v); }],
63
+ ["branchPrefix", (out, v) => { out.git.branch_prefix = String(v); }]
64
+ ];
65
+
66
+ // Boolean/undefined-check flags: [flagName, setter] — !== undefined check.
67
+ // These preserve falsy values (false, 0) so --no-rebase correctly sets
68
+ // auto_rebase=false and --reviewer-retries 0 correctly disables retries.
69
+ const UNDEF_CHECK_FLAGS = [
70
+ ["maxIterations", (out, v) => { out.max_iterations = Number(v); }],
71
+ ["reviewerRetries", (out, v) => { out.reviewer_options.retries = Number(v); }],
72
+ ["autoCommit", (out, v) => { out.git.auto_commit = Boolean(v); }],
73
+ ["autoPush", (out, v) => { out.git.auto_push = Boolean(v); }],
74
+ ["autoPr", (out, v) => { out.git.auto_pr = Boolean(v); }],
75
+ ["autoRebase", (out, v) => { out.git.auto_rebase = Boolean(v); }],
76
+ ["enableSerena", (out, v) => { out.serena.enabled = Boolean(v); }]
77
+ ];
78
+
79
+ function applyRoleOverrides(out, flags) {
80
+ // A provider override is always a string (`--security codex`). A bare
81
+ // boolean means the flag belongs to the command itself (`kj audit
82
+ // --security`, KJC-TSK-0695) and must not become a provider.
83
+ for (const [flag, role] of ROLE_PROVIDER_FLAGS) {
84
+ if (typeof flags[flag] === "string" && flags[flag]) {
85
+ // A model pin belongs to the provider it was written for — switching
86
+ // provider by flag drops it (KJC-TSK-0729: codex's pinned mini reached
87
+ // agy, which answered with its model list instead of a verdict). An
88
+ // explicit --<role>-model flag re-pins below, after this loop.
89
+ if (out.roles[role].provider && out.roles[role].provider !== flags[flag]) out.roles[role].model = null;
90
+ out.roles[role].provider = flags[flag];
91
+ }
92
+ }
93
+ // coder/reviewer also update top-level aliases
94
+ if (flags.coder) out.coder = flags.coder;
95
+ if (flags.reviewer) out.reviewer = flags.reviewer;
96
+
97
+ for (const [flag, role] of ROLE_MODEL_FLAGS) {
98
+ if (flags[flag]) out.roles[role].model = String(flags[flag]);
99
+ }
100
+ // reviewerModel also updates reviewer_options
101
+ if (flags.reviewerModel) out.reviewer_options.model = String(flags.reviewerModel);
102
+ }
103
+
104
+ function applyPipelineOverrides(out, flags) {
105
+ for (const [flag, key] of PIPELINE_ENABLE_FLAGS) {
106
+ if (flags[flag] !== undefined) out.pipeline[key].enabled = Boolean(flags[flag]);
107
+ }
108
+ if (flags.enableReviewer !== undefined) {
109
+ out.pipeline.reviewer = out.pipeline.reviewer || {};
110
+ out.pipeline.reviewer.enabled = Boolean(flags.enableReviewer);
111
+ }
112
+ }
113
+
114
+ function applyScalarAndBooleanOverrides(out, flags) {
115
+ for (const [flag, setter] of SCALAR_FLAGS) {
116
+ if (flags[flag]) setter(out, flags[flag]);
117
+ }
118
+ for (const [flag, setter] of UNDEF_CHECK_FLAGS) {
119
+ if (flags[flag] !== undefined) setter(out, flags[flag]);
120
+ }
121
+ }
122
+
123
+ function applyMethodologyOverride(out, flags) {
124
+ if (!flags.methodology) return;
125
+ const methodology = String(flags.methodology).toLowerCase();
126
+ out.development.methodology = methodology;
127
+ out.development.require_test_changes = methodology === "tdd";
128
+ }
129
+
130
+ function applyCiOverride(out, flags) {
131
+ out.ci = out.ci || { enabled: false };
132
+ if (flags.enableCi === undefined) return;
133
+ out.ci.enabled = Boolean(flags.enableCi);
134
+ // CI requires git automation (commit + push + PR)
135
+ if (out.ci.enabled) {
136
+ out.git.auto_commit = true;
137
+ out.git.auto_push = true;
138
+ out.git.auto_pr = true;
139
+ }
140
+ }
141
+
142
+ function applyOutputModeOverrides(out, flags) {
143
+ out.output = out.output || {};
144
+ // --verbose explicitly overrides quiet
145
+ if (flags.verbose === true) {
146
+ out.output.quiet = false;
147
+ } else if (flags.quiet === true) {
148
+ out.output.quiet = true;
149
+ }
150
+ // quiet defaults to true (set in DEFAULTS)
151
+ }
152
+
153
+ function applyMiscOverrides(out, flags) {
154
+ if (flags[AUTO_SIMPLIFY_FLAG] !== undefined) out.pipeline.auto_simplify = Boolean(flags[AUTO_SIMPLIFY_FLAG]);
155
+ // `--no-sonar` and `sonarqube.enabled: false` are deprecated since v2.7.4.
156
+ // Sonar is intrinsic to Karajan for code tasks (sw/refactor/add-tests) and
157
+ // skipped for non-code tasks (audit/doc/infra/analysis/no-code) by policy.
158
+ // The flag is still accepted to avoid breaking scripts; it now emits a
159
+ // warning at run start and is otherwise ignored. See preflight-checks.js
160
+ // and flow-runner.js for the actual gate (resolvedPolicies.sonar).
161
+ // --step: per-iteration supervision gate (KJC-TSK-0628).
162
+ if (flags.step === true) out.session.iteration_gate = true;
163
+
164
+ if (flags.noSonar || flags.sonar === false) {
165
+ out._deprecated = out._deprecated || {};
166
+ out._deprecated.noSonarFlag = true;
167
+ }
168
+ out.sonarcloud = out.sonarcloud || {};
169
+ if (flags.enableSonarcloud === true) out.sonarcloud.enabled = true;
170
+ if (flags.noSonarcloud === true || flags.sonarcloud === false) out.sonarcloud.enabled = false;
171
+
172
+ out.planning_game = out.planning_game || {};
173
+ if (flags.pgTask) out.planning_game.enabled = true;
174
+ if (flags.pgProject) out.planning_game.project_id = flags.pgProject;
175
+
176
+ out.model_selection = out.model_selection || { enabled: true, tiers: {}, role_overrides: {} };
177
+ if (flags.smartModels === true) out.model_selection.enabled = true;
178
+ if (flags.smartModels === false || flags.noSmartModels === true) out.model_selection.enabled = false;
179
+ }
180
+
181
+ export function applyRunOverrides(config, flags) {
182
+ const out = mergeDeep(config, {});
183
+ out.coder_options = out.coder_options || {};
184
+ out.reviewer_options = out.reviewer_options || {};
185
+ out.session = out.session || {};
186
+ out.git = out.git || {};
187
+ out.development = out.development || {};
188
+ out.sonarqube = out.sonarqube || {};
189
+ // Precedence: explicit top-level value (including null = opt-out) wins;
190
+ // the legacy session.max_budget_usd location next; the shipped default
191
+ // (KJC-TSK-0621: 5) last. When top-level still carries the default, a
192
+ // session-level value — including an explicit null — takes over.
193
+ if (out.max_budget_usd === undefined) {
194
+ out.max_budget_usd = out.session.max_budget_usd !== undefined ? out.session.max_budget_usd : DEFAULTS.max_budget_usd;
195
+ } else if (out.max_budget_usd === DEFAULTS.max_budget_usd && out.session.max_budget_usd !== undefined) {
196
+ out.max_budget_usd = out.session.max_budget_usd;
197
+ }
198
+ out.budget = mergeDeep(DEFAULTS.budget, out.budget || {});
199
+ out.roles = mergeDeep(DEFAULTS.roles, out.roles || {});
200
+ out.pipeline = mergeDeep(DEFAULTS.pipeline, out.pipeline || {});
201
+ out.serena = out.serena || { enabled: false };
202
+
203
+ applyRoleOverrides(out, flags);
204
+ applyPipelineOverrides(out, flags);
205
+ applyScalarAndBooleanOverrides(out, flags);
206
+ applyMethodologyOverride(out, flags);
207
+ applyCiOverride(out, flags);
208
+ applyMiscOverrides(out, flags);
209
+ applyOutputModeOverrides(out, flags);
210
+
211
+ // KJC-TSK-0318 — re-run schema validation after overrides so CLI flags
212
+ // that would produce an invalid config (e.g. --max-iterations 0) fail
213
+ // fast with the same clear message loadConfig would have produced.
214
+ const validation = safeParseConfig(out);
215
+ if (!validation.success) {
216
+ throw new Error(`Invalid Karajan config after CLI overrides:\n${formatConfigIssues(validation.issues)}`);
217
+ }
218
+
219
+ return out;
220
+ }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Role provider/model resolution + config validation.
3
+ *
4
+ * `resolveRole(config, role)` returns `{ provider, model }` for a role
5
+ * given the fully-overridden config, respecting legacy single-provider
6
+ * shortcuts (`coder: "claude"`) and per-role overrides
7
+ * (`roles.coder.provider`). Handles model compatibility (gemini models
8
+ * are dropped from a claude provider, etc.) so the agent subprocess
9
+ * never sees an incompatible `--model` flag.
10
+ *
11
+ * `validateConfig(config, commandName)` is the run-time guard called by
12
+ * every CLI command before spawning agents: it asserts every required
13
+ * role has a provider, otherwise throws with an actionable message
14
+ * ("Set 'roles.planner.provider' or pass '--planner <name>'").
15
+ *
16
+ * Extracted from `src/config.js` in TSK-0332 (Oleada 2 of the v2.7.4
17
+ * audit refactor). Public API: `resolveRole`, `validateConfig`. The
18
+ * inference helpers are internal.
19
+ */
20
+
21
+ /**
22
+ * Check if a model string is compatible with an agent provider.
23
+ * Only returns false when the model clearly belongs to a DIFFERENT family.
24
+ * Returns true if we can't determine or if the model is ambiguous.
25
+ *
26
+ * KJC-BUG-0144: exported and family-based — an agent may serve another
27
+ * family's models (agy is Google's Antigravity, gemini family), and the
28
+ * same check now guards BaseAgent.getRoleModel so per-role defaults
29
+ * (e.g. start's haiku) never reach a provider from another family.
30
+ */
31
+ const AGENT_MODEL_SIGNATURES = {
32
+ claude: ["claude", "sonnet", "opus", "haiku"],
33
+ codex: ["o4-", "o3-", "gpt-", "codex"],
34
+ gemini: ["gemini", "flash-"]
35
+ };
36
+
37
+ // Single-family CLIs: they only serve their own vendor's models. Agents NOT
38
+ // in this map (aider, opencode, copilot, kimi...) are multi-model hosts —
39
+ // they route to arbitrary models, so any model name is allowed there.
40
+ const AGENT_FAMILY = { claude: "claude", codex: "codex", gemini: "gemini", agy: "gemini" };
41
+
42
+ function modelFamily(model) {
43
+ const lower = model.toLowerCase();
44
+ for (const [family, signatures] of Object.entries(AGENT_MODEL_SIGNATURES)) {
45
+ if (signatures.some(s => lower.includes(s))) return family;
46
+ }
47
+ return null;
48
+ }
49
+
50
+ export function isModelCompatible(agent, model) {
51
+ if (!model || !agent) return true;
52
+ const agentFamily = AGENT_FAMILY[agent];
53
+ if (!agentFamily) return true; // multi-model host — bring your own model
54
+ const family = modelFamily(model);
55
+ if (!family) return true; // ambiguous/custom/local — allow it
56
+ return family === agentFamily;
57
+ }
58
+
59
+ // Roles that inherit provider/model from the coder when not explicitly configured
60
+ const CODER_INHERITED_ROLES = new Set([
61
+ "planner", "refactorer", "solomon", "researcher", "tester", "security",
62
+ "impeccable", "triage", "discover", "architect", "audit",
63
+ "hu_reviewer", "hu-reviewer",
64
+ "spec_reviewer", "spec-reviewer",
65
+ ]);
66
+
67
+ function resolveProvider(roleConfig, role, roles, legacyCoder, legacyReviewer) {
68
+ if (roleConfig.provider) return roleConfig.provider;
69
+
70
+ // If model has "provider/model" format (e.g. "gemini/pro"), extract the provider
71
+ if (roleConfig.model && roleConfig.model.includes("/")) {
72
+ const inferredProvider = roleConfig.model.split("/")[0].toLowerCase();
73
+ if (AGENT_MODEL_SIGNATURES[inferredProvider]) return inferredProvider;
74
+ }
75
+
76
+ if (role === "coder") return legacyCoder;
77
+ if (role === "reviewer") return legacyReviewer;
78
+ if (CODER_INHERITED_ROLES.has(role)) return roles.coder?.provider || legacyCoder;
79
+ return null;
80
+ }
81
+
82
+ function resolveModel(roleConfig, role, config) {
83
+ if (roleConfig.model) {
84
+ // Strip "provider/" prefix from models like "gemini/pro" → "pro"
85
+ const model = roleConfig.model.includes("/")
86
+ ? roleConfig.model.split("/").slice(1).join("/")
87
+ : roleConfig.model;
88
+ return { model, inherited: false };
89
+ }
90
+ if (role === "coder") return { model: config?.coder_options?.model ?? null, inherited: false };
91
+ if (role === "reviewer") return { model: config?.reviewer_options?.model ?? null, inherited: false };
92
+ if (CODER_INHERITED_ROLES.has(role)) {
93
+ const model = config?.coder_options?.model ?? null;
94
+ return { model, inherited: !!model };
95
+ }
96
+ return { model: null, inherited: false };
97
+ }
98
+
99
+ export function resolveRole(config, role) {
100
+ const roles = config?.roles || {};
101
+ const roleConfig = roles[role] || {};
102
+ const legacyCoder = config?.coder || null;
103
+ const legacyReviewer = config?.reviewer || null;
104
+
105
+ const provider = resolveProvider(roleConfig, role, roles, legacyCoder, legacyReviewer);
106
+ let { model } = resolveModel(roleConfig, role, config);
107
+
108
+ // Drop model if incompatible with the resolved provider (inherited or explicit)
109
+ if (provider && model && !isModelCompatible(provider, model)) {
110
+ model = null;
111
+ }
112
+
113
+ return { provider, model };
114
+ }
115
+
116
+ // Pipeline roles checked when commandName is "run": [pipelineKey, roleName]
117
+ const RUN_PIPELINE_ROLES = [
118
+ ["reviewer", "reviewer"], ["triage", "triage"], ["planner", "planner"],
119
+ ["refactorer", "refactorer"], ["researcher", "researcher"],
120
+ ["tester", "tester"], ["security", "security"], ["impeccable", "impeccable"]
121
+ ];
122
+
123
+ // Direct command-to-role mapping for non-"run" commands
124
+ const COMMAND_ROLE_MAP = {
125
+ discover: ["discover"],
126
+ plan: ["planner"],
127
+ code: ["coder"],
128
+ review: ["reviewer"]
129
+ };
130
+
131
+ function requiredRolesFor(commandName, config) {
132
+ if (commandName !== "run") {
133
+ return COMMAND_ROLE_MAP[commandName] || [];
134
+ }
135
+ const required = ["coder"];
136
+ for (const [pipelineKey, roleName] of RUN_PIPELINE_ROLES) {
137
+ const pipelineEntry = config?.pipeline?.[pipelineKey];
138
+ // reviewer defaults to enabled (only excluded if explicitly false)
139
+ const isEnabled = pipelineKey === "reviewer"
140
+ ? pipelineEntry?.enabled !== false
141
+ : Boolean(pipelineEntry?.enabled);
142
+ if (isEnabled) required.push(roleName);
143
+ }
144
+ return required;
145
+ }
146
+
147
+ export function validateConfig(config, commandName = "run") {
148
+ const errors = [];
149
+ if (!new Set(["paranoid", "strict", "standard", "relaxed", "custom"]).has(config.review_mode)) {
150
+ errors.push(`Invalid review_mode: ${config.review_mode}`);
151
+ }
152
+ if (!new Set(["tdd", "standard"]).has(config.development?.methodology)) {
153
+ errors.push(`Invalid development.methodology: ${config.development?.methodology}`);
154
+ }
155
+
156
+ const requiredRoles = requiredRolesFor(commandName, config);
157
+ for (const role of requiredRoles) {
158
+ const { provider } = resolveRole(config, role);
159
+ if (!provider) {
160
+ errors.push(
161
+ `Missing provider for required role '${role}'. Set 'roles.${role}.provider' or pass '--${role} <name>'`
162
+ );
163
+ }
164
+ }
165
+
166
+ if (errors.length > 0) {
167
+ throw new Error(errors.join("\n"));
168
+ }
169
+
170
+ return config;
171
+ }
@@ -0,0 +1,299 @@
1
+ /**
2
+ * Central Valibot schema for Karajan's configuration surface (KJC-TSK-0318).
3
+ *
4
+ * Origin: this schema is based on Jorge del Casar's original work in PR #379
5
+ * (closed). The full 650-line mirror-of-DEFAULTS approach he proposed drifted
6
+ * heavily against `main` by the time it was reviewed, so this file takes the
7
+ * same intent — validating Karajan config at load time with Valibot — but
8
+ * narrows the scope to the handful of fields where validation actually
9
+ * catches real bugs (review_mode, methodology, max_iterations, percentage
10
+ * budgets, port numbers). Everything else is defined with `v.looseObject` /
11
+ * `v.unknown()` so existing consumers who touch undeclared keys keep working.
12
+ *
13
+ * The runtime DEFAULTS object still lives in src/config.js — it is the source
14
+ * of truth for default values. This schema exists to reject invalid input
15
+ * early (e.g. `max_iterations: 0`, `review_mode: "yolo"`) with a clear error,
16
+ * and to give downstream consumers a typed handle via `KarajanConfig`.
17
+ *
18
+ * @typedef {import("valibot").InferOutput<typeof ConfigSchema>} KarajanConfig
19
+ */
20
+
21
+ import * as v from "valibot";
22
+
23
+ /**
24
+ * Positive integer (>= 1). Used where 0 / negative would be a clear bug
25
+ * (max_iterations, port, most timeout knobs when they gate the loop itself).
26
+ */
27
+ const PositiveInt = v.pipe(
28
+ v.number("must be a number"),
29
+ v.integer("must be an integer"),
30
+ v.minValue(1, "must be >= 1")
31
+ );
32
+
33
+ /**
34
+ * Non-negative integer (>= 0). Used for retry counts where 0 is a valid
35
+ * "don't retry" signal.
36
+ */
37
+ const NonNegativeInt = v.pipe(
38
+ v.number("must be a number"),
39
+ v.integer("must be an integer"),
40
+ v.minValue(0, "must be >= 0")
41
+ );
42
+
43
+ /**
44
+ * review_mode picklist — anything outside this set is a typo.
45
+ */
46
+ const ReviewMode = v.picklist(
47
+ ["paranoid", "strict", "standard", "relaxed", "custom"],
48
+ "review_mode must be one of: paranoid | strict | standard | relaxed | custom"
49
+ );
50
+
51
+ const Methodology = v.picklist(
52
+ ["tdd", "standard"],
53
+ "development.methodology must be one of: tdd | standard"
54
+ );
55
+
56
+ /**
57
+ * KJC-TSK-0415: fallback chain. Cuando provider primario hits
58
+ * QUOTA_EXHAUSTED_* con cooldown > max_wait_hours, switch a fallback.
59
+ * Recursivo — el fallback puede tener su propio fallback.
60
+ */
61
+ const RoleFallback = v.looseObject({
62
+ provider: v.string("fallback.provider required (claude|codex|gemini|opencode|aider)"),
63
+ model: v.optional(v.nullable(v.string())),
64
+ max_wait_hours: v.optional(v.pipe(v.number(), v.minValue(0)), 12),
65
+ // Recursivo. Schema permite cualquier shape compatible con RoleFallback.
66
+ fallback: v.optional(v.lazy(() => RoleFallback)),
67
+ });
68
+
69
+ /**
70
+ * Role entry — provider + model, both nullable. Extra keys allowed.
71
+ * KJC-TSK-0415: campo fallback opcional para Plan B en quota exhausted.
72
+ */
73
+ const RoleEntry = v.looseObject({
74
+ provider: v.optional(v.nullable(v.string())),
75
+ model: v.optional(v.nullable(v.string())),
76
+ fallback: v.optional(RoleFallback),
77
+ });
78
+
79
+ const RolesMap = v.optional(v.looseObject({
80
+ planner: v.optional(RoleEntry),
81
+ coder: v.optional(RoleEntry),
82
+ reviewer: v.optional(RoleEntry),
83
+ refactorer: v.optional(RoleEntry),
84
+ solomon: v.optional(RoleEntry),
85
+ researcher: v.optional(RoleEntry),
86
+ tester: v.optional(RoleEntry),
87
+ security: v.optional(RoleEntry),
88
+ impeccable: v.optional(RoleEntry),
89
+ triage: v.optional(RoleEntry),
90
+ discover: v.optional(RoleEntry),
91
+ architect: v.optional(RoleEntry),
92
+ hu_reviewer: v.optional(RoleEntry),
93
+ start: v.optional(RoleEntry),
94
+ }));
95
+
96
+ const PipelineEntry = v.looseObject({
97
+ enabled: v.optional(v.boolean()),
98
+ });
99
+
100
+ const PipelineMap = v.optional(v.looseObject({
101
+ planner: v.optional(PipelineEntry),
102
+ refactorer: v.optional(PipelineEntry),
103
+ solomon: v.optional(PipelineEntry),
104
+ researcher: v.optional(PipelineEntry),
105
+ tester: v.optional(PipelineEntry),
106
+ security: v.optional(PipelineEntry),
107
+ impeccable: v.optional(PipelineEntry),
108
+ perf: v.optional(PipelineEntry),
109
+ triage: v.optional(PipelineEntry),
110
+ discover: v.optional(PipelineEntry),
111
+ architect: v.optional(PipelineEntry),
112
+ hu_reviewer: v.optional(PipelineEntry),
113
+ auto_simplify: v.optional(v.boolean()),
114
+ }));
115
+
116
+ const Development = v.optional(v.looseObject({
117
+ methodology: v.optional(Methodology),
118
+ require_test_changes: v.optional(v.boolean()),
119
+ // KJC-TSK-0398: opt-in flag; module lands in PR1, integration in PR3.
120
+ require_red_then_green: v.optional(v.boolean()),
121
+ test_file_patterns: v.optional(v.array(v.string())),
122
+ source_file_extensions: v.optional(v.array(v.string())),
123
+ }));
124
+
125
+ const Git = v.optional(v.looseObject({
126
+ auto_commit: v.optional(v.boolean()),
127
+ auto_push: v.optional(v.boolean()),
128
+ auto_pr: v.optional(v.boolean()),
129
+ auto_rebase: v.optional(v.boolean()),
130
+ branch_prefix: v.optional(v.string()),
131
+ }));
132
+
133
+ const ReviewerOptions = v.optional(v.looseObject({
134
+ output_format: v.optional(v.string()),
135
+ require_schema: v.optional(v.boolean()),
136
+ model: v.optional(v.nullable(v.string())),
137
+ deterministic: v.optional(v.boolean()),
138
+ retries: v.optional(NonNegativeInt, 1),
139
+ fallback_reviewer: v.optional(v.nullable(v.string())),
140
+ }));
141
+
142
+ const CoderOptions = v.optional(v.looseObject({
143
+ model: v.optional(v.nullable(v.string())),
144
+ auto_approve: v.optional(v.boolean()),
145
+ fallback_coder: v.optional(v.nullable(v.string())),
146
+ }));
147
+
148
+ const HuBoard = v.optional(v.looseObject({
149
+ enabled: v.optional(v.boolean()),
150
+ port: v.optional(v.pipe(
151
+ v.number("hu_board.port must be a number"),
152
+ v.integer(),
153
+ v.minValue(1, "hu_board.port must be >= 1"),
154
+ v.maxValue(65535, "hu_board.port must be <= 65535")
155
+ )),
156
+ auto_start: v.optional(v.boolean()),
157
+ }));
158
+
159
+ /**
160
+ * Budget warn threshold: 0-100 percent. Anything outside is nonsense.
161
+ */
162
+ const Budget = v.optional(v.looseObject({
163
+ warn_threshold_pct: v.optional(v.pipe(
164
+ v.number(),
165
+ v.minValue(0, "budget.warn_threshold_pct must be 0-100"),
166
+ v.maxValue(100, "budget.warn_threshold_pct must be 0-100")
167
+ )),
168
+ currency: v.optional(v.string()),
169
+ exchange_rate_eur: v.optional(v.number()),
170
+ pricing: v.optional(v.unknown()),
171
+ }));
172
+
173
+ /**
174
+ * skills.addyosmani subtree (KJC-TSK-0327). Governs the addyosmani/agent-skills
175
+ * catalog provider: enabled flag, refresh TTL and repo URL.
176
+ */
177
+ const SkillsAddyosmani = v.looseObject({
178
+ enabled: v.optional(v.boolean()),
179
+ refreshDays: v.optional(v.pipe(
180
+ v.number(),
181
+ v.minValue(0, "skills.addyosmani.refreshDays must be >= 0")
182
+ )),
183
+ repoUrl: v.optional(v.string()),
184
+ });
185
+
186
+ /**
187
+ * skills.* subtree. `sources` controls which providers Karajan consults and in
188
+ * what order (addyosmani → openskills → local by default). `mode` is the
189
+ * existing regex/semantic/auto/none knob from semantic-detector.js.
190
+ */
191
+ const Skills = v.optional(v.looseObject({
192
+ mode: v.optional(v.picklist(["auto", "regex", "semantic", "none"], "skills.mode must be one of: auto | regex | semantic | none")),
193
+ sources: v.optional(v.array(v.string())),
194
+ addyosmani: v.optional(SkillsAddyosmani),
195
+ }));
196
+
197
+ /**
198
+ * KJC-TSK-0407: routing automático de modelos por HU según complexity.
199
+ * - by_provider: override de tiers per provider (claude/codex/gemini).
200
+ * Cada provider mapea level → modelo. Sobreescribe defaults del router.
201
+ * - fixed.coder / fixed.reviewer: si presentes, override TOTAL — todas
202
+ * las HUs usan esos modelos independientemente del score. Útil para
203
+ * forzar Sonnet en todo el proyecto, por ejemplo.
204
+ */
205
+ const ModelRouting = v.optional(v.looseObject({
206
+ by_provider: v.optional(v.record(
207
+ v.string(),
208
+ v.looseObject({
209
+ trivial: v.optional(v.string()),
210
+ simple: v.optional(v.string()),
211
+ medium: v.optional(v.string()),
212
+ complex: v.optional(v.string()),
213
+ })
214
+ )),
215
+ fixed: v.optional(v.looseObject({
216
+ coder: v.optional(v.nullable(v.string())),
217
+ reviewer: v.optional(v.nullable(v.string())),
218
+ })),
219
+ }));
220
+
221
+ /**
222
+ * Main config schema. `looseObject` lets every consumer that reads an
223
+ * undeclared key keep working while the schema catches the bugs we care
224
+ * about.
225
+ */
226
+ export const ConfigSchema = v.looseObject({
227
+ coder: v.optional(v.string()),
228
+ reviewer: v.optional(v.string()),
229
+ roles: RolesMap,
230
+ pipeline: PipelineMap,
231
+ review_mode: v.optional(ReviewMode),
232
+ max_iterations: v.optional(PositiveInt, 5),
233
+ hu_max_iterations: v.optional(PositiveInt, 3),
234
+ max_budget_usd: v.optional(v.nullable(v.pipe(
235
+ v.number(),
236
+ v.minValue(0, "max_budget_usd must be >= 0")
237
+ ))),
238
+ // ENV-D1 (KJC-TSK-0642): where work items live — the integrated HU Board
239
+ // or the user's Planning Game. The v4 playbook renders per backend.
240
+ state_backend: v.optional(v.picklist(
241
+ // KJC-TSK-0709 caught the drift: "external" shipped in v4.5.0 (any
242
+ // board via the agent's own MCP/tools) but this picklist never learned it.
243
+ ["hu-board", "planning-game", "external"],
244
+ "state_backend must be \"hu-board\", \"planning-game\" or \"external\""
245
+ )),
246
+ review_rules: v.optional(v.string()),
247
+ coder_rules: v.optional(v.string()),
248
+ base_branch: v.optional(v.string()),
249
+ coder_options: CoderOptions,
250
+ reviewer_options: ReviewerOptions,
251
+ development: Development,
252
+ git: Git,
253
+ hu_board: HuBoard,
254
+ budget: Budget,
255
+ language: v.optional(v.string()),
256
+ hu_language: v.optional(v.string()),
257
+ policies: v.optional(v.record(v.string(), v.unknown())),
258
+ telemetry: v.optional(v.boolean()),
259
+ skills: Skills,
260
+ model_routing: ModelRouting,
261
+ });
262
+
263
+ /**
264
+ * Parse a raw merged config against the schema. Returns the normalized
265
+ * config or throws a Valibot ValiError. Call sites should wrap this in a
266
+ * try/catch that re-throws with a friendlier prefix.
267
+ *
268
+ * @param {unknown} input
269
+ * @returns {KarajanConfig}
270
+ */
271
+ export function parseConfig(input) {
272
+ return v.parse(ConfigSchema, input);
273
+ }
274
+
275
+ /**
276
+ * Safe variant — returns { success, data } | { success: false, issues } so
277
+ * callers can decide how to report errors without catching ValiError.
278
+ * @param {unknown} input
279
+ */
280
+ export function safeParseConfig(input) {
281
+ const result = v.safeParse(ConfigSchema, input);
282
+ if (result.success) return { success: true, data: result.output };
283
+ return {
284
+ success: false,
285
+ issues: result.issues.map(i => ({
286
+ path: (i.path || []).map(p => p.key).join("."),
287
+ message: i.message,
288
+ })),
289
+ };
290
+ }
291
+
292
+ /**
293
+ * Aggregate Valibot issues into a single readable string. Used by loadConfig
294
+ * to produce actionable error messages instead of the default ValiError dump.
295
+ * @param {Array<{path: string, message: string}>} issues
296
+ */
297
+ export function formatConfigIssues(issues) {
298
+ return issues.map(i => (i.path ? `${i.path}: ${i.message}` : i.message)).join("\n");
299
+ }