@karajan-family/code 4.21.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 (746) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +98 -0
  3. package/bin/karajan-mcp.js +2 -0
  4. package/bin/kj-rag-mcp.js +75 -0
  5. package/bin/kj-tail +361 -0
  6. package/bin/kj.js +2 -0
  7. package/docs/README.es.md +80 -0
  8. package/docs/karajan-code-logo-small.png +0 -0
  9. package/library/logical-clocks-lamport.md +40 -0
  10. package/package.json +140 -0
  11. package/packages/ai-trash/README.md +37 -0
  12. package/packages/ai-trash/bin/kj-trash.js +10 -0
  13. package/packages/ai-trash/package.json +43 -0
  14. package/packages/ai-trash/src/cli.js +200 -0
  15. package/packages/ai-trash/src/destructive-parser.js +74 -0
  16. package/packages/ai-trash/src/git-snapshot.js +128 -0
  17. package/packages/ai-trash/src/hook.js +137 -0
  18. package/packages/ai-trash/src/index.js +14 -0
  19. package/packages/ai-trash/src/install.js +73 -0
  20. package/packages/ai-trash/src/logger.js +60 -0
  21. package/packages/ai-trash/src/manifest.js +91 -0
  22. package/packages/ai-trash/src/permissions.js +26 -0
  23. package/packages/ai-trash/src/snapshot.js +66 -0
  24. package/packages/ai-trash/src/ulid.js +66 -0
  25. package/packages/hu-board/README.md +69 -0
  26. package/packages/hu-board/package.json +28 -0
  27. package/packages/hu-board/public/app.js +259 -0
  28. package/packages/hu-board/public/index.html +77 -0
  29. package/packages/hu-board/public/pipeline.css +132 -0
  30. package/packages/hu-board/public/pipeline.html +48 -0
  31. package/packages/hu-board/public/pipeline.js +200 -0
  32. package/packages/hu-board/public/rag.html +21 -0
  33. package/packages/hu-board/public/rag.js +39 -0
  34. package/packages/hu-board/public/styles.css +973 -0
  35. package/packages/hu-board/public/utils/api.js +139 -0
  36. package/packages/hu-board/public/utils/board-view.js +473 -0
  37. package/packages/hu-board/public/utils/command-launcher.js +213 -0
  38. package/packages/hu-board/public/utils/config-editor.js +181 -0
  39. package/packages/hu-board/public/utils/dashboard-view.js +103 -0
  40. package/packages/hu-board/public/utils/formatters.js +300 -0
  41. package/packages/hu-board/public/utils/graph-view.js +216 -0
  42. package/packages/hu-board/public/utils/hu-actions.js +224 -0
  43. package/packages/hu-board/public/utils/init-listeners.js +180 -0
  44. package/packages/hu-board/public/utils/log-panel.js +222 -0
  45. package/packages/hu-board/public/utils/modals.js +309 -0
  46. package/packages/hu-board/public/utils/plan-rollup-view.js +53 -0
  47. package/packages/hu-board/public/utils/preflight-view.js +89 -0
  48. package/packages/hu-board/public/utils/project-actions.js +143 -0
  49. package/packages/hu-board/public/utils/project-picker-view.js +139 -0
  50. package/packages/hu-board/public/utils/run-launcher.js +169 -0
  51. package/packages/hu-board/public/utils/server-push.js +252 -0
  52. package/packages/hu-board/public/utils/sessions-view.js +199 -0
  53. package/packages/hu-board/public/utils/story-detail-view.js +324 -0
  54. package/packages/hu-board/public/utils/story-edit-form.js +271 -0
  55. package/packages/hu-board/public/wiki.html +32 -0
  56. package/packages/hu-board/public/wiki.js +53 -0
  57. package/packages/hu-board/src/auth.js +92 -0
  58. package/packages/hu-board/src/boot-info.js +16 -0
  59. package/packages/hu-board/src/cleanup-zombies.js +145 -0
  60. package/packages/hu-board/src/command-runner.js +133 -0
  61. package/packages/hu-board/src/config-yaml.js +531 -0
  62. package/packages/hu-board/src/db.js +963 -0
  63. package/packages/hu-board/src/ephemeral-cleaner.js +196 -0
  64. package/packages/hu-board/src/event-bus.js +58 -0
  65. package/packages/hu-board/src/format.js +207 -0
  66. package/packages/hu-board/src/hu-zombie-reaper.js +103 -0
  67. package/packages/hu-board/src/journal-parser.js +180 -0
  68. package/packages/hu-board/src/plan-mutations.js +424 -0
  69. package/packages/hu-board/src/preflight.js +286 -0
  70. package/packages/hu-board/src/routes/api.js +1374 -0
  71. package/packages/hu-board/src/routes/pipeline.js +45 -0
  72. package/packages/hu-board/src/routes/rag.js +42 -0
  73. package/packages/hu-board/src/routes/wiki.js +43 -0
  74. package/packages/hu-board/src/run-tracker.js +121 -0
  75. package/packages/hu-board/src/server.js +438 -0
  76. package/packages/hu-board/src/sync.js +732 -0
  77. package/packages/hu-board/src/token-store.js +56 -0
  78. package/packages/hu-board/src/zombie-reaper.js +206 -0
  79. package/scripts/ai-attribution-guard.yml +84 -0
  80. package/scripts/audit-test-diet.mjs +151 -0
  81. package/scripts/build-sea.mjs +150 -0
  82. package/scripts/diet-baseline.mjs +170 -0
  83. package/scripts/dual-publish.mjs +67 -0
  84. package/scripts/esbuild-sea.config.mjs +273 -0
  85. package/scripts/git-hooks/post-merge +18 -0
  86. package/scripts/install-binary.ps1 +174 -0
  87. package/scripts/install-binary.sh +159 -0
  88. package/scripts/install-guard-all-repos.sh +85 -0
  89. package/scripts/install-kj.sh +193 -0
  90. package/scripts/install.js +901 -0
  91. package/scripts/install.sh +7 -0
  92. package/scripts/postinstall.js +117 -0
  93. package/scripts/protect-all-repos.sh +88 -0
  94. package/scripts/quickstart-gate.mjs +97 -0
  95. package/scripts/regen-arch-stats.sh +71 -0
  96. package/scripts/setup-multi-instance.sh +150 -0
  97. package/scripts/verify-pack.mjs +278 -0
  98. package/src/activity-log.js +59 -0
  99. package/src/agents/agy-agent.js +62 -0
  100. package/src/agents/aider-agent.js +78 -0
  101. package/src/agents/availability.js +52 -0
  102. package/src/agents/base-agent.js +134 -0
  103. package/src/agents/claude-agent.js +413 -0
  104. package/src/agents/codex-agent.js +112 -0
  105. package/src/agents/copilot-agent.js +65 -0
  106. package/src/agents/gemini-agent.js +85 -0
  107. package/src/agents/host-agent.js +38 -0
  108. package/src/agents/index.js +59 -0
  109. package/src/agents/kimi-agent.js +45 -0
  110. package/src/agents/model-registry.js +137 -0
  111. package/src/agents/opencode-agent.js +72 -0
  112. package/src/agents/qwen-agent.js +20 -0
  113. package/src/agents/resolve-bin.js +73 -0
  114. package/src/audit/agent-readiness.js +259 -0
  115. package/src/audit/ai-slop-findings.js +102 -0
  116. package/src/audit/audit-fallback.js +66 -0
  117. package/src/audit/audit-history-display.js +61 -0
  118. package/src/audit/audit-history.js +102 -0
  119. package/src/audit/basal-cost.js +281 -0
  120. package/src/audit/circular-deps.js +144 -0
  121. package/src/audit/dead-exports.js +186 -0
  122. package/src/audit/deterministic-summary.js +327 -0
  123. package/src/audit/harness-scorecard.js +100 -0
  124. package/src/audit/harness-section.js +81 -0
  125. package/src/audit/infra-findings.js +100 -0
  126. package/src/audit/injection-findings.js +114 -0
  127. package/src/audit/issue-filter.js +133 -0
  128. package/src/audit/osv-findings.js +167 -0
  129. package/src/audit/plan-adherence.js +224 -0
  130. package/src/audit/semgrep-findings.js +129 -0
  131. package/src/audit/sonar-findings.js +93 -0
  132. package/src/audit/webperf-input.js +133 -0
  133. package/src/autonomy/arbiter-role.js +71 -0
  134. package/src/autonomy/arbiter.js +22 -0
  135. package/src/autonomy/decider.js +18 -0
  136. package/src/autonomy/decision-resolver.js +58 -0
  137. package/src/autonomy/outcome-report.js +55 -0
  138. package/src/autonomy/policy.js +41 -0
  139. package/src/autonomy/stage-ask.js +30 -0
  140. package/src/bootstrap.js +276 -0
  141. package/src/brain/agent-error-classifier.js +116 -0
  142. package/src/brain/decision-tracker.js +125 -0
  143. package/src/brain/decisor.js +243 -0
  144. package/src/brain/solomon-consult.js +171 -0
  145. package/src/brain/standby-scheduler.js +8 -0
  146. package/src/brain/standby-store.js +12 -0
  147. package/src/brain/with-brain-recovery.js +235 -0
  148. package/src/budget/comparison.js +96 -0
  149. package/src/canvas/parse-md.js +238 -0
  150. package/src/canvas/schema.js +101 -0
  151. package/src/canvas/to-flat.js +85 -0
  152. package/src/canvas/validate.js +163 -0
  153. package/src/checks/ai-surface.js +79 -0
  154. package/src/checks/ai-trash.js +34 -0
  155. package/src/checks/binaries.js +207 -0
  156. package/src/checks/ci.js +105 -0
  157. package/src/checks/config-files.js +217 -0
  158. package/src/checks/dir-setup.js +103 -0
  159. package/src/checks/guarantee-level.js +38 -0
  160. package/src/checks/hardware.js +150 -0
  161. package/src/checks/harness-scorecard.js +41 -0
  162. package/src/checks/mcp-health.js +169 -0
  163. package/src/checks/method.js +73 -0
  164. package/src/checks/native-build.js +67 -0
  165. package/src/checks/node.js +70 -0
  166. package/src/checks/ollama.js +41 -0
  167. package/src/checks/orphans.js +77 -0
  168. package/src/checks/ports.js +188 -0
  169. package/src/checks/project-checks.js +245 -0
  170. package/src/checks/qmd.js +41 -0
  171. package/src/checks/rag-hooks.js +54 -0
  172. package/src/checks/release-check.js +102 -0
  173. package/src/checks/rtk.js +36 -0
  174. package/src/checks/runner.js +326 -0
  175. package/src/checks/skills.js +53 -0
  176. package/src/checks/sonar.js +67 -0
  177. package/src/checks/squeezr.js +36 -0
  178. package/src/checks/system.js +53 -0
  179. package/src/checks/tokens.js +172 -0
  180. package/src/checks/types.js +103 -0
  181. package/src/ci/dispatch.js +99 -0
  182. package/src/ci/index.js +3 -0
  183. package/src/ci/pr-diff.js +26 -0
  184. package/src/ci/repo.js +45 -0
  185. package/src/cli/_shared.js +30 -0
  186. package/src/cli/advanced-commands.js +61 -0
  187. package/src/cli/register-meta.js +772 -0
  188. package/src/cli/register-pipeline.js +335 -0
  189. package/src/cli/register-plan.js +224 -0
  190. package/src/cli/register-roles-skills.js +54 -0
  191. package/src/cli/register-sonar.js +28 -0
  192. package/src/cli/register-standby.js +26 -0
  193. package/src/cli.js +155 -0
  194. package/src/commands/advanced.js +31 -0
  195. package/src/commands/agent-run.js +63 -0
  196. package/src/commands/agents.js +105 -0
  197. package/src/commands/architect.js +123 -0
  198. package/src/commands/audit.js +430 -0
  199. package/src/commands/autorun.js +49 -0
  200. package/src/commands/board.js +458 -0
  201. package/src/commands/check.js +40 -0
  202. package/src/commands/clean.js +145 -0
  203. package/src/commands/code.js +45 -0
  204. package/src/commands/config.js +74 -0
  205. package/src/commands/discover.js +105 -0
  206. package/src/commands/doctor.js +251 -0
  207. package/src/commands/env.js +207 -0
  208. package/src/commands/harden.js +203 -0
  209. package/src/commands/hu.js +210 -0
  210. package/src/commands/identity.js +60 -0
  211. package/src/commands/init.js +950 -0
  212. package/src/commands/install-tools.js +432 -0
  213. package/src/commands/mutate.js +94 -0
  214. package/src/commands/ollama.js +44 -0
  215. package/src/commands/onboard.js +45 -0
  216. package/src/commands/plan/_shared.js +55 -0
  217. package/src/commands/plan/add-hu.js +23 -0
  218. package/src/commands/plan/delete.js +9 -0
  219. package/src/commands/plan/fix.js +99 -0
  220. package/src/commands/plan/generate.js +532 -0
  221. package/src/commands/plan/index.js +13 -0
  222. package/src/commands/plan/list.js +21 -0
  223. package/src/commands/plan/migrate-result.js +41 -0
  224. package/src/commands/plan/ready.js +46 -0
  225. package/src/commands/plan/remove-hu.js +17 -0
  226. package/src/commands/plan/share.js +85 -0
  227. package/src/commands/plan/show.js +81 -0
  228. package/src/commands/plan/unshare.js +41 -0
  229. package/src/commands/plan/validate.js +20 -0
  230. package/src/commands/plan.js +17 -0
  231. package/src/commands/policy.js +164 -0
  232. package/src/commands/privacy.js +45 -0
  233. package/src/commands/qmd.js +86 -0
  234. package/src/commands/rag-mcp.js +18 -0
  235. package/src/commands/rag.js +173 -0
  236. package/src/commands/report-issue.js +77 -0
  237. package/src/commands/report.js +469 -0
  238. package/src/commands/researcher.js +54 -0
  239. package/src/commands/resume.js +68 -0
  240. package/src/commands/review-gate.js +355 -0
  241. package/src/commands/review.js +89 -0
  242. package/src/commands/roles.js +116 -0
  243. package/src/commands/run.js +185 -0
  244. package/src/commands/scan.js +18 -0
  245. package/src/commands/skills.js +86 -0
  246. package/src/commands/sonar.js +53 -0
  247. package/src/commands/standby.js +117 -0
  248. package/src/commands/start.js +121 -0
  249. package/src/commands/status.js +36 -0
  250. package/src/commands/sync.js +103 -0
  251. package/src/commands/telemetry.js +70 -0
  252. package/src/commands/tournament.js +85 -0
  253. package/src/commands/triage.js +78 -0
  254. package/src/commands/undo.js +62 -0
  255. package/src/commands/watch.js +46 -0
  256. package/src/commands/webperf.js +162 -0
  257. package/src/commands/worktree.js +76 -0
  258. package/src/config/defaults.js +270 -0
  259. package/src/config/loader.js +204 -0
  260. package/src/config/overrides.js +220 -0
  261. package/src/config/role-resolver.js +171 -0
  262. package/src/config/schema.js +299 -0
  263. package/src/config/test-harness.js +78 -0
  264. package/src/config.js +34 -0
  265. package/src/domains/domain-loader.js +245 -0
  266. package/src/domains/domain-registry.js +140 -0
  267. package/src/domains/domain-synthesizer.js +129 -0
  268. package/src/environment/adr.js +45 -0
  269. package/src/environment/board-access.js +97 -0
  270. package/src/environment/board-select.js +59 -0
  271. package/src/environment/briefs.js +129 -0
  272. package/src/environment/issue-report.js +72 -0
  273. package/src/environment/mcp-wiring.js +44 -0
  274. package/src/environment/playbook.js +127 -0
  275. package/src/git/automation.js +334 -0
  276. package/src/git/hu-automation.js +171 -0
  277. package/src/git/hu-snapshot.js +10 -0
  278. package/src/golden/asserter.js +92 -0
  279. package/src/golden/loader.js +52 -0
  280. package/src/golden/runner.js +92 -0
  281. package/src/golden/schema.js +41 -0
  282. package/src/guards/intent-guard.js +161 -0
  283. package/src/guards/output-guard.js +149 -0
  284. package/src/guards/perf-guard.js +126 -0
  285. package/src/guards/policy-guard.js +90 -0
  286. package/src/guards/policy-resolver.js +85 -0
  287. package/src/guards/secret-redactor.js +71 -0
  288. package/src/harden/advisory.js +203 -0
  289. package/src/harden/alternatives.js +50 -0
  290. package/src/harden/check.js +81 -0
  291. package/src/harden/config-engine.js +128 -0
  292. package/src/harden/config-templates.js +122 -0
  293. package/src/harden/guidelines-engine.js +48 -0
  294. package/src/harden/guidelines-templates.js +36 -0
  295. package/src/harden/harden-engine.js +80 -0
  296. package/src/harden/harness-hooks.js +107 -0
  297. package/src/harden/hook-commands.js +25 -0
  298. package/src/harden/hook-templates.js +183 -0
  299. package/src/harden/interactive.js +76 -0
  300. package/src/harden/sentinel-hooks.js +721 -0
  301. package/src/harden/stack-roots.js +105 -0
  302. package/src/harden/test-on-push.js +38 -0
  303. package/src/harden/workflow-engine.js +116 -0
  304. package/src/harden/workflow-templates.js +285 -0
  305. package/src/hu/acceptance-runner.js +138 -0
  306. package/src/hu/auto-generator.js +358 -0
  307. package/src/hu/graph.js +71 -0
  308. package/src/hu/lazy-planner.js +109 -0
  309. package/src/hu/model-router.js +108 -0
  310. package/src/hu/parallel-executor.js +94 -0
  311. package/src/hu/split-validator.js +143 -0
  312. package/src/hu/splitting-detector.js +158 -0
  313. package/src/hu/splitting-generator.js +175 -0
  314. package/src/hu/store.js +392 -0
  315. package/src/hu/worktree-bootstrap.js +59 -0
  316. package/src/identity/bootstrap.js +48 -0
  317. package/src/identity/compare.js +38 -0
  318. package/src/identity/detect.js +51 -0
  319. package/src/identity/store.js +66 -0
  320. package/src/infrastructure/command-runner.js +44 -0
  321. package/src/infrastructure/environment.js +38 -0
  322. package/src/infrastructure/file-system-service.js +63 -0
  323. package/src/infrastructure/mocks.js +182 -0
  324. package/src/lang/chunk-go.js +94 -0
  325. package/src/lang/chunk-java.js +90 -0
  326. package/src/lang/chunk-python.js +88 -0
  327. package/src/lang/chunk-rust.js +102 -0
  328. package/src/lang/go.js +14 -0
  329. package/src/lang/java.js +16 -0
  330. package/src/lang/js.js +8 -0
  331. package/src/lang/python.js +14 -0
  332. package/src/lang/registry.js +79 -0
  333. package/src/lang/rust.js +14 -0
  334. package/src/lang/tree-sitter-loader.js +45 -0
  335. package/src/mcp/direct-role-runner.js +112 -0
  336. package/src/mcp/error-classifiers.js +66 -0
  337. package/src/mcp/handlers/direct-handlers.js +486 -0
  338. package/src/mcp/handlers/hu-handlers.js +93 -0
  339. package/src/mcp/handlers/management-handlers.js +265 -0
  340. package/src/mcp/handlers/rag-handler.js +52 -0
  341. package/src/mcp/handlers/run-handler.js +313 -0
  342. package/src/mcp/orphan-guard.js +131 -0
  343. package/src/mcp/preflight.js +47 -0
  344. package/src/mcp/progress.js +149 -0
  345. package/src/mcp/response-compressor.js +81 -0
  346. package/src/mcp/run-kj.js +135 -0
  347. package/src/mcp/server-handlers.js +79 -0
  348. package/src/mcp/server.js +66 -0
  349. package/src/mcp/shared-helpers.js +297 -0
  350. package/src/mcp/sovereignty-guard.js +112 -0
  351. package/src/mcp/suggest-handler.js +92 -0
  352. package/src/mcp/tool-arg-normalizers.js +16 -0
  353. package/src/mcp/tools.js +432 -0
  354. package/src/mutate/diff-scope.js +105 -0
  355. package/src/mutate/reviewer-signal.js +76 -0
  356. package/src/mutate/runner.js +72 -0
  357. package/src/mutate/tool-registry.js +90 -0
  358. package/src/onboarder/cache.js +24 -0
  359. package/src/onboarder/collectors/framework-multilang.js +64 -0
  360. package/src/onboarder/collectors/index.js +136 -0
  361. package/src/orchestrator/agent-fallback.js +81 -0
  362. package/src/orchestrator/brain-coordinator.js +186 -0
  363. package/src/orchestrator/ci-integration.js +139 -0
  364. package/src/orchestrator/config-init.js +510 -0
  365. package/src/orchestrator/direct-actions.js +217 -0
  366. package/src/orchestrator/drivers/error-recovery.js +287 -0
  367. package/src/orchestrator/drivers/init-context.js +227 -0
  368. package/src/orchestrator/drivers/iteration-loop.js +380 -0
  369. package/src/orchestrator/drivers/iteration-phases/coder-and-refactorer.js +69 -0
  370. package/src/orchestrator/drivers/iteration-phases/guards.js +84 -0
  371. package/src/orchestrator/drivers/iteration-phases/handle-approved.js +29 -0
  372. package/src/orchestrator/drivers/iteration-phases/quality-gates.js +114 -0
  373. package/src/orchestrator/drivers/iteration-phases/reviewer-gate.js +60 -0
  374. package/src/orchestrator/drivers/post-loop.js +383 -0
  375. package/src/orchestrator/drivers/pre-loop-phases/auto-hu-batch.js +136 -0
  376. package/src/orchestrator/drivers/pre-loop-phases/config-deprecations.js +47 -0
  377. package/src/orchestrator/drivers/pre-loop-phases/ensure-addyosmani-skills.js +84 -0
  378. package/src/orchestrator/drivers/pre-loop-phases/inject-loaded-plan.js +391 -0
  379. package/src/orchestrator/drivers/pre-loop.js +507 -0
  380. package/src/orchestrator/drivers/run-hu-batch.js +427 -0
  381. package/src/orchestrator/feedback-enrichment.js +140 -0
  382. package/src/orchestrator/feedback-queue.js +151 -0
  383. package/src/orchestrator/flow-control.js +170 -0
  384. package/src/orchestrator/flow-runner.js +364 -0
  385. package/src/orchestrator/fs-leak-detector.js +210 -0
  386. package/src/orchestrator/hu-scheduler.js +114 -0
  387. package/src/orchestrator/hu-sub-pipeline.js +616 -0
  388. package/src/orchestrator/integrations.js +81 -0
  389. package/src/orchestrator/iteration-gate.js +73 -0
  390. package/src/orchestrator/iteration-stages.js +9 -0
  391. package/src/orchestrator/parallel-limiter.js +74 -0
  392. package/src/orchestrator/pipeline-context.js +72 -0
  393. package/src/orchestrator/post-loop-stages.js +468 -0
  394. package/src/orchestrator/pre-loop-stages.js +11 -0
  395. package/src/orchestrator/preflight-checks.js +462 -0
  396. package/src/orchestrator/repair/repair-detector.js +117 -0
  397. package/src/orchestrator/repair/repair-runner.js +142 -0
  398. package/src/orchestrator/reviewer-fallback.js +45 -0
  399. package/src/orchestrator/role-output-compressor.js +189 -0
  400. package/src/orchestrator/session-journal.js +308 -0
  401. package/src/orchestrator/solomon-escalation.js +305 -0
  402. package/src/orchestrator/solomon-rules.js +212 -0
  403. package/src/orchestrator/stages/architect-stage.js +172 -0
  404. package/src/orchestrator/stages/coder-stage.js +480 -0
  405. package/src/orchestrator/stages/domain-curator-stage.js +82 -0
  406. package/src/orchestrator/stages/hu-reviewer-phases/detect-and-apply-splits.js +127 -0
  407. package/src/orchestrator/stages/hu-reviewer-stage.js +394 -0
  408. package/src/orchestrator/stages/perf-stage.js +140 -0
  409. package/src/orchestrator/stages/planner-stage.js +85 -0
  410. package/src/orchestrator/stages/rag-context-stage.js +77 -0
  411. package/src/orchestrator/stages/rag-preload-decisor.js +58 -0
  412. package/src/orchestrator/stages/research-stage.js +69 -0
  413. package/src/orchestrator/stages/reviewer-stage.js +422 -0
  414. package/src/orchestrator/stages/sonar-stage.js +421 -0
  415. package/src/orchestrator/stages/stage-classes.js +84 -0
  416. package/src/orchestrator/stages/stage-executor.js +155 -0
  417. package/src/orchestrator/stages/tdd-discipline-stage.js +56 -0
  418. package/src/orchestrator/stages/tool-judge-stage.js +53 -0
  419. package/src/orchestrator/stages/triage-stage.js +172 -0
  420. package/src/orchestrator/standby.js +71 -0
  421. package/src/orchestrator/verification-gate.js +183 -0
  422. package/src/orchestrator.js +22 -0
  423. package/src/plan/adr-generator.js +170 -0
  424. package/src/plan/adr-loader.js +60 -0
  425. package/src/plan/plan-executor.js +100 -0
  426. package/src/plan/plan-fixer.js +192 -0
  427. package/src/plan/plan-hu-ops.js +16 -0
  428. package/src/plan/plan-id.js +4 -0
  429. package/src/plan/plan-migrate-result.js +24 -0
  430. package/src/plan/plan-reviewer.js +236 -0
  431. package/src/plan/plan-schema.js +334 -0
  432. package/src/plan/plan-store.js +260 -0
  433. package/src/plan/plan-structural-pass.js +122 -0
  434. package/src/plan/plan-validation.js +4 -0
  435. package/src/plan/preflight-hu.js +105 -0
  436. package/src/plan/spec-detector.js +61 -0
  437. package/src/plan/tests-synthesizer.js +131 -0
  438. package/src/planning-game/adapter.js +108 -0
  439. package/src/planning-game/architect-adrs.js +45 -0
  440. package/src/planning-game/client.js +108 -0
  441. package/src/planning-game/decomposition.js +87 -0
  442. package/src/planning-game/pipeline-adapter.js +251 -0
  443. package/src/plugins/loader.js +67 -0
  444. package/src/policy/add.js +111 -0
  445. package/src/policy/decisions.js +42 -0
  446. package/src/policy/engine.js +82 -0
  447. package/src/policy/exceptions.js +60 -0
  448. package/src/privacy/onboarding.js +35 -0
  449. package/src/privacy/scan.js +120 -0
  450. package/src/prompts/arbiter.js +38 -0
  451. package/src/prompts/architect.js +103 -0
  452. package/src/prompts/audit.js +474 -0
  453. package/src/prompts/coder.js +287 -0
  454. package/src/prompts/diff-clip.js +27 -0
  455. package/src/prompts/discover.js +222 -0
  456. package/src/prompts/hu-reviewer.js +219 -0
  457. package/src/prompts/planner.js +312 -0
  458. package/src/prompts/prompt-layout.js +80 -0
  459. package/src/prompts/prompt-resolver.js +120 -0
  460. package/src/prompts/reviewer.js +75 -0
  461. package/src/prompts/rtk-snippet.js +25 -0
  462. package/src/prompts/spec-reviewer.js +66 -0
  463. package/src/prompts/split-signal.js +82 -0
  464. package/src/prompts/start-decision.js +52 -0
  465. package/src/prompts/tool-correctness-judge.js +31 -0
  466. package/src/prompts/triage.js +57 -0
  467. package/src/rag/auto-update.js +72 -0
  468. package/src/rag/chunker-ast.js +56 -0
  469. package/src/rag/chunker.js +132 -0
  470. package/src/rag/embedder.js +2 -0
  471. package/src/rag/embedders/_cloud-base.js +2 -0
  472. package/src/rag/embedders/cohere.js +2 -0
  473. package/src/rag/embedders/factory.js +2 -0
  474. package/src/rag/embedders/mistral.js +2 -0
  475. package/src/rag/embedders/onnx.js +2 -0
  476. package/src/rag/embedders/openai.js +2 -0
  477. package/src/rag/embedders/voyage.js +2 -0
  478. package/src/rag/eval.js +61 -0
  479. package/src/rag/governed-embedder.js +39 -0
  480. package/src/rag/indexer.js +199 -0
  481. package/src/rag/library.js +62 -0
  482. package/src/rag/ollama-capability.js +59 -0
  483. package/src/rag/ollama-manager.js +98 -0
  484. package/src/rag/onnx-fallback.js +69 -0
  485. package/src/rag/rerank.js +4 -0
  486. package/src/rag/retriever.js +2 -0
  487. package/src/rag/vec-store.js +17 -0
  488. package/src/rag/watcher.js +62 -0
  489. package/src/rag/where-parser.js +4 -0
  490. package/src/repeat-detector.js +88 -0
  491. package/src/review/card-first.js +99 -0
  492. package/src/review/card-verify.js +83 -0
  493. package/src/review/diff-generator.js +112 -0
  494. package/src/review/gate-gitignore.js +77 -0
  495. package/src/review/git-stash-helper.js +104 -0
  496. package/src/review/mutation-pregate.js +48 -0
  497. package/src/review/one-shot-review.js +122 -0
  498. package/src/review/parser.js +80 -0
  499. package/src/review/policy-gate.js +26 -0
  500. package/src/review/profiles.js +66 -0
  501. package/src/review/reviewer-fallback.js +125 -0
  502. package/src/review/schema.js +31 -0
  503. package/src/review/scope-filter.js +152 -0
  504. package/src/review/snapshot-diff.js +122 -0
  505. package/src/review/solomon-arbitration.js +113 -0
  506. package/src/review/sonar-pregate.js +63 -0
  507. package/src/review/tdd-discipline.js +90 -0
  508. package/src/review/tdd-policy.js +69 -0
  509. package/src/review/tests-with-code.js +33 -0
  510. package/src/review/verdict-store.js +87 -0
  511. package/src/review/workspace.js +21 -0
  512. package/src/roles/agent-role.js +194 -0
  513. package/src/roles/architect-role.js +77 -0
  514. package/src/roles/audit-role.js +258 -0
  515. package/src/roles/base-role.js +165 -0
  516. package/src/roles/coder-role.js +107 -0
  517. package/src/roles/commiter-role.js +110 -0
  518. package/src/roles/discover-role.js +81 -0
  519. package/src/roles/domain-curator-role.js +141 -0
  520. package/src/roles/hu-reviewer-role.js +64 -0
  521. package/src/roles/impeccable-role.js +85 -0
  522. package/src/roles/index.js +18 -0
  523. package/src/roles/karajan-brain-role.js +134 -0
  524. package/src/roles/onboarder-role.js +61 -0
  525. package/src/roles/perf-role.js +122 -0
  526. package/src/roles/planner-role.js +151 -0
  527. package/src/roles/refactorer-role.js +30 -0
  528. package/src/roles/repairer-role.js +181 -0
  529. package/src/roles/researcher-role.js +70 -0
  530. package/src/roles/reviewer-role.js +104 -0
  531. package/src/roles/security-role.js +73 -0
  532. package/src/roles/solomon-role.js +265 -0
  533. package/src/roles/sonar-role.js +70 -0
  534. package/src/roles/spec-reviewer-role.js +106 -0
  535. package/src/roles/tester-role.js +235 -0
  536. package/src/roles/tool-correctness-judge-role.js +78 -0
  537. package/src/roles/triage-role.js +99 -0
  538. package/src/session/journal/decisions-writer.js +200 -0
  539. package/src/session/journal/iteration-logger.js +194 -0
  540. package/src/session/journal/summary-writer.js +320 -0
  541. package/src/session/journal/tree-writer.js +164 -0
  542. package/src/session/journal/write-all.js +89 -0
  543. package/src/session/mutators.js +339 -0
  544. package/src/session/runtime-overrides.js +59 -0
  545. package/src/session/store.js +106 -0
  546. package/src/session-cleanup.js +116 -0
  547. package/src/skills/addyosmani-catalog.js +315 -0
  548. package/src/skills/addyosmani-role-map.js +139 -0
  549. package/src/skills/openskills-client.js +157 -0
  550. package/src/skills/semantic-detector.js +167 -0
  551. package/src/skills/skill-detector.js +385 -0
  552. package/src/skills/skill-loader.js +219 -0
  553. package/src/skills/skills-cache.js +128 -0
  554. package/src/sonar/api.js +128 -0
  555. package/src/sonar/cloud-scanner.js +78 -0
  556. package/src/sonar/config-resolver.js +88 -0
  557. package/src/sonar/credentials.js +60 -0
  558. package/src/sonar/discovery.js +124 -0
  559. package/src/sonar/enforcer.js +19 -0
  560. package/src/sonar/issue-filter.js +12 -0
  561. package/src/sonar/manager.js +181 -0
  562. package/src/sonar/project-key.js +120 -0
  563. package/src/sonar/scanner.js +307 -0
  564. package/src/sonar/token-bootstrap.js +205 -0
  565. package/src/sonar/token-recovery.js +67 -0
  566. package/src/spec-review/refine-loop.js +58 -0
  567. package/src/spec-review/run-spec-review.js +119 -0
  568. package/src/start/assessment.js +74 -0
  569. package/src/start/maturity.js +73 -0
  570. package/src/start/start-decider-role.js +79 -0
  571. package/src/start/sweep.js +157 -0
  572. package/src/sync/apply-canvas-patch.js +55 -0
  573. package/src/sync/detect-drift.js +113 -0
  574. package/src/tournament/crown.js +88 -0
  575. package/src/tournament/judge.js +150 -0
  576. package/src/tournament/run.js +142 -0
  577. package/src/tournament/scoreboard.js +124 -0
  578. package/src/tracker-bootstrap.js +44 -0
  579. package/src/types/agent.js +55 -0
  580. package/src/types/config.js +114 -0
  581. package/src/types/hu.js +47 -0
  582. package/src/types/index.js +38 -0
  583. package/src/types/policy.js +43 -0
  584. package/src/types/session.js +75 -0
  585. package/src/types/solomon.js +87 -0
  586. package/src/types/stage.js +68 -0
  587. package/src/utils/agent-detect.js +93 -0
  588. package/src/utils/ai-trash.js +48 -0
  589. package/src/utils/async-lock.js +31 -0
  590. package/src/utils/atomic-write.js +6 -0
  591. package/src/utils/banner.js +66 -0
  592. package/src/utils/binary-sources.js +70 -0
  593. package/src/utils/board-prompt-bridge.js +108 -0
  594. package/src/utils/budget.js +229 -0
  595. package/src/utils/cli-ask-question.js +93 -0
  596. package/src/utils/cli-progress.js +220 -0
  597. package/src/utils/cli-run-log.js +83 -0
  598. package/src/utils/cwd-confirm.js +39 -0
  599. package/src/utils/derive-project-name-from-cwd.js +119 -0
  600. package/src/utils/display/event-handlers.js +403 -0
  601. package/src/utils/display/formatters.js +132 -0
  602. package/src/utils/display/header.js +47 -0
  603. package/src/utils/display/resume-hint.js +41 -0
  604. package/src/utils/display/session-summary.js +118 -0
  605. package/src/utils/display/solomon.js +42 -0
  606. package/src/utils/display/spec-findings.js +44 -0
  607. package/src/utils/doc-links.js +29 -0
  608. package/src/utils/docker-install.js +34 -0
  609. package/src/utils/escape-regexp.js +15 -0
  610. package/src/utils/events.js +35 -0
  611. package/src/utils/fs.js +19 -0
  612. package/src/utils/garbage-collector.js +481 -0
  613. package/src/utils/git.js +279 -0
  614. package/src/utils/home-migration.js +87 -0
  615. package/src/utils/injection-guard.js +186 -0
  616. package/src/utils/install-hints.js +180 -0
  617. package/src/utils/json-extract.js +64 -0
  618. package/src/utils/locale.js +59 -0
  619. package/src/utils/logger.js +96 -0
  620. package/src/utils/managed-markers.js +93 -0
  621. package/src/utils/manifest-sniff.js +82 -0
  622. package/src/utils/messages.js +61 -0
  623. package/src/utils/model-selector.js +107 -0
  624. package/src/utils/os-detect.js +79 -0
  625. package/src/utils/paths.js +13 -0
  626. package/src/utils/pending-user-action.js +95 -0
  627. package/src/utils/port-check.js +6 -0
  628. package/src/utils/port-occupant.js +96 -0
  629. package/src/utils/pricing.js +29 -0
  630. package/src/utils/process.js +4 -0
  631. package/src/utils/project-detect.js +160 -0
  632. package/src/utils/prompt-project-name.js +35 -0
  633. package/src/utils/provider-env.js +116 -0
  634. package/src/utils/qmd-collection.js +72 -0
  635. package/src/utils/qmd-detect.js +18 -0
  636. package/src/utils/qmd-install.js +37 -0
  637. package/src/utils/quota-simulator.js +56 -0
  638. package/src/utils/rate-limit-detector.js +181 -0
  639. package/src/utils/repo-cleaner.js +79 -0
  640. package/src/utils/retry.js +88 -0
  641. package/src/utils/role-assigner.js +137 -0
  642. package/src/utils/role-env.js +58 -0
  643. package/src/utils/rtk-detect.js +18 -0
  644. package/src/utils/rtk-install.js +38 -0
  645. package/src/utils/rtk-wrapper.js +87 -0
  646. package/src/utils/run-context.js +75 -0
  647. package/src/utils/run-log.js +217 -0
  648. package/src/utils/run-registry.js +9 -0
  649. package/src/utils/shared-paths.js +12 -0
  650. package/src/utils/squeezr-detect.js +21 -0
  651. package/src/utils/squeezr-install.js +37 -0
  652. package/src/utils/stack-detect.js +135 -0
  653. package/src/utils/stall-detector.js +159 -0
  654. package/src/utils/status-dashboard.js +254 -0
  655. package/src/utils/task-file.js +109 -0
  656. package/src/utils/telemetry.js +124 -0
  657. package/src/utils/templates-root.js +61 -0
  658. package/src/utils/tool-call-extractor.js +56 -0
  659. package/src/utils/tool-governor.js +116 -0
  660. package/src/utils/tool-installer.js +93 -0
  661. package/src/utils/update-check.js +280 -0
  662. package/src/utils/vector-store-orphans.js +89 -0
  663. package/src/utils/welcome.js +47 -0
  664. package/src/utils/with-timeout.js +42 -0
  665. package/src/utils/wizard.js +47 -0
  666. package/src/webperf/cwv-gate.js +94 -0
  667. package/src/webperf/devtools-detect.js +112 -0
  668. package/src/webperf/scanner.js +209 -0
  669. package/src/webperf/webperf-role-integration.js +66 -0
  670. package/templates/coder-rules.md +45 -0
  671. package/templates/docker-compose.sonar.yml +60 -0
  672. package/templates/domains/example-dental.md +57 -0
  673. package/templates/kj.config.yml +128 -0
  674. package/templates/review-rules.md +17 -0
  675. package/templates/roles/architect/anthropic.md +74 -0
  676. package/templates/roles/architect/default.md +72 -0
  677. package/templates/roles/architect/google.md +61 -0
  678. package/templates/roles/architect/openai.md +60 -0
  679. package/templates/roles/architect/opencode.md +59 -0
  680. package/templates/roles/architect.md +76 -0
  681. package/templates/roles/audit.md +68 -0
  682. package/templates/roles/coder/anthropic.md +81 -0
  683. package/templates/roles/coder/default.md +78 -0
  684. package/templates/roles/coder/google.md +87 -0
  685. package/templates/roles/coder/openai.md +68 -0
  686. package/templates/roles/coder/opencode.md +58 -0
  687. package/templates/roles/coder.md +152 -0
  688. package/templates/roles/commiter.md +44 -0
  689. package/templates/roles/discover.md +167 -0
  690. package/templates/roles/domain-curator.md +52 -0
  691. package/templates/roles/hu-reviewer.md +236 -0
  692. package/templates/roles/impeccable-design.md +87 -0
  693. package/templates/roles/impeccable.md +125 -0
  694. package/templates/roles/karajan-brain.md +95 -0
  695. package/templates/roles/onboarder.md +45 -0
  696. package/templates/roles/planner/anthropic.md +59 -0
  697. package/templates/roles/planner/default.md +57 -0
  698. package/templates/roles/planner/google.md +75 -0
  699. package/templates/roles/planner/openai.md +55 -0
  700. package/templates/roles/planner/opencode.md +62 -0
  701. package/templates/roles/planner.md +61 -0
  702. package/templates/roles/refactorer.md +48 -0
  703. package/templates/roles/repairer.md +103 -0
  704. package/templates/roles/researcher.md +41 -0
  705. package/templates/roles/reviewer/anthropic.md +61 -0
  706. package/templates/roles/reviewer/default.md +62 -0
  707. package/templates/roles/reviewer/google.md +63 -0
  708. package/templates/roles/reviewer/openai.md +57 -0
  709. package/templates/roles/reviewer/opencode.md +42 -0
  710. package/templates/roles/reviewer-paranoid.md +38 -0
  711. package/templates/roles/reviewer-relaxed.md +34 -0
  712. package/templates/roles/reviewer-strict.md +37 -0
  713. package/templates/roles/reviewer.md +62 -0
  714. package/templates/roles/security.md +54 -0
  715. package/templates/roles/solomon/anthropic.md +71 -0
  716. package/templates/roles/solomon/default.md +83 -0
  717. package/templates/roles/solomon/google.md +61 -0
  718. package/templates/roles/solomon/openai.md +53 -0
  719. package/templates/roles/solomon/opencode.md +55 -0
  720. package/templates/roles/solomon.md +83 -0
  721. package/templates/roles/sonar.md +49 -0
  722. package/templates/roles/spec-reviewer.md +47 -0
  723. package/templates/roles/tester.md +50 -0
  724. package/templates/roles/triage.md +69 -0
  725. package/templates/skills/kj-architect.md +45 -0
  726. package/templates/skills/kj-audit.md +63 -0
  727. package/templates/skills/kj-board.md +16 -0
  728. package/templates/skills/kj-code.md +51 -0
  729. package/templates/skills/kj-csv-transform.md +40 -0
  730. package/templates/skills/kj-data-report.md +44 -0
  731. package/templates/skills/kj-discover.md +24 -0
  732. package/templates/skills/kj-rag-query.md +50 -0
  733. package/templates/skills/kj-review.md +47 -0
  734. package/templates/skills/kj-run.md +73 -0
  735. package/templates/skills/kj-security.md +49 -0
  736. package/templates/skills/kj-sonar.md +41 -0
  737. package/templates/skills/kj-sql-analysis.md +40 -0
  738. package/templates/skills/kj-test.md +40 -0
  739. package/templates/workflows/automerge.yml +30 -0
  740. package/templates/workflows/houston-override.yml +46 -0
  741. package/templates/workflows/kj-ci-gateway.yml +58 -0
  742. package/vendor/tree-sitter-grammars/README.md +21 -0
  743. package/vendor/tree-sitter-grammars/go.wasm +0 -0
  744. package/vendor/tree-sitter-grammars/java.wasm +0 -0
  745. package/vendor/tree-sitter-grammars/python.wasm +0 -0
  746. package/vendor/tree-sitter-grammars/rust.wasm +0 -0
