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
@@ -8,10 +8,8 @@
8
8
  import { createAgent, getAvailableAgents } from "../agents/index.js";
9
9
  import { detectAvailableAgents } from "../utils/agent-detect.js";
10
10
 
11
- export async function agentRunCommand({ agent, task, config = {}, logger = null, flags = {} }) {
12
- if (!task || !task.trim()) {
13
- throw new Error("kj agent run requires a task: kj agent run <agent> \"<task>\"");
14
- }
11
+ // The agent must be one kj supports AND installed here; both errors name what is available.
12
+ async function assertAgentAvailable(agent) {
15
13
  const known = getAvailableAgents().map((a) => a.name);
16
14
  const detected = await detectAvailableAgents();
17
15
  const up = detected.filter((a) => a.available).map((a) => a.name).join(", ") || "none";
@@ -22,15 +20,44 @@ export async function agentRunCommand({ agent, task, config = {}, logger = null,
22
20
  if (!entry?.available) {
23
21
  throw new Error(`agent "${agent}" is not available on this machine (available: ${up}) — install its CLI or pick another`);
24
22
  }
23
+ }
25
24
 
26
- let timeoutMs;
27
- if (flags.timeoutMinutes !== undefined) {
28
- const minutes = Number(flags.timeoutMinutes);
29
- if (!Number.isFinite(minutes) || minutes <= 0) {
30
- throw new Error(`--timeout-minutes must be a positive number, got "${flags.timeoutMinutes}"`);
31
- }
32
- timeoutMs = minutes * 60000;
25
+ function timeoutMsOf(flags) {
26
+ if (flags.timeoutMinutes === undefined) return undefined;
27
+ const minutes = Number(flags.timeoutMinutes);
28
+ if (!Number.isFinite(minutes) || minutes <= 0) {
29
+ throw new Error(`--timeout-minutes must be a positive number, got "${flags.timeoutMinutes}"`);
33
30
  }
31
+ return minutes * 60000;
32
+ }
33
+
34
+ function delegationResult(agent, result) {
35
+ return {
36
+ ok: Boolean(result?.ok),
37
+ agent,
38
+ output: result?.output || "",
39
+ error: result?.error || null,
40
+ usage: result?.tokens_out != null
41
+ ? { tokens_in: result.tokens_in ?? null, tokens_out: result.tokens_out, cached_tokens: result.cached_tokens ?? null }
42
+ : null,
43
+ };
44
+ }
45
+
46
+ function printResult(res, json) {
47
+ if (json) {
48
+ console.log(JSON.stringify(res));
49
+ return;
50
+ }
51
+ if (res.output) console.log(res.output);
52
+ if (!res.ok && res.error) console.error(res.error);
53
+ }
54
+
55
+ export async function agentRunCommand({ agent, task, config = {}, logger = null, flags = {} }) {
56
+ if (!task || !task.trim()) {
57
+ throw new Error("kj agent run requires a task: kj agent run <agent> \"<task>\"");
58
+ }
59
+ await assertAgentAvailable(agent);
60
+ const timeoutMs = timeoutMsOf(flags);
34
61
 
35
62
  // --json must emit ONLY the serialized result — no informational lines,
36
63
  // and a crash of the delegated subprocess still yields one JSON payload.
@@ -43,21 +70,8 @@ export async function agentRunCommand({ agent, task, config = {}, logger = null,
43
70
  result = { ok: false, output: "", error: err.message };
44
71
  }
45
72
 
46
- const res = {
47
- ok: Boolean(result?.ok),
48
- agent,
49
- output: result?.output || "",
50
- error: result?.error || null,
51
- usage: result?.tokens_out != null
52
- ? { tokens_in: result.tokens_in ?? null, tokens_out: result.tokens_out, cached_tokens: result.cached_tokens ?? null }
53
- : null,
54
- };
55
- if (flags.json) {
56
- console.log(JSON.stringify(res));
57
- } else {
58
- if (res.output) console.log(res.output);
59
- if (!res.ok && res.error) console.error(res.error);
60
- }
73
+ const res = delegationResult(agent, result);
74
+ printResult(res, flags.json);
61
75
  process.exitCode = res.ok ? 0 : 1;
62
76
  return res;
63
77
  }
@@ -289,141 +289,144 @@ export async function auditCommand({ task, config, logger, dimensions, json, age
289
289
  const deterministicCtx = await role.collectDeterministic(roleInput);
290
290
  const deterministicMd = formatDeterministicSummary(deterministicCtx);
291
291
 
292
- // KJC-TSK-0471 — Harness Scorecard (Docker one-shot, golden metric).
293
- // Runs after the deterministic phase, before the LLM. Auto-skipped on
294
- // --no-harness or if `runHarnessSection` surfaces an error (graceful
295
- // degradation: a missing Docker daemon must not break `kj audit`).
296
- let harnessSummary = null;
297
- if (!noHarness) {
298
- try {
299
- harnessSummary = await runHarnessSection({
300
- projectDir: config?.projectDir || process.cwd(),
301
- harnessConfig: config?.audit?.harness || null,
302
- });
303
- if (harnessSummary?.ok && !harnessSummary.skipped) {
304
- runLog.logText(`[audit] harness score=${harnessSummary.score}/100 grade=${harnessSummary.grade}`);
305
- }
306
- } catch (e) {
307
- harnessSummary = { ok: false, error: e?.message || String(e) };
308
- runLog.logText(`[audit] harness skipped: ${harnessSummary.error}`);
309
- }
310
- }
292
+ const harnessSummary = noHarness ? null : await runHarness({ config, runLog });
311
293
  const harnessMd = formatHarnessSection(harnessSummary);
312
294
 
313
295
  // --json suppresses the interactive flow regardless: it would mangle
314
296
  // a script's stdout. JSON consumers always get the full audit unless
315
297
  // they explicitly pass deterministicOnly.
316
298
  const reportPath = await resolveReportFilePath(reportFile, json);
317
- let runLlm = !deterministicOnly;
318
- if (runLlm && !json && !yes) {
319
- console.log(deterministicMd);
320
- console.log("");
321
- const ok = await promptContinueWithLlm({ promptFn });
322
- runLlm = ok;
323
- if (!ok) logger.info("LLM analysis skipped (deterministic-only mode).");
324
- }
299
+ const runLlm = await chooseLlm({ deterministicOnly, json, yes, deterministicMd, promptFn, logger });
300
+ const invocation = { task, dimensions, noSonar, noOsv, noSemgrep, noHarness, noAiSlop, json, deterministicOnly, yes };
301
+ const ctx = { config, logger, runLog, json, trend, invocation, deterministicCtx, deterministicMd, harnessSummary, harnessMd, reportPath };
302
+ if (!runLlm) return deterministicOnlyAudit(ctx);
303
+ return llmAudit({ ...ctx, roleInput, available, fallbackCandidates });
304
+ });
305
+ }
325
306
 
326
- if (!runLlm) {
327
- // Deterministic-only path. Produce the report from what we already
328
- // have, persist it (md or json), and exit. Zero tokens spent.
329
- const harnessJson = harnessSummaryForJson(harnessSummary);
330
- const currentHarness = harnessSummary?.ok && !harnessSummary.skipped ? harnessSummary : null;
331
- const history = loadAuditHistoryContext(config?.projectDir || process.cwd(), currentHarness, { showTrend: trend });
332
- const historyMd = composeHistoryBlock(history);
333
- const stdoutContent = json
334
- ? JSON.stringify({ deterministic: deterministicCtx, harnessScore: harnessJson, harnessScoreDiff: history.diff, mode: "deterministic-only" }, null, 2)
335
- : (deterministicOnly ? `${deterministicMd}\n${harnessMd}${historyMd}` : `${harnessMd}${historyMd}`);
336
- if (json || deterministicOnly || harnessMd) console.log(stdoutContent);
337
-
338
- if (reportPath) {
339
- let payload;
340
- if (reportPath.endsWith(".json")) {
341
- payload = JSON.stringify({ deterministic: deterministicCtx, harnessScore: harnessJson, harnessScoreDiff: history.diff, mode: "deterministic-only" }, null, 2);
342
- } else {
343
- const header = await buildReportHeader(config?.projectDir, describeInvocation({ task, dimensions, noSonar, noOsv, noSemgrep, noHarness, noAiSlop, json, deterministicOnly, yes }));
344
- payload = `${header}${deterministicMd}\n${harnessMd}${historyMd}`;
345
- }
346
- await fs.writeFile(reportPath, payload, "utf8");
347
- runLog.logText(`[audit] report written (deterministic-only) → ${reportPath}`);
348
- logger.info(`Audit report written: ${reportPath}`);
349
- }
350
- persistAuditRun(config?.projectDir || process.cwd(), { runId: new Date().toISOString(), timestamp: new Date().toISOString(), gitSha: null, harness: harnessSummary?.ok && !harnessSummary.skipped ? harnessSummary : null, rawReportPath: reportPath });
351
- logger.info("Audit completed (deterministic-only).");
352
- return { ok: true, mode: "deterministic-only", reportPath: reportPath || undefined };
353
- }
307
+ /**
308
+ * KJC-TSK-0471 — Harness Scorecard (Docker one-shot, golden metric).
309
+ * Runs after the deterministic phase, before the LLM. Skipped on
310
+ * --no-harness; an error from `runHarnessSection` degrades gracefully
311
+ * (a missing Docker daemon must not break `kj audit`).
312
+ */
313
+ async function runHarness({ config, runLog }) {
314
+ try {
315
+ const summary = await runHarnessSection({
316
+ projectDir: config?.projectDir || process.cwd(),
317
+ harnessConfig: config?.audit?.harness || null,
318
+ });
319
+ if (summary?.ok && !summary.skipped) runLog.logText(`[audit] harness score=${summary.score}/100 grade=${summary.grade}`);
320
+ return summary;
321
+ } catch (e) {
322
+ const summary = { ok: false, error: e?.message || String(e) };
323
+ runLog.logText(`[audit] harness skipped: ${summary.error}`);
324
+ return summary;
325
+ }
326
+ }
354
327
 
355
- // Continue with the LLM phase using the already-collected context.
356
- const progress = createCliProgressReporter({ role: "auditor" });
357
- let roleResult;
358
- try {
359
- // KJC-BUG-0094: resilient fallback — if the configured provider/model
360
- // is down (e.g. an inherited model like "claude-fable-5" is offline),
361
- // degrade to the provider default, then to the next installed provider,
362
- // reusing the deterministic context so no static analysis is re-run.
363
- roleResult = await runAuditWithFallback({
364
- config, logger,
365
- roleInput: { ...roleInput, onOutput: progress.onOutput },
366
- deterministicCtx,
367
- available,
368
- candidates: fallbackCandidates,
369
- onFallback: (cand) => logger.warn(`Audit provider fallback → ${cand.provider}${cand.model ? ` (${cand.model})` : " (default model)"}`),
370
- });
371
- progress.finish(roleResult.ok ? "done" : "failed");
372
- } catch (err) { progress.finish("failed"); throw err; }
373
-
374
- if (!roleResult.ok) {
375
- const err = roleResult.result?.error || "Audit failed";
376
- throw new Error(err);
377
- }
328
+ /** Two-phase audit (KJC-TSK-0364): asks before spending tokens, unless --json, --yes or --deterministic-only. */
329
+ async function chooseLlm({ deterministicOnly, json, yes, deterministicMd, promptFn, logger }) {
330
+ if (deterministicOnly) return false;
331
+ if (json || yes) return true;
332
+ console.log(deterministicMd);
333
+ console.log("");
334
+ const ok = await promptContinueWithLlm({ promptFn });
335
+ if (!ok) logger.info("LLM analysis skipped (deterministic-only mode).");
336
+ return ok;
337
+ }
378
338
 
379
- const parsed = roleResult.result;
380
- const usage = roleResult.usage;
381
- if (parsed?.summary?.overallHealth) {
382
- runLog.logText(`[audit] findings=${parsed.summary.totalFindings} (critical=${parsed.summary.critical}, high=${parsed.summary.high})`);
383
- }
384
- if (usage?.available) {
385
- runLog.logText(`[audit] usage tokens=${usage.total_tokens} cost=$${(usage.cost_usd ?? 0).toFixed(4)} duration=${(usage.durationMs / 1000).toFixed(1)}s`);
386
- }
339
+ const scoredHarness = (summary) => (summary?.ok && !summary.skipped ? summary : null);
387
340
 
388
- const harnessJson = harnessSummaryForJson(harnessSummary);
389
- const currentHarness = harnessSummary?.ok && !harnessSummary.skipped ? harnessSummary : null;
390
- const history = loadAuditHistoryContext(config?.projectDir || process.cwd(), currentHarness, { showTrend: trend });
391
- const historyMd = composeHistoryBlock(history);
392
- let stdoutContent;
393
- if (json) {
394
- const jsonPayload = parsed
395
- ? { ...parsed, harnessScore: harnessJson, harnessScoreDiff: history.diff, usage: usage || undefined }
396
- : { ...roleResult, harnessScore: harnessJson, harnessScoreDiff: history.diff, usage: usage || undefined };
397
- stdoutContent = JSON.stringify(jsonPayload, null, 2);
398
- } else if (parsed?.summary?.overallHealth) {
399
- stdoutContent = `${formatAudit(parsed, usage)}\n${harnessMd}${historyMd}`;
400
- } else if (parsed?.raw) {
401
- stdoutContent = parsed.raw + (usage ? `\n\n${formatUsageSummary(usage) || ""}` : "") + `\n${harnessMd}${historyMd}`;
402
- } else {
403
- stdoutContent = `${roleResult.summary || "Audit complete."}\n${harnessMd}${historyMd}`;
404
- }
405
- console.log(stdoutContent);
406
-
407
- if (reportPath) {
408
- let payload;
409
- if (reportPath.endsWith(".json")) {
410
- const jsonPayload = parsed
411
- ? { ...parsed, harnessScore: harnessJson, harnessScoreDiff: history.diff, usage: usage || undefined }
412
- : { ...roleResult, harnessScore: harnessJson, harnessScoreDiff: history.diff, usage: usage || undefined };
413
- payload = JSON.stringify(jsonPayload, null, 2);
414
- } else {
415
- const header = await buildReportHeader(config?.projectDir, describeInvocation({ task, dimensions, noSonar, noOsv, noSemgrep, noHarness, noAiSlop, json, deterministicOnly, yes }));
416
- payload = header + stdoutContent + "\n";
417
- }
418
- await fs.writeFile(reportPath, payload, "utf8");
419
- runLog.logText(`[audit] report written → ${reportPath}`);
420
- logger.info(`Audit report written: ${reportPath}`);
421
- }
341
+ /** The harness score as JSON and the history block, shared by both paths. */
342
+ function harnessReport({ config, harnessSummary, trend }) {
343
+ const history = loadAuditHistoryContext(config?.projectDir || process.cwd(), scoredHarness(harnessSummary), { showTrend: trend });
344
+ return { harnessJson: harnessSummaryForJson(harnessSummary), history, historyMd: composeHistoryBlock(history) };
345
+ }
422
346
 
423
- persistAuditRun(config?.projectDir || process.cwd(), { runId: new Date().toISOString(), timestamp: new Date().toISOString(), gitSha: null, harness: harnessSummary?.ok && !harnessSummary.skipped ? harnessSummary : null, rawReportPath: reportPath });
424
- logger.info("Audit completed.");
425
- return { ok: true, reportPath: reportPath || undefined };
426
- });
347
+ async function writeReport({ ctx, payloadJson, markdownBody, note }) {
348
+ const { config, runLog, logger, reportPath, invocation } = ctx;
349
+ if (!reportPath) return;
350
+ const payload = reportPath.endsWith(".json")
351
+ ? payloadJson
352
+ : `${await buildReportHeader(config?.projectDir, describeInvocation(invocation))}${markdownBody}`;
353
+ await fs.writeFile(reportPath, payload, "utf8");
354
+ runLog.logText(`[audit] report written${note} → ${reportPath}`);
355
+ logger.info(`Audit report written: ${reportPath}`);
356
+ }
357
+
358
+ function persistRun(ctx) {
359
+ const now = new Date().toISOString();
360
+ persistAuditRun(ctx.config?.projectDir || process.cwd(), { runId: now, timestamp: now, gitSha: null, harness: scoredHarness(ctx.harnessSummary), rawReportPath: ctx.reportPath });
361
+ }
362
+
363
+ /** Deterministic-only path: the report from what was already collected, persisted, zero tokens spent. */
364
+ async function deterministicOnlyAudit(ctx) {
365
+ const { json, invocation, deterministicCtx, deterministicMd, harnessMd, reportPath, logger } = ctx;
366
+ const { harnessJson, history, historyMd } = harnessReport(ctx);
367
+ const payloadJson = JSON.stringify({ deterministic: deterministicCtx, harnessScore: harnessJson, harnessScoreDiff: history.diff, mode: "deterministic-only" }, null, 2);
368
+ let stdoutContent = `${harnessMd}${historyMd}`;
369
+ if (json) stdoutContent = payloadJson;
370
+ else if (invocation.deterministicOnly) stdoutContent = `${deterministicMd}\n${harnessMd}${historyMd}`;
371
+ if (json || invocation.deterministicOnly || harnessMd) console.log(stdoutContent);
372
+ await writeReport({ ctx, payloadJson, markdownBody: `${deterministicMd}\n${harnessMd}${historyMd}`, note: " (deterministic-only)" });
373
+ persistRun(ctx);
374
+ logger.info("Audit completed (deterministic-only).");
375
+ return { ok: true, mode: "deterministic-only", reportPath: reportPath || undefined };
376
+ }
377
+
378
+ /**
379
+ * KJC-BUG-0094: resilient fallback — if the configured provider/model
380
+ * is down (e.g. an inherited model like "claude-fable-5" is offline),
381
+ * degrade to the provider default, then to the next installed provider,
382
+ * reusing the deterministic context so no static analysis is re-run.
383
+ */
384
+ async function runLlmPhase({ config, logger, roleInput, deterministicCtx, available, fallbackCandidates }) {
385
+ const progress = createCliProgressReporter({ role: "auditor" });
386
+ try {
387
+ const roleResult = await runAuditWithFallback({
388
+ config, logger,
389
+ roleInput: { ...roleInput, onOutput: progress.onOutput },
390
+ deterministicCtx,
391
+ available,
392
+ candidates: fallbackCandidates,
393
+ onFallback: (cand) => logger.warn(`Audit provider fallback → ${cand.provider}${cand.model ? ` (${cand.model})` : " (default model)"}`),
394
+ });
395
+ progress.finish(roleResult.ok ? "done" : "failed");
396
+ return roleResult;
397
+ } catch (err) {
398
+ progress.finish("failed");
399
+ throw err;
400
+ }
401
+ }
402
+
403
+ function llmStdout({ parsed, usage, roleResult, harnessMd, historyMd }) {
404
+ if (parsed?.summary?.overallHealth) return `${formatAudit(parsed, usage)}\n${harnessMd}${historyMd}`;
405
+ if (parsed?.raw) return parsed.raw + (usage ? `\n\n${formatUsageSummary(usage) || ""}` : "") + `\n${harnessMd}${historyMd}`;
406
+ return `${roleResult.summary || "Audit complete."}\n${harnessMd}${historyMd}`;
407
+ }
408
+
409
+ /** The LLM phase, over the deterministic context already collected. */
410
+ async function llmAudit(ctx) {
411
+ const { json, runLog, logger, harnessMd, reportPath } = ctx;
412
+ const roleResult = await runLlmPhase(ctx);
413
+ if (!roleResult.ok) throw new Error(roleResult.result?.error || "Audit failed");
414
+ const parsed = roleResult.result;
415
+ const usage = roleResult.usage;
416
+ if (parsed?.summary?.overallHealth) {
417
+ runLog.logText(`[audit] findings=${parsed.summary.totalFindings} (critical=${parsed.summary.critical}, high=${parsed.summary.high})`);
418
+ }
419
+ if (usage?.available) {
420
+ runLog.logText(`[audit] usage tokens=${usage.total_tokens} cost=$${(usage.cost_usd ?? 0).toFixed(4)} duration=${(usage.durationMs / 1000).toFixed(1)}s`);
421
+ }
422
+ const { harnessJson, history, historyMd } = harnessReport(ctx);
423
+ const payloadJson = JSON.stringify({ ...(parsed || roleResult), harnessScore: harnessJson, harnessScoreDiff: history.diff, usage: usage || undefined }, null, 2);
424
+ const stdoutContent = json ? payloadJson : llmStdout({ parsed, usage, roleResult, harnessMd, historyMd });
425
+ console.log(stdoutContent);
426
+ await writeReport({ ctx, payloadJson, markdownBody: `${stdoutContent}\n`, note: "" });
427
+ persistRun(ctx);
428
+ logger.info("Audit completed.");
429
+ return { ok: true, reportPath: reportPath || undefined };
427
430
  }
428
431
 
429
432
  // Exposed for tests — kept module-private otherwise.
@@ -2,9 +2,10 @@ import fs from "node:fs";
2
2
  import http from "node:http";
3
3
  import path from "node:path";
4
4
  import net from "node:net";
5
- import { execFileSync, spawn } from "node:child_process";
5
+ import { spawn } from "node:child_process";
6
6
  import { getKarajanHome } from "../utils/paths.js";
7
7
  import { projectSlug as slugOfDir } from "../plan/plan-store.js";
8
+ import { mainCheckout } from "../utils/main-checkout.js";
8
9
  import { getPortOccupant } from "../utils/port-occupant.js";
9
10
 
10
11
  const BOARD_DIR = path.resolve(import.meta.dirname, "../../packages/hu-board");
@@ -90,16 +91,11 @@ export function buildBoardUrl(port, projectSlug) {
90
91
  }
91
92
 
92
93
  /**
93
- * KJC-TSK-0884: the board slug of a project is its PARENT repo's, even from a
94
- * `kj worktree` lane, or the lane opens an empty view.
94
+ * KJC-TSK-0884: the board slug of a project is its main checkout's (#2566), even from a
95
+ * lane or a subfolder, or the lane opens an empty view. A git failure throws, never falls back.
95
96
  */
96
97
  export function boardSlug(projectDir) {
97
- let dir = projectDir;
98
- try {
99
- const common = execFileSync("git", ["-C", projectDir, "rev-parse", "--path-format=absolute", "--git-common-dir"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
100
- if (path.basename(common) === ".git") dir = path.dirname(common);
101
- } catch { /* not a git repo: the dir is the project */ }
102
- return slugOfDir(dir);
98
+ return slugOfDir(mainCheckout(projectDir));
103
99
  }
104
100
 
105
101
  /**
@@ -25,16 +25,7 @@ export async function checkCommand({ projectDir = process.cwd(), profile = "stan
25
25
  // An evaluation error (corrupt store, git failure) is a failing check, never
26
26
  // a silent pass: the only tolerated non-red state is the explicit "no index".
27
27
  const ragCoverage = await createRagCoverageCheck().detect({ config, projectDir }).catch((err) => ({ ok: false, severity: "fail", detail: `rag coverage could not be evaluated: ${err.message}` }));
28
- // KJC-TSK-0694: same deal for the MCP inventory — a nudge, never a gate.
29
- // KJC-TSK-0728: observed agent CLIs ride the same snapshot as "(cli)"
30
- // entries, so a newly-appeared agent binary trips the same drift question.
31
- let aiSurface = null;
32
- try {
33
- const clis = (await detectObservedAgents().catch(() => []))
34
- .filter((a) => a.available)
35
- .map((a) => `${a.name} (cli)`);
36
- aiSurface = checkAiSurface({ projectDir, extraSurface: clis });
37
- } catch { /* inventory is best-effort */ }
28
+ const aiSurface = await observedAiSurface(projectDir);
38
29
 
39
30
  // KJC-TSK-1059: young dependencies the branch brings in, a warning outside the review.
40
31
  const depCooldown = await branchCooldownLines({ projectDir, base: config?.base_branch ?? "main", thresholdHours: config?.supply_chain?.cooldown_hours })
@@ -48,10 +39,35 @@ export async function checkCommand({ projectDir = process.cwd(), profile = "stan
48
39
  return ok ? 0 : 1;
49
40
  }
50
41
 
42
+ printReport({ profile, result, method, ragCoverage, aiSurface, depCooldown, audit, config, ok, logger });
43
+ return ok ? 0 : 1;
44
+ }
45
+
46
+ // KJC-TSK-0694: same deal for the MCP inventory — a nudge, never a gate.
47
+ // KJC-TSK-0728: observed agent CLIs ride the same snapshot as "(cli)"
48
+ // entries, so a newly-appeared agent binary trips the same drift question.
49
+ async function observedAiSurface(projectDir) {
50
+ try {
51
+ const clis = (await detectObservedAgents().catch(() => []))
52
+ .filter((a) => a.available)
53
+ .map((a) => `${a.name} (cli)`);
54
+ return checkAiSurface({ projectDir, extraSurface: clis });
55
+ } catch { return null; /* inventory is best-effort */ }
56
+ }
57
+
58
+ const mark = (ok) => (ok ? "✓" : "✗");
59
+
60
+ function verdictLine(ok, harnessOk) {
61
+ if (ok) return "Harness OK.";
62
+ if (harnessOk) return "RAG index drift detected — a gate cannot protect what it cannot see.";
63
+ return "Harness drift detected — run `kj harden` to repair.";
64
+ }
65
+
66
+ function printReport({ profile, result, method, ragCoverage, aiSurface, depCooldown, audit, config, ok, logger }) {
51
67
  logger.info?.(`kj check (${profile})`);
52
- for (const c of result.checks) logger.info?.(` ${c.ok ? "✓" : "✗"} ${c.id}: ${c.detail}`);
68
+ for (const c of result.checks) logger.info?.(` ${mark(c.ok)} ${c.id}: ${c.detail}`);
53
69
  if (method) logger.info?.(` method: ${formatMethodStats(method)}`);
54
- logger.info?.(` ${ragCoverage.ok ? "✓" : "✗"} rag-coverage: ${ragCoverage.detail}`);
70
+ logger.info?.(` ${mark(ragCoverage.ok)} rag-coverage: ${ragCoverage.detail}`);
55
71
  if (aiSurface) logger.info?.(` ${formatAiSurface(aiSurface)}`);
56
72
  for (const line of depCooldown) logger.info?.(` ${line}`);
57
73
  logger.info?.(` ${audit.line}`);
@@ -59,8 +75,5 @@ export async function checkCommand({ projectDir = process.cwd(), profile = "stan
59
75
  // said out loud next to everything else that describes the project. Never
60
76
  // a check: it has no ok/fail, it informs.
61
77
  logger.info?.(` ${panelSummary(config)}`);
62
- if (ok) logger.info?.("Harness OK.");
63
- else if (result.ok) logger.info?.("RAG index drift detected — a gate cannot protect what it cannot see.");
64
- else logger.info?.("Harness drift detected — run `kj harden` to repair.");
65
- return ok ? 0 : 1;
78
+ logger.info?.(verdictLine(ok, result.ok));
66
79
  }
@@ -33,6 +33,7 @@ import { getNodeChecks } from "../checks/node.js";
33
33
  import { getNativeBuildChecks } from "../checks/native-build.js";
34
34
  import { getPortChecks } from "../checks/ports.js";
35
35
  import { getTokenChecks } from "../checks/tokens.js";
36
+ import { getAgentTokenChecks } from "../checks/agent-token.js";
36
37
  import { getMcpHealthChecks } from "../checks/mcp-health.js";
37
38
  import { getSkillsChecks } from "../checks/skills.js";
38
39
  import { getDirSetupChecks } from "../checks/dir-setup.js";
@@ -72,6 +73,7 @@ function buildChecks(config, { projectOnly = false } = {}) {
72
73
  ...getPhoneChecks(),
73
74
  ...getPortChecks(),
74
75
  ...getTokenChecks(config),
76
+ ...getAgentTokenChecks({ projectDir }),
75
77
  ...getMcpHealthChecks(),
76
78
  ...getSkillsChecks(),
77
79
  ...getCiChecks(),