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
@@ -1,161 +0,0 @@
1
- import { VALID_TASK_TYPES } from "./policy-resolver.js";
2
-
3
- /**
4
- * Built-in intent patterns for deterministic pre-triage classification.
5
- * Each pattern maps keywords/regex to a taskType + complexity level.
6
- * Evaluated top-down; first match with confidence >= threshold wins.
7
- */
8
- const INTENT_PATTERNS = [
9
- // No-code tasks (data analysis, document generation, SQL queries, CSV transforms)
10
- // Placed before audit so specific multi-word patterns win over single-word "analizar"/"analyze"
11
- {
12
- id: "no-code",
13
- keywords: [
14
- "generate report", "create document", "analyze data", "transform csv",
15
- "sql query", "sql queries", "generate queries", "data analysis", "process data",
16
- "export to", "import from", "convert data", "parse file",
17
- "generar informe", "crear documento", "analizar datos", "transformar csv",
18
- "consulta sql", "generar consultas", "analisis de datos", "procesar datos",
19
- "exportar a", "importar de", "convertir datos", "parsear fichero",
20
- ],
21
- taskType: "no-code",
22
- level: "simple",
23
- confidence: 0.9,
24
- message: "No-code task detected — TDD and SonarQube disabled, coder generates non-code output",
25
- },
26
- // Audit / analysis tasks (no code changes, read-only analysis)
27
- {
28
- id: "audit",
29
- keywords: ["audit", "review", "analyze", "check", "verify", "inspect", "scan", "auditar", "revisar", "analizar", "comprobar", "verificar", "inspeccionar"],
30
- taskType: "audit",
31
- level: "simple",
32
- confidence: 0.9,
33
- message: "Audit/analysis task detected — skipping coder and reviewer",
34
- },
35
- // Documentation-only tasks
36
- {
37
- id: "doc-readme",
38
- keywords: ["readme", "docs", "documentation", "jsdoc", "typedoc", "changelog"],
39
- taskType: "doc",
40
- level: "trivial",
41
- confidence: 0.95,
42
- message: "Documentation-only task detected",
43
- },
44
- // Test-only tasks
45
- {
46
- id: "add-tests",
47
- keywords: ["add test", "write test", "missing test", "test coverage", "add spec", "write spec", "unit test", "integration test"],
48
- taskType: "add-tests",
49
- level: "simple",
50
- confidence: 0.9,
51
- message: "Test-addition task detected",
52
- },
53
- // Refactoring tasks
54
- {
55
- id: "refactor",
56
- keywords: ["refactor", "rename", "extract method", "extract function", "clean up", "cleanup", "reorganize", "restructure", "simplify"],
57
- taskType: "refactor",
58
- level: "simple",
59
- confidence: 0.85,
60
- message: "Refactoring task detected",
61
- },
62
- // Infrastructure / DevOps tasks
63
- {
64
- id: "infra-devops",
65
- keywords: ["ci/cd", "pipeline", "dockerfile", "docker-compose", "kubernetes", "k8s", "terraform", "deploy", "nginx", "github actions", "gitlab ci"],
66
- taskType: "infra",
67
- level: "simple",
68
- confidence: 0.85,
69
- message: "Infrastructure/DevOps task detected",
70
- },
71
- // Trivial fixes (typos, comments, formatting)
72
- {
73
- id: "trivial-fix",
74
- keywords: ["typo", "fix typo", "spelling", "comment", "fix comment", "formatting", "lint", "fix lint", "whitespace"],
75
- taskType: "sw",
76
- level: "trivial",
77
- confidence: 0.9,
78
- message: "Trivial fix detected",
79
- },
80
- // Frontend / UI tasks (sets hasFrontend flag for impeccable role activation)
81
- {
82
- id: "frontend-ui",
83
- keywords: ["html", "css", "ui", "landing", "component", "responsive", "accessibility", "a11y", "frontend", "design", "layout", "styling", "dark mode", "animation"],
84
- taskType: "sw",
85
- level: "simple",
86
- confidence: 0.8,
87
- message: "Frontend/UI task detected",
88
- hasFrontend: true,
89
- },
90
- ];
91
-
92
- /**
93
- * Compile custom intent patterns from config.guards.intent.patterns
94
- * Custom patterns are evaluated BEFORE built-in ones.
95
- */
96
- export function compileIntentPatterns(configGuards) {
97
- const custom = Array.isArray(configGuards?.intent?.patterns)
98
- ? configGuards.intent.patterns.map(p => ({
99
- id: p.id || "custom-intent",
100
- keywords: Array.isArray(p.keywords) ? p.keywords : [],
101
- taskType: VALID_TASK_TYPES.has(p.taskType) ? p.taskType : "sw",
102
- level: p.level || "simple",
103
- confidence: typeof p.confidence === "number" ? p.confidence : 0.85,
104
- message: p.message || "Custom intent pattern matched",
105
- }))
106
- : [];
107
-
108
- return [...custom, ...INTENT_PATTERNS];
109
- }
110
-
111
- /**
112
- * Check if a task description matches any of the keywords.
113
- * Returns true if at least one keyword (case-insensitive substring) appears in the task.
114
- */
115
- function matchesKeywords(task, keywords) {
116
- const lower = task.toLowerCase();
117
- for (const kw of keywords) {
118
- if (lower.includes(kw.toLowerCase())) {
119
- return true;
120
- }
121
- }
122
- return false;
123
- }
124
-
125
- /**
126
- * Classify a task description using deterministic keyword patterns.
127
- *
128
- * Returns:
129
- * { classified: true, taskType, level, confidence, patternId, message }
130
- * or { classified: false } if no pattern matches above threshold
131
- */
132
- export function classifyIntent(task, config = {}) {
133
- if (!task || typeof task !== "string") {
134
- return { classified: false };
135
- }
136
-
137
- const configGuards = config?.guards || {};
138
- const threshold = configGuards?.intent?.confidence_threshold ?? 0.85;
139
- const patterns = compileIntentPatterns(configGuards);
140
-
141
- for (const pattern of patterns) {
142
- if (!matchesKeywords(task, pattern.keywords)) continue;
143
-
144
- if (pattern.confidence >= threshold) {
145
- const result = {
146
- classified: true,
147
- taskType: pattern.taskType,
148
- level: pattern.level,
149
- confidence: pattern.confidence,
150
- patternId: pattern.id,
151
- message: pattern.message,
152
- };
153
- if (pattern.hasFrontend) result.hasFrontend = true;
154
- return result;
155
- }
156
- }
157
-
158
- return { classified: false };
159
- }
160
-
161
- export { INTENT_PATTERNS };
@@ -1,126 +0,0 @@
1
- const FRONTEND_EXTENSIONS = new Set([".html", ".htm", ".css", ".jsx", ".tsx", ".astro", ".vue", ".svelte"]);
2
-
3
- // Built-in perf anti-patterns (applied to added lines in frontend files)
4
- const PERF_PATTERNS = [
5
- { id: "img-no-dimensions", pattern: /<img\b(?![^>]*\bwidth\b)(?![^>]*\bheight\b)[^>]*>/i, severity: "warning", message: "Image without width/height attributes (causes CLS)" },
6
- { id: "img-no-lazy", pattern: /<img\b(?![^>]*\bloading\s*=)(?![^>]*\bfetchpriority\s*=)[^>]*>/i, severity: "info", message: "Image without loading=\"lazy\" or fetchpriority (consider lazy loading)" },
7
- { id: "script-no-defer", pattern: /<script\b(?![^>]*\b(?:defer|async)\b)(?![^>]*type\s*=\s*["']module["'])[^>]*src\s*=/i, severity: "warning", message: "External script without defer/async (render-blocking)" },
8
- { id: "font-no-display", pattern: /@font-face\s*\{(?![^}]*font-display)/i, severity: "warning", message: "@font-face without font-display (causes FOIT)" },
9
- { id: "css-import", pattern: /@import\s+(?:url\()?["'](?!.*\.module\.)/i, severity: "info", message: "CSS @import (causes sequential loading, prefer <link>)" },
10
- { id: "inline-style-large", pattern: /style\s*=\s*["'][^"']{200,}["']/i, severity: "warning", message: "Large inline style (>200 chars, consider external CSS)" },
11
- { id: "document-write", pattern: /document\.write\s*\(/, severity: "warning", message: "document.write() blocks parsing and degrades performance" },
12
- ];
13
-
14
- // Patterns for package.json changes (heavy dependencies added)
15
- const HEAVY_DEPS = [
16
- { id: "heavy-moment", pattern: /"moment"/, severity: "info", message: "moment.js added (consider dayjs or date-fns for smaller bundle)" },
17
- { id: "heavy-lodash", pattern: /"lodash"(?!\/)/, severity: "info", message: "Full lodash added (consider lodash-es or individual imports)" },
18
- { id: "heavy-jquery", pattern: /"jquery"/, severity: "info", message: "jQuery added (consider native DOM APIs)" },
19
- ];
20
-
21
- function getExtension(filePath) {
22
- if (!filePath) return "";
23
- const dot = filePath.lastIndexOf(".");
24
- return dot >= 0 ? filePath.slice(dot).toLowerCase() : "";
25
- }
26
-
27
- /**
28
- * Check if any modified file in the diff is a frontend file
29
- */
30
- export function hasFrontendFiles(diff) {
31
- if (!diff) return false;
32
- for (const line of diff.split("\n")) {
33
- if (line.startsWith("+++ b/")) {
34
- const ext = getExtension(line.slice(6));
35
- if (FRONTEND_EXTENSIONS.has(ext)) return true;
36
- }
37
- }
38
- return false;
39
- }
40
-
41
- /**
42
- * Extract added lines grouped by file from a unified diff
43
- */
44
- function extractAddedLinesByFile(diff) {
45
- const results = [];
46
- let currentFile = null;
47
- let lineNum = 0;
48
-
49
- for (const line of diff.split("\n")) {
50
- if (line.startsWith("+++ b/")) {
51
- currentFile = line.slice(6);
52
- continue;
53
- }
54
- if (line.startsWith("@@ ")) {
55
- const match = /@@ -\d+(?:,\d+)? \+(\d+)/.exec(line);
56
- lineNum = match ? Number.parseInt(match[1], 10) - 1 : 0;
57
- continue;
58
- }
59
- if (line.startsWith("+") && !line.startsWith("+++")) {
60
- lineNum += 1;
61
- results.push({ file: currentFile, line: lineNum, content: line.slice(1) });
62
- } else if (!line.startsWith("-")) {
63
- lineNum += 1;
64
- }
65
- }
66
- return results;
67
- }
68
-
69
- /**
70
- * Scan diff for frontend performance anti-patterns.
71
- * Returns { pass: boolean, violations: [...], skipped: boolean }
72
- */
73
- export function scanPerfDiff(diff, config = {}) {
74
- if (!diff || typeof diff !== "string") {
75
- return { pass: true, violations: [], skipped: true };
76
- }
77
-
78
- if (!hasFrontendFiles(diff)) {
79
- return { pass: true, violations: [], skipped: true };
80
- }
81
-
82
- const customPatterns = Array.isArray(config?.guards?.perf?.patterns)
83
- ? config.guards.perf.patterns.map(p => ({
84
- id: p.id || "custom-perf",
85
- pattern: typeof p.pattern === "string" ? new RegExp(p.pattern, p.flags || "i") : p.pattern,
86
- severity: p.severity || "warning",
87
- message: p.message || "Custom perf pattern matched",
88
- }))
89
- : [];
90
-
91
- const allPatterns = [...PERF_PATTERNS, ...customPatterns];
92
- const addedLines = extractAddedLinesByFile(diff);
93
- const violations = [];
94
-
95
- for (const { file, line, content } of addedLines) {
96
- const ext = getExtension(file);
97
- const isFrontend = FRONTEND_EXTENSIONS.has(ext);
98
- const isPackageJson = file?.endsWith("package.json");
99
-
100
- if (isFrontend) {
101
- for (const { id, pattern, severity, message } of allPatterns) {
102
- if (pattern.test(content)) {
103
- violations.push({ id, severity, file, line, message, matchedContent: content.trim().slice(0, 200) });
104
- }
105
- }
106
- }
107
-
108
- if (isPackageJson) {
109
- for (const { id, pattern, severity, message } of HEAVY_DEPS) {
110
- if (pattern.test(content)) {
111
- violations.push({ id, severity, file, line, message, matchedContent: content.trim().slice(0, 200) });
112
- }
113
- }
114
- }
115
- }
116
-
117
- // perf-guard is advisory by default — only blocks on critical (none built-in are critical)
118
- const blockOnWarning = Boolean(config?.guards?.perf?.block_on_warning);
119
- const hasCritical = violations.some(v => v.severity === "critical");
120
- const hasWarning = violations.some(v => v.severity === "warning");
121
- const pass = !hasCritical && !(blockOnWarning && hasWarning);
122
-
123
- return { pass, violations, skipped: false };
124
- }
125
-
126
- export { PERF_PATTERNS, HEAVY_DEPS, FRONTEND_EXTENSIONS };
@@ -1,90 +0,0 @@
1
- /**
2
- * Policy guard for kj_resume answers.
3
- *
4
- * Detects host-AI attempts to bypass pipeline policies (skip TDD, ignore
5
- * reviewer, disable security, etc.) and rejects them with context-aware
6
- * suggestions for valid alternatives.
7
- */
8
-
9
- const BYPASS_PATTERNS = [
10
- { regex: /\b(?:skip|omit|drop)\s+(?:the\s+)?(?:tests?|tdd|testing)\b/i, policy: "tdd" },
11
- { regex: /\bdisable\s+(?:the\s+)?(?:tests?|tdd|testing)\b/i, policy: "tdd" },
12
- { regex: /\b(?:don'?t|do\s+not|no\s+need\s+(?:for|to))\s+(?:run\s+)?tests?\b/i, policy: "tdd" },
13
- { regex: /\bcontinue\s+without\s+test/i, policy: "tdd" },
14
- { regex: /\bswitch\s+(?:to\s+)?(?:standard\s+)?methodology/i, policy: "tdd" },
15
- { regex: /\bswitch\s+methodology/i, policy: "tdd" },
16
- { regex: /\b(?:skip|ignore|bypass|disable)\s+(?:the\s+)?(?:review(?:er)?|code\s+review)\b/i, policy: "reviewer" },
17
- { regex: /\b(?:approve|pass)\s+without\s+review/i, policy: "reviewer" },
18
- { regex: /\bmark\s+as\s+approved\b/i, policy: "reviewer" },
19
- { regex: /\bjust\s+ship\s+it/i, policy: "reviewer" },
20
- { regex: /\bignore\s+(?:the\s+)?(?:review(?:er)?\s+)?feedback\b/i, policy: "reviewer" },
21
- { regex: /\b(?:skip|ignore|bypass|disable)\s+(?:the\s+)?(?:sonar|sonarqube|quality\s+gate)/i, policy: "sonar" },
22
- { regex: /\bignore\s+sonar\s+issues?\b/i, policy: "sonar" },
23
- { regex: /\b(?:skip|ignore|bypass|disable)\s+(?:the\s+)?security(?:\s+checks?)?\b/i, policy: "security" },
24
- ];
25
-
26
- const SUGGESTIONS_BY_POLICY = {
27
- tdd: [
28
- "Provide specific guidance for the coder on how to fix the failing tests",
29
- "Describe the expected behavior so the coder can write correct tests",
30
- "Suggest a different approach to implement the feature that is easier to test",
31
- ],
32
- reviewer: [
33
- "Provide technical guidance to address the reviewer's feedback",
34
- "Explain why the current approach is correct if you disagree with the review",
35
- "Suggest alternative implementations that satisfy the reviewer's concerns",
36
- ],
37
- sonar: [
38
- "Provide guidance on how to resolve the SonarQube issues",
39
- "Explain the code changes needed to satisfy quality gate requirements",
40
- ],
41
- security: [
42
- "Provide guidance on how to fix the security issues found",
43
- "Explain the secure alternative for the flagged pattern",
44
- ],
45
- };
46
-
47
- const CONTEXT_SUGGESTIONS = {
48
- reviewer_fail_fast: [
49
- "Provide specific technical guidance for the coder to address the reviewer's feedback",
50
- "Suggest a different approach that resolves the reviewer's concerns",
51
- ],
52
- max_iterations: [
53
- "Provide focused guidance to help the coder converge on a solution",
54
- "Narrow the scope: specify exactly which file/function to change and how",
55
- "Continue with additional iterations by choosing option 1 or 2",
56
- ],
57
- standby_exhausted: [
58
- "Wait and retry — the provider may be temporarily overloaded",
59
- "Continue to resume from where the pipeline left off",
60
- ],
61
- };
62
-
63
- /**
64
- * Validate a resume answer against pipeline policies.
65
- *
66
- * @param {string|null} answer - The resume answer from the host
67
- * @param {string} [pauseContext] - The context of the pause (e.g. "reviewer_fail_fast")
68
- * @returns {{ valid: boolean, reason?: string, suggestions?: string[] }}
69
- */
70
- export function validatePolicyCompliance(answer, pauseContext) {
71
- if (answer == null || answer === "") return { valid: true };
72
- if (typeof answer !== "string") return { valid: true };
73
-
74
- const text = answer.trim();
75
- for (const { regex, policy } of BYPASS_PATTERNS) {
76
- if (regex.test(text)) {
77
- const suggestions = [
78
- ...(CONTEXT_SUGGESTIONS[pauseContext] || []),
79
- ...(SUGGESTIONS_BY_POLICY[policy] || []),
80
- ];
81
- return {
82
- valid: false,
83
- reason: `Pipeline policy "${policy}" cannot be bypassed. Provide technical guidance instead.`,
84
- suggestions: [...new Set(suggestions)],
85
- };
86
- }
87
- }
88
-
89
- return { valid: true };
90
- }
@@ -1,85 +0,0 @@
1
- /**
2
- * Policy resolution: map task type + config toggles to concrete pipeline
3
- * flags (TDD, Sonar, reviewer retries, tests required, coder required, ...).
4
- *
5
- * @typedef {import("../types/policy.js").ResolvedPolicies} ResolvedPolicies
6
- * @typedef {import("../types/config.js").KarajanConfig} KarajanConfig
7
- */
8
-
9
- // KJC-TSK-0400: spike y research añadidos como tipos no-code. Comparten
10
- // las defaults restrictivas (tdd/sonar/testsRequired off) porque NO
11
- // producen código que medir: un SPIKE es un .md de decisión, un research
12
- // es una nota de investigación. Forzar Sonar les hace fallar siempre.
13
- export const VALID_TASK_TYPES = new Set([
14
- "sw", "infra", "doc", "add-tests", "refactor",
15
- "audit", "analysis", "no-code", "spike", "research",
16
- ]);
17
-
18
- export const DEFAULT_POLICIES = {
19
- sw: { tdd: true, sonar: true, reviewer: true, testsRequired: true },
20
- infra: { tdd: false, sonar: false, reviewer: false, testsRequired: false },
21
- doc: { tdd: false, sonar: false, reviewer: true, testsRequired: false },
22
- "add-tests": { tdd: false, sonar: true, reviewer: true, testsRequired: true },
23
- refactor: { tdd: true, sonar: true, reviewer: true, testsRequired: false },
24
- audit: { tdd: false, sonar: false, reviewer: false, testsRequired: false, coderRequired: false },
25
- analysis: { tdd: false, sonar: false, reviewer: false, testsRequired: false, coderRequired: false },
26
- "no-code": { tdd: false, sonar: false, reviewer: true, testsRequired: false, coderRequired: true },
27
- spike: { tdd: false, sonar: false, reviewer: true, testsRequired: false, coderRequired: true },
28
- research: { tdd: false, sonar: false, reviewer: true, testsRequired: false, coderRequired: true },
29
- };
30
-
31
- // KJC-TSK-0400: si el title arranca con [SPIKE] / [DOC] / [RESEARCH] /
32
- // [NOCODE], inferimos el task_type del prefijo. Pensado para casos donde
33
- // el planner emite task_type='sw' por defecto pero el title declara
34
- // claramente que la HU no produce código. Devuelve null si no hay match.
35
- const TITLE_PREFIX_TO_TYPE = {
36
- SPIKE: "spike",
37
- RESEARCH: "research",
38
- DOC: "doc",
39
- NOCODE: "no-code",
40
- "NO-CODE": "no-code",
41
- };
42
-
43
- export function inferTaskTypeFromTitle(title) {
44
- if (typeof title !== "string") return null;
45
- const m = title.match(/^\s*\[([A-Z][A-Z-]+)\]/);
46
- if (!m) return null;
47
- return TITLE_PREFIX_TO_TYPE[m[1].toUpperCase()] || null;
48
- }
49
-
50
- // KJC-TSK-0400: dado un HU del plan, devuelve el task_type efectivo:
51
- // explícito en story.task_type si es válido, si no inferido del title.
52
- // Fallback 'sw' (conservador, mantiene gates activos cuando hay duda).
53
- export function effectiveTaskType(story) {
54
- if (!story || typeof story !== "object") return "sw";
55
- if (VALID_TASK_TYPES.has(story.task_type)) return story.task_type;
56
- return inferTaskTypeFromTitle(story.title) || "sw";
57
- }
58
-
59
- const FALLBACK_TYPE = "sw";
60
-
61
- /**
62
- * Resolve pipeline policies for a given taskType.
63
- * Unknown / null / undefined taskType falls back to "sw" (conservative).
64
- * configOverrides optionally merges over defaults per taskType.
65
- */
66
- export function resolvePolicies(taskType, configOverrides) {
67
- const resolvedType = VALID_TASK_TYPES.has(taskType) ? taskType : FALLBACK_TYPE;
68
- const base = { ...DEFAULT_POLICIES[resolvedType] };
69
- const overrides = configOverrides?.[resolvedType];
70
- if (overrides && typeof overrides === "object") {
71
- Object.assign(base, overrides);
72
- }
73
- return base;
74
- }
75
-
76
- /**
77
- * Resolve policies for a taskType and return a flat object with the resolved
78
- * taskType plus all policy flags. This is the main entry point for the
79
- * orchestrator to determine which pipeline stages to enable/disable.
80
- */
81
- export function applyPolicies({ taskType, policies } = {}) {
82
- const resolvedType = VALID_TASK_TYPES.has(taskType) ? taskType : FALLBACK_TYPE;
83
- const resolved = resolvePolicies(taskType, policies);
84
- return { taskType: resolvedType, ...resolved };
85
- }
@@ -1,71 +0,0 @@
1
- // KJC-TSK-0583 — Inbound secret redaction for the review boundary.
2
- //
3
- // The outbound boundary (output-guard.js) already blocks committing secrets.
4
- // This closes the *inbound* gap: a hardcoded credential in the working tree
5
- // would otherwise reach the (possibly cloud) reviewer model verbatim inside the
6
- // diff. `redactSecrets` masks each credential before the model sees it, reusing
7
- // the SAME catalog as output-guard — no new patterns, no AI cost, deterministic.
8
- import { CREDENTIAL_PATTERNS } from "./output-guard.js";
9
-
10
- // Families whose match begins with a fixed, identifiable literal prefix. The
11
- // placeholder keeps that prefix so the reviewer can still tell WHAT kind of
12
- // secret was redacted (and say "move it to .env") without seeing the value.
13
- const STABLE_PREFIX_BY_ID = {
14
- "aws-key": "AKIA",
15
- "npm-token": "npm_",
16
- "openai-key": "sk-",
17
- "anthropic-key": "sk-ant-",
18
- "stripe-key": "sk_live_",
19
- "stripe-test": "sk_test_",
20
- "google-api-key": "AIza",
21
- };
22
-
23
- // Families with a dynamic prefix: keep the real match up to and including the
24
- // first separator (ghp_/gho_/…, xoxb-/xoxp-/…), then mask the rest.
25
- const DYNAMIC_SEPARATOR_BY_ID = {
26
- "github-token": "_",
27
- "slack-token": "-",
28
- };
29
-
30
- const GENERIC = "***REDACTED-SECRET***";
31
- const SUFFIX = "***REDACTED***";
32
-
33
- function placeholderFor(id, match) {
34
- const stable = STABLE_PREFIX_BY_ID[id];
35
- if (stable) return `${stable}${SUFFIX}`;
36
- const sep = DYNAMIC_SEPARATOR_BY_ID[id];
37
- if (sep) {
38
- const idx = match.indexOf(sep);
39
- return idx >= 0 ? `${match.slice(0, idx + 1)}${SUFFIX}` : GENERIC;
40
- }
41
- return GENERIC;
42
- }
43
-
44
- // Apply the most specific prefixes first so `sk-ant-` wins over `sk-`; families
45
- // without a stable prefix keep their catalog order (stable sort) and run last.
46
- function orderedPatterns() {
47
- return [...CREDENTIAL_PATTERNS].sort(
48
- (a, b) => (STABLE_PREFIX_BY_ID[b.id]?.length ?? 0) - (STABLE_PREFIX_BY_ID[a.id]?.length ?? 0)
49
- );
50
- }
51
-
52
- /**
53
- * Mask every credential in `text`, preserving line structure. Pure and
54
- * deterministic: clean text is returned untouched (no-op, no cost).
55
- * @param {string} text
56
- * @returns {string}
57
- */
58
- export function redactSecrets(text) {
59
- if (typeof text !== "string" || text.length === 0) return text;
60
- let out = text;
61
- for (const { id, pattern } of orderedPatterns()) {
62
- const flags = pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`;
63
- out = out.replace(new RegExp(pattern.source, flags), (match) =>
64
- // Idempotent: a broad pattern (generic-secret, hardcoded-key-assignment)
65
- // can span an already-masked value; leave the more specific placeholder
66
- // in place instead of clobbering it with the generic one.
67
- match.includes("REDACTED") ? match : placeholderFor(id, match)
68
- );
69
- }
70
- return out;
71
- }
@@ -1,138 +0,0 @@
1
- /**
2
- * HU Acceptance Test Runner.
3
- * Executes acceptance_tests commands for an HU and returns structured results.
4
- * Brain uses this to determine if an HU is done (all pass) or needs fixing (with diagnostics).
5
- */
6
- import { runCommand } from "../utils/process.js";
7
-
8
- /**
9
- * Run a single acceptance test command.
10
- * @param {string} cmd - Shell command to execute
11
- * @param {string} cwd - Working directory
12
- * @param {number} [timeoutMs=30000] - Timeout per test
13
- * @returns {Promise<{cmd: string, passed: boolean, output: string, exitCode: number}>}
14
- */
15
- async function runSingleTest(cmd, cwd, timeoutMs = 30000, env = null) {
16
- try {
17
- const result = await runCommand("bash", ["-c", cmd], {
18
- timeout: timeoutMs,
19
- cwd,
20
- // PAR-H (KJC-TSK-0631): lane env (KJ_LANE_SLOT / KJ_PORT_OFFSET) so
21
- // tests that start services can offset their ports. execa merges
22
- // this on top of process.env.
23
- ...(env ? { env } : {})
24
- });
25
- const output = (result.stdout || "") + (result.stderr || "");
26
- return {
27
- cmd,
28
- passed: result.exitCode === 0,
29
- output: output.slice(-500), // last 500 chars for diagnostics
30
- exitCode: result.exitCode
31
- };
32
- } catch (err) {
33
- return {
34
- cmd,
35
- passed: false,
36
- output: err.message?.slice(-500) || "Command timed out or crashed",
37
- exitCode: -1
38
- };
39
- }
40
- }
41
-
42
- /**
43
- * Run all acceptance tests for an HU.
44
- *
45
- * Accepts two shapes:
46
- * - Legacy: plain string shell command.
47
- * - v2.7.5: { type: "shell" | "gherkin", content, file? }
48
- *
49
- * Shell entries execute immediately (exit code 0 = pass). Gherkin entries
50
- * cannot be executed directly — that's Phase 3 of tests-first where the
51
- * tester role translates them into real code tests. Until then, we report
52
- * them as "pending" and skip rather than failing the whole gate.
53
- *
54
- * @param {Array<string|object>} tests - Array of shell commands OR structured entries
55
- * @param {string} cwd - Working directory
56
- * @returns {Promise<{allPassed: boolean, results: object[], summary: string, diagnostics: string|null, pending: number}>}
57
- */
58
- export async function runAcceptanceTests(tests, cwd, { env = null } = {}) {
59
- if (!tests || tests.length === 0) {
60
- return { allPassed: false, results: [], summary: "No acceptance tests defined", diagnostics: null, pending: 0 };
61
- }
62
-
63
- const results = [];
64
- let pending = 0;
65
- for (const entry of tests) {
66
- let type = "shell";
67
- let cmd;
68
- if (typeof entry === "string") {
69
- cmd = entry;
70
- } else if (entry && typeof entry === "object" && typeof entry.content === "string") {
71
- type = entry.type === "gherkin" ? "gherkin" : "shell";
72
- cmd = entry.content;
73
- } else {
74
- results.push({ cmd: JSON.stringify(entry), passed: false, output: "Malformed acceptance_test entry", exitCode: -2, type: "invalid" });
75
- continue;
76
- }
77
- if (type === "gherkin") {
78
- // Gherkin entries need a translator (Phase 3). Mark them pending
79
- // so the coder/tester sees they haven't been validated yet —
80
- // better than silently passing (would lie) or hard-failing
81
- // (would block the whole pipeline).
82
- pending += 1;
83
- results.push({
84
- cmd,
85
- passed: false,
86
- output: "Gherkin test not yet executable (awaits tester translation — Phase 3)",
87
- exitCode: 0,
88
- type: "gherkin",
89
- pending: true,
90
- });
91
- continue;
92
- }
93
- const result = await runSingleTest(cmd, cwd, 30000, env);
94
- results.push({ ...result, type: "shell" });
95
- }
96
-
97
- const executedResults = results.filter((r) => !r.pending);
98
- const passed = executedResults.filter((r) => r.passed);
99
- const failed = executedResults.filter((r) => !r.passed);
100
- const allPassed = failed.length === 0;
101
-
102
- const shellSummary = `${passed.length}/${executedResults.length} shell tests passed`;
103
- const summary = pending > 0
104
- ? `${shellSummary}, ${pending} gherkin test(s) pending translation`
105
- : shellSummary;
106
-
107
- let diagnostics = null;
108
- if (!allPassed) {
109
- diagnostics = failed.map((f) =>
110
- `FAIL: ${f.cmd}\n exit=${f.exitCode}\n output: ${f.output.trim().split("\n").slice(-5).join("\n ")}`
111
- ).join("\n\n");
112
- }
113
-
114
- return { allPassed, results, summary, diagnostics, pending };
115
- }
116
-
117
- /**
118
- * Build a concrete diagnostic prompt for Brain to send to the coder.
119
- * Reads the failed test outputs and produces actionable instructions.
120
- * @param {object[]} failedResults - Array of failed test results
121
- * @returns {string} Prompt for the coder with concrete fix instructions
122
- */
123
- export function buildDiagnosticPrompt(failedResults) {
124
- if (!failedResults || failedResults.length === 0) return "";
125
- const lines = ["The following acceptance tests FAILED. Fix each one:", ""];
126
- for (const f of failedResults) {
127
- lines.push(`❌ Command: ${f.cmd}`);
128
- lines.push(` Exit code: ${f.exitCode}`);
129
- const lastLines = f.output.trim().split("\n").slice(-8);
130
- lines.push(` Last output:`);
131
- for (const l of lastLines) {
132
- lines.push(` ${l}`);
133
- }
134
- lines.push("");
135
- }
136
- lines.push("Fix ALL failing tests. Run each command yourself to verify before finishing.");
137
- return lines.join("\n");
138
- }