@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,481 @@
1
+ /**
2
+ * Karajan garbage collector — keeps `~/.kj/` and `~/.karajan/` from
3
+ * accumulating dead state across runs.
4
+ *
5
+ * Dogfood motivation: after ~1 year the user's `~/.kj/plans/` had 2 464
6
+ * plans in `home_manu_ws_npm-packages_karajan-code/` plus 35 plans from
7
+ * worktrees that no longer exist. Same story for `~/.karajan/sessions/`
8
+ * and `hu-stories/`. None ever got cleaned because no command did it.
9
+ */
10
+
11
+ /**
12
+ * Two entry points:
13
+ * - `runAutoGC(opts)` — silent, called at the top of every `kj run`
14
+ * and `kj plan`. Prints a single one-liner ONLY when something was
15
+ * removed.
16
+ * - `runManualGC(opts)` — backs `kj clean`. Caller decides `dryRun`;
17
+ * returns the full `{ removed, bytesFreed, errors }` record.
18
+ *
19
+ * Retention policy (all adjustable via opts):
20
+ * • Plans whose stamped `projectDir` no longer exists → delete.
21
+ * • Plans with final status (approved/rejected/executed) older than
22
+ * `planRetentionDays` (30) → delete.
23
+ * • Draft plans older than `draftRetentionDays` (60) → delete.
24
+ * • Finalised sessions older than `sessionRetentionDays` (7) → delete.
25
+ * • HU story batches older than `huRetentionDays` (14) → delete.
26
+ *
27
+ * Failures are NEVER fatal — every op is try/catch and non-blocking.
28
+ * Tested against an isolated tmp KJ_HOME so production data is safe.
29
+ */
30
+
31
+ import fs from "node:fs/promises";
32
+ import path from "node:path";
33
+ import { resolveHome } from "./paths.js";
34
+
35
+ const FINAL_PLAN_STATUSES = new Set([
36
+ // Terminal plan states the user explicitly opted into — retention applies.
37
+ "approved", "rejected", "executed", "completed", "ready",
38
+ ]);
39
+ const KNOWN_PLAN_STATUSES = new Set([...FINAL_PLAN_STATUSES, "draft", "running"]);
40
+ const FINAL_SESSION_STATUSES = new Set(["approved", "failed", "stopped", "rejected", "completed"]);
41
+
42
+ // Post-PR-3 of KJC-PCS-0047 both helpers point at the canonical
43
+ // `~/.karajan/` root. `getKjHome` is kept as an alias of
44
+ // `getKarajanHome` so legacy callers inside the file continue to
45
+ // compile during the consolidation; both names resolve to the same
46
+ // directory now that the layout is unified.
47
+ function getKarajanHome() {
48
+ return resolveHome({ defaultSegment: ".karajan" });
49
+ }
50
+ const getKjHome = getKarajanHome;
51
+
52
+ const DAY_MS = 24 * 60 * 60 * 1000;
53
+
54
+ /**
55
+ * @typedef {Object} GCOptions
56
+ * @property {number} [planRetentionDays=30]
57
+ * @property {number} [draftRetentionDays=60]
58
+ * @property {number} [sessionRetentionDays=7]
59
+ * @property {number} [huRetentionDays=14]
60
+ * @property {boolean} [dryRun=false] - only report what would be removed
61
+ * @property {Date} [now] - clock injection for tests
62
+ */
63
+
64
+ /**
65
+ * @typedef {Object} GCResult
66
+ * @property {Array<{path: string, reason: string, kind: string, bytes?: number}>} removed
67
+ * @property {number} bytesFreed
68
+ * @property {Array<{path: string, error: string}>} errors
69
+ */
70
+
71
+ async function tryReadJson(file) {
72
+ try {
73
+ const txt = await fs.readFile(file, "utf8");
74
+ return JSON.parse(txt);
75
+ } catch {
76
+ return null;
77
+ }
78
+ }
79
+
80
+ async function tryStat(p) {
81
+ try { return await fs.stat(p); } catch { return null; }
82
+ }
83
+
84
+ async function exists(p) {
85
+ return Boolean(await tryStat(p));
86
+ }
87
+
88
+ async function listDir(dir) {
89
+ try { return await fs.readdir(dir, { withFileTypes: true }); } catch { return []; }
90
+ }
91
+
92
+ async function unlinkOrRm(p, dryRun) {
93
+ if (dryRun) return;
94
+ try {
95
+ const st = await fs.stat(p);
96
+ if (st.isDirectory()) await fs.rm(p, { recursive: true, force: true });
97
+ else await fs.unlink(p);
98
+ } catch { /* best-effort */ }
99
+ }
100
+
101
+ function ageInDays(stat, now) {
102
+ return (now.getTime() - stat.mtimeMs) / DAY_MS;
103
+ }
104
+
105
+ // Slugs are lossy — we CAN'T recover a path from one because `_` in
106
+ // the slug could have come from either `/` in the path or a literal
107
+ // `_` in a directory name (e.g. `ws_ai` vs `ws/ai`). That's why plans
108
+ // saved via `savePlan()` now stamp their absolute `projectDir`; the GC
109
+ // reads it from there. Legacy plans without the field fall back to
110
+ // age-only retention — never marked as orphans on the way up.
111
+
112
+ /**
113
+ * Per-plan classifier. Pulled out so the surrounding GC can evaluate
114
+ * candidates in parallel — every plan is independent (no shared state
115
+ * across iterations except the result accumulator, which is folded back
116
+ * after the bursts settle).
117
+ */
118
+ async function classifyPlan(filePath, opts, now) {
119
+ const stat = await tryStat(filePath);
120
+ if (!stat) return { skip: true };
121
+ const data = await tryReadJson(filePath);
122
+ const status = data?.status || "draft";
123
+ const storedDir = typeof data?.projectDir === "string" ? data.projectDir : null;
124
+ const days = ageInDays(stat, now);
125
+
126
+ let reason = null;
127
+ // Orphan check: only when the plan stamped its origin projectDir.
128
+ // Plans older than the savePlan-stamping change don't have it, so
129
+ // we refuse to guess and just apply age-based retention.
130
+ if (storedDir && !(await exists(storedDir))) {
131
+ reason = "orphaned (project dir no longer exists)";
132
+ } else if (FINAL_PLAN_STATUSES.has(status) && days > opts.planRetentionDays) {
133
+ reason = `${status} plan older than ${opts.planRetentionDays}d (${Math.round(days)}d)`;
134
+ } else if (status === "draft" && days > opts.draftRetentionDays) {
135
+ reason = `stale draft older than ${opts.draftRetentionDays}d (${Math.round(days)}d)`;
136
+ } else if (!KNOWN_PLAN_STATUSES.has(status) && days > opts.draftRetentionDays) {
137
+ // Unknown status (corrupt/legacy/plan from a future version) —
138
+ // apply the draft window. Doing nothing would leak them forever.
139
+ reason = `plan with unknown status "${status}" older than ${opts.draftRetentionDays}d (${Math.round(days)}d)`;
140
+ }
141
+ return { skip: false, stat, reason };
142
+ }
143
+
144
+ async function gcPlans(opts) {
145
+ const result = { removed: [], bytesFreed: 0, errors: [] };
146
+ const now = opts.now || new Date();
147
+ const plansRoot = path.join(getKjHome(), "plans");
148
+ if (!(await exists(plansRoot))) return result;
149
+
150
+ // Audit follow-up: was nested sequential for-of with multiple awaits per
151
+ // iteration. Plans are independent files so we now classify them in
152
+ // parallel per project directory. Each project dir's bursts complete
153
+ // before we move to the next so the order of `result.removed` stays
154
+ // deterministic for tests; cross-project parallelism wasn't worth the
155
+ // complexity given KJ_HOME rarely has more than a handful of projects.
156
+ const projEntries = (await listDir(plansRoot)).filter((e) => e.isDirectory());
157
+ for (const projEntry of projEntries) {
158
+ const projDir = path.join(plansRoot, projEntry.name);
159
+ const planEntries = (await listDir(projDir))
160
+ .filter((e) => e.isFile() && e.name.endsWith(".json"));
161
+
162
+ const verdicts = await Promise.all(
163
+ planEntries.map(async (planEntry) => {
164
+ const filePath = path.join(projDir, planEntry.name);
165
+ const verdict = await classifyPlan(filePath, opts, now);
166
+ return { filePath, verdict };
167
+ })
168
+ );
169
+
170
+ let removedCountInDir = 0;
171
+ const totalCountInDir = planEntries.length;
172
+ for (const { filePath, verdict } of verdicts) {
173
+ if (verdict.skip || !verdict.reason) continue;
174
+ try {
175
+ await unlinkOrRm(filePath, opts.dryRun);
176
+ result.removed.push({ path: filePath, reason: verdict.reason, kind: "plan", bytes: verdict.stat.size });
177
+ result.bytesFreed += verdict.stat.size;
178
+ removedCountInDir += 1;
179
+ } catch (err) {
180
+ result.errors.push({ path: filePath, error: err.message });
181
+ }
182
+ }
183
+
184
+ // If we just emptied the project dir, remove it too.
185
+ if (removedCountInDir === totalCountInDir && totalCountInDir > 0) {
186
+ try {
187
+ await unlinkOrRm(projDir, opts.dryRun);
188
+ } catch { /* ignore */ }
189
+ }
190
+ }
191
+ return result;
192
+ }
193
+
194
+ async function gcSessions(opts) {
195
+ const result = { removed: [], bytesFreed: 0, errors: [] };
196
+ const now = opts.now || new Date();
197
+ const sessionsRoot = path.join(getKarajanHome(), "sessions");
198
+ if (!(await exists(sessionsRoot))) return result;
199
+
200
+ // Pipelines that get SIGKILL'd (power off, terminal closed, OOM…) leave
201
+ // their session.json stuck at status="running" forever. The original GC
202
+ // only reclaimed FINAL statuses and these zombies accumulated on the
203
+ // board. After `staleRunningDays` with no activity we assume the run is
204
+ // dead and sweep them. Default mirrors sessionRetentionDays so there is
205
+ // only one knob to reason about.
206
+ const staleRunningDays = opts.staleRunningDays ?? opts.sessionRetentionDays;
207
+
208
+ // Audit follow-up: parallelise the per-session classification (read
209
+ // session.json + stat the dir). Deletion stays sequential so the
210
+ // result.removed order is deterministic for tests.
211
+ const sessEntries = (await listDir(sessionsRoot)).filter((e) => e.isDirectory());
212
+ const verdicts = await Promise.all(
213
+ sessEntries.map(async (sessEntry) => {
214
+ const sessDir = path.join(sessionsRoot, sessEntry.name);
215
+ const sessionFile = path.join(sessDir, "session.json");
216
+ const stat = await tryStat(sessDir);
217
+ if (!stat) return { sessDir, reason: null };
218
+ const data = await tryReadJson(sessionFile);
219
+ const status = data?.status;
220
+ const days = ageInDays(stat, now);
221
+
222
+ let reason = null;
223
+ if (status && FINAL_SESSION_STATUSES.has(status) && days > opts.sessionRetentionDays) {
224
+ reason = `${status} session older than ${opts.sessionRetentionDays}d (${Math.round(days)}d)`;
225
+ } else if ((!status || status === "running") && days > staleRunningDays) {
226
+ reason = `zombie ${status || "unknown-status"} session older than ${staleRunningDays}d (${Math.round(days)}d)`;
227
+ }
228
+ return { sessDir, reason };
229
+ })
230
+ );
231
+
232
+ for (const { sessDir, reason } of verdicts) {
233
+ if (!reason) continue;
234
+ try {
235
+ await unlinkOrRm(sessDir, opts.dryRun);
236
+ result.removed.push({ path: sessDir, reason, kind: "session" });
237
+ } catch (err) {
238
+ result.errors.push({ path: sessDir, error: err.message });
239
+ }
240
+ }
241
+ return result;
242
+ }
243
+
244
+ async function gcHuStories(opts) {
245
+ const result = { removed: [], bytesFreed: 0, errors: [] };
246
+ const now = opts.now || new Date();
247
+ const huRoot = path.join(getKarajanHome(), "hu-stories");
248
+ if (!(await exists(huRoot))) return result;
249
+
250
+ // Audit follow-up: stat-then-classify in parallel; deletion stays
251
+ // sequential for deterministic ordering.
252
+ const batchEntries = (await listDir(huRoot)).filter((e) => e.isDirectory());
253
+ const verdicts = await Promise.all(
254
+ batchEntries.map(async (batchEntry) => {
255
+ const batchDir = path.join(huRoot, batchEntry.name);
256
+ const stat = await tryStat(batchDir);
257
+ if (!stat) return null;
258
+ const days = ageInDays(stat, now);
259
+ if (days <= opts.huRetentionDays) return null;
260
+ return {
261
+ batchDir,
262
+ reason: `HU batch older than ${opts.huRetentionDays}d (${Math.round(days)}d)`,
263
+ };
264
+ })
265
+ );
266
+
267
+ for (const v of verdicts) {
268
+ if (!v) continue;
269
+ try {
270
+ await unlinkOrRm(v.batchDir, opts.dryRun);
271
+ result.removed.push({ path: v.batchDir, reason: v.reason, kind: "hu-batch" });
272
+ } catch (err) {
273
+ result.errors.push({ path: v.batchDir, error: err.message });
274
+ }
275
+ }
276
+ return result;
277
+ }
278
+
279
+ // KJC-TSK-0414 PR3: limpia ~/.kj/standby/done/<id>-<ts>.json finalizadas
280
+ // hace > N días. Las pendientes en ~/.kj/standby/<id>.json NUNCA se tocan
281
+ // — son las que esperan resume.
282
+ async function gcStandbyDone(opts) {
283
+ const result = { removed: [], bytesFreed: 0, errors: [] };
284
+ const now = opts.now || new Date();
285
+ const doneDir = path.join(getKjHome(), "standby", "done");
286
+ if (!(await exists(doneDir))) return result;
287
+
288
+ const entries = (await listDir(doneDir)).filter((e) => e.isFile());
289
+ for (const entry of entries) {
290
+ const file = path.join(doneDir, entry.name);
291
+ const stat = await tryStat(file);
292
+ if (!stat) continue;
293
+ const days = ageInDays(stat, now);
294
+ if (days <= opts.standbyDoneRetentionDays) continue;
295
+ try {
296
+ await unlinkOrRm(file, opts.dryRun);
297
+ result.removed.push({ path: file, reason: `standby done > ${opts.standbyDoneRetentionDays}d (${Math.round(days)}d)`, kind: "standby-done" });
298
+ } catch (err) {
299
+ result.errors.push({ path: file, error: err.message });
300
+ }
301
+ }
302
+ return result;
303
+ }
304
+
305
+ // KJC-TSK-0414 PR3: limpia ~/.karajan/audits/<project>/ con last mtime > N días.
306
+ // El usuario reportó decenas de carpetas kj-test-* huérfanas — son audits de
307
+ // runs antiguos cuyo project ya no existe o que llevan meses sin actividad.
308
+ async function gcAudits(opts) {
309
+ const result = { removed: [], bytesFreed: 0, errors: [] };
310
+ const now = opts.now || new Date();
311
+ const auditRoot = path.join(getKarajanHome(), "audits");
312
+ if (!(await exists(auditRoot))) return result;
313
+
314
+ const entries = (await listDir(auditRoot)).filter((e) => e.isDirectory());
315
+ for (const entry of entries) {
316
+ const dir = path.join(auditRoot, entry.name);
317
+ const stat = await tryStat(dir);
318
+ if (!stat) continue;
319
+ const days = ageInDays(stat, now);
320
+ if (days <= opts.auditsRetentionDays) continue;
321
+ try {
322
+ await unlinkOrRm(dir, opts.dryRun);
323
+ result.removed.push({ path: dir, reason: `audit > ${opts.auditsRetentionDays}d sin actividad (${Math.round(days)}d)`, kind: "audit" });
324
+ } catch (err) {
325
+ result.errors.push({ path: dir, error: err.message });
326
+ }
327
+ }
328
+ return result;
329
+ }
330
+
331
+ // KJC-TSK-0414 PR3: ~/.karajan/hu-board-runs/<runId>/ con mtime > N días.
332
+ async function gcHuBoardRuns(opts) {
333
+ const result = { removed: [], bytesFreed: 0, errors: [] };
334
+ const now = opts.now || new Date();
335
+ const root = path.join(getKarajanHome(), "hu-board-runs");
336
+ if (!(await exists(root))) return result;
337
+
338
+ const entries = (await listDir(root)).filter((e) => e.isDirectory());
339
+ for (const entry of entries) {
340
+ const dir = path.join(root, entry.name);
341
+ const stat = await tryStat(dir);
342
+ if (!stat) continue;
343
+ const days = ageInDays(stat, now);
344
+ if (days <= opts.huBoardRunsRetentionDays) continue;
345
+ try {
346
+ await unlinkOrRm(dir, opts.dryRun);
347
+ result.removed.push({ path: dir, reason: `hu-board-run > ${opts.huBoardRunsRetentionDays}d (${Math.round(days)}d)`, kind: "hu-board-run" });
348
+ } catch (err) {
349
+ result.errors.push({ path: dir, error: err.message });
350
+ }
351
+ }
352
+ return result;
353
+ }
354
+
355
+ function mergeResults(...parts) {
356
+ const out = { removed: [], bytesFreed: 0, errors: [] };
357
+ for (const r of parts) {
358
+ out.removed.push(...r.removed);
359
+ out.bytesFreed += r.bytesFreed;
360
+ out.errors.push(...r.errors);
361
+ }
362
+ return out;
363
+ }
364
+
365
+ function defaultOpts(opts = {}) {
366
+ return {
367
+ planRetentionDays: opts.planRetentionDays ?? 30,
368
+ draftRetentionDays: opts.draftRetentionDays ?? 60,
369
+ sessionRetentionDays: opts.sessionRetentionDays ?? 7,
370
+ huRetentionDays: opts.huRetentionDays ?? 14,
371
+ // KJC-TSK-0414 PR3
372
+ standbyDoneRetentionDays: opts.standbyDoneRetentionDays ?? 7,
373
+ auditsRetentionDays: opts.auditsRetentionDays ?? 30,
374
+ huBoardRunsRetentionDays: opts.huBoardRunsRetentionDays ?? 30,
375
+ dryRun: Boolean(opts.dryRun),
376
+ now: opts.now || new Date(),
377
+ };
378
+ }
379
+
380
+ /**
381
+ * Manual GC — backs `kj clean`. Caller decides dryRun via opts.
382
+ *
383
+ * @param {GCOptions} [opts]
384
+ * @returns {Promise<GCResult>}
385
+ */
386
+ export async function runManualGC(opts = {}) {
387
+ const o = defaultOpts(opts);
388
+ // Audit follow-up: was 3 sequential awaits. Plans, sessions and HU
389
+ // stories live in different directory subtrees and don't share state,
390
+ // so Promise.all is safe and roughly halves end-to-end GC latency on
391
+ // a populated KJ_HOME.
392
+ const [plans, sessions, huStories, standbyDone, audits, huBoardRuns] = await Promise.all([
393
+ gcPlans(o),
394
+ gcSessions(o),
395
+ gcHuStories(o),
396
+ gcStandbyDone(o),
397
+ gcAudits(o),
398
+ gcHuBoardRuns(o),
399
+ ]);
400
+ return mergeResults(plans, sessions, huStories, standbyDone, audits, huBoardRuns);
401
+ }
402
+
403
+ /**
404
+ * Auto GC — silent, called at the top of `kj run` / `kj plan`. Always
405
+ * destructive (dryRun ignored). Returns the same result so the caller
406
+ * can print a one-liner summary if anything was removed.
407
+ *
408
+ * @param {Omit<GCOptions, 'dryRun'>} [opts]
409
+ * @returns {Promise<GCResult>}
410
+ */
411
+ export async function runAutoGC(opts = {}) {
412
+ return runManualGC({ ...opts, dryRun: false });
413
+ }
414
+
415
+ /**
416
+ * Wipe the HU board SQLite database (+WAL + SHM + pidfile). Used by
417
+ * `kj clean --nuke` as the "reset everything" button.
418
+ *
419
+ * When the caller also stops the board process (we do so best-effort
420
+ * here via the pidfile), re-starting the board with `kj board start`
421
+ * or via the post-`kj plan` autostart recreates the DB empty.
422
+ *
423
+ * @param {{ dryRun?: boolean }} [opts]
424
+ * @returns {Promise<GCResult>}
425
+ */
426
+ export async function nukeBoardDb(opts = {}) {
427
+ const result = { removed: [], bytesFreed: 0, errors: [] };
428
+ const dryRun = Boolean(opts.dryRun);
429
+ const kHome = getKarajanHome();
430
+
431
+ // Stop the board if we find a live pidfile — UNLESS we're being
432
+ // invoked from inside the board itself (the user clicked ⚡ →
433
+ // `kj clean --nuke` in the launcher). In that case killing the
434
+ // board would kill the page they're looking at, mid-confirm.
435
+ // command-runner.js sets KJ_INSIDE_BOARD=1 on every spawn so we
436
+ // can detect this. The nuke still wipes the DB; the board picks
437
+ // up the empty DB on its next chokidar tick.
438
+ const insideBoard = process.env.KJ_INSIDE_BOARD === "1";
439
+ const pidFile = path.join(kHome, "hu-board.pid");
440
+ try {
441
+ const pid = Number.parseInt(await fs.readFile(pidFile, "utf8"), 10);
442
+ if (!dryRun && Number.isFinite(pid) && !insideBoard) {
443
+ try { process.kill(pid, 0); process.kill(pid, "SIGTERM"); } catch { /* already dead */ }
444
+ }
445
+ } catch { /* no pidfile */ }
446
+
447
+ // The DB (+ WAL / SHM) can be hundreds of MB on old installs; report
448
+ // the bytes freed so the user sees the reward.
449
+ for (const name of ["hu-board.db", "hu-board.db-wal", "hu-board.db-shm", "hu-board.pid"]) {
450
+ const p = path.join(kHome, name);
451
+ const stat = await tryStat(p);
452
+ if (!stat) continue;
453
+ try {
454
+ await unlinkOrRm(p, dryRun);
455
+ result.removed.push({ path: p, reason: "board db wiped (--nuke)", kind: "board", bytes: stat.size });
456
+ result.bytesFreed += stat.size;
457
+ } catch (err) {
458
+ result.errors.push({ path: p, error: err.message });
459
+ }
460
+ }
461
+ return result;
462
+ }
463
+
464
+ /**
465
+ * Format a GC result as a single human-readable line. Used by the
466
+ * silent auto-GC path so the user only sees one line in the rare case
467
+ * something was actually cleaned.
468
+ *
469
+ * @param {GCResult} result
470
+ * @returns {string|null} null when nothing was removed
471
+ */
472
+ export function summarizeGC(result) {
473
+ if (!result.removed.length) return null;
474
+ const byKind = {};
475
+ for (const r of result.removed) {
476
+ byKind[r.kind] = (byKind[r.kind] || 0) + 1;
477
+ }
478
+ const parts = Object.entries(byKind).map(([k, n]) => `${n} ${k}${n === 1 ? "" : "s"}`);
479
+ const kb = Math.round(result.bytesFreed / 1024);
480
+ return `[gc] removed ${parts.join(", ")}${kb ? ` (${kb} KiB)` : ""}`;
481
+ }