@@ -0,0 +1,413 @@
1
+ import { BaseAgent } from "./base-agent.js";
2
+ import { resolveBin } from "./resolve-bin.js";
3
+
4
+ /**
5
+ * Safely parse a JSON line, returning null on failure.
6
+ */
7
+ function tryParseJson(line) {
8
+ try {
9
+ return JSON.parse(line);
10
+ } catch { return null; }
11
+ }
12
+
13
+ /**
14
+ * Extract a human-readable error message from Claude's raw NDJSON stderr output.
15
+ * Claude emits system init, api_retry, assistant error, and result lines.
16
+ * We extract just the meaningful error text, discarding the JSON noise.
17
+ */
18
+ export function sanitizeClaudeError(raw) {
19
+ if (!raw) return "Unknown error";
20
+ const lines = raw.split("\n").filter(Boolean);
21
+
22
+ // Look for a result line with the actual error
23
+ for (let i = lines.length - 1; i >= 0; i--) {
24
+ const obj = tryParseJson(lines[i]);
25
+ if (!obj) continue;
26
+ if (obj.type === "result" && obj.result) return String(obj.result);
27
+ if (obj.type === "assistant" && obj.message?.content) {
28
+ const text = obj.message.content
29
+ .filter(b => b.type === "text" && b.text)
30
+ .map(b => b.text)
31
+ .join(" ");
32
+ if (text) return text;
33
+ }
34
+ }
35
+
36
+ // If no JSON parsed, return first non-JSON line or truncated raw
37
+ for (const line of lines) {
38
+ if (!line.startsWith("{")) return line.slice(0, 200);
39
+ }
40
+
41
+ return raw.slice(0, 200);
42
+ }
43
+
44
+ /**
45
+ * Try to extract a result string from a parsed JSON object.
46
+ * Returns the result string or null if the object is not a result message.
47
+ */
48
+ function extractResultText(obj) {
49
+ if (obj.type === "result" && obj.result) {
50
+ return typeof obj.result === "string" ? obj.result : JSON.stringify(obj.result);
51
+ }
52
+ if (obj.result && typeof obj.result === "string") {
53
+ return obj.result;
54
+ }
55
+ return null;
56
+ }
57
+
58
+ /**
59
+ * Collect text parts from an assistant message's content blocks.
60
+ */
61
+ function collectAssistantText(obj) {
62
+ if (obj.type !== "assistant" || !obj.message?.content) return [];
63
+ return obj.message.content
64
+ .filter(block => block.type === "text" && block.text)
65
+ .map(block => block.text);
66
+ }
67
+
68
+ /**
69
+ * Extract usage metrics from stream-json/json NDJSON output.
70
+ * Looks for the "result" line which contains total_cost_usd,
71
+ * usage.input_tokens/output_tokens, cache_read_input_tokens,
72
+ * cache_creation_input_tokens, and modelUsage.
73
+ *
74
+ * `cached_tokens` aggregates both cache_read_input_tokens (tokens served
75
+ * from Anthropic's automatic prompt cache) and cache_creation_input_tokens
76
+ * (tokens just written to the cache during this call). The sum represents
77
+ * the total volume of input that benefited from prompt-caching machinery.
78
+ *
79
+ * Returns an object with tokens_in, tokens_out, cached_tokens, cost_usd,
80
+ * model or null if not found.
81
+ */
82
+ function extractUsageFromStreamJson(raw) {
83
+ const events = parseClaudeEvents(raw);
84
+ for (let i = events.length - 1; i >= 0; i--) {
85
+ const obj = events[i];
86
+ if (obj.type !== "result") continue;
87
+
88
+ const tokens_in = obj.usage?.input_tokens ?? 0;
89
+ const tokens_out = obj.usage?.output_tokens ?? 0;
90
+ const cache_read = obj.usage?.cache_read_input_tokens ?? 0;
91
+ const cache_creation = obj.usage?.cache_creation_input_tokens ?? 0;
92
+ const cached_tokens = cache_read + cache_creation;
93
+ const cost_usd = obj.total_cost_usd ?? undefined;
94
+ const modelUsage = obj.modelUsage;
95
+ const model = modelUsage ? Object.keys(modelUsage)[0] || null : null;
96
+
97
+ return { tokens_in, tokens_out, cached_tokens, cost_usd, model };
98
+ }
99
+ return null;
100
+ }
101
+
102
+ /**
103
+ * Normalize Claude's output payload to a flat array of event objects.
104
+ *
105
+ * Claude 2.x supports two on-disk shapes depending on the output-format flag:
106
+ *
107
+ * --output-format stream-json → NDJSON, one event JSON per line
108
+ * --output-format json → a single JSON array with every event
109
+ *
110
+ * The legacy path assumed NDJSON and silently dropped the `json` variant
111
+ * (split("\n") returned one unparseable "line" containing the whole array,
112
+ * extract returned empty, caller fell back to the raw string — which made
113
+ * `kj plan` save the whole stream as `approach` with zero HUs). Handle both
114
+ * shapes here so callers see a uniform list of events regardless of flag.
115
+ */
116
+ function parseClaudeEvents(raw) {
117
+ const trimmed = (raw || "").trim();
118
+ if (!trimmed) return [];
119
+ // `--output-format json` → single JSON array.
120
+ if (trimmed.startsWith("[")) {
121
+ try {
122
+ const arr = JSON.parse(trimmed);
123
+ if (Array.isArray(arr)) return arr.filter((o) => o && typeof o === "object");
124
+ } catch { /* fall through to NDJSON */ }
125
+ }
126
+ // `--output-format stream-json` → NDJSON, one event per line.
127
+ const events = [];
128
+ for (const line of trimmed.split("\n")) {
129
+ const obj = tryParseJson(line);
130
+ if (obj && typeof obj === "object") events.push(obj);
131
+ }
132
+ return events;
133
+ }
134
+
135
+ /**
136
+ * Extract the final text result from Claude's output (either stream-json
137
+ * NDJSON or single-array json — see parseClaudeEvents). We look for the
138
+ * `result` message first (holds the final text); if missing, concatenate
139
+ * every assistant text block encountered.
140
+ */
141
+ function extractTextFromStreamJson(raw) {
142
+ const events = parseClaudeEvents(raw);
143
+ // Walk from the end — the final `result` event is the last one.
144
+ for (let i = events.length - 1; i >= 0; i--) {
145
+ const result = extractResultText(events[i]);
146
+ if (result) return result;
147
+ }
148
+ // Fallback: accumulate every assistant text block in order.
149
+ const parts = [];
150
+ for (const obj of events) parts.push(...collectAssistantText(obj));
151
+ return parts.join("") || raw;
152
+ }
153
+
154
+ /**
155
+ * Create a wrapping onOutput that parses stream-json lines and forwards
156
+ * meaningful content (assistant text, tool usage) to the original callback.
157
+ */
158
+ function summarizeToolCall(name, input = {}) {
159
+ // Produce concise human-readable action line from tool_use block
160
+ const rel = (p) => String(p || "").replace(process.cwd() + "/", "");
161
+ switch (name) {
162
+ case "Read": return `Read ${rel(input.file_path)}`;
163
+ case "Write": return `Write ${rel(input.file_path)}`;
164
+ case "Edit": return `Edit ${rel(input.file_path)}`;
165
+ case "MultiEdit": return `MultiEdit ${rel(input.file_path)} (${(input.edits || []).length} edits)`;
166
+ case "NotebookEdit": return `NotebookEdit ${rel(input.notebook_path)}`;
167
+ case "Glob": return `Glob ${input.pattern || ""}`.trim();
168
+ case "Grep": return `Grep "${(input.pattern || "").slice(0, 60)}"${input.path ? ` in ${rel(input.path)}` : ""}`;
169
+ case "Bash": {
170
+ const cmd = String(input.command || "").replaceAll(/\s+/g, " ").slice(0, 100);
171
+ return `Bash $ ${cmd}`;
172
+ }
173
+ case "TodoWrite": return `Todo ${(input.todos || []).length} items`;
174
+ case "WebFetch": return `WebFetch ${input.url || ""}`;
175
+ case "WebSearch": return `WebSearch "${(input.query || "").slice(0, 60)}"`;
176
+ case "Task": return `Agent: ${input.subagent_type || "?"} — ${(input.description || "").slice(0, 60)}`;
177
+ default: return `${name}${Object.keys(input).length > 0 ? " (…)" : ""}`;
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Translate a Claude stream-json event into a human-readable summary.
183
+ *
184
+ * Returns a `{ line, kind }` object for the cli-progress reporter to
185
+ * display, OR `null` to suppress (the event is structural noise).
186
+ *
187
+ * Whitelist semantics on purpose: pre-v2.7.5 the filter ONLY recognised
188
+ * `assistant` + `result` and forwarded everything else as raw JSON,
189
+ * which dumped 6 KB of `system/init` + `rate_limit_event` to the user
190
+ * on every `kj plan`. Now we recognise every event type Claude can
191
+ * emit and either condense it to one line or drop it. Unknown types
192
+ * are dropped silently unless DEBUG_KJ_AGENT is set.
193
+ */
194
+ export function translateStreamJsonEvent(obj) {
195
+ switch (obj.type) {
196
+ case "system": {
197
+ // The init frame announces session id + model. Useful as a single
198
+ // status line on first connect; the rate_limit subtype on a
199
+ // healthy session is pure noise — suppress.
200
+ if (obj.subtype === "init") {
201
+ const model = typeof obj.model === "string" ? obj.model : "?";
202
+ return { line: `Claude session ready (model: ${model.replace(/\[.*\]/, "")})`, kind: "text" };
203
+ }
204
+ return null;
205
+ }
206
+ case "rate_limit_event": {
207
+ const status = obj.rate_limit_info?.status;
208
+ // Only surface when actually throttled — "allowed" is the happy
209
+ // path and surfaces every few seconds during normal runs.
210
+ if (status && status !== "allowed") {
211
+ return { line: `Rate limit: ${status}`, kind: "text" };
212
+ }
213
+ return null;
214
+ }
215
+ case "user": {
216
+ // Echoes of the previous tool's output coming back into the
217
+ // conversation. Adding a line per echo would double the noise of
218
+ // the corresponding tool_use line, which we already render.
219
+ return null;
220
+ }
221
+ case "assistant": {
222
+ const content = obj.message?.content;
223
+ if (!Array.isArray(content)) return null;
224
+ // Each content block becomes a separate line — we return them
225
+ // through the caller's emit-many path below.
226
+ return { _multi: true, blocks: content };
227
+ }
228
+ case "result": {
229
+ const summary = typeof obj.result === "string"
230
+ ? obj.result.split("\n")[0].slice(0, 120)
231
+ : "result received";
232
+ return { line: `done: ${summary}` };
233
+ }
234
+ default: {
235
+ if (process.env.DEBUG_KJ_AGENT) {
236
+ return { line: `[debug] unknown event: ${obj.type}` };
237
+ }
238
+ return null;
239
+ }
240
+ }
241
+ }
242
+
243
+ export function createStreamJsonFilter(onOutput) {
244
+ if (!onOutput) return null;
245
+ return ({ stream, line }) => {
246
+ let obj;
247
+ try {
248
+ obj = JSON.parse(line);
249
+ } catch {
250
+ // Not JSON — forward verbatim ONLY if it's short and not a partial
251
+ // JSON chunk (some agents print stderr diagnostics through the
252
+ // same channel). Anything that smells like JSON-debris gets
253
+ // dropped to keep the CLI clean.
254
+ const trimmed = line.trim();
255
+ if (trimmed && !trimmed.startsWith("{") && !trimmed.startsWith("[") && trimmed.length < 200) {
256
+ onOutput({ stream, line: trimmed });
257
+ }
258
+ return;
259
+ }
260
+
261
+ const summary = translateStreamJsonEvent(obj);
262
+ if (!summary) return;
263
+
264
+ // Multi-block assistant payload: one line per content block.
265
+ if (summary._multi) {
266
+ for (const block of summary.blocks) {
267
+ if (block.type === "text" && block.text) {
268
+ const firstLine = block.text.split("\n")[0].trim().slice(0, 120);
269
+ if (firstLine) onOutput({ stream, line: firstLine, kind: "text" });
270
+ } else if (block.type === "tool_use") {
271
+ onOutput({ stream, line: summarizeToolCall(block.name, block.input), kind: "tool" });
272
+ } else if (block.type === "thinking" && block.thinking) {
273
+ onOutput({ stream, line: `thinking: ${block.thinking.slice(0, 80).replaceAll(/\s+/g, " ")}…`, kind: "thinking" });
274
+ }
275
+ }
276
+ return;
277
+ }
278
+
279
+ onOutput({ stream, line: summary.line, kind: summary.kind });
280
+ };
281
+ }
282
+
283
+ /**
284
+ * Build clean execa options for Claude subprocess.
285
+ *
286
+ * Three critical fixes for running `claude -p` from Node.js:
287
+ *
288
+ * 1. Strip CLAUDECODE env var — Claude Code 2.x sets this to block nested
289
+ * sessions. The spawned `claude -p` is a separate non-interactive
290
+ * invocation, not a true nested session.
291
+ *
292
+ * 2. Detach stdin (stdin: "ignore") — When launched from Node.js (which is
293
+ * how Claude Code / Karajan MCP runs), the child inherits the parent's
294
+ * stdin. `claude -p` then blocks waiting to read from a stdin that the
295
+ * parent is already consuming. Ignoring stdin prevents the hang.
296
+ *
297
+ * 3. Claude Code 2.x writes all structured output (stream-json, json) to
298
+ * stderr, NOT stdout. The agent must read from stderr for the actual
299
+ * response data.
300
+ */
301
+ function cleanExecaOpts(extra = {}) {
302
+ const { CLAUDECODE: _CLAUDECODE, ...env } = process.env;
303
+ // PAR-H (KJC-TSK-0631): extra.env ADDS to the inherited env (lane slot
304
+ // vars) instead of replacing it wholesale.
305
+ const { env: extraEnv, ...rest } = extra;
306
+ return { env: extraEnv ? { ...env, ...extraEnv } : env, stdin: "ignore", ...rest };
307
+ }
308
+
309
+ /**
310
+ * Pick the best raw output from a claude subprocess result.
311
+ * Claude 2.x sends structured output to stderr; stdout is often empty.
312
+ */
313
+ function pickOutput(res) {
314
+ return res.stdout || res.stderr || "";
315
+ }
316
+
317
+ /**
318
+ * Default tools to allow for Claude subprocess.
319
+ * Since claude -p runs non-interactively (stdin: "ignore"), it cannot ask for
320
+ * permission approval. Without --allowedTools, it blocks waiting for approval
321
+ * that never comes.
322
+ */
323
+ const ALLOWED_TOOLS = [
324
+ "Read", "Write", "Edit", "Bash", "Glob", "Grep"
325
+ ];
326
+
327
+ /**
328
+ * Base CLI args for a task (Φ1-D, KJC-PCS-0057). When the role provides the
329
+ * prompt split in stable/volatile buckets, the stable block travels via
330
+ * --append-system-prompt — the CLI places cache breakpoints on the system
331
+ * block, so Anthropic's prompt cache hits on it across iterations — and only
332
+ * the volatile block goes in the -p message. Without the split, legacy
333
+ * single-prompt behavior is unchanged.
334
+ */
335
+ function buildPromptArgs(task) {
336
+ if (task.stablePrompt && task.volatilePrompt) {
337
+ return ["-p", task.volatilePrompt, "--append-system-prompt", task.stablePrompt, "--allowedTools", ...ALLOWED_TOOLS];
338
+ }
339
+ return ["-p", task.prompt, "--allowedTools", ...ALLOWED_TOOLS];
340
+ }
341
+
342
+ export class ClaudeAgent extends BaseAgent {
343
+ async runTask(task) {
344
+ const role = task.role || "coder";
345
+ const model = this.getRoleModel(role);
346
+ const result = await this._runTaskExec(task, model, role);
347
+ if (!result.ok && model && this.isModelNotSupportedError(result)) {
348
+ this.logger?.warn(`Claude model "${model}" not supported — retrying with agent default`);
349
+ return this._runTaskExec(task, null, role);
350
+ }
351
+ return result;
352
+ }
353
+
354
+ async reviewTask(task) {
355
+ const role = task.role || "reviewer";
356
+ const model = this.getRoleModel(role);
357
+ const result = await this._reviewTaskExec(task, model);
358
+ if (!result.ok && model && this.isModelNotSupportedError(result)) {
359
+ this.logger?.warn(`Claude model "${model}" not supported — retrying with agent default`);
360
+ return this._reviewTaskExec(task, null);
361
+ }
362
+ return result;
363
+ }
364
+
365
+ async _runTaskExec(task, model, _role) {
366
+ const args = buildPromptArgs(task);
367
+ if (model) args.push("--model", model);
368
+ // PL-C (KJC-TSK-0735): el rol del subproceso viaja en KJ_POLICY_ROLE —
369
+ // si su harness corre hooks (claude -p), el tier A evalúa la policy con
370
+ // el rol que ACTÚA. El rol del ORQUESTADOR es autoritativo: task.env no
371
+ // puede spoofearlo (catch de codex: sería un bypass de reglas por rol).
372
+ const roleEnv = task.role ? { ...task.env, KJ_POLICY_ROLE: task.role } : task.env;
373
+
374
+ // Use stream-json when onOutput is provided to get real-time feedback
375
+ if (task.onOutput) {
376
+ args.push("--output-format", "stream-json", "--verbose");
377
+ const streamFilter = createStreamJsonFilter(task.onOutput);
378
+ const res = await this.runCommand(resolveBin("claude"), args, cleanExecaOpts({
379
+ onOutput: streamFilter,
380
+ silenceTimeoutMs: task.silenceTimeoutMs,
381
+ timeout: task.timeoutMs,
382
+ env: roleEnv,
383
+ cwd: task.cwd
384
+ }));
385
+ const raw = pickOutput(res);
386
+ const output = extractTextFromStreamJson(raw);
387
+ const usage = extractUsageFromStreamJson(raw);
388
+ return { ok: res.exitCode === 0, output, error: res.exitCode === 0 ? "" : sanitizeClaudeError(raw), exitCode: res.exitCode, ...usage };
389
+ }
390
+
391
+ // Without streaming, use json output to get structured response via stderr
392
+ args.push("--output-format", "json");
393
+ const res = await this.runCommand(resolveBin("claude"), args, cleanExecaOpts({ env: roleEnv, cwd: task.cwd }));
394
+ const raw = pickOutput(res);
395
+ const output = extractTextFromStreamJson(raw);
396
+ const usage = extractUsageFromStreamJson(raw);
397
+ return { ok: res.exitCode === 0, output, error: res.exitCode === 0 ? "" : sanitizeClaudeError(raw), exitCode: res.exitCode, ...usage };
398
+ }
399
+
400
+ async _reviewTaskExec(task, model) {
401
+ const args = [...buildPromptArgs(task), "--output-format", "stream-json", "--verbose"];
402
+ if (model) args.push("--model", model);
403
+ const res = await this.runCommand(resolveBin("claude"), args, cleanExecaOpts({
404
+ onOutput: task.onOutput,
405
+ silenceTimeoutMs: task.silenceTimeoutMs,
406
+ timeout: task.timeoutMs,
407
+ env: task.env
408
+ }));
409
+ const raw = pickOutput(res);
410
+ const usage = extractUsageFromStreamJson(raw);
411
+ return { ok: res.exitCode === 0, output: raw, error: res.exitCode === 0 ? "" : raw, exitCode: res.exitCode, ...usage };
412
+ }
413
+ }
@@ -0,0 +1,112 @@
1
+ import { BaseAgent } from "./base-agent.js";
2
+ import { resolveBin } from "./resolve-bin.js";
3
+
4
+ /**
5
+ * Try to extract an OpenAI-style `usage` JSON block from codex stdout.
6
+ *
7
+ * OpenAI's Chat Completions API exposes prompt-cache hits via
8
+ * `usage.prompt_tokens_details.cached_tokens` (since Aug 2024). Codex
9
+ * CLI doesn't surface this in its default footer, but newer flags or a
10
+ * wrapper may emit a JSON `usage` line. We scan stdout line-by-line and
11
+ * accept the first parseable object that carries `prompt_tokens`.
12
+ *
13
+ * Returns `{tokens_in, tokens_out, cached_tokens}` or null when absent.
14
+ */
15
+ function extractJsonUsage(stdout) {
16
+ for (const line of stdout.split("\n")) {
17
+ const trimmed = line.trim();
18
+ if (!trimmed.startsWith("{")) continue;
19
+ let obj;
20
+ try { obj = JSON.parse(trimmed); } catch { continue; }
21
+ const usage = obj?.usage ?? (typeof obj?.prompt_tokens === "number" ? obj : null);
22
+ if (!usage || typeof usage.prompt_tokens !== "number") continue;
23
+ return {
24
+ tokens_in: usage.prompt_tokens,
25
+ tokens_out: usage.completion_tokens ?? 0,
26
+ cached_tokens: usage.prompt_tokens_details?.cached_tokens ?? 0
27
+ };
28
+ }
29
+ return null;
30
+ }
31
+
32
+ /**
33
+ * Extract token usage from Codex CLI stdout. Two shapes are supported:
34
+ *
35
+ * 1. OpenAI-style JSON `usage` block (preferred):
36
+ * `{"usage":{"prompt_tokens":N,"completion_tokens":M,
37
+ * "prompt_tokens_details":{"cached_tokens":C}}}`
38
+ * → `{tokens_in:N, tokens_out:M, cached_tokens:C}`.
39
+ *
40
+ * 2. Legacy "tokens used\n<number>" footer (codex's default total,
41
+ * no cache visibility) → `{tokens_in:0, tokens_out, cached_tokens:0}`.
42
+ *
43
+ * Returns null when neither shape is found.
44
+ */
45
+ function extractCodexTokens(stdout) {
46
+ if (!stdout) return null;
47
+ const json = extractJsonUsage(stdout);
48
+ if (json) return json;
49
+ const match = stdout.match(/tokens?\s+used\s*\n\s*([\d,]+)/i);
50
+ if (!match) return null;
51
+ const total = Number(match[1].replaceAll(/,/g, ""));
52
+ if (!Number.isFinite(total) || total <= 0) return null;
53
+ return { tokens_in: 0, tokens_out: total, cached_tokens: 0 };
54
+ }
55
+
56
+ export class CodexAgent extends BaseAgent {
57
+ async runTask(task) {
58
+ const role = task.role || "coder";
59
+ const model = this.getRoleModel(role);
60
+ const result = await this._exec(task, model, role);
61
+ if (!result.ok && model && this.isModelNotSupportedError(result)) {
62
+ this.logger?.warn(`Codex model "${model}" not supported — retrying with agent default`);
63
+ return this._exec(task, null, role);
64
+ }
65
+ return result;
66
+ }
67
+
68
+ async reviewTask(task) {
69
+ const role = task.role || "reviewer";
70
+ const model = this.getRoleModel(role);
71
+ const result = await this._exec(task, model, role);
72
+ if (!result.ok && model && this.isModelNotSupportedError(result)) {
73
+ this.logger?.warn(`Codex model "${model}" not supported — retrying with agent default`);
74
+ return this._exec(task, null, role);
75
+ }
76
+ return result;
77
+ }
78
+
79
+ async _exec(task, model, role) {
80
+ // KJC-BUG-0143 (campo, issue #1471): codex exec >= 0.146 rechaza
81
+ // --full-auto ("unexpected argument"); el auto-approve moderno para un
82
+ // coder que escribe es --sandbox workspace-write. Los CLIs antiguos no
83
+ // conocen --sandbox: si lo rechazan, se reintenta con el flag legacy.
84
+ const autoApprove = role !== "reviewer" && this.isAutoApproveEnabled(role);
85
+ const res = await this._execOnce(task, model, role, { autoApprove, legacyAutoFlag: false });
86
+ if (!res.ok && autoApprove && /unexpected argument '--sandbox'/.test(res.error || "")) {
87
+ this.logger?.warn("codex CLI sin --sandbox — reintentando con el flag legacy --full-auto");
88
+ return this._execOnce(task, model, role, { autoApprove, legacyAutoFlag: true });
89
+ }
90
+ return res;
91
+ }
92
+
93
+ async _execOnce(task, model, role, { autoApprove, legacyAutoFlag }) {
94
+ const args = ["exec", "--skip-git-repo-check"];
95
+ if (model) args.push("--model", model);
96
+ if (autoApprove) {
97
+ if (legacyAutoFlag) args.push("--full-auto");
98
+ else args.push("--sandbox", "workspace-write");
99
+ }
100
+ args.push("-");
101
+ const res = await this.runCommand(resolveBin("codex"), args, {
102
+ onOutput: task.onOutput,
103
+ silenceTimeoutMs: task.silenceTimeoutMs,
104
+ timeout: task.timeoutMs,
105
+ input: task.prompt,
106
+ // TOR-A (KJC-TSK-0723): tournament lanes run the coder INSIDE the lane.
107
+ cwd: task.cwd
108
+ });
109
+ const usage = extractCodexTokens(res.stdout);
110
+ return { ok: res.exitCode === 0, output: res.stdout, error: res.stderr, exitCode: res.exitCode, ...usage };
111
+ }
112
+ }
@@ -0,0 +1,65 @@
1
+ import { BaseAgent } from "./base-agent.js";
2
+ import { resolveBin } from "./resolve-bin.js";
3
+
4
+ /**
5
+ * GitHub Copilot CLI (@github/copilot, binary `copilot`) — KJC-TSK-0666.
6
+ * The seventh built-in agent, and the user-chosen third AI for solomon
7
+ * arbitration (gemini tier retired, Qwen OAuth free tier discontinued).
8
+ *
9
+ * Verified live against copilot 1.0.73:
10
+ * - `-p <prompt>` runs non-interactive; copilot has NO stdin prompt channel
11
+ * (stdin is treated as context, spawning an agentic run) — so the prompt
12
+ * travels as an argument. Same documented E2BIG limit as claude-agent
13
+ * (KJC-BUG-0121) for multi-hundred-KB diffs.
14
+ * - `--output-format json` emits JSONL; the final text lives in the
15
+ * `assistant.message` event's `data.content`, run metadata in `result`.
16
+ * - Default model routing is automatic and varies per run (haiku, gpt-5-mini
17
+ * observed) — pin it per role via `roles.<role>.model` when neutrality
18
+ * matters (e.g. a gemini-family model for solomon).
19
+ */
20
+ export class CopilotAgent extends BaseAgent {
21
+ async runTask(task) {
22
+ // Coder mode IS an agentic run: file edits and shell need permissions.
23
+ return this._exec(task, this.getRoleModel(task.role || "coder"), ["--allow-all-tools"]);
24
+ }
25
+
26
+ async reviewTask(task) {
27
+ // Review/arbitration answers from the prompt alone — no tool grants, and
28
+ // an explicit no-tools instruction so copilot never stalls waiting for
29
+ // an approval that headless mode cannot give.
30
+ return this._exec(
31
+ { ...task, prompt: `Do NOT use any tools. Answer directly from this prompt only.\n\n${task.prompt}` },
32
+ this.getRoleModel(task.role || "reviewer"),
33
+ [],
34
+ );
35
+ }
36
+
37
+ async _exec(task, model, extraArgs) {
38
+ const args = ["-p", task.prompt, "--output-format", "json", "--no-color", ...extraArgs];
39
+ if (model) args.push("--model", model);
40
+ const res = await this.runCommand(resolveBin("copilot"), args, {
41
+ onOutput: task.onOutput,
42
+ silenceTimeoutMs: task.silenceTimeoutMs,
43
+ timeout: task.timeoutMs
44
+ });
45
+ const output = extractCopilotOutput(res.stdout);
46
+ return { ok: res.exitCode === 0, output: output ?? res.stdout, error: res.stderr, exitCode: res.exitCode };
47
+ }
48
+ }
49
+
50
+ /** Last `assistant.message` content from copilot's JSONL stream (null if none). */
51
+ export function extractCopilotOutput(stdout) {
52
+ if (!stdout) return null;
53
+ let content = null;
54
+ for (const line of stdout.split("\n")) {
55
+ const t = line.trim();
56
+ if (!t.startsWith("{")) continue;
57
+ try {
58
+ const obj = JSON.parse(t);
59
+ if (obj?.type === "assistant.message" && typeof obj?.data?.content === "string") {
60
+ content = obj.data.content;
61
+ }
62
+ } catch { /* non-JSON noise between events */ }
63
+ }
64
+ return content;
65
+ }
@@ -0,0 +1,85 @@
1
+ import { BaseAgent } from "./base-agent.js";
2
+ import { resolveBin } from "./resolve-bin.js";
3
+
4
+ /**
5
+ * Extract Gemini's `usageMetadata` from stdout, including
6
+ * `cachedContentTokenCount` (Gemini's context-caching API metric,
7
+ * https://ai.google.dev/api/caching).
8
+ *
9
+ * The gemini CLI emits `usageMetadata` as part of its JSON response
10
+ * shape (today: only `--output-format json`, used in reviewTask; in
11
+ * plain runTask mode no usage is surfaced). When absent, returns null
12
+ * so the caller leaves `cached_tokens` undefined (= unmeasured,
13
+ * distinct from a measured 0).
14
+ */
15
+ function extractGeminiUsage(text) {
16
+ if (!text) return null;
17
+ const candidates = [];
18
+ for (const line of text.split("\n")) {
19
+ const t = line.trim();
20
+ if (t.startsWith("{") || t.startsWith("[")) candidates.push(t);
21
+ }
22
+ candidates.push(text.trim());
23
+ for (const candidate of candidates) {
24
+ let obj;
25
+ try { obj = JSON.parse(candidate); } catch { continue; }
26
+ const meta = obj?.usageMetadata
27
+ ?? obj?.usage_metadata
28
+ ?? obj?.response?.usageMetadata;
29
+ if (!meta || typeof meta.promptTokenCount !== "number") continue;
30
+ return {
31
+ tokens_in: meta.promptTokenCount,
32
+ tokens_out: meta.candidatesTokenCount ?? 0,
33
+ cached_tokens: meta.cachedContentTokenCount ?? 0
34
+ };
35
+ }
36
+ return null;
37
+ }
38
+
39
+ export class GeminiAgent extends BaseAgent {
40
+ // Overridden by forks with the same headless interface (QwenAgent).
41
+ get cliBin() { return "gemini"; }
42
+ get spawnEnv() { return { GEMINI_CLI_TRUST_WORKSPACE: "true" }; }
43
+
44
+ async runTask(task) {
45
+ const role = task.role || "coder";
46
+ const model = this.getRoleModel(role);
47
+ const result = await this._exec(task, model, "run");
48
+ if (!result.ok && model && this.isModelNotSupportedError(result)) {
49
+ this.logger?.warn(`${this.cliBin} model "${model}" not supported — retrying with agent default`);
50
+ return this._exec(task, null, "run");
51
+ }
52
+ return result;
53
+ }
54
+
55
+ async reviewTask(task) {
56
+ const role = task.role || "reviewer";
57
+ const model = this.getRoleModel(role);
58
+ const result = await this._exec(task, model, "review");
59
+ if (!result.ok && model && this.isModelNotSupportedError(result)) {
60
+ this.logger?.warn(`${this.cliBin} model "${model}" not supported — retrying with agent default`);
61
+ return this._exec(task, null, "review");
62
+ }
63
+ return result;
64
+ }
65
+
66
+ async _exec(task, model, mode) {
67
+ // KJC-BUG-0121: the prompt NEVER travels as a CLI argument — a solomon
68
+ // prompt embeds the full diff, and large diffs blow past the kernel's
69
+ // per-argument limit (E2BIG). gemini reads the prompt from stdin in
70
+ // headless mode. Same bug's layer 2: headless gemini refuses untrusted
71
+ // workspaces unless GEMINI_CLI_TRUST_WORKSPACE is set.
72
+ const args = [];
73
+ if (mode === "review") args.push("--output-format", "json");
74
+ if (model) args.push("--model", model);
75
+ const res = await this.runCommand(resolveBin(this.cliBin), args, {
76
+ input: task.prompt,
77
+ env: this.spawnEnv,
78
+ onOutput: task.onOutput,
79
+ silenceTimeoutMs: task.silenceTimeoutMs,
80
+ timeout: task.timeoutMs
81
+ });
82
+ const usage = extractGeminiUsage(res.stdout) ?? extractGeminiUsage(res.stderr);
83
+ return { ok: res.exitCode === 0, output: res.stdout, error: res.stderr, exitCode: res.exitCode, ...usage };
84
+ }
85
+ }