karajan-code 4.70.4 → 5.0.1

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 (390) hide show
  1. package/README.md +2 -2
  2. package/package.json +6 -14
  3. package/packages/hu-board/package.json +1 -1
  4. package/packages/hu-board/public/app.js +4 -18
  5. package/packages/hu-board/public/index.html +5 -2
  6. package/packages/hu-board/public/styles.css +26 -0
  7. package/packages/hu-board/public/utils/api.js +0 -74
  8. package/packages/hu-board/public/utils/board-tour.js +62 -0
  9. package/packages/hu-board/public/utils/board-view.js +49 -144
  10. package/packages/hu-board/public/utils/command-launcher.js +8 -70
  11. package/packages/hu-board/public/utils/formatters.js +10 -0
  12. package/packages/hu-board/public/utils/governance-view.js +13 -1
  13. package/packages/hu-board/public/utils/hu-actions.js +17 -68
  14. package/packages/hu-board/public/utils/init-listeners.js +7 -8
  15. package/packages/hu-board/public/utils/log-panel.js +3 -16
  16. package/packages/hu-board/public/utils/maggle-mode.js +2 -12
  17. package/packages/hu-board/public/utils/modals.js +7 -0
  18. package/packages/hu-board/public/utils/project-picker-view.js +1 -1
  19. package/packages/hu-board/public/utils/server-push.js +3 -22
  20. package/packages/hu-board/public/utils/shared-board-notice.js +33 -0
  21. package/packages/hu-board/public/utils/story-detail-view.js +5 -19
  22. package/packages/hu-board/src/cleanup-zombies.js +0 -1
  23. package/packages/hu-board/src/command-runner.js +36 -25
  24. package/packages/hu-board/src/db-connection.js +282 -0
  25. package/packages/hu-board/src/db-paths.js +113 -0
  26. package/packages/hu-board/src/db-queries.js +188 -0
  27. package/packages/hu-board/src/db-tombstones.js +51 -0
  28. package/packages/hu-board/src/db.js +28 -619
  29. package/packages/hu-board/src/ephemeral-cleaner.js +2 -3
  30. package/packages/hu-board/src/hu-zombie-reaper.js +5 -1
  31. package/packages/hu-board/src/plan-mutations.js +3 -153
  32. package/packages/hu-board/src/plans-service.js +55 -0
  33. package/packages/hu-board/src/routes/api/commands.js +122 -0
  34. package/packages/hu-board/src/routes/api/plans.js +55 -0
  35. package/packages/hu-board/src/routes/api/projects.js +267 -0
  36. package/packages/hu-board/src/routes/api/prompts.js +135 -0
  37. package/packages/hu-board/src/routes/api/sessions.js +95 -0
  38. package/packages/hu-board/src/routes/api/stories.js +183 -0
  39. package/packages/hu-board/src/routes/api/system.js +241 -0
  40. package/packages/hu-board/src/routes/api/tombstones.js +38 -0
  41. package/packages/hu-board/src/routes/api.js +24 -1371
  42. package/packages/hu-board/src/routes/governance.js +23 -1
  43. package/packages/hu-board/src/routes/lanes.js +40 -0
  44. package/packages/hu-board/src/routes/rag.js +4 -3
  45. package/packages/hu-board/src/server.js +20 -34
  46. package/packages/hu-board/src/sync-files.js +318 -0
  47. package/packages/hu-board/src/sync-project.js +99 -0
  48. package/packages/hu-board/src/sync.js +22 -566
  49. package/packages/hu-board/src/zombie-reaper.js +1 -1
  50. package/scripts/dual-publish.mjs +19 -3
  51. package/scripts/postinstall.js +15 -2
  52. package/scripts/verify-pack-mcp.mjs +11 -7
  53. package/scripts/verify-pack.mjs +4 -4
  54. package/src/audit/audit-history.js +3 -2
  55. package/src/audit/dead-exports.js +68 -59
  56. package/src/audit/deterministic-summary.js +33 -13
  57. package/src/audit/npm-signatures.js +41 -0
  58. package/src/audit/sbom.js +35 -0
  59. package/src/checks/agent-token.js +112 -0
  60. package/src/checks/dir-setup.js +34 -1
  61. package/src/checks/native-build.js +21 -39
  62. package/src/checks/runner.js +128 -142
  63. package/src/cli/advanced-commands.js +6 -6
  64. package/src/cli/register-meta.js +42 -82
  65. package/src/cli/register-pipeline.js +8 -143
  66. package/src/cli/register-plan.js +9 -100
  67. package/src/cli/register-roles-skills.js +1 -1
  68. package/src/cli/retired-commands.js +49 -0
  69. package/src/cli.js +18 -5
  70. package/src/commands/agent-run.js +40 -26
  71. package/src/commands/audit.js +127 -124
  72. package/src/commands/board.js +5 -9
  73. package/src/commands/check.js +29 -16
  74. package/src/commands/doctor.js +2 -0
  75. package/src/commands/env.js +176 -150
  76. package/src/commands/go.js +71 -53
  77. package/src/commands/harden.js +90 -81
  78. package/src/commands/hu-start.js +52 -0
  79. package/src/commands/hu.js +288 -221
  80. package/src/commands/identity.js +111 -78
  81. package/src/commands/init-atril.js +47 -0
  82. package/src/commands/init.js +11 -40
  83. package/src/commands/install-tools.js +70 -85
  84. package/src/commands/main-guard.js +78 -0
  85. package/src/commands/policy.js +226 -184
  86. package/src/commands/qmd.js +27 -21
  87. package/src/commands/rag.js +121 -55
  88. package/src/commands/review-gate.js +421 -341
  89. package/src/commands/skills.js +4 -1
  90. package/src/commands/start.js +4 -5
  91. package/src/commands/steward.js +96 -47
  92. package/src/commands/tournament.js +46 -37
  93. package/src/commands/worktree.js +107 -26
  94. package/src/config/defaults.js +1 -0
  95. package/src/config/loader.js +34 -17
  96. package/src/config/role-resolver.js +1 -0
  97. package/src/config/schema.js +1 -0
  98. package/src/config/secrets.js +36 -0
  99. package/src/config.js +1 -0
  100. package/src/environment/adr.js +16 -2
  101. package/src/git/karajan-label.js +20 -8
  102. package/src/guards/main-guard.js +54 -0
  103. package/src/harden/hook-templates.js +16 -1
  104. package/src/harden/sentinel/posttooluse.mjs +18 -6
  105. package/src/harden/sentinel/pretooluse-sentinel.mjs +64 -36
  106. package/src/harden/sentinel/sentinel-lib.mjs +129 -26
  107. package/src/harden/sentinel/sentinel-shell.mjs +5 -16
  108. package/src/harden/sentinel/sessionstart.mjs +3 -2
  109. package/src/harden/sentinel/stop.mjs +5 -4
  110. package/src/harden/workflow-templates.js +1 -1
  111. package/src/hu/board-lookup.js +29 -0
  112. package/src/hu/gh-identity.js +23 -0
  113. package/src/hu/github-board.js +4 -2
  114. package/src/hu/lane-prune.js +83 -13
  115. package/src/hu/lane-setup.js +65 -0
  116. package/src/hu/lanes.js +66 -0
  117. package/src/hu/team-board-mirror.js +3 -2
  118. package/src/infrastructure/command-runner.js +1 -1
  119. package/src/infrastructure/file-system-service.js +1 -1
  120. package/src/mcp/handlers/direct-handlers.js +2 -292
  121. package/src/mcp/handlers/hu-handlers.js +1 -35
  122. package/src/mcp/handlers/management-handlers.js +2 -42
  123. package/src/mcp/handlers/rag-handler.js +9 -1
  124. package/src/mcp/server-handlers.js +16 -22
  125. package/src/mcp/tools.js +1 -196
  126. package/src/plan/lane-plans.js +54 -0
  127. package/src/plan/plan-store.js +7 -4
  128. package/src/policy/add.js +26 -12
  129. package/src/policy/adr-number-check.js +31 -0
  130. package/src/prompts/audit.js +299 -339
  131. package/src/prompts/card-context.js +10 -5
  132. package/src/prompts/reviewer.js +5 -2
  133. package/src/rag/auto-update.js +71 -44
  134. package/src/rag/bootstrap-index.js +46 -54
  135. package/src/rag/indexer.js +9 -4
  136. package/src/rag/migrate.js +1 -1
  137. package/src/rag/onnx-fallback.js +2 -2
  138. package/src/rag/pid-lease.js +181 -0
  139. package/src/rag/project-store.js +2 -12
  140. package/src/review/card-first.js +57 -46
  141. package/src/review/gate-gitignore.js +23 -4
  142. package/src/review/gate-hook.js +12 -4
  143. package/src/review/one-shot-review.js +45 -35
  144. package/src/review/publish-pr.js +14 -2
  145. package/src/review/repo-docs.js +73 -0
  146. package/src/review/repo-stack.js +59 -0
  147. package/src/review/reviewer-fallback.js +19 -4
  148. package/src/review/seal-commit.js +29 -0
  149. package/src/review/sonar-pregate.js +59 -44
  150. package/src/review/third-party-review.js +90 -16
  151. package/src/review/third-party-size.js +43 -0
  152. package/src/review/third-party-skills.js +54 -0
  153. package/src/review/verify-pr.js +1 -24
  154. package/src/roles/audit-role.js +106 -125
  155. package/src/skills/atril-cli.js +19 -1
  156. package/src/skills/atril-skills.js +5 -4
  157. package/src/skills/skills-search.js +72 -0
  158. package/src/skills/translate.js +62 -0
  159. package/src/sonar/scanner.js +116 -101
  160. package/src/steward/invariants.js +5 -4
  161. package/src/steward/phantom-sweep.js +66 -0
  162. package/src/tournament/judge.js +60 -53
  163. package/src/utils/gh-account.js +37 -0
  164. package/src/utils/git.js +0 -13
  165. package/src/utils/home-migration.js +7 -1
  166. package/src/utils/main-checkout.js +53 -0
  167. package/src/utils/qmd-collection.js +3 -1
  168. package/src/utils/welcome.js +3 -3
  169. package/templates/{skills → atril/commands}/kj-architect.md +5 -0
  170. package/templates/{skills → atril/commands}/kj-audit.md +5 -0
  171. package/templates/{skills → atril/commands}/kj-board.md +5 -0
  172. package/templates/{skills → atril/commands}/kj-code.md +5 -0
  173. package/templates/{skills → atril/commands}/kj-csv-transform.md +5 -0
  174. package/templates/{skills → atril/commands}/kj-data-report.md +5 -0
  175. package/templates/{skills → atril/commands}/kj-discover.md +5 -0
  176. package/templates/{skills → atril/commands}/kj-rag-query.md +5 -0
  177. package/templates/{skills → atril/commands}/kj-review.md +5 -0
  178. package/templates/{skills → atril/commands}/kj-security.md +5 -0
  179. package/templates/{skills → atril/commands}/kj-sonar.md +5 -0
  180. package/templates/{skills → atril/commands}/kj-sql-analysis.md +5 -0
  181. package/templates/{skills → atril/commands}/kj-test.md +5 -0
  182. package/packages/hu-board/public/utils/run-launcher.js +0 -169
  183. package/packages/hu-board/src/run-tracker.js +0 -121
  184. package/src/activity-log.js +0 -59
  185. package/src/agents/host-agent.js +0 -38
  186. package/src/agents/model-registry.js +0 -142
  187. package/src/audit/plan-adherence.js +0 -224
  188. package/src/autonomy/arbiter-role.js +0 -71
  189. package/src/autonomy/arbiter.js +0 -22
  190. package/src/autonomy/decider.js +0 -18
  191. package/src/autonomy/decision-resolver.js +0 -58
  192. package/src/autonomy/outcome-report.js +0 -55
  193. package/src/autonomy/stage-ask.js +0 -30
  194. package/src/brain/decision-tracker.js +0 -125
  195. package/src/brain/decisor.js +0 -243
  196. package/src/brain/solomon-consult.js +0 -171
  197. package/src/brain/standby-scheduler.js +0 -8
  198. package/src/budget/comparison.js +0 -96
  199. package/src/canvas/parse-md.js +0 -238
  200. package/src/canvas/schema.js +0 -101
  201. package/src/canvas/to-flat.js +0 -85
  202. package/src/canvas/validate.js +0 -163
  203. package/src/ci/dispatch.js +0 -99
  204. package/src/ci/index.js +0 -3
  205. package/src/ci/pr-diff.js +0 -26
  206. package/src/cli/register-standby.js +0 -26
  207. package/src/commands/architect.js +0 -111
  208. package/src/commands/autorun.js +0 -49
  209. package/src/commands/researcher.js +0 -54
  210. package/src/commands/resume.js +0 -72
  211. package/src/commands/review.js +0 -89
  212. package/src/commands/run.js +0 -185
  213. package/src/commands/standby.js +0 -117
  214. package/src/commands/status.js +0 -36
  215. package/src/commands/triage.js +0 -78
  216. package/src/commands/undo.js +0 -62
  217. package/src/domains/domain-loader.js +0 -245
  218. package/src/domains/domain-registry.js +0 -140
  219. package/src/domains/domain-synthesizer.js +0 -129
  220. package/src/git/automation.js +0 -433
  221. package/src/git/coder-commit-guard.js +0 -40
  222. package/src/git/hu-automation.js +0 -243
  223. package/src/git/hu-snapshot.js +0 -10
  224. package/src/git/symlink-targets.js +0 -57
  225. package/src/golden/asserter.js +0 -92
  226. package/src/golden/loader.js +0 -52
  227. package/src/golden/runner.js +0 -92
  228. package/src/golden/schema.js +0 -41
  229. package/src/guards/duplicate-members.js +0 -86
  230. package/src/guards/intent-guard.js +0 -161
  231. package/src/guards/perf-guard.js +0 -126
  232. package/src/guards/policy-guard.js +0 -90
  233. package/src/guards/policy-resolver.js +0 -85
  234. package/src/guards/secret-redactor.js +0 -71
  235. package/src/hu/acceptance-runner.js +0 -138
  236. package/src/hu/lazy-planner.js +0 -109
  237. package/src/hu/parallel-executor.js +0 -94
  238. package/src/hu/routing-metrics.js +0 -17
  239. package/src/hu/split-validator.js +0 -143
  240. package/src/hu/splitting-detector.js +0 -158
  241. package/src/hu/splitting-generator.js +0 -175
  242. package/src/hu/store.js +0 -394
  243. package/src/infrastructure/mocks.js +0 -182
  244. package/src/mcp/handlers/run-handler.js +0 -318
  245. package/src/mcp/sovereignty-guard.js +0 -113
  246. package/src/mcp/tool-arg-normalizers.js +0 -16
  247. package/src/mutate/reviewer-signal.js +0 -76
  248. package/src/orchestrator/agent-fallback.js +0 -81
  249. package/src/orchestrator/brain-coordinator.js +0 -186
  250. package/src/orchestrator/ci-integration.js +0 -139
  251. package/src/orchestrator/config-init.js +0 -426
  252. package/src/orchestrator/direct-actions.js +0 -217
  253. package/src/orchestrator/drivers/error-recovery.js +0 -287
  254. package/src/orchestrator/drivers/init-context.js +0 -238
  255. package/src/orchestrator/drivers/iteration-loop.js +0 -382
  256. package/src/orchestrator/drivers/iteration-phases/coder-and-refactorer.js +0 -69
  257. package/src/orchestrator/drivers/iteration-phases/guards.js +0 -91
  258. package/src/orchestrator/drivers/iteration-phases/handle-approved.js +0 -29
  259. package/src/orchestrator/drivers/iteration-phases/quality-gates.js +0 -125
  260. package/src/orchestrator/drivers/iteration-phases/reviewer-gate.js +0 -62
  261. package/src/orchestrator/drivers/post-loop.js +0 -404
  262. package/src/orchestrator/drivers/pre-loop-phases/auto-hu-batch.js +0 -137
  263. package/src/orchestrator/drivers/pre-loop-phases/config-deprecations.js +0 -47
  264. package/src/orchestrator/drivers/pre-loop-phases/ensure-addyosmani-skills.js +0 -84
  265. package/src/orchestrator/drivers/pre-loop-phases/inject-loaded-plan.js +0 -393
  266. package/src/orchestrator/drivers/pre-loop.js +0 -524
  267. package/src/orchestrator/drivers/run-hu-batch.js +0 -468
  268. package/src/orchestrator/feedback-enrichment.js +0 -140
  269. package/src/orchestrator/feedback-queue.js +0 -151
  270. package/src/orchestrator/flow-control.js +0 -170
  271. package/src/orchestrator/flow-runner.js +0 -365
  272. package/src/orchestrator/fs-leak-detector.js +0 -228
  273. package/src/orchestrator/hu-scheduler.js +0 -114
  274. package/src/orchestrator/hu-sub-pipeline.js +0 -642
  275. package/src/orchestrator/iteration-gate.js +0 -73
  276. package/src/orchestrator/iteration-stages.js +0 -9
  277. package/src/orchestrator/parallel-limiter.js +0 -74
  278. package/src/orchestrator/pipeline-context.js +0 -72
  279. package/src/orchestrator/post-loop-stages.js +0 -468
  280. package/src/orchestrator/pre-loop-stages.js +0 -11
  281. package/src/orchestrator/preflight-checks.js +0 -487
  282. package/src/orchestrator/repair/repair-detector.js +0 -117
  283. package/src/orchestrator/repair/repair-runner.js +0 -142
  284. package/src/orchestrator/reviewer-fallback.js +0 -45
  285. package/src/orchestrator/role-output-compressor.js +0 -189
  286. package/src/orchestrator/session-journal.js +0 -308
  287. package/src/orchestrator/solomon-escalation.js +0 -331
  288. package/src/orchestrator/solomon-rules.js +0 -212
  289. package/src/orchestrator/stages/acceptance-coverage-stage.js +0 -47
  290. package/src/orchestrator/stages/architect-stage.js +0 -172
  291. package/src/orchestrator/stages/coder-stage.js +0 -506
  292. package/src/orchestrator/stages/domain-curator-stage.js +0 -82
  293. package/src/orchestrator/stages/format-stage.js +0 -61
  294. package/src/orchestrator/stages/hu-reviewer-phases/detect-and-apply-splits.js +0 -127
  295. package/src/orchestrator/stages/hu-reviewer-stage.js +0 -419
  296. package/src/orchestrator/stages/perf-stage.js +0 -140
  297. package/src/orchestrator/stages/planner-stage.js +0 -85
  298. package/src/orchestrator/stages/rag-context-stage.js +0 -80
  299. package/src/orchestrator/stages/rag-preload-decisor.js +0 -58
  300. package/src/orchestrator/stages/research-stage.js +0 -69
  301. package/src/orchestrator/stages/reviewer-stage.js +0 -431
  302. package/src/orchestrator/stages/sonar-stage.js +0 -439
  303. package/src/orchestrator/stages/stage-classes.js +0 -84
  304. package/src/orchestrator/stages/stage-executor.js +0 -155
  305. package/src/orchestrator/stages/suite-green-stage.js +0 -62
  306. package/src/orchestrator/stages/tdd-discipline-stage.js +0 -56
  307. package/src/orchestrator/stages/tool-judge-stage.js +0 -53
  308. package/src/orchestrator/stages/triage-stage.js +0 -172
  309. package/src/orchestrator/standby.js +0 -71
  310. package/src/orchestrator/verification-gate.js +0 -183
  311. package/src/orchestrator.js +0 -22
  312. package/src/plan/adr-generator.js +0 -170
  313. package/src/plan/adr-loader.js +0 -60
  314. package/src/plan/plan-executor.js +0 -100
  315. package/src/plan/plan-validation.js +0 -4
  316. package/src/prompts/acceptance-coverage.js +0 -32
  317. package/src/prompts/arbiter.js +0 -38
  318. package/src/prompts/architect.js +0 -103
  319. package/src/prompts/hu-reviewer.js +0 -244
  320. package/src/prompts/tool-correctness-judge.js +0 -31
  321. package/src/prompts/triage.js +0 -57
  322. package/src/rag/embedder.js +0 -2
  323. package/src/rag/embedders/_cloud-base.js +0 -2
  324. package/src/rag/embedders/cohere.js +0 -2
  325. package/src/rag/embedders/mistral.js +0 -2
  326. package/src/rag/embedders/onnx.js +0 -2
  327. package/src/rag/embedders/openai.js +0 -2
  328. package/src/rag/embedders/voyage.js +0 -2
  329. package/src/rag/rerank.js +0 -4
  330. package/src/rag/where-parser.js +0 -4
  331. package/src/repeat-detector.js +0 -88
  332. package/src/review/diff-generator.js +0 -112
  333. package/src/review/git-stash-helper.js +0 -104
  334. package/src/review/schema.js +0 -31
  335. package/src/review/scope-filter.js +0 -152
  336. package/src/review/snapshot-diff.js +0 -122
  337. package/src/review/tdd-discipline.js +0 -90
  338. package/src/review/tdd-policy.js +0 -69
  339. package/src/roles/acceptance-coverage-role.js +0 -78
  340. package/src/roles/architect-role.js +0 -77
  341. package/src/roles/coder-role.js +0 -113
  342. package/src/roles/commiter-role.js +0 -113
  343. package/src/roles/domain-curator-role.js +0 -134
  344. package/src/roles/hu-reviewer-role.js +0 -64
  345. package/src/roles/impeccable-role.js +0 -85
  346. package/src/roles/index.js +0 -18
  347. package/src/roles/karajan-brain-role.js +0 -134
  348. package/src/roles/perf-role.js +0 -122
  349. package/src/roles/planner-role.js +0 -151
  350. package/src/roles/refactorer-role.js +0 -30
  351. package/src/roles/repairer-role.js +0 -181
  352. package/src/roles/researcher-role.js +0 -70
  353. package/src/roles/reviewer-role.js +0 -115
  354. package/src/roles/security-role.js +0 -73
  355. package/src/roles/solomon-role.js +0 -271
  356. package/src/roles/sonar-role.js +0 -72
  357. package/src/roles/tester-role.js +0 -235
  358. package/src/roles/tool-correctness-judge-role.js +0 -78
  359. package/src/roles/triage-role.js +0 -99
  360. package/src/session/journal/decisions-writer.js +0 -200
  361. package/src/session/journal/iteration-logger.js +0 -194
  362. package/src/session/journal/summary-writer.js +0 -320
  363. package/src/session/journal/tree-writer.js +0 -164
  364. package/src/session/journal/write-all.js +0 -89
  365. package/src/session-cleanup.js +0 -116
  366. package/src/skills/semantic-detector.js +0 -167
  367. package/src/skills/skill-detector.js +0 -385
  368. package/src/sonar/cloud-scanner.js +0 -78
  369. package/src/tracker-bootstrap.js +0 -44
  370. package/src/types/index.js +0 -38
  371. package/src/types/policy.js +0 -43
  372. package/src/types/solomon.js +0 -87
  373. package/src/utils/async-lock.js +0 -31
  374. package/src/utils/budget.js +0 -229
  375. package/src/utils/cwd-confirm.js +0 -39
  376. package/src/utils/display/event-handlers.js +0 -403
  377. package/src/utils/display/formatters.js +0 -132
  378. package/src/utils/display/header.js +0 -47
  379. package/src/utils/display/session-summary.js +0 -118
  380. package/src/utils/display/solomon.js +0 -42
  381. package/src/utils/pricing.js +0 -29
  382. package/src/utils/quota-simulator.js +0 -56
  383. package/src/utils/role-assigner.js +0 -137
  384. package/src/utils/rtk-wrapper.js +0 -87
  385. package/src/utils/run-registry.js +0 -9
  386. package/src/utils/status-dashboard.js +0 -254
  387. package/src/utils/tool-call-extractor.js +0 -56
  388. package/src/webperf/devtools-detect.js +0 -112
  389. package/src/webperf/webperf-role-integration.js +0 -66
  390. package/templates/skills/kj-run.md +0 -73
