@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,180 @@
1
+ /**
2
+ * Journal parser — reads `.reviews/<sessionId>/` artefacts produced by Karajan's
3
+ * session journal writers (decisions.md, iterations.md, summary.md, tree.txt)
4
+ * and exposes them as structured JSON for the /pipeline dashboard.
5
+ *
6
+ * Scope: zero new runtime dependencies. Uses only node:fs + node:path.
7
+ * Poll-friendly: every call re-reads from disk so the UI can refresh by
8
+ * hitting the API endpoint on an interval (1-2 s). No WebSocket, no SSE —
9
+ * keeps the surface minimal (TSK-0325).
10
+ *
11
+ * Input layout (as written by src/session/journal/*):
12
+ *
13
+ * <projectDir>/.reviews/<sessionId>/
14
+ * ├─ decisions.md — Brain / Solomon / standby / checkpoint decisions
15
+ * ├─ iterations.md — per-iteration coder/reviewer/sonar/solomon detail
16
+ * ├─ summary.md — executive view: stages, budget, links
17
+ * └─ tree.txt — directory-grouped file status
18
+ */
19
+
20
+ import fs from "node:fs";
21
+ import path from "node:path";
22
+
23
+ /**
24
+ * Resolve the directory where session journals live. Honours KJ_PROJECT_DIR
25
+ * for the common "hu-board runs in a different process from the pipeline"
26
+ * case; otherwise falls back to the current working directory.
27
+ */
28
+ function getProjectDir() {
29
+ return process.env.KJ_PROJECT_DIR || process.cwd();
30
+ }
31
+
32
+ /**
33
+ * Resolve the `.reviews/` directory under the active project dir.
34
+ */
35
+ function getReviewsDir() {
36
+ return path.join(getProjectDir(), ".reviews");
37
+ }
38
+
39
+ /**
40
+ * List available sessions. Each entry points at a `.reviews/<sessionId>/`
41
+ * directory that has been produced by Karajan's journal writers.
42
+ *
43
+ * @returns {Array<{id: string, journalPath: string, mtimeMs: number}>}
44
+ */
45
+ export function listSessions() {
46
+ const reviewsDir = getReviewsDir();
47
+ if (!fs.existsSync(reviewsDir)) return [];
48
+ const entries = fs.readdirSync(reviewsDir, { withFileTypes: true });
49
+ const sessions = [];
50
+ for (const entry of entries) {
51
+ if (!entry.isDirectory()) continue;
52
+ const dir = path.join(reviewsDir, entry.name);
53
+ let mtimeMs = 0;
54
+ try {
55
+ mtimeMs = fs.statSync(dir).mtimeMs;
56
+ } catch { /* stat failed — skip this entry */ }
57
+ sessions.push({ id: entry.name, journalPath: dir, mtimeMs });
58
+ }
59
+ // Most recent first — what the UI wants by default.
60
+ sessions.sort((a, b) => b.mtimeMs - a.mtimeMs);
61
+ return sessions;
62
+ }
63
+
64
+ /**
65
+ * Split a markdown doc into `## Heading` blocks. Keeps the heading name
66
+ * and the body under it.
67
+ * @param {string} md
68
+ * @returns {Array<{heading: string, body: string}>}
69
+ */
70
+ export function splitByHeading(md, level = 2) {
71
+ if (!md) return [];
72
+ const prefix = "#".repeat(level) + " ";
73
+ const lines = md.split(/\r?\n/);
74
+ const blocks = [];
75
+ let current = null;
76
+ for (const line of lines) {
77
+ if (line.startsWith(prefix) && !line.startsWith(prefix + "# ")) {
78
+ if (current) blocks.push(current);
79
+ current = { heading: line.slice(prefix.length).trim(), body: "" };
80
+ } else if (current) {
81
+ current.body += (current.body ? "\n" : "") + line;
82
+ }
83
+ }
84
+ if (current) blocks.push(current);
85
+ return blocks;
86
+ }
87
+
88
+ /**
89
+ * Parse iterations.md into a list of iteration objects. The journal writer
90
+ * (src/session/journal/iteration-logger.js) produces `## Iteration N`
91
+ * sections, each with `### Stage` sub-blocks (Coder, Reviewer, Sonar, …).
92
+ *
93
+ * @param {string} md
94
+ * @returns {Array<{number: number, title: string, duration?: string, stages: Array<{name: string, body: string}>}>}
95
+ */
96
+ export function parseIterations(md) {
97
+ return splitByHeading(md, 2).map((block) => {
98
+ const match = block.heading.match(/Iteration\s+(\d+)/i);
99
+ const number = match ? Number(match[1]) : 0;
100
+ const durationMatch = block.body.match(/\*\*Duration\*\*:\s*([^\n]+)/i);
101
+ const duration = durationMatch ? durationMatch[1].trim() : null;
102
+ const stages = splitByHeading(block.body, 3).map((s) => ({
103
+ name: s.heading,
104
+ body: s.body.trim(),
105
+ }));
106
+ return { number, title: block.heading, duration, stages };
107
+ });
108
+ }
109
+
110
+ /**
111
+ * Parse decisions.md into structured records. The writer uses
112
+ * `## <timestamp> — <decisionType>` headings; fall back to a flat list
113
+ * when the format drifts.
114
+ *
115
+ * @param {string} md
116
+ * @returns {Array<{heading: string, body: string}>}
117
+ */
118
+ export function parseDecisions(md) {
119
+ return splitByHeading(md, 2).map((b) => ({
120
+ heading: b.heading,
121
+ body: b.body.trim(),
122
+ }));
123
+ }
124
+
125
+ /**
126
+ * Parse summary.md into { preamble, sections }. The preamble is anything
127
+ * before the first `## Heading`.
128
+ *
129
+ * @param {string} md
130
+ * @returns {{preamble: string, sections: Array<{heading: string, body: string}>}}
131
+ */
132
+ export function parseSummary(md) {
133
+ const sections = splitByHeading(md, 2);
134
+ const firstHeadingIdx = md.indexOf("## ");
135
+ const preamble = firstHeadingIdx === -1 ? md.trim() : md.slice(0, firstHeadingIdx).trim();
136
+ return { preamble, sections };
137
+ }
138
+
139
+ /**
140
+ * Read a file defensively — returns null when missing, the content when
141
+ * present. Lets `readJournal` return a consistent shape even when the
142
+ * pipeline wrote some artefacts but not others.
143
+ */
144
+ function readIfExists(file) {
145
+ try {
146
+ return fs.readFileSync(file, "utf8");
147
+ } catch {
148
+ return null;
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Read + parse the four journal artefacts for a given session. Always
154
+ * returns an object with the same keys so the client has predictable
155
+ * defaults.
156
+ *
157
+ * @param {string} sessionId
158
+ */
159
+ export function readJournal(sessionId) {
160
+ const dir = path.join(getReviewsDir(), sessionId);
161
+ const iterationsRaw = readIfExists(path.join(dir, "iterations.md"));
162
+ const decisionsRaw = readIfExists(path.join(dir, "decisions.md"));
163
+ const summaryRaw = readIfExists(path.join(dir, "summary.md"));
164
+ const treeRaw = readIfExists(path.join(dir, "tree.txt"));
165
+ return {
166
+ id: sessionId,
167
+ journalPath: dir,
168
+ exists: fs.existsSync(dir),
169
+ iterations: iterationsRaw ? parseIterations(iterationsRaw) : [],
170
+ decisions: decisionsRaw ? parseDecisions(decisionsRaw) : [],
171
+ summary: summaryRaw ? parseSummary(summaryRaw) : { preamble: "", sections: [] },
172
+ tree: treeRaw || "",
173
+ raw: {
174
+ iterations: iterationsRaw,
175
+ decisions: decisionsRaw,
176
+ summary: summaryRaw,
177
+ tree: treeRaw,
178
+ },
179
+ };
180
+ }
@@ -0,0 +1,424 @@
1
+ /**
2
+ * Server-side mutations for plan HUs triggered from the board UI.
3
+ *
4
+ * The source of truth is the plan JSON at `~/.kj/plans/<slug>/<planId>.json`.
5
+ * Every mutation in this module follows the same protocol:
6
+ *
7
+ * 1. Load the plan JSON from disk.
8
+ * 2. Mutate it in-memory using the helpers from `src/plan/plan-hu-ops.js`
9
+ * (so CLI `kj plan ready` and board "Mark ready" stay in lock-step).
10
+ * 3. Write the JSON back.
11
+ * 4. Re-sync the plan into SQLite via `syncPlanFile` so the board
12
+ * reflects the change on the next render without a manual refresh.
13
+ *
14
+ * Steps 2-4 must share a single on-disk write: we never ack the mutation to
15
+ * the UI without persisting it, otherwise a reload would silently revert.
16
+ */
17
+ import { readFileSync, existsSync, readdirSync, mkdirSync, openSync } from 'node:fs';
18
+ import { join, dirname } from 'node:path';
19
+ import { writeJsonAtomicSync } from 'karajan-core/atomic-write';
20
+ import { spawn } from 'node:child_process';
21
+ import { trackRun, untrack } from './run-tracker.js';
22
+ import { getHuBoardRunsDir } from './db.js';
23
+ import { fileURLToPath } from 'node:url';
24
+ import { updateHuStatus, certifyAllHus, updateHu } from 'karajan-core/plan-hu-ops';
25
+ import { validateBlockedByChange } from 'karajan-core/plan-validation';
26
+ import { syncPlanFile } from './sync.js';
27
+ import { getDb } from './db.js';
28
+
29
+ // Repo root → so we can spawn `node <repo>/src/cli.js run ...` without
30
+ // depending on `kj` being in $PATH of the board process.
31
+ const REPO_ROOT = (() => {
32
+ const here = dirname(fileURLToPath(import.meta.url)); // packages/hu-board/src
33
+ return join(here, '..', '..', '..'); // → repo root
34
+ })();
35
+ const KJ_CLI = join(REPO_ROOT, 'src', 'cli.js');
36
+
37
+ /**
38
+ * Root directory where `kj plan` writes `<slug>/<planId>.json`. Honours
39
+ * `KJ_PLANS_DIR` (tests) then `KJ_HOME/plans` (power users) then
40
+ * `~/.kj/plans/` (default) — same precedence as `sync.js::fullScan`.
41
+ */
42
+ // KJC-BUG-0059: write operations go to the canonical plans dir.
43
+ // Read operations should iterate getHuBoardPlansDirs() instead, to
44
+ // pick up plans the user has under the legacy ~/.kj/plans/ tree
45
+ // while the auto-migrator has not yet fired.
46
+ import { getHuBoardPlansDir } from './db.js';
47
+ function plansRoot() {
48
+ return getHuBoardPlansDir();
49
+ }
50
+
51
+ /**
52
+ * Locate `<planId>.json` on disk. If `projectId` is known we go straight
53
+ * to that subdir; otherwise we fall back to scanning every project dir
54
+ * (costly, only used when the HU row pre-dates the `plan_id` migration
55
+ * and we have nothing else to go on).
56
+ *
57
+ * @param {string} planId
58
+ * @param {string|undefined} projectId
59
+ * @returns {string|null}
60
+ */
61
+ function findPlanFilePath(planId, projectId) {
62
+ const root = plansRoot();
63
+ if (!existsSync(root)) return null;
64
+ const fileName = `${planId}.json`;
65
+ if (projectId) {
66
+ const direct = join(root, projectId, fileName);
67
+ if (existsSync(direct)) return direct;
68
+ }
69
+ // Fall back to a scan so legacy rows without plan_id still work.
70
+ let dirs;
71
+ try { dirs = readdirSync(root); } catch { return null; }
72
+ for (const d of dirs) {
73
+ const candidate = join(root, d, fileName);
74
+ if (existsSync(candidate)) return candidate;
75
+ }
76
+ return null;
77
+ }
78
+
79
+ /**
80
+ * Load + validate a plan file in one step. Callers pass the on-disk path
81
+ * returned by `findPlanFilePath`.
82
+ * @param {string} filePath
83
+ */
84
+ function readPlan(filePath) {
85
+ const raw = readFileSync(filePath, 'utf-8');
86
+ return JSON.parse(raw);
87
+ }
88
+
89
+ function writePlan(filePath, plan) {
90
+ writeJsonAtomicSync(filePath, plan);
91
+ }
92
+
93
+ /**
94
+ * Change a single HU's status inside its plan JSON and re-sync into
95
+ * SQLite. Returns the new status, so callers can 200-reply with the
96
+ * committed value (not a shadow copy that might differ from disk).
97
+ *
98
+ * Valid statuses mirror the CLI: pending, certified, done. Anything else
99
+ * is rejected up the stack — we don't silently coerce.
100
+ *
101
+ * @param {object} args
102
+ * @param {string} args.planId
103
+ * @param {string} args.huId
104
+ * @param {string} args.status - new status
105
+ * @param {string} [args.projectId] - optional hint to avoid the scan
106
+ * @returns {{ ok: true, status: string, planStatus: string } | { ok: false, error: string }}
107
+ */
108
+ export function setHuStatus({ planId, huId, status, projectId }) {
109
+ const filePath = findPlanFilePath(planId, projectId);
110
+ if (!filePath) return { ok: false, error: `plan not found: ${planId}` };
111
+
112
+ const plan = readPlan(filePath);
113
+ if (!Array.isArray(plan.hus)) return { ok: false, error: 'plan has no hus[]' };
114
+
115
+ const existed = updateHuStatus(plan, huId, status);
116
+ if (!existed) return { ok: false, error: `hu not found: ${huId}` };
117
+
118
+ // When every remaining HU is certified, auto-advance the plan status
119
+ // to "ready" so the CLI hint `kj run --plan <id>` matches reality.
120
+ // Conversely, downgrading the last certified HU back to pending must
121
+ // roll the plan back to "draft" — otherwise `kj run` would refuse to
122
+ // reload a supposedly-ready plan whose HUs disagree.
123
+ if (plan.hus.every((h) => h.status === 'certified' || h.status === 'done')) {
124
+ plan.status = 'ready';
125
+ } else if (plan.hus.some((h) => h.status === 'pending')) {
126
+ plan.status = 'draft';
127
+ }
128
+
129
+ writePlan(filePath, plan);
130
+ syncPlanFile(filePath);
131
+ return { ok: true, status, planStatus: plan.status };
132
+ }
133
+
134
+ /**
135
+ * Edit one HU's user-facing fields in place. Whitelist mirrors
136
+ * `updateHu` in plan-hu-ops so the CLI and the board stay in lock-step:
137
+ * title, task_type, scope, acceptance_criteria, acceptance_tests,
138
+ * blocked_by. Unknown keys are silently ignored.
139
+ *
140
+ * The caller passes a `patch` object with whichever subset of fields
141
+ * the user actually changed — pushing the whole HU every time would
142
+ * make optimistic concurrency harder to add later.
143
+ *
144
+ * @param {object} args
145
+ * @param {string} args.planId
146
+ * @param {string} args.huId
147
+ * @param {object} args.patch
148
+ * @param {string} [args.projectId]
149
+ * @returns {{ ok: true, hu: object } | { ok: false, error: string }}
150
+ */
151
+ export function setHuFields({ planId, huId, patch, projectId }) {
152
+ const filePath = findPlanFilePath(planId, projectId);
153
+ if (!filePath) return { ok: false, error: `plan not found: ${planId}` };
154
+
155
+ const plan = readPlan(filePath);
156
+ if (!Array.isArray(plan.hus)) return { ok: false, error: 'plan has no hus[]' };
157
+
158
+ // KJC-TSK-0401: si el patch toca blocked_by, validar antes de
159
+ // persistir. Rechazar refs huérfanas, auto-deps y ciclos.
160
+ if (patch && Object.prototype.hasOwnProperty.call(patch, 'blocked_by')) {
161
+ const result = validateBlockedByChange(plan, huId, patch.blocked_by);
162
+ if (!result.ok) {
163
+ return { ok: false, error: result.error, code: 'INVALID_BLOCKED_BY', cycle: result.cycle };
164
+ }
165
+ }
166
+
167
+ const updated = updateHu(plan, huId, patch || {});
168
+ if (!updated) return { ok: false, error: `hu not found: ${huId}` };
169
+
170
+ writePlan(filePath, plan);
171
+ syncPlanFile(filePath);
172
+ return { ok: true, hu: updated };
173
+ }
174
+
175
+ /**
176
+ * KJC-TSK-0404: marcar una HU como fallida con blocker y timestamp
177
+ * (sin tocar status). Usado por el zombie-reaper al detectar timeout
178
+ * para que el siguiente `kj run` sepa el motivo del último fallo.
179
+ *
180
+ * @param {object} args
181
+ * @param {string} args.planId
182
+ * @param {string} args.huId
183
+ * @param {string} args.blocker - razón del fallo
184
+ * @param {string} [args.projectId]
185
+ * @returns {{ ok: boolean, hu?: object, error?: string }}
186
+ */
187
+ export function setHuFailResult({ planId, huId, blocker, projectId }) {
188
+ const filePath = findPlanFilePath(planId, projectId);
189
+ if (!filePath) return { ok: false, error: `plan not found: ${planId}` };
190
+ const plan = readPlan(filePath);
191
+ if (!Array.isArray(plan.hus)) return { ok: false, error: 'plan has no hus[]' };
192
+ const hu = plan.hus.find(h => h.id === huId);
193
+ if (!hu) return { ok: false, error: `hu not found: ${huId}` };
194
+
195
+ hu.result = 'fail';
196
+ hu.updatedAt = new Date().toISOString();
197
+ const existing = (hu.outcome && Array.isArray(hu.outcome.blockers)) ? hu.outcome.blockers : [];
198
+ hu.outcome = {
199
+ ...(hu.outcome || {}),
200
+ blockers: blocker ? [...existing, blocker] : existing,
201
+ failedAt: new Date().toISOString(),
202
+ };
203
+ writePlan(filePath, plan);
204
+ syncPlanFile(filePath);
205
+ return { ok: true, hu };
206
+ }
207
+
208
+ /**
209
+ * KJC-TSK-0408 step 2/3: deshace los cambios de una HU restaurando
210
+ * el snapshot git tomado pre-run. Marca status=pending, result=null
211
+ * y outcome.reverted=true para que la UI muestre "deshecha".
212
+ *
213
+ * @param {object} args
214
+ * @param {string} args.planId
215
+ * @param {string} args.huId
216
+ * @param {string} [args.projectId]
217
+ * @returns {Promise<{ ok: boolean, sha?: string, error?: string }>}
218
+ */
219
+ export async function revertHuFromSnapshot({ planId, huId, projectId }) {
220
+ const filePath = findPlanFilePath(planId, projectId);
221
+ if (!filePath) return { ok: false, error: `plan not found: ${planId}` };
222
+ const plan = readPlan(filePath);
223
+ const hu = plan.hus?.find(h => h.id === huId);
224
+ if (!hu) return { ok: false, error: `hu not found: ${huId}` };
225
+ const snapshotSha = hu.outcome?.snapshot_sha;
226
+ if (!snapshotSha) {
227
+ return { ok: false, error: 'esta HU no tiene snapshot — solo HUs ejecutadas con KJC-TSK-0408 pueden deshacerse' };
228
+ }
229
+ const projectDir = plan.projectDir;
230
+ if (!projectDir) return { ok: false, error: 'plan sin projectDir' };
231
+
232
+ const { restoreHuSnapshot } = await import('karajan-core/hu-snapshot');
233
+ const restore = await restoreHuSnapshot({ projectDir, huId });
234
+ if (!restore.ok) return { ok: false, error: `restore falló: ${restore.error}` };
235
+
236
+ hu.status = 'pending';
237
+ hu.result = null;
238
+ hu.outcome = { ...(hu.outcome || {}), reverted: true, revertedAt: new Date().toISOString() };
239
+ hu.updatedAt = new Date().toISOString();
240
+ writePlan(filePath, plan);
241
+ syncPlanFile(filePath);
242
+ return { ok: true, sha: restore.sha };
243
+ }
244
+
245
+ /**
246
+ * Bulk certify: equivalent to `kj plan ready <planId>` over HTTP. Flips
247
+ * every pending HU to certified and sets `plan.status = "ready"`. No-op
248
+ * on already-ready plans (idempotent, returns count=0).
249
+ *
250
+ * @param {object} args
251
+ * @param {string} args.planId
252
+ * @param {string} [args.projectId]
253
+ * @returns {{ ok: true, count: number, planStatus: string } | { ok: false, error: string }}
254
+ */
255
+ /**
256
+ * PR-G: rename a project from the board.
257
+ *
258
+ * "Project" on the board is just a row in the projects table that
259
+ * groups stories. The source of truth for the human-readable name
260
+ * is `plan.name` inside each plan JSON for that project (PR-B made
261
+ * sync.js prefer it over the cwd basename). So a rename has to:
262
+ *
263
+ * 1. Update plan.name on every plan JSON belonging to this project,
264
+ * so the next sync doesn't clobber the rename back to the
265
+ * basename derivation.
266
+ * 2. Update the projects.name column directly so the UI updates
267
+ * without waiting for a full re-scan.
268
+ *
269
+ * Validates name length (1–120) and rejects empty/whitespace-only.
270
+ *
271
+ * @returns {{ ok: true, name, plansUpdated } | { ok: false, error }}
272
+ */
273
+ export function renameProject({ projectId, name }) {
274
+ const trimmed = String(name || '').trim();
275
+ if (!trimmed) return { ok: false, error: 'El nombre no puede estar vacío.' };
276
+ if (trimmed.length > 120) return { ok: false, error: 'El nombre no puede superar los 120 caracteres.' };
277
+
278
+ const root = plansRoot();
279
+ let plansUpdated = 0;
280
+ if (existsSync(root)) {
281
+ const projDir = join(root, projectId);
282
+ if (existsSync(projDir)) {
283
+ let files;
284
+ try { files = readdirSync(projDir); } catch { files = []; }
285
+ for (const f of files) {
286
+ if (!f.endsWith('.json')) continue;
287
+ const filePath = join(projDir, f);
288
+ try {
289
+ const plan = readPlan(filePath);
290
+ plan.name = trimmed;
291
+ plan.updatedAt = new Date().toISOString();
292
+ writePlan(filePath, plan);
293
+ plansUpdated += 1;
294
+ } catch { /* skip unreadable plan */ }
295
+ }
296
+ }
297
+ }
298
+
299
+ // Update the project row directly so the UI reflects the new name
300
+ // without waiting for the chokidar watcher to fire on the plan
301
+ // edits above.
302
+ try {
303
+ const stmt = getDb().prepare('UPDATE projects SET name = ? WHERE id = ?');
304
+ const res = stmt.run(trimmed, projectId);
305
+ if (res.changes === 0 && plansUpdated === 0) {
306
+ return { ok: false, error: `Proyecto no encontrado: ${projectId}` };
307
+ }
308
+ } catch (err) {
309
+ return { ok: false, error: `Error actualizando la base de datos: ${err.message}` };
310
+ }
311
+ return { ok: true, name: trimmed, plansUpdated };
312
+ }
313
+
314
+ export function markPlanReady({ planId, projectId }) {
315
+ const filePath = findPlanFilePath(planId, projectId);
316
+ if (!filePath) return { ok: false, error: `plan not found: ${planId}` };
317
+
318
+ const plan = readPlan(filePath);
319
+ if (!Array.isArray(plan.hus) || plan.hus.length === 0) {
320
+ return { ok: false, error: 'plan has no hus[]' };
321
+ }
322
+
323
+ const count = certifyAllHus(plan);
324
+ writePlan(filePath, plan);
325
+ syncPlanFile(filePath);
326
+ return { ok: true, count, planStatus: plan.status };
327
+ }
328
+
329
+ /**
330
+ * Launch `kj run --plan <planId>` as a detached child. This is the
331
+ * "Run plan" button's server-side handler: the board no longer asks the
332
+ * user to drop to the terminal to kick off execution.
333
+ *
334
+ * We spawn against the repo's own CLI (`node <repo>/src/cli.js`) instead
335
+ * of a `kj` binary on $PATH — the board is always shipped with the repo
336
+ * that hosts it, so that entry point is guaranteed to exist. Tests set
337
+ * `KJ_RUN_SPAWN_MODE=echo` to replace the spawn with a no-op that just
338
+ * returns a fake pid, so we can assert payload shape without booting the
339
+ * whole orchestrator inside vitest.
340
+ *
341
+ * Output is redirected to `~/.karajan/hu-board-runs/<planId>.log` so the
342
+ * board can tail it later if we wire up a live log viewer — for now it's
343
+ * just a durable trail. stdin is `ignore` (coder subprocess requirement
344
+ * documented in CLAUDE.md).
345
+ *
346
+ * @param {object} args
347
+ * @param {string} args.planId
348
+ * @param {string} [args.projectId]
349
+ * @param {string} [args.taskOverride] - optional custom task string
350
+ * @returns {{ ok: true, pid: number, logPath: string } | { ok: false, error: string }}
351
+ */
352
+ export function runPlan({ planId, projectId, taskOverride, huIds = null } = {}) {
353
+ const filePath = findPlanFilePath(planId, projectId);
354
+ if (!filePath) return { ok: false, error: `plan not found: ${planId}` };
355
+
356
+ const plan = readPlan(filePath);
357
+ if (!Array.isArray(plan.hus) || plan.hus.length === 0) {
358
+ return { ok: false, error: 'plan has no hus[]' };
359
+ }
360
+ const projectDir = plan.projectDir;
361
+ if (!projectDir) {
362
+ return {
363
+ ok: false,
364
+ error:
365
+ 'plan has no projectDir stamped — re-run `kj plan` to regenerate it, '
366
+ + 'then try again (plans pre-v2.7.4 did not persist the dir).',
367
+ };
368
+ }
369
+
370
+ // PR4: when huIds is set, validate they exist before spawning so
371
+ // we surface "unknown HU" as an HTTP 400 instead of letting the
372
+ // CLI fail mid-startup with a less obvious message.
373
+ let huFlag = null;
374
+ if (Array.isArray(huIds) && huIds.length > 0) {
375
+ const validIds = new Set(plan.hus.map((h) => h.id));
376
+ const unknown = huIds.filter((id) => !validIds.has(id));
377
+ if (unknown.length > 0) {
378
+ return { ok: false, error: `unknown HU id(s) in plan ${planId}: ${unknown.join(', ')}` };
379
+ }
380
+ huFlag = huIds.join(',');
381
+ }
382
+
383
+ const runsDir = getHuBoardRunsDir(); // KJC-TSK-0421 — centralised in db.js
384
+ mkdirSync(runsDir, { recursive: true });
385
+ // PR4: when running a single HU, suffix the log with the HU id so
386
+ // a subsequent full-plan run doesn't overwrite the per-HU log.
387
+ const logName = huFlag
388
+ ? `${planId}--hu-${huIds.join('-').replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 60)}.log`
389
+ : `${planId}.log`;
390
+ const logPath = join(runsDir, logName);
391
+
392
+ const task = taskOverride || plan.task || `run plan ${planId}`;
393
+ const args = [KJ_CLI, 'run', '--plan', planId];
394
+ if (huFlag) args.push('--hu', huFlag);
395
+ args.push(task);
396
+
397
+ // Escape hatch for tests — hard-kill the spawn so vitest doesn't boot
398
+ // the full orchestrator. The shape of the response is what we assert.
399
+ if (process.env.KJ_RUN_SPAWN_MODE === 'echo') {
400
+ return { ok: true, pid: 0, logPath, argv: [process.execPath, ...args], projectDir, huIds: huIds || null };
401
+ }
402
+
403
+ const out = openSync(logPath, 'a');
404
+ const err = openSync(logPath, 'a');
405
+ const child = spawn(process.execPath, args, {
406
+ cwd: projectDir,
407
+ detached: true,
408
+ stdio: ['ignore', out, err],
409
+ // Strip CLAUDECODE so the Claude agent subprocess trick in
410
+ // claude-agent.js doesn't misbehave (documented in CLAUDE.md).
411
+ env: { ...process.env, CLAUDECODE: undefined },
412
+ });
413
+ child.unref();
414
+ // KJC-TSK-0396: registrar el PID para que el botón ⏹ Stop del board
415
+ // sepa qué procesos matar. trackRun usa Set, así que llamadas
416
+ // duplicadas no inflan el registro. Cuando el proceso muera, el
417
+ // event 'exit' lo des-registra (defensa adicional: reaper periódico
418
+ // que limpia PIDs muertos cada 30s).
419
+ if (child.pid) {
420
+ trackRun(planId, { pid: child.pid, huIds: huIds || null });
421
+ child.on('exit', () => { untrack(planId, child.pid); });
422
+ }
423
+ return { ok: true, pid: child.pid ?? 0, logPath };
424
+ }