karajan-code 4.70.4 → 5.0.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 (380) 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/retired-commands.js +49 -0
  68. package/src/cli.js +18 -5
  69. package/src/commands/agent-run.js +40 -26
  70. package/src/commands/audit.js +127 -124
  71. package/src/commands/board.js +5 -9
  72. package/src/commands/check.js +29 -16
  73. package/src/commands/doctor.js +2 -0
  74. package/src/commands/env.js +176 -150
  75. package/src/commands/go.js +71 -53
  76. package/src/commands/harden.js +90 -81
  77. package/src/commands/hu-start.js +52 -0
  78. package/src/commands/hu.js +288 -221
  79. package/src/commands/identity.js +111 -78
  80. package/src/commands/init-atril.js +47 -0
  81. package/src/commands/init.js +11 -40
  82. package/src/commands/install-tools.js +70 -85
  83. package/src/commands/main-guard.js +78 -0
  84. package/src/commands/policy.js +226 -184
  85. package/src/commands/qmd.js +27 -21
  86. package/src/commands/rag.js +75 -48
  87. package/src/commands/review-gate.js +421 -341
  88. package/src/commands/start.js +4 -5
  89. package/src/commands/steward.js +96 -47
  90. package/src/commands/tournament.js +46 -37
  91. package/src/commands/worktree.js +94 -25
  92. package/src/config/loader.js +34 -17
  93. package/src/config/secrets.js +36 -0
  94. package/src/config.js +1 -0
  95. package/src/environment/adr.js +16 -2
  96. package/src/git/karajan-label.js +20 -8
  97. package/src/guards/main-guard.js +54 -0
  98. package/src/harden/hook-templates.js +16 -1
  99. package/src/harden/sentinel/posttooluse.mjs +18 -6
  100. package/src/harden/sentinel/pretooluse-sentinel.mjs +64 -36
  101. package/src/harden/sentinel/sentinel-lib.mjs +129 -26
  102. package/src/harden/sentinel/sentinel-shell.mjs +5 -16
  103. package/src/harden/sentinel/sessionstart.mjs +3 -2
  104. package/src/harden/sentinel/stop.mjs +5 -4
  105. package/src/harden/workflow-templates.js +1 -1
  106. package/src/hu/board-lookup.js +29 -0
  107. package/src/hu/gh-identity.js +23 -0
  108. package/src/hu/github-board.js +4 -2
  109. package/src/hu/lane-prune.js +15 -7
  110. package/src/hu/lane-setup.js +65 -0
  111. package/src/hu/lanes.js +66 -0
  112. package/src/hu/team-board-mirror.js +3 -2
  113. package/src/infrastructure/command-runner.js +1 -1
  114. package/src/infrastructure/file-system-service.js +1 -1
  115. package/src/mcp/handlers/direct-handlers.js +2 -292
  116. package/src/mcp/handlers/hu-handlers.js +1 -35
  117. package/src/mcp/handlers/management-handlers.js +2 -42
  118. package/src/mcp/server-handlers.js +16 -22
  119. package/src/mcp/tools.js +1 -196
  120. package/src/plan/lane-plans.js +54 -0
  121. package/src/plan/plan-store.js +7 -4
  122. package/src/policy/add.js +26 -12
  123. package/src/policy/adr-number-check.js +31 -0
  124. package/src/prompts/audit.js +299 -339
  125. package/src/prompts/card-context.js +10 -5
  126. package/src/prompts/reviewer.js +5 -2
  127. package/src/rag/auto-update.js +56 -43
  128. package/src/rag/bootstrap-index.js +28 -54
  129. package/src/rag/migrate.js +1 -1
  130. package/src/rag/onnx-fallback.js +2 -2
  131. package/src/rag/pid-lease.js +70 -0
  132. package/src/rag/project-store.js +2 -12
  133. package/src/review/card-first.js +57 -46
  134. package/src/review/gate-gitignore.js +23 -4
  135. package/src/review/gate-hook.js +12 -4
  136. package/src/review/one-shot-review.js +45 -35
  137. package/src/review/publish-pr.js +14 -2
  138. package/src/review/repo-docs.js +73 -0
  139. package/src/review/repo-stack.js +59 -0
  140. package/src/review/reviewer-fallback.js +19 -4
  141. package/src/review/seal-commit.js +29 -0
  142. package/src/review/sonar-pregate.js +59 -44
  143. package/src/review/third-party-review.js +90 -16
  144. package/src/review/third-party-size.js +43 -0
  145. package/src/review/third-party-skills.js +54 -0
  146. package/src/review/verify-pr.js +1 -24
  147. package/src/roles/audit-role.js +106 -125
  148. package/src/skills/atril-skills.js +5 -4
  149. package/src/sonar/scanner.js +116 -101
  150. package/src/steward/invariants.js +5 -4
  151. package/src/steward/phantom-sweep.js +66 -0
  152. package/src/tournament/judge.js +60 -53
  153. package/src/utils/gh-account.js +37 -0
  154. package/src/utils/git.js +0 -13
  155. package/src/utils/home-migration.js +7 -1
  156. package/src/utils/main-checkout.js +53 -0
  157. package/src/utils/qmd-collection.js +3 -1
  158. package/src/utils/welcome.js +3 -3
  159. package/templates/{skills → atril/commands}/kj-architect.md +5 -0
  160. package/templates/{skills → atril/commands}/kj-audit.md +5 -0
  161. package/templates/{skills → atril/commands}/kj-board.md +5 -0
  162. package/templates/{skills → atril/commands}/kj-code.md +5 -0
  163. package/templates/{skills → atril/commands}/kj-csv-transform.md +5 -0
  164. package/templates/{skills → atril/commands}/kj-data-report.md +5 -0
  165. package/templates/{skills → atril/commands}/kj-discover.md +5 -0
  166. package/templates/{skills → atril/commands}/kj-rag-query.md +5 -0
  167. package/templates/{skills → atril/commands}/kj-review.md +5 -0
  168. package/templates/{skills → atril/commands}/kj-security.md +5 -0
  169. package/templates/{skills → atril/commands}/kj-sonar.md +5 -0
  170. package/templates/{skills → atril/commands}/kj-sql-analysis.md +5 -0
  171. package/templates/{skills → atril/commands}/kj-test.md +5 -0
  172. package/packages/hu-board/public/utils/run-launcher.js +0 -169
  173. package/packages/hu-board/src/run-tracker.js +0 -121
  174. package/src/activity-log.js +0 -59
  175. package/src/agents/host-agent.js +0 -38
  176. package/src/agents/model-registry.js +0 -142
  177. package/src/audit/plan-adherence.js +0 -224
  178. package/src/autonomy/arbiter-role.js +0 -71
  179. package/src/autonomy/arbiter.js +0 -22
  180. package/src/autonomy/decider.js +0 -18
  181. package/src/autonomy/decision-resolver.js +0 -58
  182. package/src/autonomy/outcome-report.js +0 -55
  183. package/src/autonomy/stage-ask.js +0 -30
  184. package/src/brain/decision-tracker.js +0 -125
  185. package/src/brain/decisor.js +0 -243
  186. package/src/brain/solomon-consult.js +0 -171
  187. package/src/brain/standby-scheduler.js +0 -8
  188. package/src/budget/comparison.js +0 -96
  189. package/src/canvas/parse-md.js +0 -238
  190. package/src/canvas/schema.js +0 -101
  191. package/src/canvas/to-flat.js +0 -85
  192. package/src/canvas/validate.js +0 -163
  193. package/src/ci/dispatch.js +0 -99
  194. package/src/ci/index.js +0 -3
  195. package/src/ci/pr-diff.js +0 -26
  196. package/src/cli/register-standby.js +0 -26
  197. package/src/commands/architect.js +0 -111
  198. package/src/commands/autorun.js +0 -49
  199. package/src/commands/researcher.js +0 -54
  200. package/src/commands/resume.js +0 -72
  201. package/src/commands/review.js +0 -89
  202. package/src/commands/run.js +0 -185
  203. package/src/commands/standby.js +0 -117
  204. package/src/commands/status.js +0 -36
  205. package/src/commands/triage.js +0 -78
  206. package/src/commands/undo.js +0 -62
  207. package/src/domains/domain-loader.js +0 -245
  208. package/src/domains/domain-registry.js +0 -140
  209. package/src/domains/domain-synthesizer.js +0 -129
  210. package/src/git/automation.js +0 -433
  211. package/src/git/coder-commit-guard.js +0 -40
  212. package/src/git/hu-automation.js +0 -243
  213. package/src/git/hu-snapshot.js +0 -10
  214. package/src/git/symlink-targets.js +0 -57
  215. package/src/golden/asserter.js +0 -92
  216. package/src/golden/loader.js +0 -52
  217. package/src/golden/runner.js +0 -92
  218. package/src/golden/schema.js +0 -41
  219. package/src/guards/duplicate-members.js +0 -86
  220. package/src/guards/intent-guard.js +0 -161
  221. package/src/guards/perf-guard.js +0 -126
  222. package/src/guards/policy-guard.js +0 -90
  223. package/src/guards/policy-resolver.js +0 -85
  224. package/src/guards/secret-redactor.js +0 -71
  225. package/src/hu/acceptance-runner.js +0 -138
  226. package/src/hu/lazy-planner.js +0 -109
  227. package/src/hu/parallel-executor.js +0 -94
  228. package/src/hu/routing-metrics.js +0 -17
  229. package/src/hu/split-validator.js +0 -143
  230. package/src/hu/splitting-detector.js +0 -158
  231. package/src/hu/splitting-generator.js +0 -175
  232. package/src/hu/store.js +0 -394
  233. package/src/infrastructure/mocks.js +0 -182
  234. package/src/mcp/handlers/run-handler.js +0 -318
  235. package/src/mcp/sovereignty-guard.js +0 -113
  236. package/src/mcp/tool-arg-normalizers.js +0 -16
  237. package/src/mutate/reviewer-signal.js +0 -76
  238. package/src/orchestrator/agent-fallback.js +0 -81
  239. package/src/orchestrator/brain-coordinator.js +0 -186
  240. package/src/orchestrator/ci-integration.js +0 -139
  241. package/src/orchestrator/config-init.js +0 -426
  242. package/src/orchestrator/direct-actions.js +0 -217
  243. package/src/orchestrator/drivers/error-recovery.js +0 -287
  244. package/src/orchestrator/drivers/init-context.js +0 -238
  245. package/src/orchestrator/drivers/iteration-loop.js +0 -382
  246. package/src/orchestrator/drivers/iteration-phases/coder-and-refactorer.js +0 -69
  247. package/src/orchestrator/drivers/iteration-phases/guards.js +0 -91
  248. package/src/orchestrator/drivers/iteration-phases/handle-approved.js +0 -29
  249. package/src/orchestrator/drivers/iteration-phases/quality-gates.js +0 -125
  250. package/src/orchestrator/drivers/iteration-phases/reviewer-gate.js +0 -62
  251. package/src/orchestrator/drivers/post-loop.js +0 -404
  252. package/src/orchestrator/drivers/pre-loop-phases/auto-hu-batch.js +0 -137
  253. package/src/orchestrator/drivers/pre-loop-phases/config-deprecations.js +0 -47
  254. package/src/orchestrator/drivers/pre-loop-phases/ensure-addyosmani-skills.js +0 -84
  255. package/src/orchestrator/drivers/pre-loop-phases/inject-loaded-plan.js +0 -393
  256. package/src/orchestrator/drivers/pre-loop.js +0 -524
  257. package/src/orchestrator/drivers/run-hu-batch.js +0 -468
  258. package/src/orchestrator/feedback-enrichment.js +0 -140
  259. package/src/orchestrator/feedback-queue.js +0 -151
  260. package/src/orchestrator/flow-control.js +0 -170
  261. package/src/orchestrator/flow-runner.js +0 -365
  262. package/src/orchestrator/fs-leak-detector.js +0 -228
  263. package/src/orchestrator/hu-scheduler.js +0 -114
  264. package/src/orchestrator/hu-sub-pipeline.js +0 -642
  265. package/src/orchestrator/iteration-gate.js +0 -73
  266. package/src/orchestrator/iteration-stages.js +0 -9
  267. package/src/orchestrator/parallel-limiter.js +0 -74
  268. package/src/orchestrator/pipeline-context.js +0 -72
  269. package/src/orchestrator/post-loop-stages.js +0 -468
  270. package/src/orchestrator/pre-loop-stages.js +0 -11
  271. package/src/orchestrator/preflight-checks.js +0 -487
  272. package/src/orchestrator/repair/repair-detector.js +0 -117
  273. package/src/orchestrator/repair/repair-runner.js +0 -142
  274. package/src/orchestrator/reviewer-fallback.js +0 -45
  275. package/src/orchestrator/role-output-compressor.js +0 -189
  276. package/src/orchestrator/session-journal.js +0 -308
  277. package/src/orchestrator/solomon-escalation.js +0 -331
  278. package/src/orchestrator/solomon-rules.js +0 -212
  279. package/src/orchestrator/stages/acceptance-coverage-stage.js +0 -47
  280. package/src/orchestrator/stages/architect-stage.js +0 -172
  281. package/src/orchestrator/stages/coder-stage.js +0 -506
  282. package/src/orchestrator/stages/domain-curator-stage.js +0 -82
  283. package/src/orchestrator/stages/format-stage.js +0 -61
  284. package/src/orchestrator/stages/hu-reviewer-phases/detect-and-apply-splits.js +0 -127
  285. package/src/orchestrator/stages/hu-reviewer-stage.js +0 -419
  286. package/src/orchestrator/stages/perf-stage.js +0 -140
  287. package/src/orchestrator/stages/planner-stage.js +0 -85
  288. package/src/orchestrator/stages/rag-context-stage.js +0 -80
  289. package/src/orchestrator/stages/rag-preload-decisor.js +0 -58
  290. package/src/orchestrator/stages/research-stage.js +0 -69
  291. package/src/orchestrator/stages/reviewer-stage.js +0 -431
  292. package/src/orchestrator/stages/sonar-stage.js +0 -439
  293. package/src/orchestrator/stages/stage-classes.js +0 -84
  294. package/src/orchestrator/stages/stage-executor.js +0 -155
  295. package/src/orchestrator/stages/suite-green-stage.js +0 -62
  296. package/src/orchestrator/stages/tdd-discipline-stage.js +0 -56
  297. package/src/orchestrator/stages/tool-judge-stage.js +0 -53
  298. package/src/orchestrator/stages/triage-stage.js +0 -172
  299. package/src/orchestrator/standby.js +0 -71
  300. package/src/orchestrator/verification-gate.js +0 -183
  301. package/src/orchestrator.js +0 -22
  302. package/src/plan/adr-generator.js +0 -170
  303. package/src/plan/adr-loader.js +0 -60
  304. package/src/plan/plan-executor.js +0 -100
  305. package/src/plan/plan-validation.js +0 -4
  306. package/src/prompts/acceptance-coverage.js +0 -32
  307. package/src/prompts/arbiter.js +0 -38
  308. package/src/prompts/architect.js +0 -103
  309. package/src/prompts/hu-reviewer.js +0 -244
  310. package/src/prompts/tool-correctness-judge.js +0 -31
  311. package/src/prompts/triage.js +0 -57
  312. package/src/rag/embedder.js +0 -2
  313. package/src/rag/embedders/_cloud-base.js +0 -2
  314. package/src/rag/embedders/cohere.js +0 -2
  315. package/src/rag/embedders/mistral.js +0 -2
  316. package/src/rag/embedders/onnx.js +0 -2
  317. package/src/rag/embedders/openai.js +0 -2
  318. package/src/rag/embedders/voyage.js +0 -2
  319. package/src/rag/rerank.js +0 -4
  320. package/src/rag/where-parser.js +0 -4
  321. package/src/repeat-detector.js +0 -88
  322. package/src/review/diff-generator.js +0 -112
  323. package/src/review/git-stash-helper.js +0 -104
  324. package/src/review/schema.js +0 -31
  325. package/src/review/scope-filter.js +0 -152
  326. package/src/review/snapshot-diff.js +0 -122
  327. package/src/review/tdd-discipline.js +0 -90
  328. package/src/review/tdd-policy.js +0 -69
  329. package/src/roles/acceptance-coverage-role.js +0 -78
  330. package/src/roles/architect-role.js +0 -77
  331. package/src/roles/coder-role.js +0 -113
  332. package/src/roles/commiter-role.js +0 -113
  333. package/src/roles/domain-curator-role.js +0 -134
  334. package/src/roles/hu-reviewer-role.js +0 -64
  335. package/src/roles/impeccable-role.js +0 -85
  336. package/src/roles/index.js +0 -18
  337. package/src/roles/karajan-brain-role.js +0 -134
  338. package/src/roles/perf-role.js +0 -122
  339. package/src/roles/planner-role.js +0 -151
  340. package/src/roles/refactorer-role.js +0 -30
  341. package/src/roles/repairer-role.js +0 -181
  342. package/src/roles/researcher-role.js +0 -70
  343. package/src/roles/reviewer-role.js +0 -115
  344. package/src/roles/security-role.js +0 -73
  345. package/src/roles/solomon-role.js +0 -271
  346. package/src/roles/sonar-role.js +0 -72
  347. package/src/roles/tester-role.js +0 -235
  348. package/src/roles/tool-correctness-judge-role.js +0 -78
  349. package/src/roles/triage-role.js +0 -99
  350. package/src/session/journal/decisions-writer.js +0 -200
  351. package/src/session/journal/iteration-logger.js +0 -194
  352. package/src/session/journal/summary-writer.js +0 -320
  353. package/src/session/journal/tree-writer.js +0 -164
  354. package/src/session/journal/write-all.js +0 -89
  355. package/src/session-cleanup.js +0 -116
  356. package/src/skills/semantic-detector.js +0 -167
  357. package/src/skills/skill-detector.js +0 -385
  358. package/src/sonar/cloud-scanner.js +0 -78
  359. package/src/tracker-bootstrap.js +0 -44
  360. package/src/types/index.js +0 -38
  361. package/src/types/policy.js +0 -43
  362. package/src/types/solomon.js +0 -87
  363. package/src/utils/async-lock.js +0 -31
  364. package/src/utils/budget.js +0 -229
  365. package/src/utils/cwd-confirm.js +0 -39
  366. package/src/utils/display/event-handlers.js +0 -403
  367. package/src/utils/display/formatters.js +0 -132
  368. package/src/utils/display/header.js +0 -47
  369. package/src/utils/display/session-summary.js +0 -118
  370. package/src/utils/display/solomon.js +0 -42
  371. package/src/utils/pricing.js +0 -29
  372. package/src/utils/quota-simulator.js +0 -56
  373. package/src/utils/role-assigner.js +0 -137
  374. package/src/utils/rtk-wrapper.js +0 -87
  375. package/src/utils/run-registry.js +0 -9
  376. package/src/utils/status-dashboard.js +0 -254
  377. package/src/utils/tool-call-extractor.js +0 -56
  378. package/src/webperf/devtools-detect.js +0 -112
  379. package/src/webperf/webperf-role-integration.js +0 -66
  380. 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
- }