package/src/hu/store.js DELETED
@@ -1,394 +0,0 @@
1
- /**
2
- * HU (Historia de Usuario / User Story) storage. Persists HU batches and
3
- * their stories to the session directory.
4
- *
5
- * @typedef {import("../types/hu.js").HUStory} HUStory
6
- * @typedef {import("../types/hu.js").HUBatch} HUBatch
7
- * @typedef {import("../types/hu.js").HUStatus} HUStatus
8
- */
9
-
10
- import fs from "node:fs/promises";
11
- import path from "node:path";
12
- import { execFile } from "node:child_process";
13
- import { promisify } from "node:util";
14
-
15
- const execFileAsync = promisify(execFile);
16
- import { getKarajanHome } from "../utils/paths.js";
17
-
18
- // FUTURE: hu-storage adapter for PG/Trello/etc — currently local files only
19
-
20
- /**
21
- * Valid HU story status values.
22
- * @type {Readonly<{PENDING: "pending", CODING: "coding", REVIEWING: "reviewing", DONE: "done", FAILED: "failed", BLOCKED: "blocked", CERTIFIED: "certified", NEEDS_CONTEXT: "needs_context"}>}
23
- */
24
- export const HU_STATUS = Object.freeze({
25
- PENDING: "pending",
26
- CODING: "coding",
27
- REVIEWING: "reviewing",
28
- DONE: "done",
29
- FAILED: "failed",
30
- BLOCKED: "blocked",
31
- CERTIFIED: "certified",
32
- NEEDS_CONTEXT: "needs_context"
33
- });
34
-
35
- /** @returns {string} Path to the hu-stories directory (evaluated at call time). */
36
- function getHuDir() {
37
- return path.join(getKarajanHome(), "hu-stories");
38
- }
39
-
40
- /**
41
- * Create a new HU batch from an array of story definitions.
42
- * @param {string} sessionId - The session identifier.
43
- * @param {Array<{id?: string, text: string, blocked_by?: string[]}>} stories - Raw story inputs.
44
- * @returns {Promise<object>} The created batch object.
45
- */
46
- export async function createHuBatch(sessionId, stories) {
47
- const dir = path.join(getHuDir(), sessionId);
48
- await fs.mkdir(dir, { recursive: true });
49
-
50
- const batch = {
51
- session_id: sessionId,
52
- created_at: new Date().toISOString(),
53
- stories: stories.map((s, i) => ({
54
- id: s.id || `HU-${Date.now()}-${i}`,
55
- status: "pending",
56
- original: { text: s.text },
57
- blocked_by: s.blocked_by || [],
58
- // KJC-TSK-0698: the tier travels with the HU; a story without one (single auto-story) carries none.
59
- ...(s.complexity ? { complexity: s.complexity } : {}),
60
- certified: null,
61
- quality: null,
62
- context_requests: [],
63
- created_at: new Date().toISOString(),
64
- updated_at: new Date().toISOString()
65
- }))
66
- };
67
-
68
- await fs.writeFile(path.join(dir, "batch.json"), JSON.stringify(batch, null, 2));
69
- return batch;
70
- }
71
-
72
- /**
73
- * Load an existing HU batch from disk.
74
- * @param {string} sessionId - The session identifier.
75
- * @returns {Promise<object>} The loaded batch object.
76
- */
77
- export async function loadHuBatch(sessionId) {
78
- const file = path.join(getHuDir(), sessionId, "batch.json");
79
- const raw = await fs.readFile(file, "utf8");
80
- return JSON.parse(raw);
81
- }
82
-
83
- /**
84
- * Save a batch back to disk.
85
- *
86
- * Ensures the target directory exists before writing — the previous
87
- * implementation assumed createHuBatch had already done the mkdir,
88
- * which holds for the auto-HU path but NOT for plan-driven runs:
89
- * `kj run --plan <id>` derives a batchSessionId from the plan id,
90
- * loads the (non-existent) batch.json, falls back to in-memory, then
91
- * crashes here with ENOENT on the first save attempt because no one
92
- * created `~/.karajan/hu-stories/<sessionId>/`. The fix is one
93
- * mkdir -p; cheap and idempotent.
94
- *
95
- * @param {string} sessionId - The session identifier.
96
- * @param {object} batch - The batch object to persist.
97
- * @returns {Promise<void>}
98
- */
99
- export async function saveHuBatch(sessionId, batch) {
100
- const dir = path.join(getHuDir(), sessionId);
101
- await fs.mkdir(dir, { recursive: true });
102
- batch.updated_at = new Date().toISOString();
103
- await fs.writeFile(path.join(dir, "batch.json"), JSON.stringify(batch, null, 2));
104
- }
105
-
106
- /**
107
- * Update the status of a single story within a batch.
108
- * @param {object} batch - The batch object.
109
- * @param {string} storyId - Story ID to update.
110
- * @param {string} status - New status value.
111
- * @param {object} [extra={}] - Additional fields to merge.
112
- * @returns {object} The updated story.
113
- */
114
- export function updateStoryStatus(batch, storyId, status, extra = {}) {
115
- const story = batch.stories.find(s => s.id === storyId);
116
- if (!story) throw new Error(`Story ${storyId} not found`);
117
- story.status = status;
118
- const now = new Date().toISOString();
119
- story.statusChangedAt = now;
120
- story.updated_at = now;
121
- Object.assign(story, extra);
122
- return story;
123
- }
124
-
125
- /**
126
- * Store quality scores on a story.
127
- * @param {object} batch - The batch object.
128
- * @param {string} storyId - Story ID.
129
- * @param {object} quality - Quality scores object.
130
- * @returns {object} The updated story.
131
- */
132
- export function updateStoryQuality(batch, storyId, quality) {
133
- const story = batch.stories.find(s => s.id === storyId);
134
- if (!story) throw new Error(`Story ${storyId} not found`);
135
- story.quality = { ...quality, evaluated_at: new Date().toISOString() };
136
- story.updated_at = new Date().toISOString();
137
- return story;
138
- }
139
-
140
- /**
141
- * Mark a story as certified with the provided certified data.
142
- * @param {object} batch - The batch object.
143
- * @param {string} storyId - Story ID.
144
- * @param {object} certified - Certified HU data.
145
- * @returns {object} The updated story.
146
- */
147
- export function updateStoryCertified(batch, storyId, certified) {
148
- const story = batch.stories.find(s => s.id === storyId);
149
- if (!story) throw new Error(`Story ${storyId} not found`);
150
- story.certified = certified;
151
- story.status = "certified";
152
- story.updated_at = new Date().toISOString();
153
- return story;
154
- }
155
-
156
- /**
157
- * Add a context request to a story and set its status to needs_context.
158
- * @param {object} batch - The batch object.
159
- * @param {string} storyId - Story ID.
160
- * @param {{fields_needed: string[], question: string}} request - Context request.
161
- * @returns {object} The updated story.
162
- */
163
- export function addContextRequest(batch, storyId, request) {
164
- const story = batch.stories.find(s => s.id === storyId);
165
- if (!story) throw new Error(`Story ${storyId} not found`);
166
- story.context_requests.push({
167
- requested_at: new Date().toISOString(),
168
- fields_needed: request.fields_needed,
169
- question_to_fde: request.question,
170
- answered_at: null,
171
- answer: null
172
- });
173
- story.status = "needs_context";
174
- story.updated_at = new Date().toISOString();
175
- return story;
176
- }
177
-
178
- /**
179
- * Answer the most recent pending context request and reset status to pending.
180
- * @param {object} batch - The batch object.
181
- * @param {string} storyId - Story ID.
182
- * @param {string} answer - The FDE's answer.
183
- * @returns {object} The updated story.
184
- */
185
- export function answerContextRequest(batch, storyId, answer) {
186
- const story = batch.stories.find(s => s.id === storyId);
187
- if (!story) throw new Error(`Story ${storyId} not found`);
188
- const pending = story.context_requests.find(r => !r.answered_at);
189
- if (pending) {
190
- pending.answered_at = new Date().toISOString();
191
- pending.answer = answer;
192
- }
193
- story.status = "pending"; // back to pending for re-evaluation
194
- story.updated_at = new Date().toISOString();
195
- return story;
196
- }
197
-
198
- /**
199
- * Create a lightweight history record for a pipeline run.
200
- * Stores a minimal single-HU batch in the same hu-stories directory
201
- * so the HU Board can pick it up alongside full batches.
202
- * @param {string} sessionId - The session identifier.
203
- * @param {{task: string, result: string, approved: boolean, summary?: string, timestamp?: string}} data
204
- * @returns {Promise<object>} The created batch object.
205
- */
206
- export async function createHistoryRecord(sessionId, { task, result, approved, summary, timestamp }) {
207
- const ts = timestamp || new Date().toISOString();
208
- const dir = path.join(getHuDir(), sessionId);
209
- await fs.mkdir(dir, { recursive: true });
210
-
211
- const batch = {
212
- session_id: sessionId,
213
- created_at: ts,
214
- stories: [
215
- {
216
- id: `HU-hist-${sessionId}`,
217
- status: approved ? "certified" : "failed",
218
- original: { text: task },
219
- blocked_by: [],
220
- certified: approved ? { summary: summary || result } : null,
221
- quality: null,
222
- context_requests: [],
223
- created_at: ts,
224
- updated_at: ts
225
- }
226
- ],
227
- history: {
228
- task,
229
- result,
230
- approved,
231
- summary: summary || null,
232
- timestamp: ts
233
- }
234
- };
235
-
236
- await fs.writeFile(path.join(dir, "batch.json"), JSON.stringify(batch, null, 2));
237
- return batch;
238
- }
239
-
240
- /* ── Splitting metadata ──────────────────────────────────────────── */
241
-
242
- /**
243
- * Add splitting metadata to a story within a batch.
244
- * Used to track the relationship between an original HU and its sub-HUs
245
- * after a split operation.
246
- *
247
- * @param {object} batch - The batch object.
248
- * @param {string} huId - Story ID to annotate.
249
- * @param {{ original_hu_id?: string, split_children?: string[], indicators_detected?: string[], heuristic_applied?: string, split_confirmed_by_fde?: boolean, validation?: object }} metadata - Splitting metadata.
250
- * @returns {object} The updated story.
251
- */
252
- export function addSplittingMetadata(batch, huId, metadata) {
253
- const story = batch.stories.find(s => s.id === huId);
254
- if (!story) throw new Error(`Story ${huId} not found`);
255
- story.splitting = { ...(story.splitting || {}), ...metadata };
256
- story.updated_at = new Date().toISOString();
257
- return story;
258
- }
259
-
260
- /* ── Manual HU management ─────────────────────────────────────────── */
261
-
262
- /**
263
- * Detect project info from the given directory.
264
- * @param {string} cwd - Directory to detect project from.
265
- * @returns {Promise<{ name: string, remoteUrl: string|null }>}
266
- */
267
- export async function detectProject(cwd) {
268
- const name = path.basename(cwd);
269
- let remoteUrl = null;
270
- try {
271
- const { stdout } = await execFileAsync("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
272
- remoteUrl = stdout.trim();
273
- } catch { /* not a git repo or no remote */ }
274
- return { name, remoteUrl };
275
- }
276
-
277
- /**
278
- * Derive a project slug from a directory path.
279
- * @param {string} projectDir - Absolute path to the project.
280
- * @returns {string}
281
- */
282
- function projectSlug(projectDir) {
283
- return path.basename(projectDir);
284
- }
285
-
286
- /**
287
- * Get the batch file path for a project.
288
- * @param {string} projectDir
289
- * @returns {string}
290
- */
291
- function projectBatchPath(projectDir) {
292
- return path.join(getHuDir(), projectSlug(projectDir), "batch.json");
293
- }
294
-
295
- /**
296
- * Load the project batch, or return a fresh empty batch if none exists.
297
- * @param {string} projectDir
298
- * @returns {Promise<object>}
299
- */
300
- async function loadProjectBatch(projectDir) {
301
- const filePath = projectBatchPath(projectDir);
302
- try {
303
- const raw = await fs.readFile(filePath, "utf8");
304
- return JSON.parse(raw);
305
- } catch { /* no saved batch for this project */
306
- return {
307
- project: projectSlug(projectDir),
308
- created_at: new Date().toISOString(),
309
- stories: []
310
- };
311
- }
312
- }
313
-
314
- /**
315
- * Save a project batch to disk (auto-creates directory).
316
- * @param {string} projectDir
317
- * @param {object} batch
318
- */
319
- async function saveProjectBatch(projectDir, batch) {
320
- const filePath = projectBatchPath(projectDir);
321
- await fs.mkdir(path.dirname(filePath), { recursive: true });
322
- batch.updated_at = new Date().toISOString();
323
- await fs.writeFile(filePath, JSON.stringify(batch, null, 2));
324
- }
325
-
326
- /**
327
- * Create a manual HU (user story) for a project.
328
- * Auto-creates the batch file if it doesn't exist.
329
- * @param {string} projectDir
330
- * @param {{ title: string, description?: string, status?: string, acceptanceCriteria?: string }} data
331
- * @returns {Promise<object>} The created HU.
332
- */
333
- export async function createManualHu(projectDir, { title, description, status, acceptanceCriteria }) {
334
- if (!title) throw new Error("title is required to create a HU");
335
- const batch = await loadProjectBatch(projectDir);
336
- const now = new Date().toISOString();
337
- const hu = {
338
- id: `HU-${Date.now()}-${batch.stories.length}`,
339
- title,
340
- description: description || "",
341
- status: status || HU_STATUS.PENDING,
342
- acceptanceCriteria: acceptanceCriteria || "",
343
- createdAt: now,
344
- updatedAt: now
345
- };
346
- batch.stories.push(hu);
347
- await saveProjectBatch(projectDir, batch);
348
- return hu;
349
- }
350
-
351
- /**
352
- * List all HUs for a project.
353
- * @param {string} projectDir
354
- * @returns {Promise<Array<{ id: string, title: string, status: string, createdAt: string }>>}
355
- */
356
- export async function listHus(projectDir) {
357
- const batch = await loadProjectBatch(projectDir);
358
- return batch.stories.map(s => ({
359
- id: s.id,
360
- title: s.title,
361
- status: s.status,
362
- createdAt: s.createdAt
363
- }));
364
- }
365
-
366
- /**
367
- * Update the status of a specific HU.
368
- * @param {string} projectDir
369
- * @param {string} huId
370
- * @param {string} status
371
- * @returns {Promise<object>} The updated HU.
372
- */
373
- export async function updateHuStatus(projectDir, huId, status) {
374
- const batch = await loadProjectBatch(projectDir);
375
- const hu = batch.stories.find(s => s.id === huId);
376
- if (!hu) throw new Error(`HU ${huId} not found`);
377
- hu.status = status;
378
- hu.updatedAt = new Date().toISOString();
379
- await saveProjectBatch(projectDir, batch);
380
- return hu;
381
- }
382
-
383
- /**
384
- * Get a single HU by id.
385
- * @param {string} projectDir
386
- * @param {string} huId
387
- * @returns {Promise<object>}
388
- */
389
- export async function getHu(projectDir, huId) {
390
- const batch = await loadProjectBatch(projectDir);
391
- const hu = batch.stories.find(s => s.id === huId);
392
- if (!hu) throw new Error(`HU ${huId} not found`);
393
- return hu;
394
- }
@@ -1,182 +0,0 @@
1
- /**
2
- * Test doubles for the DI surfaces. Intentionally dumb — tests stage inputs
3
- * through simple setters and assert on recorded calls.
4
- *
5
- * @typedef {import("./file-system-service.js").FileSystemService} FileSystemService
6
- * @typedef {import("./command-runner.js").CommandRunner} CommandRunner
7
- * @typedef {import("./command-runner.js").CommandRunResult} CommandRunResult
8
- * @typedef {import("./command-runner.js").CommandRunOptions} CommandRunOptions
9
- */
10
-
11
- /**
12
- * In-memory file system. Paths are stored as-is (no normalization) to keep
13
- * behaviour predictable in tests.
14
- */
15
- export class MockFileSystem {
16
- constructor(initial = {}) {
17
- /** @type {Map<string, string|Buffer>} */
18
- this.files = new Map(Object.entries(initial));
19
- /** @type {Set<string>} */
20
- this.directories = new Set();
21
- /** @type {Array<{op: string, path: string, args?: unknown}>} */
22
- this.calls = [];
23
- }
24
-
25
- _record(op, path, args) {
26
- this.calls.push({ op, path, args });
27
- }
28
-
29
- async readFile(path, encoding = "utf8") {
30
- this._record("readFile", path, { encoding });
31
- if (!this.files.has(path)) {
32
- const err = new Error(`ENOENT: no such file or directory, open '${path}'`);
33
- err.code = "ENOENT";
34
- throw err;
35
- }
36
- const data = this.files.get(path);
37
- if (Buffer.isBuffer(data) && encoding) return data.toString(encoding);
38
- return data;
39
- }
40
-
41
- async writeFile(path, data, encoding = "utf8") {
42
- this._record("writeFile", path, { encoding });
43
- this.files.set(path, data);
44
- }
45
-
46
- async appendFile(path, data, encoding = "utf8") {
47
- this._record("appendFile", path, { encoding });
48
- const prev = this.files.get(path) ?? "";
49
- this.files.set(path, String(prev) + String(data));
50
- }
51
-
52
- async exists(path) {
53
- this._record("exists", path);
54
- return this.files.has(path) || this.directories.has(path);
55
- }
56
-
57
- async ensureDir(path) {
58
- this._record("ensureDir", path);
59
- this.directories.add(path);
60
- }
61
-
62
- async readdir(path) {
63
- this._record("readdir", path);
64
- const prefix = path.endsWith("/") ? path : path + "/";
65
- const entries = new Set();
66
- for (const p of this.files.keys()) {
67
- if (p.startsWith(prefix)) {
68
- const tail = p.slice(prefix.length).split("/")[0];
69
- if (tail) entries.add(tail);
70
- }
71
- }
72
- if (!entries.size && !this.directories.has(path)) {
73
- const err = new Error(`ENOENT: no such file or directory, scandir '${path}'`);
74
- err.code = "ENOENT";
75
- throw err;
76
- }
77
- return [...entries];
78
- }
79
-
80
- async copyFile(src, dest) {
81
- this._record("copyFile", src, { dest });
82
- if (!this.files.has(src)) {
83
- const err = new Error(`ENOENT: no such file or directory, copyfile '${src}' -> '${dest}'`);
84
- err.code = "ENOENT";
85
- throw err;
86
- }
87
- this.files.set(dest, this.files.get(src));
88
- }
89
-
90
- async stat(path) {
91
- this._record("stat", path);
92
- if (this.files.has(path)) {
93
- const data = this.files.get(path);
94
- const size = Buffer.isBuffer(data) ? data.length : Buffer.byteLength(String(data));
95
- return {
96
- size,
97
- isDirectory: () => false,
98
- isFile: () => true
99
- };
100
- }
101
- if (this.directories.has(path)) {
102
- return {
103
- size: 0,
104
- isDirectory: () => true,
105
- isFile: () => false
106
- };
107
- }
108
- const err = new Error(`ENOENT: no such file or directory, stat '${path}'`);
109
- err.code = "ENOENT";
110
- throw err;
111
- }
112
- }
113
-
114
- /**
115
- * Scripted command runner. Tests call enqueue() to stage responses in order,
116
- * or setResponder() for a per-invocation decision. Every run() is recorded.
117
- */
118
- export class MockCommandRunner {
119
- constructor() {
120
- /** @type {Array<CommandRunResult>} */
121
- this.queue = [];
122
- /** @type {Array<{command: string, args: string[], options: CommandRunOptions}>} */
123
- this.calls = [];
124
- /** @type {null | ((args: {command: string, args: string[], options: CommandRunOptions}) => CommandRunResult|Promise<CommandRunResult>)} */
125
- this.responder = null;
126
- /** @type {CommandRunResult} */
127
- this.defaultResponse = { exitCode: 0, stdout: "", stderr: "" };
128
- }
129
-
130
- /**
131
- * Queue a response. FIFO — first enqueued is returned on the first run().
132
- * @param {Partial<CommandRunResult>} response
133
- */
134
- enqueue(response) {
135
- this.queue.push({ exitCode: 0, stdout: "", stderr: "", ...response });
136
- return this;
137
- }
138
-
139
- /**
140
- * Replace FIFO queue with a function that decides per-call.
141
- * @param {(args: {command: string, args: string[], options: CommandRunOptions}) => CommandRunResult|Promise<CommandRunResult>} fn
142
- */
143
- setResponder(fn) {
144
- this.responder = fn;
145
- return this;
146
- }
147
-
148
- /**
149
- * @param {string} command
150
- * @param {string[]} args
151
- * @param {CommandRunOptions} options
152
- * @returns {Promise<CommandRunResult>}
153
- */
154
- async run(command, args = [], options = {}) {
155
- this.calls.push({ command, args: [...args], options: { ...options } });
156
- if (this.responder) return this.responder({ command, args, options });
157
- if (this.queue.length > 0) return this.queue.shift();
158
- return { ...this.defaultResponse };
159
- }
160
-
161
- /**
162
- * Convenience: last recorded invocation, or null.
163
- */
164
- get lastCall() {
165
- return this.calls.at(-1) ?? null;
166
- }
167
- }
168
-
169
- /**
170
- * Build an Environment backed by the mocks above. Useful shorthand in tests:
171
- *
172
- * const { env, fs, runner } = buildMockEnvironment();
173
- * const agent = new BaseAgent(..., env);
174
- *
175
- * @param {{files?: Record<string, string|Buffer>}} [opts]
176
- */
177
- export function buildMockEnvironment({ files } = {}) {
178
- const fs = new MockFileSystem(files);
179
- const runner = new MockCommandRunner();
180
- const env = Object.freeze({ fs, runner });
181
- return { env, fs, runner };
182
- }