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
@@ -1,12 +1,11 @@
1
1
  /**
2
- * Native build availability check (KJC-BUG-0092).
2
+ * Native module check (KJC-BUG-0092, KJC-TSK-1353).
3
3
  *
4
- * better-sqlite3 ships a native addon compiled in a postinstall step. pnpm
5
- * blocks dependency build scripts by default (ERR_PNPM_IGNORED_BUILDS), so a
6
- * `pnpm add -g karajan-code` leaves the addon uncompiled: `kj --version` still
7
- * works (it never opens a DB) but any DB-backed command (board, rag, cost
8
- * tracking) crashes with "Cannot find module 'better-sqlite3'". This surfaces a
9
- * WARN in `kj doctor` with the exact remedy instead of a cryptic runtime crash.
4
+ * node-pty is kj's only native module since better-sqlite3 left (KJC-TSK-1353:
5
+ * every SQLite store runs on node:sqlite, built into Node). It is optional: it
6
+ * powers the HU Board's terminal. Where it needs its install script, pnpm
7
+ * (ERR_PNPM_IGNORED_BUILDS) and npm 12 (allowScripts) block it by default, so
8
+ * `kj doctor` warns with the exact remedy instead of a broken terminal.
10
9
  */
11
10
 
12
11
  import { execFileSync } from "node:child_process";
