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