@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,427 @@
1
+ /**
2
+ * HU sub-pipeline driver — extracted from flow-runner.js in PR-L
3
+ * (audit follow-up). Was 244 LOC inlined inside _runFlowInner, the
4
+ * second-worst long-function per `kj audit`.
5
+ *
6
+ * Behaviour identical to the inlined version — the closure passed to
7
+ * runHuSubPipeline is moved verbatim, only its surrounding context
8
+ * (logger, emitter, ctx, askQuestion) becomes function parameters.
9
+ *
10
+ * The extraction also fixes a latent bug: flow-runner.js called
11
+ * `runCoderStage`, `runSonarStage`, `generateDiff` and
12
+ * `runSecurityStage` without static OR dynamic imports. Those
13
+ * references would ReferenceError at runtime on the HU sub-pipeline
14
+ * path; the fact that no test caught it suggests the path was rarely
15
+ * exercised end-to-end (preflight failures often abort earlier).
16
+ * This module imports them explicitly.
17
+ *
18
+ * Returns:
19
+ * - { handled: true, result } — needsSubPipeline matched; caller
20
+ * must short-circuit and return `result`.
21
+ * - { handled: false } — caller falls through to the standard
22
+ * single-task pipeline (runIterationLoop).
23
+ */
24
+
25
+ import { emitProgress, makeEvent } from "../../utils/events.js";
26
+ import { needsSubPipeline, runHuSubPipeline } from "../hu-sub-pipeline.js";
27
+ import { runCoderStage } from "../stages/coder-stage.js";
28
+ import { runSonarStage } from "../stages/sonar-stage.js";
29
+ import { runIterationLoop } from "./iteration-loop.js";
30
+ import { classifyFailure, createFailureTracker } from "../repair/repair-detector.js";
31
+ import { runRepair } from "../repair/repair-runner.js";
32
+ import { writeHistoryRecord } from "./post-loop.js";
33
+ import { setReviewerFeedback } from "../../session/mutators.js";
34
+ import { createHuSnapshot } from "../../git/hu-snapshot.js";
35
+
36
+ /**
37
+ * @param {object} args
38
+ * @param {object} args.ctx — PipelineContext from initFlowContext
39
+ * @param {string} args.task — original task description (for writeHistoryRecord)
40
+ * @param {object} args.askQuestion
41
+ * @param {object} args.emitter
42
+ * @param {object} args.logger
43
+ * @returns {Promise<{handled: boolean, result?: object}>}
44
+ */
45
+ export async function runHuBatch({ ctx, task, askQuestion, emitter, logger }) {
46
+ if (!needsSubPipeline(ctx.stageResults.huReviewer)) {
47
+ return { handled: false };
48
+ }
49
+
50
+ // --- HU Sub-Pipeline: run each certified HU as an independent iteration loop ---
51
+ logger.info(`HU sub-pipeline: ${ctx.stageResults.huReviewer.certified} certified stories — running each as a sub-pipeline`);
52
+ emitProgress(emitter, makeEvent("hu:sub-pipeline:start", { ...ctx.eventBase, stage: "hu-sub-pipeline" }, {
53
+ message: `Running ${ctx.stageResults.huReviewer.certified} HUs as sub-pipelines`,
54
+ detail: { total: ctx.stageResults.huReviewer.total, certified: ctx.stageResults.huReviewer.certified }
55
+ }));
56
+
57
+ // Per-HU pipeline: focused max_iterations, fresh Brain state, own git branch.
58
+ const huMaxIterations = ctx.config.hu_max_iterations ?? 3;
59
+ const huBranches = new Map();
60
+ const { prepareHuBranch, finalizeHuCommit } = await import("../../git/hu-automation.js");
61
+
62
+ // PR F (v2.7.5): the per-HU loop needs three pieces of plan context:
63
+ // • ADRs (loaded once from disk, applies to every HU on this plan)
64
+ // • the plan-reviewer findings (rides on the huBatch)
65
+ // • each HU's spec_section (rides on the story)
66
+ // Loading ADRs once outside the loop avoids hitting the filesystem
67
+ // per-HU. When the plan never went through `kj plan` (legacy
68
+ // auto-generated batch), planId is null → loadActiveAdrs falls back
69
+ // to the "_loose" bucket and returns [] silently.
70
+ const planId = ctx.stageResults.huReviewer.planId || null;
71
+ const reviewerFindings = ctx.stageResults.huReviewer.review || null;
72
+ let planAdrs = [];
73
+ try {
74
+ const { loadActiveAdrs } = await import("../../plan/adr-loader.js");
75
+ planAdrs = await loadActiveAdrs(ctx.config.projectDir || process.cwd(), planId);
76
+ if (planAdrs.length > 0) {
77
+ logger.info(`Plan ${planId || "(loose)"}: loaded ${planAdrs.length} ADR(s) for coder context`);
78
+ }
79
+ } catch (err) {
80
+ logger.warn(`ADR loader failed (non-blocking): ${err.message}`);
81
+ }
82
+
83
+ const subPipelineResult = await runHuSubPipeline({
84
+ huReviewerResult: ctx.stageResults.huReviewer,
85
+ runIterationFn: async (huTask, story, laneOpts = {}) => {
86
+ // Apply per-HU policies based on task_type (infra skips reviewer/sonar/tdd).
87
+ // KJC-TSK-0400: `effectiveTaskType` mira primero story.task_type y, si
88
+ // no es válido, infiere del prefijo del title ([SPIKE], [DOC]…). Así
89
+ // un HU con title "[SPIKE] Decidir auth" entra automáticamente a las
90
+ // policies de spike (sin Sonar/TDD/tests).
91
+ const { applyPolicies, effectiveTaskType } = await import("../../guards/policy-resolver.js");
92
+ const resolvedTaskType = effectiveTaskType(story);
93
+ if (resolvedTaskType !== story.task_type) {
94
+ logger.info(`HU ${story.id}: task_type inferido del title → ${resolvedTaskType} (era ${story.task_type || "null"})`);
95
+ }
96
+ const huPolicies = applyPolicies({ taskType: resolvedTaskType, policies: ctx.config.policies });
97
+ logger.info(`HU ${story.id} (${resolvedTaskType}): policies → reviewer=${huPolicies.reviewer}, tdd=${huPolicies.tdd}, sonar=${huPolicies.sonar}, tests=${huPolicies.testsRequired}`);
98
+
99
+ // PAR-E2 PR2 (KJC-TSK-0629): every per-HU adjustment lands on LANE
100
+ // copies. ctx.config/pipelineFlags/session/brainCtx are shared by all
101
+ // lanes, so concurrent lanes must never mutate them. This also fixes a
102
+ // latent leak: max_iterations was mutated on ctx.config and only the
103
+ // fallback path restored it — the acceptance path returned early and
104
+ // left it clamped to hu_max_iterations for the rest of the run.
105
+ const worktreePath = laneOpts?.worktreePath || null;
106
+ const projectDir = worktreePath || ctx.config.projectDir || process.cwd();
107
+ // PAR-H (KJC-TSK-0631): the lane's slot travels as env vars so any
108
+ // service the coder or the acceptance tests start can offset its
109
+ // ports (offset = slot × 100; the project applies its own base).
110
+ const laneEnv = Number.isInteger(laneOpts?.laneSlot)
111
+ ? { KJ_LANE_SLOT: String(laneOpts.laneSlot), KJ_PORT_OFFSET: String(laneOpts.laneSlot * 100) }
112
+ : null;
113
+ const laneConfig = { ...ctx.config, projectDir, max_iterations: huMaxIterations, lane_env: laneEnv };
114
+ if (!huPolicies.tdd) laneConfig.development = { ...laneConfig.development, methodology: "standard", require_test_changes: false };
115
+ if (!huPolicies.sonar) laneConfig.sonarqube = { ...laneConfig.sonarqube, enabled: false };
116
+ const laneFlags = { ...ctx.pipelineFlags };
117
+ if (!huPolicies.reviewer) laneFlags.reviewerEnabled = false;
118
+ if (!huPolicies.testsRequired) laneFlags.testerEnabled = false;
119
+ // Worktree lanes clone the session so reviewer feedback never leaks
120
+ // across concurrent HUs. Disk saves still share the session id (last
121
+ // writer wins) — journal noise at most; in-memory isolation is what
122
+ // guards correctness.
123
+ const laneSession = worktreePath ? { ...ctx.session } : ctx.session;
124
+ let laneBrain = ctx.brainCtx;
125
+ if (ctx.brainCtx?.enabled) {
126
+ const { createBrainContext } = await import("../brain-coordinator.js");
127
+ const fresh = createBrainContext({ enabled: true });
128
+ if (worktreePath) {
129
+ laneBrain = { ...ctx.brainCtx, extensionCount: 0, feedbackQueue: fresh.feedbackQueue, verificationTracker: fresh.verificationTracker };
130
+ } else {
131
+ ctx.brainCtx.extensionCount = 0;
132
+ ctx.brainCtx.feedbackQueue = fresh.feedbackQueue;
133
+ ctx.brainCtx.verificationTracker = fresh.verificationTracker;
134
+ }
135
+ }
136
+ let lanePlannedTask = ctx.plannedTask;
137
+
138
+ // A worktree lane skips the main-tree checkout: `git worktree add -b`
139
+ // already left the worktree checked out on kj-hu-<id>.
140
+ let branchName;
141
+ if (worktreePath) {
142
+ branchName = `kj-hu-${story.id}`;
143
+ huBranches.set(story.id, branchName);
144
+ } else {
145
+ branchName = await prepareHuBranch({ story, huBranches, config: laneConfig, logger });
146
+ }
147
+
148
+ // KJC-TSK-0408 step 2: snapshot del workspace ANTES de invocar al
149
+ // coder. Si el usuario hace Undo después, restaura este SHA. El
150
+ // ref se crea sobre el HEAD ya checkouteado a la rama de la HU,
151
+ // así que apunta al estado limpio sobre el que el coder empieza.
152
+ // Idempotente — si la HU ya tenía snapshot, se sobreescribe.
153
+ try {
154
+ const snap = await createHuSnapshot({ projectDir, huId: story.id });
155
+ if (snap.ok) {
156
+ story.outcome = { ...(story.outcome || {}), snapshot_sha: snap.sha, snapshot_ref: snap.ref };
157
+ logger.info(`HU ${story.id}: snapshot ${snap.sha.slice(0, 7)} (ref ${snap.ref})`);
158
+ } else {
159
+ logger.warn(`HU ${story.id}: snapshot falló (non-blocking): ${snap.error}`);
160
+ }
161
+ } catch (err) {
162
+ logger.warn(`HU ${story.id}: snapshot threw (non-blocking): ${err.message}`);
163
+ }
164
+
165
+ // If HU has acceptance_tests, Brain runs them as the gate instead of
166
+ // the standard reviewer/tester pipeline. This is the radical fix:
167
+ // concrete executable tests replace subjective reviewer opinions.
168
+ if (story.acceptance_tests?.length > 0) {
169
+ const { runAcceptanceTests, buildDiagnosticPrompt } = await import("../../hu/acceptance-runner.js");
170
+ // Issue #538 — Repairer runtime safety net. The tracker remembers
171
+ // each test's last failure signature; classifyFailure flags
172
+ // structural bugs (broken jq, shell parse error, missing tool)
173
+ // after 2 consecutive identical failures so we don't burn 5
174
+ // iterations against an irreparable test (the 2026-04-29 bug).
175
+ const failureTracker = createFailureTracker();
176
+
177
+ for (let attempt = 1; attempt <= laneConfig.max_iterations; attempt++) {
178
+ logger.info(`HU ${story.id}: coder iteration ${attempt}/${laneConfig.max_iterations}`);
179
+ emitProgress(emitter, makeEvent("iteration:start", { ...ctx.eventBase, stage: "iteration" }, {
180
+ message: `Iteration ${attempt}/${laneConfig.max_iterations}`,
181
+ detail: { iteration: attempt, maxIterations: laneConfig.max_iterations }
182
+ }));
183
+
184
+ // Coder runs with the HU task + any diagnostic feedback from previous attempt.
185
+ // Phase 2 of tests-first (v2.7.5): also hand over the declared
186
+ // acceptance_tests so the coder has the contract from turn 1,
187
+ // not only after the first failed run.
188
+ const coderResult = await runCoderStage({
189
+ coderRoleInstance: ctx.coderRoleInstance, coderRole: ctx.coderRole,
190
+ config: laneConfig, logger, emitter, eventBase: ctx.eventBase,
191
+ session: laneSession, plannedTask: lanePlannedTask,
192
+ trackBudget: ctx.trackBudget, iteration: attempt, brainCtx: laneBrain,
193
+ acceptanceTests: story.acceptance_tests,
194
+ // PR F (v2.7.5): plan-aware coder context. ADRs are
195
+ // shared across the plan, the rest are scoped to this
196
+ // HU. buildCoderPrompt skips any section whose data is
197
+ // null/empty so legacy paths render the same prompt.
198
+ adrs: planAdrs,
199
+ specSection: story.spec_section || null,
200
+ reviewerFindings,
201
+ huId: story.id
202
+ });
203
+ if (coderResult?.action === "standby" || coderResult?.action === "pause") {
204
+ return coderResult?.result || { approved: false, reason: "coder_failed" };
205
+ }
206
+
207
+ // Sonar quality gate (sw task_type only, when policies say sonar=true)
208
+ if (huPolicies.sonar && laneConfig.sonarqube?.enabled) {
209
+ try {
210
+ const sonarResult = await runSonarStage({
211
+ config: laneConfig, logger, emitter, eventBase: ctx.eventBase,
212
+ session: laneSession, trackBudget: ctx.trackBudget, iteration: attempt,
213
+ // sonarState is required: runSonarStage reads/writes
214
+ // .issuesInitial / .issuesFinal on it. Forgetting this
215
+ // surfaces as `Cannot read properties of undefined
216
+ // (reading 'issuesInitial')` and the catch below swallows
217
+ // it as "sonar stage failed (non-blocking)" — the gate
218
+ // never actually runs for the HU, every iteration burns
219
+ // budget for nothing. Mirrors iteration-loop.js.
220
+ sonarState: ctx.sonarState,
221
+ repeatDetector: ctx.repeatDetector,
222
+ budgetSummary: ctx.budgetSummary,
223
+ askQuestion, brainCtx: laneBrain
224
+ });
225
+ if (sonarResult?.action === "continue") {
226
+ // Sonar failed — add to feedback for next coder attempt
227
+ lanePlannedTask = `${huTask}\n\n--- SONAR FAILURE ---\n${laneSession.last_reviewer_feedback}`;
228
+ continue;
229
+ }
230
+ } catch (err) {
231
+ logger.warn(`HU ${story.id}: sonar stage failed (non-blocking): ${err.message}`);
232
+ }
233
+ }
234
+
235
+ // Brain runs acceptance tests
236
+ logger.info(`HU ${story.id}: running ${story.acceptance_tests.length} acceptance tests`);
237
+ emitProgress(emitter, makeEvent("hu:acceptance-start", { ...ctx.eventBase, stage: "acceptance" }, {
238
+ message: `Running ${story.acceptance_tests.length} acceptance tests`,
239
+ detail: { huId: story.id, testCount: story.acceptance_tests.length }
240
+ }));
241
+
242
+ const testResult = await runAcceptanceTests(story.acceptance_tests, projectDir, { env: laneEnv });
243
+ emitProgress(emitter, makeEvent("hu:acceptance-end", { ...ctx.eventBase, stage: "acceptance" }, {
244
+ status: testResult.allPassed ? "ok" : "fail",
245
+ message: testResult.summary,
246
+ detail: { allPassed: testResult.allPassed, results: testResult.results.map(r => ({ cmd: r.cmd, passed: r.passed })) }
247
+ }));
248
+
249
+ if (testResult.allPassed) {
250
+ // Phase 3 of tests-first (v2.7.5): the shell gate is green,
251
+ // but any Gherkin scenarios are still "pending" — they need
252
+ // the tester to translate them into real test files before
253
+ // we can call the HU done. If none are pending, approve
254
+ // immediately like before.
255
+ const pendingGherkinTests = (story.acceptance_tests || []).filter(
256
+ (t) => t && typeof t === "object" && t.type === "gherkin"
257
+ );
258
+ if (pendingGherkinTests.length === 0) {
259
+ logger.info(`HU ${story.id}: all shell acceptance tests PASSED, no Gherkin to translate — approved`);
260
+ await finalizeHuCommit({ story, branchName, config: laneConfig, logger, cwd: worktreePath });
261
+ return { approved: true, sessionId: laneSession.id, reason: "acceptance_tests_passed" };
262
+ }
263
+
264
+ // Shell tests passed; Gherkin needs translation. Invoke the
265
+ // tester with both so it writes code tests for the Gherkin,
266
+ // runs the suite, and returns verdict.
267
+ logger.info(`HU ${story.id}: shell gate passed, asking tester to translate ${pendingGherkinTests.length} Gherkin scenario(s)`);
268
+ const { runTesterStage } = await import("../post-loop-stages.js");
269
+ const shellResults = testResult.results.filter((r) => r.type !== "gherkin");
270
+ const testerOutcome = await runTesterStage({
271
+ config: laneConfig, logger, emitter, eventBase: ctx.eventBase,
272
+ session: laneSession, coderRole: ctx.coderRole, trackBudget: ctx.trackBudget,
273
+ iteration: attempt, task: huTask, diff: null, askQuestion,
274
+ pendingGherkinTests, shellTestResults: shellResults,
275
+ });
276
+ const testerStage = testerOutcome?.stageResult;
277
+ if (testerOutcome?.action !== "continue" && testerStage?.verdict === "pass") {
278
+ logger.info(`HU ${story.id}: tester translated Gherkin and verdict=pass — approved`);
279
+ await finalizeHuCommit({ story, branchName, config: laneConfig, logger, cwd: worktreePath });
280
+ return { approved: true, sessionId: laneSession.id, reason: "acceptance_tests_passed" };
281
+ }
282
+
283
+ // Tester rejected (translated tests failed or coverage
284
+ // inadequate). Feed its diagnostic back to the coder for
285
+ // another iteration.
286
+ const failing = testerStage?.failing_scenarios?.join("\n- ") || testerStage?.summary || "tester rejected";
287
+ const diagnostic = [
288
+ "Tester translated the Gherkin acceptance tests and some FAILED:",
289
+ failing ? `- ${failing}` : "",
290
+ "",
291
+ "Fix the implementation so every scenario passes. Do NOT soften the scenarios.",
292
+ ].filter(Boolean).join("\n");
293
+ logger.warn(`HU ${story.id}: tester verdict=fail after Gherkin translation — sending back to coder`);
294
+ setReviewerFeedback(laneSession, diagnostic);
295
+ lanePlannedTask = `${huTask}\n\n--- GHERKIN TRANSLATION FAILURES ---\n${diagnostic}`;
296
+ continue;
297
+ }
298
+
299
+ // Brain diagnoses failures and sends concrete fix to coder
300
+ const failed = testResult.results.filter(r => !r.passed);
301
+
302
+ // Repairer (#538): for each failing shell test, see if it's been
303
+ // failing the same way 2× in a row AND looks structurally broken
304
+ // (jq syntax, shell parse error, missing tool, etc.). If yes,
305
+ // invoke RepairerRole to rewrite the test in the plan + batch
306
+ // before iterating again. If unfixable, escalate (fail the HU
307
+ // immediately rather than burn the rest of max_iterations).
308
+ let repairedAny = false;
309
+ let repairEscalate = null;
310
+ for (const f of failed) {
311
+ if (f.type !== "shell") continue;
312
+ const consecutive = failureTracker.record(f.cmd, f.output);
313
+ const cls = classifyFailure({
314
+ cmd: f.cmd, errorOutput: f.output, consecutive,
315
+ });
316
+ if (!cls.infeasible) continue;
317
+ const testIndex = story.acceptance_tests.findIndex((t) => {
318
+ const c = typeof t === "object" ? t.content : t;
319
+ return c === f.cmd;
320
+ });
321
+ if (testIndex < 0) continue;
322
+ // Mutate `story.acceptance_tests` in place. The same story
323
+ // reference lives inside runHuSubPipeline's batch (passed
324
+ // by ref), so the next iteration's acceptance runner sees
325
+ // the fixed test without us needing batch+batchSessionId
326
+ // in this scope. The reconcile from PR #544 will sync the
327
+ // plan JSON on the next run.
328
+ const repair = await runRepair({
329
+ story, testIndex, errorOutput: f.output,
330
+ batchSessionId: ctx.stageResults.huReviewer.batchSessionId,
331
+ // batch unavailable in this closure scope — runRepair
332
+ // gates saveHuBatch on its presence and skips when
333
+ // missing; the in-memory mutation alone is sufficient
334
+ // for the next acceptance run within this same HU loop.
335
+ batch: null,
336
+ config: ctx.config, logger, emitter, eventBase: ctx.eventBase,
337
+ failureKind: cls.kind,
338
+ savePlanPatch: null,
339
+ });
340
+ if (repair.escalate) { repairEscalate = repair.reason; break; }
341
+ if (repair.repaired) {
342
+ repairedAny = true;
343
+ failureTracker.reset(f.cmd);
344
+ }
345
+ }
346
+
347
+ if (repairEscalate) {
348
+ logger.warn(`HU ${story.id}: Repairer escalated — ${repairEscalate}`);
349
+ return { approved: false, sessionId: laneSession.id, reason: "repair_escalated" };
350
+ }
351
+ if (repairedAny) {
352
+ // Tests were rewritten — restart iteration with the fixed
353
+ // tests in place; do NOT increment attempt against the user
354
+ // because the failure was the planner's bug, not the coder's.
355
+ logger.info(`HU ${story.id}: Repairer fixed test(s); restarting acceptance gate without consuming iteration`);
356
+ attempt -= 1;
357
+ continue;
358
+ }
359
+
360
+ // No repair happened — normal coder feedback path.
361
+ const diagnostic = buildDiagnosticPrompt(failed);
362
+ logger.warn(`HU ${story.id}: ${failed.length} acceptance test(s) FAILED — sending diagnostic to coder`);
363
+ setReviewerFeedback(laneSession, diagnostic);
364
+ lanePlannedTask = `${huTask}\n\n--- ACCEPTANCE TEST FAILURES ---\n${diagnostic}`;
365
+ }
366
+
367
+ // All iterations exhausted
368
+ logger.warn(`HU ${story.id}: max iterations reached with acceptance tests still failing`);
369
+ return { approved: false, sessionId: laneSession.id, reason: "acceptance_tests_failed" };
370
+ }
371
+
372
+ // Fallback: no acceptance_tests → standard pipeline (reviewer/tester).
373
+ // The loop reads everything through ctx.* — handing it a lane view
374
+ // keeps the shared ctx untouched (no more mutate-and-restore dance).
375
+ const laneCtx = { ...ctx, config: laneConfig, pipelineFlags: laneFlags, session: laneSession, brainCtx: laneBrain, plannedTask: lanePlannedTask };
376
+ const result = await runIterationLoop(laneCtx, { task: huTask, askQuestion, emitter, logger });
377
+ if (result?.approved) {
378
+ await finalizeHuCommit({ story, branchName, config: laneConfig, logger, cwd: worktreePath });
379
+ }
380
+ return result;
381
+ },
382
+ emitter,
383
+ eventBase: ctx.eventBase,
384
+ logger,
385
+ config: ctx.config,
386
+ // PR1 (live HU status): forward the per-HU status updater so
387
+ // hu-sub-pipeline can patch the plan JSON on every transition,
388
+ // not only at the end of the run. The board's chokidar then
389
+ // fires per-HU and the Kanban columns update in real time.
390
+ onStatusChange: ctx.session?._liveStatusUpdater || null,
391
+ // PR3 (per-HU outcome): same pattern, but for the rich
392
+ // outcome blob written ONCE per HU at the end of runSingleHu.
393
+ onOutcome: ctx.session?._liveOutcomeUpdater || null,
394
+ // Cost D (KJC-TSK-0515): the sub-pipeline slices BudgetTracker
395
+ // entries per HU and stamps `outcome.cost_usd` so the board can
396
+ // show per-HU spend. Without this the tracker is invisible to it.
397
+ budgetTracker: ctx.budgetTracker || null
398
+ });
399
+
400
+ emitProgress(emitter, makeEvent("hu:sub-pipeline:end", { ...ctx.eventBase, stage: "hu-sub-pipeline" }, {
401
+ status: subPipelineResult.approved ? "ok" : "fail",
402
+ message: subPipelineResult.approved
403
+ ? "All HUs completed successfully"
404
+ : `Sub-pipeline finished with failures (${subPipelineResult.blockedIds.length} blocked)`,
405
+ detail: { results: subPipelineResult.results, blockedIds: subPipelineResult.blockedIds }
406
+ }));
407
+
408
+ // Sync results back to plan file if this was a plan-based run
409
+ if (ctx.session._syncResultsToPlan) {
410
+ try {
411
+ await ctx.session._syncResultsToPlan(subPipelineResult);
412
+ logger.info("Plan updated with execution results");
413
+ } catch (err) {
414
+ logger.warn(`Failed to sync results to plan: ${err.message}`);
415
+ }
416
+ }
417
+
418
+ const finalResult = {
419
+ approved: subPipelineResult.approved,
420
+ sessionId: ctx.session.id,
421
+ huResults: subPipelineResult.results,
422
+ blockedIds: subPipelineResult.blockedIds,
423
+ planId: ctx.session._planRef?.planId || null
424
+ };
425
+ await writeHistoryRecord({ sessionId: ctx.session.id, task, result: finalResult, logger });
426
+ return { handled: true, result: finalResult };
427
+ }
@@ -0,0 +1,140 @@
1
+ // Feedback enrichment: transform vague feedback into actionable instructions.
2
+ // Extracts file hints, line hints, and suggests fixes from feedback text.
3
+
4
+ /**
5
+ * Extract file path hints from feedback description.
6
+ * Matches patterns like: packages/server/auth.js, src/routes/*, auth/middleware.ts
7
+ */
8
+ export function extractFileHints(description) {
9
+ if (!description) return [];
10
+ const hints = new Set();
11
+
12
+ // Explicit paths with extensions (supports bare filenames too)
13
+ const pathRe = /\b([\w./_-]+\.(?:js|ts|jsx|tsx|py|go|rs|java|rb|php|cs|swift|dart|html|css|vue|svelte|astro|json|yml|yaml|md|sql))\b/gi;
14
+ const paths = description.match(pathRe) || [];
15
+ for (const p of paths) hints.add(p);
16
+
17
+ // Directory references
18
+ const dirRe = /\b(packages\/[\w-]+|src\/[\w/-]+|tests?\/[\w/-]+|lib\/[\w/-]+)/gi;
19
+ const dirs = description.match(dirRe) || [];
20
+ for (const d of dirs) hints.add(d);
21
+
22
+ return [...hints];
23
+ }
24
+
25
+ /**
26
+ * Detect the category of feedback from description keywords.
27
+ */
28
+ export function detectCategory(description) {
29
+ const text = (description || "").toLowerCase();
30
+ if (/\b(injection|xss|csrf|httponly|sql\s*injection|exposed\s*secret|auth(?:entication)?\s*bypass|authoriza(?:tion)?\s*bypass|cryptograph|token\s*expos)/i.test(text)) return "security";
31
+ if (/\btest|coverage|assert|spec|mock\b/.test(text)) return "tests";
32
+ if (/\bperf(ormance)?|slow|optimize|bottleneck|latency\b/.test(text)) return "performance";
33
+ if (/\b(style|format(?:ting)?|naming|rename|indent|whitespace|comment|jsdoc|cosmetic)\b/.test(text)) return "style";
34
+ if (/\b(bug|incorrect|wrong|fails?|error|crash|broken)\b/.test(text)) return "correctness";
35
+ return "other";
36
+ }
37
+
38
+ /**
39
+ * Detect severity from description keywords and existing severity field.
40
+ */
41
+ export function detectSeverity(description, existingSeverity) {
42
+ if (existingSeverity) return existingSeverity;
43
+ const text = (description || "").toLowerCase();
44
+ if (/\bcritical|blocker|severe|production\s*risk\b/.test(text)) return "critical";
45
+ if (/\b(high|security|vulnerab|breaking|regression|injection|xss|csrf)\b/.test(text)) return "high";
46
+ if (/\blow|minor|cosmetic|nitpick\b/.test(text)) return "low";
47
+ return "medium";
48
+ }
49
+
50
+ /**
51
+ * Generate actionable instructions from a feedback entry.
52
+ * Adds concrete steps the coder can follow.
53
+ */
54
+ export function generateActionPlan(entry) {
55
+ const steps = [];
56
+ const cat = entry.category || detectCategory(entry.description);
57
+
58
+ // Always start with locating the problem
59
+ if (entry.file) {
60
+ steps.push(`Open ${entry.file}${entry.line ? ` at line ${entry.line}` : ""}`);
61
+ } else {
62
+ const hints = extractFileHints(entry.description);
63
+ if (hints.length > 0) {
64
+ steps.push(`Look in: ${hints.join(", ")}`);
65
+ } else {
66
+ steps.push("Identify the relevant file(s) using grep/search");
67
+ }
68
+ }
69
+
70
+ // Category-specific actions
71
+ if (cat === "security") {
72
+ steps.push("Apply the security fix (validate input, escape output, use parameterized queries, etc.)");
73
+ } else if (cat === "tests") {
74
+ steps.push("Write the missing test cases covering the scenario described");
75
+ steps.push("Run tests to verify they pass: npm test or equivalent");
76
+ } else if (cat === "correctness") {
77
+ steps.push("Fix the bug described");
78
+ steps.push("Add a regression test");
79
+ } else if (cat === "performance") {
80
+ steps.push("Optimize the identified bottleneck");
81
+ steps.push("Verify improvement with measurements");
82
+ } else if (cat === "style") {
83
+ steps.push("Apply the style fix (run linter if available)");
84
+ }
85
+
86
+ // Include suggested fix if provided
87
+ if (entry.suggestedFix) {
88
+ steps.push(`Suggested approach: ${entry.suggestedFix}`);
89
+ }
90
+
91
+ return steps;
92
+ }
93
+
94
+ /**
95
+ * Enrich a single feedback entry with file hints, category, severity, and action plan.
96
+ */
97
+ export function enrichEntry(entry) {
98
+ const description = entry.description || "";
99
+ // Re-detect category when missing or set to default "other"
100
+ const category = (!entry.category || entry.category === "other")
101
+ ? detectCategory(description)
102
+ : entry.category;
103
+ const severity = detectSeverity(description, entry.severity);
104
+ const fileHints = entry.file ? [entry.file] : extractFileHints(description);
105
+
106
+ return {
107
+ ...entry,
108
+ category,
109
+ severity,
110
+ fileHints,
111
+ actionPlan: generateActionPlan({ ...entry, category })
112
+ };
113
+ }
114
+
115
+ /**
116
+ * Enrich all entries in a queue in-place.
117
+ */
118
+ export function enrichQueue(queue) {
119
+ queue.entries = queue.entries.map(enrichEntry);
120
+ return queue;
121
+ }
122
+
123
+ /**
124
+ * Format enriched entries as actionable coder prompt sections.
125
+ */
126
+ export function formatEnrichedForCoder(enrichedEntries) {
127
+ if (!enrichedEntries?.length) return "";
128
+
129
+ const sections = [];
130
+ enrichedEntries.forEach((e, i) => {
131
+ const header = `### Issue ${i + 1}: [${e.severity}] ${e.category} — ${e.description}`;
132
+ const location = e.fileHints?.length
133
+ ? `**Location hints:** ${e.fileHints.join(", ")}`
134
+ : "**Location:** search the project for relevant files";
135
+ const plan = `**Action plan:**\n${e.actionPlan.map(s => `- ${s}`).join("\n")}`;
136
+ sections.push([header, location, plan].join("\n"));
137
+ });
138
+
139
+ return sections.join("\n\n");
140
+ }