@@ -32,19 +31,7 @@ function defaultNpmMajor() {
32
31
  * allowScripts covers it; reinstalling blocks it again. Approve, then rebuild, in kj's dir.
33
32
  */
34
33
  export const npmAllowScriptsRemedy = (pkgDir = OWN_PKG_DIR) =>
35
- `cd "${pkgDir}" && npm install-scripts approve better-sqlite3 && npm install-scripts approve node-pty && npm rebuild better-sqlite3 node-pty`;
36
-
37
- // The addon is installed but its native build is missing (a missing package needs a reinstall, not this).
38
- const UNBUILT_ADDON = /bindings file|better_sqlite3\.node/;
39
-
40
- /** A missing better-sqlite3 addon becomes an error that says how to build it; any other error passes as it is. */
41
- export function explainNativeLoadError(err, { npmMajor = defaultNpmMajor, pkgDir = OWN_PKG_DIR } = {}) {
42
- if (!UNBUILT_ADDON.test(String(err?.message ?? err))) return err;
43
- const remedy = (npmMajor() ?? 0) >= 12
44
- ? `npm 12 skipped its install script (allowScripts); build it with: ${npmAllowScriptsRemedy(pkgDir)}`
45
- : "run kj doctor for the exact fix on this install";
46
- return new Error(`better-sqlite3 native addon is not built, so the RAG cannot open its index — ${remedy}`, { cause: err });
47
- }
34
+ `cd "${pkgDir}" && npm install-scripts approve node-pty && npm rebuild node-pty`;
48
35
 
49
36
  /** pnpm lays packages out under a `.pnpm` virtual store and symlinks them in. */
50
37
  function isPnpmInstall() {
@@ -54,50 +41,45 @@ function isPnpmInstall() {
54
41
  );
55
42
  }
56
43
 
57
- /** Force the native binding to load by opening an in-memory database. */
58
- async function defaultLoadDb() {
59
- const Database = (await import("better-sqlite3")).default;
60
- new Database(":memory:").close();
44
+ /** Load node-pty's native binding. */
45
+ async function defaultLoadPty() {
46
+ await import("node-pty");
61
47
  }
62
48
 
63
- export function createNativeBuildCheck({ loadDb = defaultLoadDb, isPnpm = isPnpmInstall, npmMajor = defaultNpmMajor, pkgDir = OWN_PKG_DIR } = {}) {
49
+ const NO_TERMINAL = "the HU Board terminal is unavailable (everything else works)";
50
+
51
+ export function createNativeBuildCheck({ loadPty = defaultLoadPty, isPnpm = isPnpmInstall, npmMajor = defaultNpmMajor, pkgDir = OWN_PKG_DIR } = {}) {
64
52
  return {
65
53
  name: "native-build",
66
- label: "native modules (better-sqlite3)",
54
+ label: "native modules (node-pty)",
67
55
  strategy: STRATEGY.MANUAL,
68
56
  async detect() {
69
57
  try {
70
- await loadDb();
71
- return {
72
- ok: true,
73
- severity: "info",
74
- detail: "better-sqlite3 native addon loads — DB-backed commands available",
75
- };
58
+ await loadPty();
59
+ return { ok: true, severity: "info", detail: "node-pty loads — the HU Board terminal is available" };
76
60
  } catch (err) {
77
61
  const firstLine = String(err?.message || err).split("\n")[0];
78
62
  if (isPnpm()) {
79
63
  return {
80
64
  ok: false,
81
65
  severity: "warn",
82
- detail: "better-sqlite3 native build was skipped — pnpm blocks dependency build scripts by default",
83
- fix: "Approve the build then reinstall: `pnpm approve-builds better-sqlite3` — or install with npm: `npm i -g @karajan-family/code`",
66
+ detail: `node-pty native build was skipped — pnpm blocks dependency build scripts by default; ${NO_TERMINAL}`,
67
+ fix: "Approve the build then reinstall: `pnpm approve-builds node-pty` — or install with npm: `npm i -g @karajan-family/code`",
84
68
  };
85
69
  }
86
70
  if ((npmMajor() ?? 0) >= 12) {
87
71
  return {
88
72
  ok: false,
89
73
  severity: "warn",
90
- detail: `better-sqlite3 failed to load: ${firstLine} — npm 12 skips dependency install scripts that allowScripts does not cover, so the native build never ran`,
74
+ detail: `node-pty failed to load: ${firstLine} — npm 12 skips dependency install scripts that allowScripts does not cover; ${NO_TERMINAL}`,
91
75
  fix: npmAllowScriptsRemedy(pkgDir),
92
76
  };
93
77
  }
94
78
  return {
95
79
  ok: false,
96
80
  severity: "warn",
97
- // KJC-BUG-0118: the standalone (curl) binary ships without native
98
- // modules by design — name the consequence (RAG/board/MCP need the
99
- // npm install) instead of implying the install is broken.
100
- detail: `better-sqlite3 failed to load: ${firstLine} — DB-backed features (RAG, board, MCP) unavailable; the standalone binary does not bundle native modules`,
81
+ // KJC-BUG-0118: the standalone (curl) binary ships without native modules by design.
82
+ detail: `node-pty failed to load: ${firstLine} — ${NO_TERMINAL}; the standalone binary does not bundle native modules`,
101
83
  fix: "Reinstall to rebuild native modules: `npm i -g @karajan-family/code`",
102
84
  };
103
85
  }
@@ -51,162 +51,25 @@ export async function runChecks(checks, ctx, options = {}) {
51
51
 
52
52
  const { config } = ctx;
53
53
  const startTotal = Date.now();
54
- const entries = [];
54
+ const entries = checks.map((check) => entryFor(check, config));
55
55
  const overrides = {};
56
56
 
57
- // Build applicable entries (skipped checks noted up front).
58
- // A check.applies(config) may return:
59
- // - true / false — boolean form (back-compat).
60
- // - an object { applies: boolean, reason?: string } — when false, `reason`
61
- // is shown verbatim so users see WHY (e.g. "sonarqube.enabled is false
62
- // in kj.config.yml") instead of the opaque "Not applicable" message
63
- // that confused users debugging their config.
64
- for (const check of checks) {
65
- if (typeof check.applies !== "function") {
66
- entries.push({ check, status: null, detail: null, runMs: 0, detectResult: null, remediated: false });
67
- continue;
68
- }
69
- const verdict = check.applies(config);
70
- const isObj = verdict && typeof verdict === "object" && "applies" in verdict;
71
- const applies = isObj ? Boolean(verdict.applies) : Boolean(verdict);
72
- if (applies) {
73
- entries.push({ check, status: null, detail: null, runMs: 0, detectResult: null, remediated: false });
74
- continue;
75
- }
76
- const reason = (isObj && typeof verdict.reason === "string" && verdict.reason.trim())
77
- ? verdict.reason.trim()
78
- : "Not applicable for current configuration";
79
- entries.push({
80
- check,
81
- status: STATUS.SKIPPED,
82
- detail: reason,
83
- runMs: 0,
84
- detectResult: null,
85
- remediated: false,
86
- });
87
- }
88
-
89
57
  // Phase 1: detect in parallel with timeout.
90
58
  const detectBase = Date.now();
91
- await Promise.all(
92
- entries.map(async (entry) => {
93
- if (entry.status === STATUS.SKIPPED) return;
94
- const start = Date.now();
95
- // Per-check override: checks that spawn slow tools (e.g. `npx <pkg>` cold
96
- // start) declare detectTimeoutMs so the blanket pipeline timeout doesn't
97
- // kill them. Falls back to the pipeline-wide timeoutMs.
98
- const detectTimeoutMs = Number.isFinite(entry.check.detectTimeoutMs)
99
- ? entry.check.detectTimeoutMs
100
- : timeoutMs;
101
- try {
102
- const result = await withTimeout(entry.check.detect({ config, signal }), detectTimeoutMs, entry.check.name);
103
- entry.detectResult = result;
104
- entry.runMs = Date.now() - start;
105
- entry.status = resolveStatusFromDetect(entry.check, result);
106
- entry.detail = result.detail;
107
- // KJC-BUG-0049: si el check es degradable y falló (status FAIL/WARN tras
108
- // resolveStatusFromDetect), aplicar disables al overrides y degradar a WARN.
109
- // Así el preflight no aborta — la feature opcional se desactiva con aviso.
110
- if (!result.ok && entry.check.degradable && (entry.status === STATUS.FAIL || entry.status === STATUS.WARN)) {
111
- for (const dotPath of entry.check.degradable.disables || []) {
112
- setDotPath(overrides, dotPath, false);
113
- }
114
- entry.status = STATUS.WARN;
115
- entry.detail = `${result.detail} → ${entry.check.degradable.warn}`;
116
- entry.degraded = true;
117
- }
118
- } catch (err) {
119
- entry.runMs = Date.now() - start;
120
- if (err instanceof TimeoutError) {
121
- entry.detectResult = { ok: false, severity: "warn", detail: `Timeout after ${detectTimeoutMs}ms` };
122
- // A timeout on a degradable (optional) check must not block the
123
- // pipeline: degrade to WARN and disable its feature flags, same as
124
- // a regular failure (KJC-BUG-0049 semantics).
125
- if (entry.check.degradable) {
126
- for (const dotPath of entry.check.degradable.disables || []) {
127
- setDotPath(overrides, dotPath, false);
128
- }
129
- entry.status = STATUS.WARN;
130
- entry.detail = `${entry.detectResult.detail} → ${entry.check.degradable.warn}`;
131
- entry.degraded = true;
132
- } else {
133
- entry.status = STATUS.TIMEOUT;
134
- entry.detail = entry.detectResult.detail;
135
- }
136
- } else {
137
- entry.detectResult = { ok: false, severity: "fail", detail: `Detection error: ${err.message}` };
138
- entry.status = STATUS.FAIL;
139
- entry.detail = entry.detectResult.detail;
140
- }
141
- }
142
- })
143
- );
59
+ await Promise.all(entries.map((entry) => detectEntry(entry, { config, signal, timeoutMs, overrides })));
144
60
  logger.info?.(`Detect phase done in ${Date.now() - detectBase}ms`);
145
61
 
146
- // Phase 2: remediate sequentially (only in "fix" mode, only failing checks).
147
62
  if (mode !== "check-only") {
63
+ // Phase 2: remediate sequentially (only in "fix" mode, only failing checks).
148
64
  for (const entry of entries) {
149
- if (!shouldAttemptRemediation(entry)) continue;
150
- const { check, detectResult } = entry;
151
- if (check.strategy === STRATEGY.PROMPT && !yes) {
152
- const approved = await onConfirm(check.describe || `Fix: ${check.name}`);
153
- if (!approved) {
154
- logger.info?.(`User declined remediation for ${check.name}`);
155
- continue;
156
- }
157
- }
158
- const start = Date.now();
159
- try {
160
- const rem = await withTimeout(
161
- check.remediate({ config, extra: detectResult.extra, signal, logger }),
162
- remediateTimeoutMs,
163
- `${check.name}:remediate`
164
- );
165
- entry.remediated = rem.fixed === true;
166
- entry.runMs += Date.now() - start;
167
- if (rem.fixed) {
168
- if (rem.changes) mergeOverrides(overrides, rem.changes);
169
- entry.detail = rem.detail;
170
- entry.status = STATUS.FIXED;
171
- } else {
172
- entry.detail = rem.detail || entry.detail;
173
- entry.status = STATUS.FAIL;
174
- }
175
- } catch (err) {
176
- entry.runMs += Date.now() - start;
177
- const msg = err instanceof TimeoutError ? `Remediation timeout after ${remediateTimeoutMs}ms` : `Remediation error: ${err.message}`;
178
- entry.detail = msg;
179
- entry.status = STATUS.FAIL;
180
- logger.warn?.(`Remediation failed for ${check.name}: ${msg}`);
181
- }
65
+ if (shouldAttemptRemediation(entry)) await remediateEntry(entry, { config, signal, logger, yes, onConfirm, remediateTimeoutMs, overrides });
182
66
  }
183
-
184
67
  // Phase 3: re-verify fixed checks using the config merged with any
185
68
  // runtime overrides applied by remediations. Without this merge, a check
186
69
  // that rebinds to a new port (override) would re-verify against the old
187
70
  // config and wrongly report failure.
188
71
  const verifyConfig = mergeConfigAndOverrides(config, overrides);
189
- await Promise.all(
190
- entries.filter((e) => e.remediated).map(async (entry) => {
191
- const start = Date.now();
192
- try {
193
- const result = await withTimeout(
194
- entry.check.detect({ config: verifyConfig, signal }),
195
- Number.isFinite(entry.check.detectTimeoutMs) ? entry.check.detectTimeoutMs : timeoutMs,
196
- `${entry.check.name}:reverify`
197
- );
198
- entry.runMs += Date.now() - start;
199
- if (!result.ok) {
200
- entry.status = STATUS.FAIL;
201
- entry.detail = `${entry.detail} (but re-verify failed: ${result.detail})`;
202
- }
203
- } catch (err) {
204
- entry.runMs += Date.now() - start;
205
- entry.status = STATUS.FAIL;
206
- entry.detail = `${entry.detail} (re-verify error: ${err.message})`;
207
- }
208
- })
209
- );
72
+ await Promise.all(entries.filter((e) => e.remediated).map((entry) => reverifyEntry(entry, { verifyConfig, signal, timeoutMs })));
210
73
  }
211
74
 
212
75
  // Flatten into reports.
@@ -234,6 +97,129 @@ export async function runChecks(checks, ctx, options = {}) {
234
97
  };
235
98
  }
236
99
 
100
+ // Build an applicable entry (skipped checks noted up front).
101
+ // A check.applies(config) may return:
102
+ // - true / false — boolean form (back-compat).
103
+ // - an object { applies: boolean, reason?: string } — when false, `reason`
104
+ // is shown verbatim so users see WHY (e.g. "sonarqube.enabled is false
105
+ // in kj.config.yml") instead of the opaque "Not applicable" message
106
+ // that confused users debugging their config.
107
+ function entryFor(check, config) {
108
+ const entry = { check, status: null, detail: null, runMs: 0, detectResult: null, remediated: false };
109
+ if (typeof check.applies !== "function") return entry;
110
+ const verdict = check.applies(config);
111
+ const isObj = verdict && typeof verdict === "object" && "applies" in verdict;
112
+ const applies = isObj ? Boolean(verdict.applies) : Boolean(verdict);
113
+ if (applies) return entry;
114
+ const reason = (isObj && typeof verdict.reason === "string" && verdict.reason.trim())
115
+ ? verdict.reason.trim()
116
+ : "Not applicable for current configuration";
117
+ return { ...entry, status: STATUS.SKIPPED, detail: reason };
118
+ }
119
+
120
+ // Per-check override: checks that spawn slow tools (e.g. `npx <pkg>` cold
121
+ // start) declare detectTimeoutMs so the blanket pipeline timeout doesn't
122
+ // kill them. Falls back to the pipeline-wide timeoutMs.
123
+ const detectTimeoutOf = (check, timeoutMs) => (Number.isFinite(check.detectTimeoutMs) ? check.detectTimeoutMs : timeoutMs);
124
+
125
+ // KJC-BUG-0049: an optional (degradable) check that fails or times out does not
126
+ // block the pipeline: its feature flags are disabled and it degrades to WARN.
127
+ function degrade(entry, overrides, detail) {
128
+ for (const dotPath of entry.check.degradable.disables || []) {
129
+ setDotPath(overrides, dotPath, false);
130
+ }
131
+ entry.status = STATUS.WARN;
132
+ entry.detail = `${detail} → ${entry.check.degradable.warn}`;
133
+ entry.degraded = true;
134
+ }
135
+
136
+ async function detectEntry(entry, { config, signal, timeoutMs, overrides }) {
137
+ if (entry.status === STATUS.SKIPPED) return;
138
+ const start = Date.now();
139
+ const detectTimeoutMs = detectTimeoutOf(entry.check, timeoutMs);
140
+ try {
141
+ const result = await withTimeout(entry.check.detect({ config, signal }), detectTimeoutMs, entry.check.name);
142
+ entry.detectResult = result;
143
+ entry.runMs = Date.now() - start;
144
+ entry.status = resolveStatusFromDetect(entry.check, result);
145
+ entry.detail = result.detail;
146
+ // Así el preflight no aborta — la feature opcional se desactiva con aviso.
147
+ if (!result.ok && entry.check.degradable && (entry.status === STATUS.FAIL || entry.status === STATUS.WARN)) {
148
+ degrade(entry, overrides, result.detail);
149
+ }
150
+ } catch (err) {
151
+ entry.runMs = Date.now() - start;
152
+ if (!(err instanceof TimeoutError)) {
153
+ entry.detectResult = { ok: false, severity: "fail", detail: `Detection error: ${err.message}` };
154
+ entry.status = STATUS.FAIL;
155
+ entry.detail = entry.detectResult.detail;
156
+ return;
157
+ }
158
+ entry.detectResult = { ok: false, severity: "warn", detail: `Timeout after ${detectTimeoutMs}ms` };
159
+ if (entry.check.degradable) {
160
+ degrade(entry, overrides, entry.detectResult.detail);
161
+ } else {
162
+ entry.status = STATUS.TIMEOUT;
163
+ entry.detail = entry.detectResult.detail;
164
+ }
165
+ }
166
+ }
167
+
168
+ async function remediateEntry(entry, { config, signal, logger, yes, onConfirm, remediateTimeoutMs, overrides }) {
169
+ const { check, detectResult } = entry;
170
+ if (check.strategy === STRATEGY.PROMPT && !yes) {
171
+ const approved = await onConfirm(check.describe || `Fix: ${check.name}`);
172
+ if (!approved) {
173
+ logger.info?.(`User declined remediation for ${check.name}`);
174
+ return;
175
+ }
176
+ }
177
+ const start = Date.now();
178
+ try {
179
+ const rem = await withTimeout(
180
+ check.remediate({ config, extra: detectResult.extra, signal, logger }),
181
+ remediateTimeoutMs,
182
+ `${check.name}:remediate`
183
+ );
184
+ entry.remediated = rem.fixed === true;
185
+ entry.runMs += Date.now() - start;
186
+ if (rem.fixed) {
187
+ if (rem.changes) mergeOverrides(overrides, rem.changes);
188
+ entry.detail = rem.detail;
189
+ entry.status = STATUS.FIXED;
190
+ } else {
191
+ entry.detail = rem.detail || entry.detail;
192
+ entry.status = STATUS.FAIL;
193
+ }
194
+ } catch (err) {
195
+ entry.runMs += Date.now() - start;
196
+ const msg = err instanceof TimeoutError ? `Remediation timeout after ${remediateTimeoutMs}ms` : `Remediation error: ${err.message}`;
197
+ entry.detail = msg;
198
+ entry.status = STATUS.FAIL;
199
+ logger.warn?.(`Remediation failed for ${check.name}: ${msg}`);
200
+ }
201
+ }
202
+
203
+ async function reverifyEntry(entry, { verifyConfig, signal, timeoutMs }) {
204
+ const start = Date.now();
205
+ try {
206
+ const result = await withTimeout(
207
+ entry.check.detect({ config: verifyConfig, signal }),
208
+ detectTimeoutOf(entry.check, timeoutMs),
209
+ `${entry.check.name}:reverify`
210
+ );
211
+ entry.runMs += Date.now() - start;
212
+ if (!result.ok) {
213
+ entry.status = STATUS.FAIL;
214
+ entry.detail = `${entry.detail} (but re-verify failed: ${result.detail})`;
215
+ }
216
+ } catch (err) {
217
+ entry.runMs += Date.now() - start;
218
+ entry.status = STATUS.FAIL;
219
+ entry.detail = `${entry.detail} (re-verify error: ${err.message})`;
220
+ }
221
+ }
222
+
237
223
  function shouldAttemptRemediation(entry) {
238
224
  if (entry.status === STATUS.SKIPPED) return false;
239
225
  if (entry.status === STATUS.OK) return false;
@@ -13,9 +13,9 @@ export const CORE_COMMANDS = [
13
13
  // gets — it is the first command a new project ever runs.
14
14
  "bootstrap",
15
15
  "init",
16
- "run",
16
+ "hu",
17
+ "review",
17
18
  "plan",
18
- "status",
19
19
  "doctor",
20
20
  "harden",
21
21
  "config",
@@ -31,11 +31,11 @@ export const META_COMMANDS = ["advanced", "help"];
31
31
  * so a newly-registered command can never silently vanish from `kj advanced`.
32
32
  */
33
33
  export const ADVANCED_GROUPS = [
34
- { title: "Pipeline (piezas sueltas)", commands: ["autorun", "code", "review", "solomon", "agent", "scan", "tournament"] },
35
- { title: "Análisis pre-run", commands: ["discover", "spec", "triage", "researcher", "architect", "onboard", "brief"] },
34
+ { title: "Agentes y revisión", commands: ["code", "solomon", "agent", "scan", "tournament"] },
35
+ { title: "Análisis previo", commands: ["discover", "spec", "onboard", "brief"] },
36
36
  { title: "Búsqueda / RAG", commands: ["rag", "qmd", "watch", "doc"] },
37
- { title: "Calidad / auditoría", commands: ["audit", "check", "mutate", "webperf", "sonar", "privacy", "release", "policy", "rules", "rulesync", "claims", "steward", "pr-size"] },
38
- { title: "Sesión / board", commands: ["resume", "report", "board", "hu", "adr", "worktree", "undo", "standby", "sentinel", "identity", "security", "handoff"] },
37
+ { title: "Calidad / auditoría", commands: ["audit", "check", "mutate", "webperf", "sonar", "privacy", "release", "policy", "rules", "rulesync", "claims", "steward", "pr-size", "main-guard"] },
38
+ { title: "Sesión / board", commands: ["report", "board", "adr", "worktree", "sentinel", "identity", "security", "handoff"] },
39
39
  { title: "Infra / setup", commands: ["install-tools", "ollama", "skills", "roles", "agents", "env"] },
40
40
  { title: "Mantenimiento", commands: ["clean", "sync", "telemetry", "report-issue"] },
41
41
  ];
@@ -1,8 +1,6 @@
1
1
  import { discoverCommand } from "../commands/discover.js";
2
2
  import { specRefineCommand } from "../commands/spec-refine.js";
3
- import { triageCommand } from "../commands/triage.js";
4
- import { researcherCommand } from "../commands/researcher.js";
5
- import { architectCommand } from "../commands/architect.js";
3
+ import { writeSbom } from "../audit/sbom.js";
6
4
  import { onboardCommand } from "../commands/onboard.js";
7
5
  import { startCommand } from "../commands/start.js";
8
6
  import { goCommand } from "../commands/go.js";
@@ -19,10 +17,8 @@ import { qmdQueryCommand } from "../commands/qmd.js";
19
17
  import { ragMcpCommand } from "../commands/rag-mcp.js";
20
18
  import { watchStartCommand, watchStopCommand, watchStatusCommand } from "../commands/watch.js";
21
19
  import { auditCommand } from "../commands/audit.js";
22
- import { resumeCommand } from "../commands/resume.js";
23
20
  import { boardCommand } from "../commands/board.js";
24
21
  import { webperfCommand } from "../commands/webperf.js";
25
- import { undoCommand } from "../commands/undo.js";
26
22
  import { syncCommand } from "../commands/sync.js";
27
23
  import { cleanCommand } from "../commands/clean.js";
28
24
  import { checkCommand } from "../commands/check.js";
@@ -106,54 +102,6 @@ export function registerMeta(program, { pkgVersion }) {
106
102
  });
107
103
  });
108
104
 
109
- program
110
- .command("triage")
111
- .description("Classify task complexity and recommend pipeline roles")
112
- .argument("[task]", "Task description (REQUIRED — provide as argument or via --task-file)")
113
- .option("--task-file <path>", "Read the task from a file (e.g. .md)")
114
- .option("--triage <name>", "Override triage agent")
115
- .option("--triage-model <name>", "Override triage model")
116
- .option("--json", "Output raw JSON")
117
- .action(async (task, flags) => {
118
- await withConfig(pkgVersion, "triage", flags, async ({ config, logger }) => {
119
- const { resolveTaskInput } = await import("../utils/task-file.js");
120
- const resolvedTask = await resolveTaskInput({ task, taskFile: flags.taskFile, projectDir: config.projectDir, logger, indexFirstBytes: config.docs?.indexFirstBytes });
121
- await triageCommand({ task: resolvedTask, config, logger, json: flags.json });
122
- });
123
- });
124
-
125
- program
126
- .command("researcher")
127
- .description("Research codebase for a task (files, patterns, constraints)")
128
- .argument("[task]", "Task description (REQUIRED — provide as argument or via --task-file)")
129
- .option("--task-file <path>", "Read the task from a file (e.g. .md)")
130
- .option("--researcher <name>", "Override researcher agent")
131
- .option("--researcher-model <name>", "Override researcher model")
132
- .action(async (task, flags) => {
133
- await withConfig(pkgVersion, "researcher", flags, async ({ config, logger }) => {
134
- const { resolveTaskInput } = await import("../utils/task-file.js");
135
- const resolvedTask = await resolveTaskInput({ task, taskFile: flags.taskFile, projectDir: config.projectDir, logger, indexFirstBytes: config.docs?.indexFirstBytes });
136
- await researcherCommand({ task: resolvedTask, config, logger });
137
- });
138
- });
139
-
140
- program
141
- .command("architect")
142
- .description("Design solution architecture (layers, patterns, contracts)")
143
- .argument("[task]", "Task description (REQUIRED — provide as argument or via --task-file)")
144
- .option("--task-file <path>", "Read the task from a file (e.g. .md)")
145
- .option("--architect <name>", "Override architect agent")
146
- .option("--architect-model <name>", "Override architect model")
147
- .option("--context <text>", "Additional context (e.g. researcher output)")
148
- .option("--json", "Output raw JSON")
149
- .action(async (task, flags) => {
150
- await withConfig(pkgVersion, "architect", flags, async ({ config, logger }) => {
151
- const { resolveTaskInput } = await import("../utils/task-file.js");
152
- const resolvedTask = await resolveTaskInput({ task, taskFile: flags.taskFile, projectDir: config.projectDir, logger, indexFirstBytes: config.docs?.indexFirstBytes });
153
- await architectCommand({ task: resolvedTask, config, logger, context: flags.context, json: flags.json });
154
- });
155
- });
156
-
157
105
  program
158
106
  .command("onboard")
159
107
  .description("Analyze an existing codebase (brownfield) and produce an Architecture Brief")
@@ -317,7 +265,7 @@ export function registerMeta(program, { pkgVersion }) {
317
265
  // beats ordering a practice an agent can narrate without doing.
318
266
  const wt = program.command("worktree").description("Isolated task lanes (git worktrees) for the host agent");
319
267
  wt.command("start <slug>")
320
- .description("Create a lane: worktree + feat/<slug> branch + dependency bootstrap")
268
+ .description("Create a lane: worktree + feat/<slug> branch from origin/<base> + dependency bootstrap")
321
269
  .action(async (slug, flags) => {
322
270
  await withConfig(pkgVersion, "worktree", flags, async ({ config }) => {
323
271
  await worktreeCommand({ config, action: "start", args: [slug], flags });
@@ -325,14 +273,16 @@ export function registerMeta(program, { pkgVersion }) {
325
273
  });
326
274
  wt.command("list")
327
275
  .description("List the active lanes")
276
+ .option("--json", "Each lane with its commits ahead of the base and the cards its branch names")
277
+ .option("--prs", "With --json, each lane's PR (one gh call)")
328
278
  .action(async (flags) => {
329
279
  await withConfig(pkgVersion, "worktree", flags, async ({ config }) => {
330
280
  await worktreeCommand({ config, action: "list", flags });
331
281
  });
332
282
  });
333
283
  wt.command("done <slug>")
334
- .option("--force", "Remove the lane even with uncommitted changes")
335
- .description("Remove a merged lane (refuses uncommitted changes without --force)")
284
+ .option("--force", "Remove the lane even with uncommitted changes, and delete its branch even if not merged")
285
+ .description("Remove a lane and delete its branch when merged into the base (refuses uncommitted changes without --force)")
336
286
  .action(async (slug, flags) => {
337
287
  await withConfig(pkgVersion, "worktree", flags, async ({ config }) => {
338
288
  await worktreeCommand({ config, action: "done", args: [slug], flags });
@@ -372,6 +322,16 @@ export function registerMeta(program, { pkgVersion }) {
372
322
  await huCommand({ config, action: "move", args: [id, status], flags });
373
323
  });
374
324
  });
325
+ // KJC-TSK-1372 (#2853): one step to start a task: card, lane, running.
326
+ hu.command("start <id> [slug]")
327
+ .description("Start a card: open its lane feat/<id>-<slug> from origin/<base> and move it to running (slug from the title if omitted)")
328
+ .option("--code", "Then have the panel coder write the card in the lane (kj code --card)")
329
+ .option("--json", "Machine-readable output")
330
+ .action(async (id, slug, flags) => {
331
+ await withConfig(pkgVersion, "hu", flags, async ({ config }) => {
332
+ await huCommand({ config, action: "start", args: [id, slug].filter(Boolean), flags });
333
+ });
334
+ });
375
335
  hu.command("list")
376
336
  .option("--json", "Machine-readable output")
377
337
  .action(async (flags) => {
@@ -801,6 +761,19 @@ export function registerMeta(program, { pkgVersion }) {
801
761
  });
802
762
  });
803
763
 
764
+ // KJC-TSK-1369 (ADR 0037): the audit job on every push to the protected branch.
765
+ program.command("main-guard")
766
+ .description("Audit a push to the protected branch: merges without their required checks, and the protection (exit 1 with a markdown report)")
767
+ .requiredOption("--repo <owner/repo>", "GitHub repository")
768
+ .requiredOption("--before <sha>", "The branch tip before the push")
769
+ .requiredOption("--after <sha>", "The branch tip after the push")
770
+ .requiredOption("--require <checks...>", "Required checks that every merge must have passed")
771
+ .option("--branch <name>", "Protected branch", "main")
772
+ .action(async (flags) => {
773
+ const { mainGuardCommand } = await import("../commands/main-guard.js");
774
+ await mainGuardCommand({ flags });
775
+ });
776
+
804
777
  const rag = program.command("rag").description("Retrieval-augmented search over Karajan plans, onboarding briefs and project code");
805
778
  rag.command("index")
806
779
  .description("Index plans + onboarding (and optionally project sources) into the local vector store")
@@ -947,8 +920,21 @@ export function registerMeta(program, { pkgVersion }) {
947
920
  .option("--deterministic-only", "Skip the LLM analysis entirely. Print/persist only the deterministic findings (basalCost, sonar, stack, growth-delta, webperf). Zero tokens spent. Compatible with --report-file and --json.")
948
921
  .option("--security", "Focused deterministic security pass: prompt-injection scan over the agent-context surface (CLAUDE.md/AGENTS.md/templates/.rulesync/.karajan) + OSV + Semgrep + Sonar. Skips basal-cost, webperf, madge, knip, ai-slop, harness and the LLM — zero tokens. Designed for agents to self-invoke when a task touches sensitive surface (KJC-TSK-0695).")
949
922
  .option("-y, --yes", "Auto-confirm the 'Continue with LLM analysis?' prompt. Useful in scripts that want the full audit non-interactively. CI/non-TTY paths already auto-confirm without this flag.")
923
+ .option("--sbom <file>", "Only write the CycloneDX SBOM of the npm project's production dependencies to <file> (from package-lock.json) and stop. KJC-TSK-1255.")
950
924
  .action(async (task, flags) => {
951
925
  await withConfig(pkgVersion, "audit", flags, async ({ config, logger }) => {
926
+ if (flags.sbom) {
927
+ try {
928
+ const res = await writeSbom({ projectDir: config.projectDir || process.cwd(), file: flags.sbom });
929
+ if (flags.json) console.log(JSON.stringify(res));
930
+ else console.log(`SBOM (CycloneDX, ${res.components} components): ${res.file}`);
931
+ } catch (err) {
932
+ if (flags.json) console.log(JSON.stringify({ error: err.message }));
933
+ else logger.error(err.message);
934
+ process.exitCode = 1;
935
+ }
936
+ return;
937
+ }
952
938
  let resolvedTask = task;
953
939
  if (flags.taskFile) {
954
940
  const { readTaskFile } = await import("../utils/task-file.js");
@@ -997,21 +983,6 @@ export function registerMeta(program, { pkgVersion }) {
997
983
  });
998
984
  });
999
985
 
1000
- program
1001
- .command("resume")
1002
- .description("Resume a paused session")
1003
- .argument("<sessionId>")
1004
- .option("--answer <text>", "Answer to the question that caused the pause")
1005
- .option("--non-interactive", "Agents/CI: auto-answer prompts with their safe defaults; stop when there is none. Also enabled by KJ_NON_INTERACTIVE=1.")
1006
- .option("--json", "Output JSON only")
1007
- .action(async (sessionId, flags) => {
1008
- await withConfig(pkgVersion, "resume", flags, async ({ config, logger }) => {
1009
- const res = await resumeCommand({ sessionId, answer: flags.answer, config, logger, flags });
1010
- // KJC-TSK-0674 (issue #1289): same contract as kj run — an aborted
1011
- // resume must be observable by CI.
1012
- if (res?.aborted) process.exitCode = 1;
1013
- });
1014
- });
1015
986
 
1016
987
  program
1017
988
  .command("update")
@@ -1061,17 +1032,6 @@ export function registerMeta(program, { pkgVersion }) {
1061
1032
  });
1062
1033
  });
1063
1034
 
1064
- program
1065
- .command("undo")
1066
- .description("Revert last pipeline run")
1067
- .option("--hard", "Discard all changes (default: soft reset, keeps changes staged)")
1068
- .action(async (flags) => {
1069
- await withConfig(pkgVersion, "undo", flags, async ({ logger }) => {
1070
- const result = await undoCommand({ hard: !!flags.hard, logger });
1071
- if (!result.ok) process.exit(1);
1072
- });
1073
- });
1074
-
1075
1035
  program
1076
1036
  .command("sync")
1077
1037
  .description("Detect drift between code and the latest plan. Read-only by default; --apply writes a patch back to the Canvas.")