@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,23 @@
1
+ /**
2
+ * kj plan add-hu <planId> --title "..." --type sw --deps hu_xxx
3
+ */
4
+ export async function planAddHuCommand({ config, planId, title, type, deps, scope }) {
5
+ const { loadPlan, savePlan } = await import("../../plan/plan-store.js");
6
+ const { addHu } = await import("../../plan/plan-hu-ops.js");
7
+ const projectDir = config.projectDir || process.cwd();
8
+
9
+ const plan = await loadPlan(projectDir, planId);
10
+ if (!plan) { console.error(`Plan not found: ${planId}`); process.exitCode = 1; return; }
11
+
12
+ const hu = addHu(plan, {
13
+ title: title || "New HU",
14
+ task_type: type || "sw",
15
+ scope: scope || null,
16
+ blocked_by: deps ? deps.split(",").map(d => d.trim()) : []
17
+ });
18
+ const { creatorLabel } = await import("../hu.js");
19
+ hu.created_by = creatorLabel(); // KJC-TSK-0661: provenance stamp
20
+
21
+ await savePlan(projectDir, plan);
22
+ console.log(`Added: ${hu.id} — ${hu.title}`);
23
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * kj plan delete <planId>
3
+ */
4
+ export async function planDeleteCommand({ config, planId }) {
5
+ const { deletePlan } = await import("../../plan/plan-store.js");
6
+ const projectDir = config.projectDir || process.cwd();
7
+ const ok = await deletePlan(projectDir, planId);
8
+ console.log(ok ? `Deleted: ${planId}` : `Not found: ${planId}`);
9
+ }
@@ -0,0 +1,99 @@
1
+ // KJC-TSK-0402: relanzar el self-fix loop sobre un plan ya generado,
2
+ // opcionalmente con feedback humano que guía al reviewer hacia los
3
+ // puntos débiles que la primera pasada no detectó. NO regenera desde
4
+ // cero — preserva el trabajo previo.
5
+
6
+ import { createAgent } from "../../agents/index.js";
7
+ import { assertAgentsAvailable } from "../../agents/availability.js";
8
+ import { resolveRole } from "../../config.js";
9
+ import { createCliProgressReporter } from "../../utils/cli-progress.js";
10
+ import { reviewPlan, findingsCount, findingsCountByType } from "../../plan/plan-reviewer.js";
11
+ import { applyReviewerFeedback, applyFixerPatch } from "../../plan/plan-fixer.js";
12
+ import { runStructuralPass } from "../../plan/plan-structural-pass.js";
13
+ import { loadPlan, savePlan } from "../../plan/plan-store.js";
14
+
15
+ export async function planFixCommand({ config, planId, prompt, logger, json }) {
16
+ const projectDir = config.projectDir || process.cwd();
17
+ const plan = await loadPlan(projectDir, planId);
18
+ if (!plan) throw new Error(`Plan no encontrado: ${planId}`);
19
+ if (!Array.isArray(plan.hus) || plan.hus.length === 0) {
20
+ throw new Error(`Plan ${planId} no tiene HUs.`);
21
+ }
22
+
23
+ const before = { hus: plan.hus.length, deps: plan.hus.reduce((a, h) => a + (h.blocked_by?.length || 0), 0) };
24
+
25
+ const plannerRole = resolveRole(config, "planner");
26
+ await assertAgentsAvailable([plannerRole.provider]);
27
+ const planner = createAgent(plannerRole.provider, config, logger);
28
+ const silenceTimeoutMs = Number(config?.session?.max_agent_silence_minutes) > 0
29
+ ? Math.round(Number(config.session.max_agent_silence_minutes) * 60 * 1000) : undefined;
30
+ const timeoutMs = Number(config?.session?.max_planner_minutes) > 0
31
+ ? Math.round(Number(config.session.max_planner_minutes) * 60 * 1000) : undefined;
32
+
33
+ // Reviewer con prompt opcional del usuario inyectado como contexto extra.
34
+ // El prompt se prepende al task original para que el reviewer LLM lo lea
35
+ // antes de la HU list.
36
+ const enrichedTask = prompt
37
+ ? `${plan.task || ""}\n\n## USER FEEDBACK (priorizar estos puntos)\n${prompt}\n`
38
+ : plan.task || "";
39
+
40
+ const reviewProgress = createCliProgressReporter({ role: "plan-fix:reviewer", quiet: Boolean(json) });
41
+ const review = await reviewPlan({
42
+ agent: planner, task: enrichedTask, hus: plan.hus,
43
+ onOutput: reviewProgress.onOutput, silenceTimeoutMs, timeoutMs,
44
+ });
45
+ reviewProgress.finish(review.ok ? "done" : "failed");
46
+ if (!review.ok) throw new Error(`Reviewer falló: ${review.error}`);
47
+ plan.review = review.findings;
48
+ let count = findingsCount(review.findings);
49
+ if (!json) process.stderr.write(`Reviewer encontró ${count} item(s).\n`);
50
+
51
+ // Self-fix loop con smart convergence guard (mismo que generate.js).
52
+ const maxIter = 4;
53
+ for (let i = 1; i <= maxIter && count > 0; i++) {
54
+ const husSnapshot = JSON.parse(JSON.stringify(plan.hus));
55
+ const reviewSnapshot = JSON.parse(JSON.stringify(plan.review));
56
+ const fixProgress = createCliProgressReporter({ role: `plan-fix:iter ${i}`, quiet: Boolean(json) });
57
+ const fix = await applyReviewerFeedback({
58
+ agent: planner, task: enrichedTask, hus: plan.hus, findings: plan.review,
59
+ onOutput: fixProgress.onOutput, silenceTimeoutMs, timeoutMs,
60
+ });
61
+ if (!fix.ok) { fixProgress.finish("failed"); break; }
62
+ const ops = applyFixerPatch(plan, fix.patch);
63
+ if (ops.added + ops.depsAdded + ops.archived === 0) { fixProgress.finish("done"); break; }
64
+ const review2 = await reviewPlan({
65
+ agent: planner, task: enrichedTask, hus: plan.hus,
66
+ onOutput: fixProgress.onOutput, silenceTimeoutMs, timeoutMs,
67
+ });
68
+ if (!review2.ok) { plan.hus = husSnapshot; plan.review = reviewSnapshot; fixProgress.finish("failed"); break; }
69
+ const b = findingsCountByType(reviewSnapshot);
70
+ const a = findingsCountByType(review2.findings);
71
+ const accept = a.priority < b.priority || (a.priority === b.priority && a.total <= b.total);
72
+ if (!accept) { plan.hus = husSnapshot; plan.review = reviewSnapshot; fixProgress.finish("reverted"); break; }
73
+ plan.review = review2.findings;
74
+ count = a.total;
75
+ fixProgress.finish("done");
76
+ }
77
+
78
+ // Structural integrity pass determinístico al final.
79
+ const { fixes: structuralFixes } = runStructuralPass(plan);
80
+ if (structuralFixes.length > 0) {
81
+ if (!plan.review) plan.review = {};
82
+ plan.review.structural_fixes = structuralFixes;
83
+ }
84
+
85
+ await savePlan(projectDir, plan);
86
+ const after = { hus: plan.hus.length, deps: plan.hus.reduce((a, h) => a + (h.blocked_by?.length || 0), 0) };
87
+
88
+ if (json) {
89
+ console.log(JSON.stringify({ planId, before, after, structuralFixes }, null, 2));
90
+ return;
91
+ }
92
+ const diffHus = after.hus - before.hus;
93
+ const diffDeps = after.deps - before.deps;
94
+ const structuralSummary = structuralFixes.length > 0
95
+ ? `, ${structuralFixes.length} structural fix(es)` : "";
96
+ console.log(
97
+ `kj plan fix ${planId}: ${diffHus >= 0 ? "+" : ""}${diffHus} HUs, ${diffDeps >= 0 ? "+" : ""}${diffDeps} deps${structuralSummary}. Findings: ${count} restantes.`
98
+ );
99
+ }
@@ -0,0 +1,532 @@
1
+ import { createAgent } from "../../agents/index.js";
2
+ import { assertAgentsAvailable } from "../../agents/availability.js";
3
+ import { resolveRole } from "../../config.js";
4
+ import { buildPlannerPrompt } from "../../prompts/planner.js";
5
+ import { parseMaybeJsonString } from "../../review/parser.js";
6
+ import { createCliProgressReporter } from "../../utils/cli-progress.js";
7
+ import { runAutoGC, summarizeGC } from "../../utils/garbage-collector.js";
8
+ import { promptProjectName } from "../../utils/prompt-project-name.js";
9
+ import { deriveProjectNameFromCwd } from "../../utils/derive-project-name-from-cwd.js";
10
+ import { applyReviewerFeedback, applyFixerPatch } from "../../plan/plan-fixer.js";
11
+ import { runStructuralPass } from "../../plan/plan-structural-pass.js";
12
+ import { recommendModelsForHu, complexityFromTaskType } from "../../hu/model-router.js";
13
+ import { withBrainRecovery } from "../../brain/with-brain-recovery.js";
14
+ import { buildStandbyState } from "../../brain/standby-store.js";
15
+ import { printResumeHint } from "../../utils/display/resume-hint.js";
16
+ import { runSpecReview } from "../../spec-review/run-spec-review.js";
17
+ import { prependPreflightHu } from "../../plan/preflight-hu.js";
18
+ import { readCachedBrief } from "../../onboarder/cache.js";
19
+ import { formatPlan, formatHuTable } from "./_shared.js";
20
+
21
+ /**
22
+ * kj plan "task" — generate plan + HUs
23
+ */
24
+ export async function planGenerateCommand({ task, config, logger, json, context, flags = {}, sourceCanvasPath = null }) {
25
+ const { withCliRunLog } = await import("../../utils/cli-run-log.js");
26
+ return withCliRunLog("plan", { projectDir: config?.projectDir, logger }, async ({ runLog }) => {
27
+ return planGenerateImpl({ task, config, logger, json, context, runLog, flags, sourceCanvasPath });
28
+ });
29
+ }
30
+
31
+ async function planGenerateImpl({ task, config, logger, json, context, runLog, flags = {}, sourceCanvasPath = null }) {
32
+ // Auto-GC: prune orphan plans (project dir gone) + old finalised plans/
33
+ // sessions/HU batches before we start. Silent unless something was
34
+ // removed; one-liner summary on stderr in that case so --json stays
35
+ // untouched. Skipped in test env.
36
+ if (!process.env.VITEST && process.env.NODE_ENV !== "test") {
37
+ try {
38
+ const gcResult = await runAutoGC();
39
+ const line = summarizeGC(gcResult);
40
+ if (line && !json) process.stderr.write(`${line}\n`);
41
+ } catch (err) {
42
+ logger?.warn?.(`Auto-GC failed (non-blocking): ${err.message}`);
43
+ }
44
+ }
45
+
46
+ // Spec-reviewer pre-planner audit (KJC-PCS-0048). Static import —
47
+ // role runs every invocation (modulo bypass); no lazy-load benefit.
48
+ const reviewResult = await runSpecReview({
49
+ spec: task, config, logger, flags,
50
+ askQuestion: json ? null : (typeof flags?.askQuestion === "function" ? flags.askQuestion : null),
51
+ });
52
+ if (!reviewResult.proceed) {
53
+ logger?.info?.("Aborted by user after spec review.");
54
+ return { ok: false, aborted: true, reason: "spec-review-cancelled" };
55
+ }
56
+ if (reviewResult.refined && reviewResult.finalSpec) {
57
+ task = reviewResult.finalSpec; // downstream runs the refined spec
58
+ }
59
+
60
+ // KJC-TSK-0384 PR 3: --use-onboarding folds the cached Architecture Brief
61
+ // from `kj onboard` into the planner's context. Silent when no brief is
62
+ // cached so users without one don't see noise; loud warn when they passed
63
+ // the flag explicitly but the cache is empty so the missed wiring surfaces.
64
+ let resolvedContext = context;
65
+ if (flags?.useOnboarding) {
66
+ const brief = await readCachedBrief(config?.projectDir || process.cwd());
67
+ if (brief.found) {
68
+ resolvedContext = [
69
+ resolvedContext, brief.content ? `## Architecture Brief (from kj onboard)\n\n${brief.content}` : null,
70
+ ].filter(Boolean).join("\n\n");
71
+ runLog.logText(`[planner] onboarding brief injected from ${brief.path}`);
72
+ } else {
73
+ logger?.warn?.(`--use-onboarding passed but no brief at ${brief.path}. Run 'kj onboard' first.`);
74
+ }
75
+ }
76
+
77
+ const plannerRole = resolveRole(config, "planner");
78
+ await assertAgentsAvailable([plannerRole.provider]);
79
+ runLog.logText(`[planner] provider=${plannerRole.provider}`);
80
+
81
+ const planner = createAgent(plannerRole.provider, config, logger);
82
+ const prompt = buildPlannerPrompt({ task, context: resolvedContext });
83
+ const silenceTimeoutMs = Number(config?.session?.max_agent_silence_minutes) > 0
84
+ ? Math.round(Number(config.session.max_agent_silence_minutes) * 60 * 1000)
85
+ : undefined;
86
+ const timeoutMs = Number(config?.session?.max_planner_minutes) > 0
87
+ ? Math.round(Number(config.session.max_planner_minutes) * 60 * 1000)
88
+ : undefined;
89
+
90
+ // Live progress to stderr so the user sees what the planner is doing
91
+ // instead of staring at a silent shell for 30 s – 3 min. `--json` mode
92
+ // stays quiet so stdout remains a pure JSON document.
93
+ // Sub-progress por fase (KJC outputs limpios): cada llamada al
94
+ // agente arranca su propio "[planner:fase] starting → done" para
95
+ // que el usuario no piense que "[planner] done" significa "el
96
+ // comando entero terminó" — sigue habiendo tests-synth, reviewer,
97
+ // self-fix iter N por delante.
98
+ const progress = createCliProgressReporter({ role: "planner:initial", quiet: Boolean(json) });
99
+ let result;
100
+ // `kj plan` has no pipeline session, so synthesise a standby id
101
+ // (`plan_<stamp>`). If the planner hits a quota cap, withBrainRecovery
102
+ // persists it and `kj standby resume` re-spawns via the saved argv.
103
+ const plannerSessionState = buildStandbyState({
104
+ session: { id: `plan_${new Date().toISOString().replaceAll(/[:.]/g, "-")}` },
105
+ config,
106
+ });
107
+ try {
108
+ // KJC-TSK-0413: planner inicial pasa por Brain Recovery — clasifica
109
+ // 401/429/5xx/SILENCED/etc y decide retry/standby/hibernate/abort.
110
+ result = await withBrainRecovery({
111
+ agent: planner,
112
+ taskArgs: { prompt, role: "planner", silenceTimeoutMs, timeoutMs, onOutput: progress.onOutput },
113
+ role: "planner",
114
+ logger,
115
+ sessionState: plannerSessionState,
116
+ });
117
+ progress.finish(result.ok ? "done" : (result.action || "failed"));
118
+ } catch (err) {
119
+ progress.finish("failed");
120
+ throw err;
121
+ }
122
+
123
+ if (!result.ok && result.action === "hibernate") {
124
+ // Quota cap: Brain Recovery persisted a standby snapshot. Stop
125
+ // cleanly with a resume hint instead of throwing a generic error.
126
+ const hibResult = {
127
+ ok: false, hibernated: true,
128
+ sessionId: plannerSessionState?.sessionId || null,
129
+ standbyFile: result.standbyFile || null,
130
+ };
131
+ if (!json) printResumeHint(hibResult);
132
+ process.exitCode = 1;
133
+ return hibResult;
134
+ }
135
+ if (!result.ok) throw new Error(result.error || result.output || "Planner failed");
136
+
137
+ const parsed = parseMaybeJsonString(result.output);
138
+
139
+ // Create v2 plan with HUs from planner steps
140
+ const { createPlanV2 } = await import("../../plan/plan-schema.js");
141
+ const { addHu } = await import("../../plan/plan-hu-ops.js");
142
+ const { savePlan } = await import("../../plan/plan-store.js");
143
+ const { deriveProjectName, classifyTaskType } = await import("../../hu/auto-generator.js");
144
+
145
+ const projectDir = config.projectDir || process.cwd();
146
+ const plan = createPlanV2(task);
147
+ // Default project name resolution, in priority order:
148
+ // 1. cwd signals — package.json "name" → git remote basename →
149
+ // directory basename. This is what the user actually expects:
150
+ // if they ran `git init` in `~/ws_demo/weather-dashboard` and
151
+ // then asked Karajan to plan, the default name should reflect
152
+ // THAT directory, not the words of the SPEC document.
153
+ // 2. Fallback: derive from the task text (legacy heuristic). Used
154
+ // when none of the cwd signals yields anything (e.g. running
155
+ // from a tmp dir, no git, no package.json — typical for
156
+ // ad-hoc planning sessions).
157
+ // The user is still prompted to confirm / override on TTY runs;
158
+ // non-interactive runs (--json, --yes, board-spawned subprocesses)
159
+ // accept the default silently.
160
+ const defaultName = deriveProjectNameFromCwd(projectDir) || deriveProjectName(task);
161
+ const canPrompt = process.stdin.isTTY && process.stdout.isTTY
162
+ && !json && !flags?.yes && flags?.interactive !== false;
163
+ plan.name = canPrompt ? await promptProjectName(defaultName) : defaultName;
164
+ plan.approach = parsed?.approach || (typeof parsed === "string" ? parsed : (typeof result.output === "string" ? result.output : null));
165
+ plan.risks = parsed?.risks || [];
166
+ plan.outOfScope = parsed?.outOfScope || [];
167
+ // KJC-TSK-0348: persist the SPEC.canvas.md path so `kj sync --apply`
168
+ // can later locate it and append drift notes. Only set when the plan
169
+ // actually came from a Canvas SPEC; flat-task plans stay unstamped.
170
+ if (typeof sourceCanvasPath === "string" && sourceCanvasPath.length > 0) {
171
+ plan.sourceCanvasPath = sourceCanvasPath;
172
+ }
173
+
174
+ // Convert planner steps → HUs, carrying the structured acceptance_tests
175
+ // the LLM was asked to emit. If the LLM fell back to legacy behaviour
176
+ // (omitted tests or returned an empty array) we DO NOT inject a fake
177
+ // suite-wide placeholder — that was the pre-v2.7.5 lie. Instead we
178
+ // stamp `acceptance_tests: []` and the board / CLI flag the HU as
179
+ // "missing test contract" so the user can either regenerate or edit
180
+ // the tests manually before executing.
181
+ const { normaliseAcceptanceTests } = await import("../../plan/plan-schema.js");
182
+ const steps = parsed?.steps || [];
183
+ let stepsMissingTests = 0;
184
+
185
+ // Two-pass HU creation (KJC-TSK-0382 follow-up):
186
+ // Pass 1 — create every HU with `blocked_by: []` and remember the
187
+ // mapping `<symbolic id from planner> -> <stable hu id>`.
188
+ // Pass 2 — translate the planner's `dependencies: [symbolicId, …]`
189
+ // into the corresponding `blocked_by: [huId, …]`.
190
+ // The split is needed because `addHu` assigns the stable id at insertion
191
+ // time, so a forward reference (step B depends on step A whose hu id
192
+ // doesn't exist yet) would otherwise be impossible to resolve.
193
+ const symbolicToHuId = new Map();
194
+ const stepDeps = []; // parallel array: stepDeps[i] = array of symbolic dep ids
195
+ const stepReuse = []; // parallel array: stepReuse[i] = array of symbolic reuse ids (KJC-BUG-0044 / P3)
196
+ for (const step of steps) {
197
+ const desc = typeof step === "string" ? step : step.description || step.title || JSON.stringify(step);
198
+ const rawTests = typeof step === "object" ? step.acceptance_tests : null;
199
+ const tests = normaliseAcceptanceTests(rawTests);
200
+ if (tests.length === 0) stepsMissingTests += 1;
201
+ // Humanización IDs: preservar el id simbólico del planner ("INFRA-001",
202
+ // "AUTH-SIGNUP", etc.) como short_id del HU. La clave canónica
203
+ // sigue siendo el `hu.id` largo (`hu_<planId>_<NNN>`); el short_id
204
+ // solo da una etiqueta amigable que la CLI muestra y acepta como
205
+ // referencia en `kj run --hu INFRA-001`.
206
+ const symbolicId = typeof step === "object" && typeof step.id === "string" && step.id.trim()
207
+ ? step.id.trim() : null;
208
+ const taskType = classifyTaskType(desc);
209
+ // KJC-TSK-0405 step 2: asigna coder_model + reviewer_model
210
+ // automáticamente según complexity inferido del task_type. El
211
+ // planner puede emitir `step.complexity` (trivial/simple/medium/complex
212
+ // o score 1-5) para override más fino. Cross-provider reviewer
213
+ // (claude↔codex) por defecto.
214
+ const coderProvider = config?.roles?.coder?.provider || config?.coder || "claude";
215
+ const complexity = (typeof step === "object" && step.complexity)
216
+ ? step.complexity
217
+ : complexityFromTaskType(taskType);
218
+ const models = recommendModelsForHu({ complexity, coderProvider, config });
219
+ const hu = addHu(plan, {
220
+ title: desc.slice(0, 80),
221
+ task_type: taskType,
222
+ scope: desc,
223
+ blocked_by: [],
224
+ acceptance_tests: tests,
225
+ short_id: symbolicId,
226
+ spec_section: typeof step === "object" && typeof step.spec_section === "string" ? step.spec_section.trim() || null : null,
227
+ coder_model: models.coder_model,
228
+ coder_provider: models.coder_provider,
229
+ reviewer_model: models.reviewer_model,
230
+ reviewer_provider: models.reviewer_provider,
231
+ });
232
+ // KJC-TSK-0661: provenance stamp (addHu builds its object explicitly,
233
+ // so this goes on the returned reference, not in huData).
234
+ hu.created_by = "planner";
235
+ if (symbolicId) symbolicToHuId.set(symbolicId, hu.id);
236
+ const deps = typeof step === "object" && Array.isArray(step.dependencies) ? step.dependencies : [];
237
+ stepDeps.push(deps);
238
+ const reuse = typeof step === "object" && Array.isArray(step.reuse) ? step.reuse : [];
239
+ stepReuse.push(reuse);
240
+ }
241
+ // Pass 2: resolve dependencies + reuse. Unknown symbolic ids are dropped
242
+ // silently (the plan-reviewer pass will flag them as gaps if they matter).
243
+ for (let i = 0; i < plan.hus.length; i++) {
244
+ const symbolicDeps = stepDeps[i] || [];
245
+ const resolvedDeps = symbolicDeps
246
+ .map((s) => typeof s === "string" ? symbolicToHuId.get(s.trim()) : null)
247
+ .filter(Boolean);
248
+ if (resolvedDeps.length > 0) plan.hus[i].blocked_by = resolvedDeps;
249
+ const symbolicReuse = stepReuse[i] || [];
250
+ const resolvedReuse = symbolicReuse
251
+ .map((s) => typeof s === "string" ? symbolicToHuId.get(s.trim()) : null)
252
+ .filter(Boolean);
253
+ if (resolvedReuse.length > 0) plan.hus[i].reuse = resolvedReuse;
254
+ }
255
+ // Tests-synthesizer pass (v2.7.5): the first planner call asks for
256
+ // acceptance_tests inline, but Claude / Codex frequently skip that
257
+ // sub-instruction on dense specs. If any HU came back without
258
+ // tests, run a focused second pass that ONLY synthesizes tests for
259
+ // those HUs. This is ON by default — the user is paying a CLI
260
+ // subscription, not per-token, so we optimise for plan quality, not
261
+ // round-trip cost. If `--no-tests-synth` (or `--quick`) is set, we
262
+ // skip and warn loudly that `kj run --plan` will refuse to execute.
263
+ const skipSynth = Boolean(flags?.noTestsSynth || flags?.quick);
264
+ if (stepsMissingTests > 0 && !skipSynth) {
265
+ runLog.logText(
266
+ `[planner] ${stepsMissingTests}/${steps.length} HUs missing acceptance_tests — running tests-synthesizer pass`
267
+ );
268
+ // Sub-progress reporter por fase: el usuario ve cada fase como su
269
+ // propio "[planner:X] starting → done" en vez de un solo
270
+ // "[planner] done" que oculta que aún hay trabajo en cola.
271
+ const synthProgress = createCliProgressReporter({ role: "planner:tests-synth", quiet: Boolean(json) });
272
+ const { synthesizeMissingTests } = await import("../../plan/tests-synthesizer.js");
273
+ const husNeedingTests = plan.hus
274
+ .filter((h) => !Array.isArray(h.acceptance_tests) || h.acceptance_tests.length === 0)
275
+ .map((h) => ({ id: h.id, title: h.title, scope: h.scope }));
276
+ const synthResult = await synthesizeMissingTests({
277
+ agent: planner,
278
+ task,
279
+ husNeedingTests,
280
+ onOutput: synthProgress.onOutput,
281
+ silenceTimeoutMs,
282
+ timeoutMs,
283
+ });
284
+ synthProgress.finish(synthResult.ok ? "done" : "failed");
285
+ if (synthResult.ok) {
286
+ let patched = 0;
287
+ for (const hu of plan.hus) {
288
+ const tests = synthResult.tests[hu.id];
289
+ if (tests && tests.length > 0) {
290
+ hu.acceptance_tests = tests;
291
+ hu.updatedAt = new Date().toISOString();
292
+ patched += 1;
293
+ }
294
+ }
295
+ stepsMissingTests = plan.hus.filter((h) => !h.acceptance_tests?.length).length;
296
+ runLog.logText(
297
+ `[planner] tests-synthesizer patched ${patched}/${husNeedingTests.length} HUs (${stepsMissingTests} still missing)`
298
+ );
299
+ } else {
300
+ runLog.logText(`[planner] tests-synthesizer failed: ${synthResult.error}`);
301
+ }
302
+ }
303
+
304
+ if (stepsMissingTests > 0) {
305
+ runLog.logText(
306
+ `[planner] WARN: ${stepsMissingTests}/${steps.length} HUs still have no acceptance_tests after the synthesizer pass. `
307
+ + `Edit them on the board (✎ Edit) or re-run \`kj plan\`. \`kj run --plan\` will refuse to execute these HUs.`
308
+ );
309
+ }
310
+
311
+ // Plan reviewer pass (PR D). Asks five closed-ended questions
312
+ // about the just-generated plan: missing HUs, missing deps, scope
313
+ // overlap, parallelisable groups, order issues. Output is purely
314
+ // advisory — we surface the findings to the user and they decide
315
+ // whether to apply them. Skipped when --no-plan-review or --quick.
316
+ const skipReview = Boolean(flags?.noPlanReview || flags?.quick);
317
+ if (plan.hus.length > 0 && !skipReview) {
318
+ runLog.logText("[planner] running plan reviewer pass");
319
+ const reviewProgress = createCliProgressReporter({ role: "planner:reviewer", quiet: Boolean(json) });
320
+ const { reviewPlan, findingsCount, findingsCountByType } = await import("../../plan/plan-reviewer.js");
321
+ const review = await reviewPlan({
322
+ agent: planner,
323
+ task,
324
+ hus: plan.hus,
325
+ onOutput: reviewProgress.onOutput,
326
+ silenceTimeoutMs,
327
+ timeoutMs,
328
+ });
329
+ reviewProgress.finish(review.ok ? "done" : "failed");
330
+ if (review.ok) {
331
+ plan.review = review.findings;
332
+ const count = findingsCount(review.findings);
333
+ runLog.logText(
334
+ count > 0
335
+ ? `[planner] reviewer surfaced ${count} item(s): ${review.findings.summary || "(see plan.review for details)"}`
336
+ : `[planner] reviewer found nothing actionable: ${review.findings.summary || "OK"}`
337
+ );
338
+ // KJC-BUG-0045 / P4: self-fix loop. The reviewer is flag-only by
339
+ // contract; when it surfaces actionable issues, ask the planner to
340
+ // PATCH the plan, apply in-process, and re-review. Loop until 0
341
+ // findings or maxIterations. Skippable with --no-plan-fixer/--quick.
342
+ const skipFixer = Boolean(flags?.noPlanFixer || flags?.quick);
343
+ // KJC-BUG-0054: bumped 2 → 4 — el guard antiguo cortaba el loop
344
+ // demasiado pronto cuando había muchos hallazgos. Con el guard
345
+ // inteligente (priority vs secondary) podemos permitir más
346
+ // iteraciones sin riesgo de divergir indefinidamente, porque
347
+ // cada iter que NO reduce priority ni total se revierte.
348
+ const maxFixerIterations = 4;
349
+ if (count > 0 && !skipFixer) {
350
+ for (let i = 1; i <= maxFixerIterations; i++) {
351
+ const currentCount = findingsCount(plan.review);
352
+ if (currentCount === 0) break;
353
+ runLog.logText(`[planner] self-fix iter ${i}/${maxFixerIterations} — patching ${currentCount} issue(s)`);
354
+ const fixProgress = createCliProgressReporter({ role: `planner:self-fix iter ${i}`, quiet: Boolean(json) });
355
+ // KJC-BUG-0046 / P5: snapshot plan + review BEFORE applying the patch
356
+ // so we can revert if the iteration regresses (newCount > currentCount).
357
+ // Dogfooding 2026-05-12: GRETA Plan 2 iter 2 went 10 → 17 findings
358
+ // because the fixer's deletions created dangling references the
359
+ // reviewer then flagged. Without the guard, the loop made the plan
360
+ // strictly worse than before iter 2 started.
361
+ const husSnapshot = JSON.parse(JSON.stringify(plan.hus));
362
+ const reviewSnapshot = JSON.parse(JSON.stringify(plan.review));
363
+ const fix = await applyReviewerFeedback({
364
+ agent: planner, task, hus: plan.hus, findings: plan.review,
365
+ onOutput: fixProgress.onOutput, silenceTimeoutMs, timeoutMs,
366
+ });
367
+ if (!fix.ok) {
368
+ fixProgress.finish("failed");
369
+ runLog.logText(`[planner] self-fix iter ${i} failed (non-blocking): ${fix.error}`);
370
+ break;
371
+ }
372
+ const ops = applyFixerPatch(plan, fix.patch);
373
+ runLog.logText(`[planner] self-fix iter ${i} applied: +${ops.added} HU, +${ops.depsAdded} dep, ${ops.archived} archived`);
374
+ if (ops.added + ops.depsAdded + ops.archived === 0) {
375
+ fixProgress.finish("done");
376
+ runLog.logText(`[planner] self-fix iter ${i} empty patch — stopping`);
377
+ break;
378
+ }
379
+ const review2 = await reviewPlan({
380
+ agent: planner, task, hus: plan.hus,
381
+ onOutput: fixProgress.onOutput, silenceTimeoutMs, timeoutMs,
382
+ });
383
+ if (!review2.ok) {
384
+ fixProgress.finish("failed");
385
+ runLog.logText(`[planner] re-review after iter ${i} failed: ${review2.error}`);
386
+ plan.hus = husSnapshot;
387
+ plan.review = reviewSnapshot;
388
+ break;
389
+ }
390
+ // KJC-BUG-0054: smart convergence guard. El guard antiguo
391
+ // (newCount > currentCount → revert) tiraba iteraciones
392
+ // VÁLIDAS que arreglaban un ciclo o una HU faltante pero
393
+ // a cambio creaban deps menores. Ahora distinguimos:
394
+ // - priority = cycles + missing_hus (CRÍTICO, no ignorable)
395
+ // - secondary = missing_deps + overlaps + order non-circular
396
+ //
397
+ // Reglas (en orden):
398
+ // 1. priority bajó → ACEPTAR (aunque secondary suba — el plan es ejecutable)
399
+ // 2. priority igual + total bajó → ACEPTAR
400
+ // 3. priority igual + total igual → ACEPTAR (lateral move, da chance)
401
+ // 4. priority subió → REVERT
402
+ // 5. priority igual + total subió → REVERT (regresión genuina)
403
+ const before = findingsCountByType(reviewSnapshot);
404
+ const after = findingsCountByType(review2.findings);
405
+ const newCount = after.total;
406
+ const accept =
407
+ after.priority < before.priority
408
+ || (after.priority === before.priority && after.total <= before.total);
409
+ if (!accept) {
410
+ const reason = after.priority > before.priority
411
+ ? `priority ${before.priority}→${after.priority}`
412
+ : `total ${before.total}→${after.total} (priority unchanged at ${after.priority})`;
413
+ plan.hus = husSnapshot;
414
+ plan.review = reviewSnapshot;
415
+ fixProgress.finish("reverted");
416
+ runLog.logText(`[planner] self-fix iter ${i} regressed (${reason}) — reverted, stopping`);
417
+ break;
418
+ }
419
+ plan.review = review2.findings;
420
+ fixProgress.finish("done");
421
+ const deltaSummary = after.priority < before.priority
422
+ ? `priority ${before.priority}→${after.priority}, total ${before.total}→${after.total}`
423
+ : `total ${before.total}→${after.total}`;
424
+ runLog.logText(`[planner] after self-fix iter ${i}: ${newCount} issue(s) (${deltaSummary})`);
425
+ }
426
+ }
427
+ } else {
428
+ runLog.logText(`[planner] reviewer pass failed (non-blocking): ${review.error}`);
429
+ }
430
+ }
431
+
432
+ // KJC-TSK-0399: deterministic structural integrity pass — cycles,
433
+ // orphan refs, missing short_id. Runs after self-fix loop because
434
+ // the LLM is bad at graph problems. Always runs (cheap, no network).
435
+ if (plan.hus.length > 0) {
436
+ const { fixes } = runStructuralPass(plan);
437
+ if (fixes.length > 0) {
438
+ if (!plan.review) plan.review = {};
439
+ plan.review.structural_fixes = fixes;
440
+ const byKind = fixes.reduce((a, f) => ({ ...a, [f.kind]: (a[f.kind] || 0) + 1 }), {});
441
+ runLog.logText(`[planner] structural pass: ${fixes.length} fix(es) — ${JSON.stringify(byKind)}`);
442
+ }
443
+ }
444
+
445
+ // KJC-TSK-0397: inject a [PREFLIGHT-000] HU at the top of the plan so
446
+ // every functional HU gates on a clean environment (deps + build + tests
447
+ // + lint + git clean, plus cloud auth when applicable). Idempotent: a
448
+ // plan that already has a preflight HU is left untouched. Disable with
449
+ // `--no-preflight-hu`.
450
+ if (flags.preflightHu !== false) {
451
+ await prependPreflightHu(plan, projectDir);
452
+ }
453
+
454
+ const planId = await savePlan(projectDir, plan);
455
+ const { plansDir } = await import("../../plan/plan-store.js");
456
+ const path = await import("node:path");
457
+ const planPath = path.join(plansDir(projectDir), `${planId}.json`);
458
+
459
+ runLog.logText(`[planner] finished — hus=${plan.hus.length} plan=${planId}`);
460
+
461
+ if (json) {
462
+ console.log(JSON.stringify(plan, null, 2));
463
+ return { ok: true, planId, ref: plan.alias || planId, huCount: plan.hus.length };
464
+ }
465
+
466
+ if (parsed?.approach) {
467
+ console.log(formatPlan(parsed));
468
+ } else if (plan.approach) {
469
+ console.log(plan.approach);
470
+ }
471
+ console.log(`\n## HUs (${plan.hus.length})`);
472
+ console.log(formatHuTable(plan.hus));
473
+ // Tell the user what just happened and what their two real options are.
474
+ // Pre-v2.7.5 we printed Review / Approve / Execute as three lines, which
475
+ // confused everyone:
476
+ // - "Review" sounded like an action; `kj plan show` is read-only.
477
+ // - "Approve" exposed the internal `kj plan ready` gate that the board
478
+ // workflow now bypasses (the board's "Run plan" button calls
479
+ // `kj run --plan` directly and the tests-first gate is enforced there).
480
+ // Now we just say: inspect, then run. The board is the recommended UI.
481
+ console.log(`\nPlan saved: ${planId} (${plan.hus.length} HUs, status: ${plan.status})`);
482
+ console.log(` → ${planPath}`);
483
+ // Reviewer findings: print a short summary so the user knows
484
+ // at-a-glance whether the plan needs attention. Full details live
485
+ // in `plan.review` and `kj plan show <id>` renders them.
486
+ if (plan.review) {
487
+ const r = plan.review;
488
+ const counts = [
489
+ r.missing_hus?.length ? `${r.missing_hus.length} missing HU(s)` : null,
490
+ r.missing_dependencies?.length ? `${r.missing_dependencies.length} missing dep(s)` : null,
491
+ r.scope_overlaps?.length ? `${r.scope_overlaps.length} overlap(s)` : null,
492
+ r.order_issues?.length ? `${r.order_issues.length} order issue(s)` : null,
493
+ ].filter(Boolean);
494
+ if (counts.length === 0) {
495
+ console.log(`Reviewer: ✓ ${r.summary || "no issues"}`);
496
+ } else {
497
+ console.log(`Reviewer flagged: ${counts.join(", ")} — see \`kj plan show ${planId}\``);
498
+ }
499
+ }
500
+ // Humanización IDs: si el plan tiene alias, lo preferimos como
501
+ // referencia visible (más legible). Fallback al planId.
502
+ const ref = plan.alias || planId;
503
+ console.log("");
504
+ console.log(`Inspect: kj plan show ${ref}`);
505
+ console.log(`Run: kj run --plan ${ref}`);
506
+ console.log(` (or click ▶ Run plan on the board, if running)`);
507
+
508
+ // UX boost for HU-bearing plans: auto-start the HU Board (same pattern as
509
+ // `kj run` auto-HU generation) so the user can inspect the 12-HU plan in a
510
+ // browser instead of squinting at the ASCII table. The board reads
511
+ // ~/.kj/plans/ directly, so this plan is visible immediately. Never in
512
+ // vitest / test mode, and never when `--json` is active.
513
+ if (plan.hus.length > 0 && !process.env.VITEST && process.env.NODE_ENV !== "test") {
514
+ try {
515
+ const { startBoard, renderBoardBanner } = await import("../board.js");
516
+ const { projectSlug: slugFor } = await import("../../plan/plan-store.js");
517
+ const boardPort = config?.hu_board?.port ?? 4000;
518
+ // `projectSlug` scopes the board URL to this project's view
519
+ // (`#board/<slug>`) instead of the global "All projects" dashboard.
520
+ // The hash route opens the board pre-filtered to that project.
521
+ const boardResult = await startBoard(boardPort, { projectSlug: slugFor(projectDir) });
522
+ const status = boardResult.alreadyRunning ? "already running" : "started";
523
+ console.log(renderBoardBanner({ url: boardResult.url, status, projectName: plan.name }));
524
+ } catch (err) {
525
+ // Non-blocking: plan is saved, only the visualiser failed.
526
+ logger?.warn?.(`HU Board auto-start failed (non-blocking): ${err.message}`);
527
+ console.log(`\nVisualize: kj board start (http://localhost:${config?.hu_board?.port ?? 4000})`);
528
+ }
529
+ }
530
+ // KJC-TSK-0574: surface planId/ref so `kj autorun` can chain plan → run.
531
+ return { ok: true, planId, ref, huCount: plan.hus.length };
532
+ }