@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,432 @@
1
+ // `kj install-tools` — install the external audit tools that `kj audit`
2
+ // and `kj webperf` rely on but does not bundle.
3
+ //
4
+ // Covers semgrep, osv-scanner, lighthouse, docker, sonar. Picks the
5
+ // install command per tool from `getInstallHint` (HU1) so the command
6
+ // matches the package manager available on the user's system.
7
+ //
8
+ // Lighthouse is stack-gated: by default we only prompt about it when
9
+ // the project is frontend/fullstack. Pass `--only lighthouse` to bypass
10
+ // the gate.
11
+ //
12
+ // Docker and sonar are special cases — see TOOL_HANDLERS below.
13
+
14
+ import readline from "node:readline";
15
+ import { execFile } from "node:child_process";
16
+ import { promisify } from "node:util";
17
+ import os from "node:os";
18
+ import path from "node:path";
19
+ import fs from "node:fs/promises";
20
+ import { checkBinary } from "../utils/agent-detect.js";
21
+ import { getInstallHint, detectPackageManagers, appliesToStack, gitInstallPlan } from "../utils/install-hints.js";
22
+ import { detectProjectStack } from "../utils/stack-detect.js";
23
+ import { resolveStandalone } from "../utils/binary-sources.js";
24
+ import { downloadBinary, binDir, runInstallCommand } from "../utils/tool-installer.js";
25
+ import { dockerInstallPlan } from "../utils/docker-install.js";
26
+ import { collectPending, renderPendingBlock, PENDING_EXIT_CODE } from "../utils/pending-user-action.js";
27
+
28
+ const execFileAsync = promisify(execFile);
29
+
30
+ // git and the agent CLI lead the list: both are `kj doctor` *required* tools,
31
+ // so a blank machine wants them before the optional audit tools.
32
+ // KJC-TSK-0657: rtk/squeezr/qmd (token+context optimizers, previously
33
+ // init-only) complete the list — one pass leaves the machine 100%
34
+ // operational, per the quality-default-on product rule.
35
+ const ALL_TOOLS = ["git", "agent-cli", "semgrep", "osv-scanner", "lighthouse", "docker", "sonar", "rtk", "squeezr", "qmd"];
36
+
37
+ // Optimizer tools reuse the init installers (same {ok, version, error} contract).
38
+ const OPTIMIZER_INSTALLERS = {
39
+ rtk: () => import("../utils/rtk-install.js").then((m) => m.installRtk),
40
+ squeezr: () => import("../utils/squeezr-install.js").then((m) => m.installSqueezr),
41
+ qmd: () => import("../utils/qmd-install.js").then((m) => m.installQmd),
42
+ };
43
+
44
+ // The default pipeline is coder=claude, reviewer=codex, so `agent-cli` installs
45
+ // exactly those two. gemini stays out of the default: it is a supported
46
+ // reviewer, not a required one. Both ship as global npm packages.
47
+ const AGENT_CLIS = [
48
+ { bin: "claude", pkg: "@anthropic-ai/claude-code", url: "https://docs.anthropic.com/en/docs/claude-code" },
49
+ { bin: "codex", pkg: "@openai/codex", url: "https://github.com/openai/codex" },
50
+ ];
51
+
52
+ function parseOnlyList(raw) {
53
+ if (!raw) return null;
54
+ const wanted = String(raw).split(",").map((t) => t.trim()).filter(Boolean);
55
+ const invalid = wanted.filter((t) => !ALL_TOOLS.includes(t));
56
+ if (invalid.length > 0) {
57
+ throw new Error(`Unknown tool(s) in --only: ${invalid.join(", ")}. Valid: ${ALL_TOOLS.join(", ")}`);
58
+ }
59
+ return wanted;
60
+ }
61
+
62
+ function promptYesNo(question, defaultYes = true) {
63
+ return new Promise((resolve) => {
64
+ if (!process.stdin.isTTY) { resolve(defaultYes); return; } // non-TTY → assume default
65
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
66
+ const hint = defaultYes ? "Y/n" : "y/N";
67
+ rl.question(`${question} [${hint}] `, (answer) => {
68
+ rl.close();
69
+ const a = (answer || "").trim().toLowerCase();
70
+ if (!a) return resolve(defaultYes);
71
+ resolve(a === "y" || a === "yes");
72
+ });
73
+ });
74
+ }
75
+
76
+ async function isInstalled(tool) {
77
+ // sonar is a Docker container, not a binary. We check it separately
78
+ // by looking at running containers via `docker ps`.
79
+ if (tool === "sonar") return checkSonarRunning();
80
+ // agent-cli is "installed" only when BOTH default-pipeline CLIs are present;
81
+ // a partial install (one of two) still routes to handleAgentCli.
82
+ if (tool === "agent-cli") {
83
+ const present = await Promise.all(AGENT_CLIS.map(async (c) => (await checkBinary(c.bin)).ok));
84
+ return present.every(Boolean);
85
+ }
86
+ if (tool === "lighthouse") return (await checkBinary("lighthouse")).ok;
87
+ if (tool === "docker") return (await checkBinary("docker")).ok;
88
+ return (await checkBinary(tool)).ok;
89
+ }
90
+
91
+ /**
92
+ * Agent-CLI handler. Installs whichever of the default-pipeline CLIs
93
+ * (coder=claude, reviewer=codex) are missing, via global npm. Opt-in with the
94
+ * exact command shown first. When npm itself is absent we surface the manual
95
+ * commands + URLs rather than failing silently.
96
+ */
97
+ async function handleAgentCli({ available, dryRun, yes, logger }) {
98
+ const missing = [];
99
+ for (const cli of AGENT_CLIS) {
100
+ if (!(await checkBinary(cli.bin)).ok) missing.push(cli);
101
+ }
102
+ if (missing.length === 0) return { tool: "agent-cli", action: "already-installed" };
103
+
104
+ if (!available.npm) {
105
+ const commands = missing.map((c) => `npm install -g ${c.pkg}`);
106
+ logger.warn?.(`✗ agent-cli: npm not found — install manually: ${commands.join(" ; ")}`);
107
+ return { tool: "agent-cli", action: "manual", reason: "npm not available", missing: missing.map((c) => c.bin), commands, manualUrls: missing.map((c) => c.url) };
108
+ }
109
+
110
+ const installed = [];
111
+ for (const cli of missing) {
112
+ const command = `npm install -g ${cli.pkg}`;
113
+ if (dryRun) {
114
+ logger.info?.(`▸ ${cli.bin}: would run \`${command}\``);
115
+ installed.push({ bin: cli.bin, action: "dry-run", command });
116
+ continue;
117
+ }
118
+ const proceed = yes || await promptYesNo(`Install ${cli.bin} with: ${command}?`);
119
+ if (!proceed) {
120
+ logger.info?.(`⊘ ${cli.bin}: declined`);
121
+ installed.push({ bin: cli.bin, action: "declined", command });
122
+ continue;
123
+ }
124
+ logger.info?.(`▸ ${cli.bin}: running \`${command}\`...`);
125
+ const r = await runInstallCommand(command, { interactive: false });
126
+ if (r.ok) { logger.info?.(`✓ ${cli.bin}: installed`); installed.push({ bin: cli.bin, action: "installed", command }); }
127
+ else { const err = r.error || `exit ${r.code}`; logger.warn?.(`✗ ${cli.bin}: install failed (${err})`); installed.push({ bin: cli.bin, action: "failed", command, error: err }); }
128
+ }
129
+
130
+ const action = dryRun ? "dry-run"
131
+ : installed.some((r) => r.action === "failed") ? "failed"
132
+ : installed.some((r) => r.action === "installed") ? "installed"
133
+ : "declined";
134
+ return { tool: "agent-cli", action, installed };
135
+ }
136
+
137
+ /**
138
+ * git handler. git installs through the OS package manager; on Linux that
139
+ * needs root, so those runs go through the interactive (tty-inherited) path
140
+ * where sudo prompts the user directly — kj never captures the password. The
141
+ * exact command is shown first and the install is opt-in, defaulting to NO for
142
+ * the privileged case. When no package manager matches, we surface the manual
143
+ * download URL instead of failing silently.
144
+ */
145
+ async function handleGit({ available, dryRun, yes, logger }) {
146
+ const plan = gitInstallPlan(available);
147
+ if (!plan.command) {
148
+ logger.warn?.(`✗ git: no supported package manager here — install manually: ${plan.manualUrl}`);
149
+ return { tool: "git", action: "manual", reason: "no supported package manager found", manualUrl: plan.manualUrl };
150
+ }
151
+ if (dryRun) {
152
+ logger.info?.(`▸ git: would run \`${plan.command}\` (manager: ${plan.manager}${plan.needsSudo ? ", needs sudo" : ""})`);
153
+ return { tool: "git", action: "dry-run", command: plan.command, manager: plan.manager };
154
+ }
155
+ if (plan.needsSudo) {
156
+ logger.warn?.("git: this step needs sudo — you'll be prompted for your password on your own terminal (kj never sees it).");
157
+ }
158
+ const proceed = yes || await promptYesNo(`Install git with: ${plan.command}?`, !plan.needsSudo);
159
+ if (!proceed) {
160
+ logger.info?.("⊘ git: declined");
161
+ return { tool: "git", action: "declined", command: plan.command };
162
+ }
163
+ logger.info?.(`▸ git: running \`${plan.command}\`...`);
164
+ const r = await runInstallCommand(plan.command, { interactive: plan.needsSudo });
165
+ if (r.ok) {
166
+ logger.info?.(`✓ git: installed via ${plan.manager}`);
167
+ return { tool: "git", action: "installed", manager: plan.manager };
168
+ }
169
+ const err = r.error || `exit ${r.code}`;
170
+ logger.warn?.(`✗ git: install failed (${err})`);
171
+ return { tool: "git", action: "failed", command: plan.command, error: err };
172
+ }
173
+
174
+ async function checkSonarRunning() {
175
+ try {
176
+ const { stdout } = await execFileAsync("docker", ["ps", "--format", "{{.Names}}", "--filter", "name=sonarqube"], { timeout: 5000 });
177
+ return stdout.trim().length > 0;
178
+ } catch { return false; }
179
+ }
180
+
181
+ async function runInstall(command) {
182
+ // Split on whitespace (commands here are simple, no nested quoting).
183
+ const [bin, ...args] = command.split(/\s+/);
184
+ try {
185
+ const { stdout, stderr } = await execFileAsync(bin, args, { timeout: 300_000, maxBuffer: 32 * 1024 * 1024 });
186
+ return { ok: true, stdout, stderr };
187
+ } catch (err) {
188
+ return { ok: false, error: err?.shortMessage || err?.message || String(err), stderr: err?.stderr };
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Sonar handler: special case. Cannot install Sonar via package manager;
194
+ * the install is `docker compose -f <our-template> up -d` if Docker is
195
+ * available. Falls back to a manual URL otherwise.
196
+ */
197
+ async function handleSonar({ available, dryRun }) {
198
+ if (!available.docker) {
199
+ return {
200
+ tool: "sonar",
201
+ action: "manual",
202
+ reason: "SonarQube runs as a Docker container — install Docker first (kj install-tools --only docker), then re-run.",
203
+ manualUrl: "https://docs.docker.com/get-docker/",
204
+ };
205
+ }
206
+ const composePath = path.join(process.cwd(), "docker-compose.yml");
207
+ let composeExists = false;
208
+ try { await fs.access(composePath); composeExists = true; } catch { /* missing */ }
209
+ const command = composeExists
210
+ ? "docker compose up -d"
211
+ : "docker run -d --name sonarqube -p 9000:9000 sonarqube:lts-community";
212
+ return {
213
+ tool: "sonar",
214
+ action: dryRun ? "dry-run" : "ready",
215
+ command,
216
+ manualUrl: "https://docs.sonarsource.com/sonarqube-community-build/try-out-sonarqube/",
217
+ };
218
+ }
219
+
220
+ /**
221
+ * Docker handler. On macOS/Windows we never auto-install — point at the docs.
222
+ * On Linux we offer the distro package manager (apt/dnf) or, failing that, the
223
+ * official convenience script downloaded to a file. The install runs through
224
+ * `sudo` on the user's own tty (kj never sees the password) and is opt-in,
225
+ * defaulting to NO, with the exact command shown first.
226
+ */
227
+ async function handleDocker({ available, dryRun, yes, logger }) {
228
+ const plan = dockerInstallPlan(process.platform, available);
229
+ if (plan.kind === "manual") {
230
+ logger.info?.(`▸ docker: ${plan.suggested || plan.manualUrl}`);
231
+ return { tool: "docker", action: "manual", reason: "Docker install is platform-specific — see official docs.", manualUrl: plan.manualUrl, suggested: plan.suggested };
232
+ }
233
+
234
+ const shownCommand = plan.kind === "package" ? plan.command : `sudo sh <get-docker.sh from ${plan.url}>`;
235
+ if (dryRun) {
236
+ logger.info?.(`▸ docker: would run \`${shownCommand}\` (invasive, needs sudo)`);
237
+ return { tool: "docker", action: "dry-run", command: shownCommand, via: plan.kind };
238
+ }
239
+
240
+ logger.warn?.("docker: this step is invasive and needs sudo — you'll be prompted for your password on your own terminal (kj never sees it).");
241
+ const proceed = yes || await promptYesNo(`Install Docker with: ${shownCommand}?`, false);
242
+ if (!proceed) {
243
+ logger.info?.("⊘ docker: declined");
244
+ return { tool: "docker", action: "declined", command: shownCommand };
245
+ }
246
+
247
+ if (plan.kind === "package") {
248
+ logger.info?.(`▸ docker: running \`${plan.command}\`...`);
249
+ const r = await runInstallCommand(plan.command, { interactive: true });
250
+ if (r.ok) { logger.info?.(`✓ docker: installed via ${plan.manager}`); return { tool: "docker", action: "installed", via: "package", manager: plan.manager }; }
251
+ const err = r.error || `exit ${r.code}`;
252
+ logger.warn?.(`✗ docker: install failed (${err})`);
253
+ return { tool: "docker", action: "failed", via: "package", error: err };
254
+ }
255
+
256
+ // script route: download to a temp file, then run with sudo — never `curl | sh`.
257
+ const dl = await downloadBinary({ url: plan.url, name: "get-docker.sh", dir: os.tmpdir() });
258
+ if (!dl.ok) { logger.warn?.(`✗ docker: script download failed (${dl.error})`); return { tool: "docker", action: "failed", via: "script", error: dl.error }; }
259
+ logger.info?.(`▸ docker: running \`sudo sh ${dl.dest}\`...`);
260
+ const r = await runInstallCommand(`sudo sh ${dl.dest}`, { interactive: true });
261
+ if (r.ok) { logger.info?.("✓ docker: installed via get.docker.com"); return { tool: "docker", action: "installed", via: "script", dest: dl.dest }; }
262
+ const err = r.error || `exit ${r.code}`;
263
+ logger.warn?.(`✗ docker: install failed (${err})`);
264
+ return { tool: "docker", action: "failed", via: "script", error: err };
265
+ }
266
+
267
+ /**
268
+ * Standalone route when no package manager matched: download a static binary
269
+ * (osv-scanner) or surface a concrete command to run (semgrep).
270
+ */
271
+ async function handleStandalone({ tool, standalone, manualUrl, dryRun, yes, logger }) {
272
+ if (standalone.kind === "command") {
273
+ logger.warn?.(`✗ ${tool}: no package manager here. Try: ${standalone.command} · \`kj doctor\` shows what's missing (docs: ${manualUrl})`);
274
+ return { tool, action: "manual", reason: "no package manager — suggested route below", suggested: standalone.command, via: standalone.via, manualUrl };
275
+ }
276
+ // kind === "binary"
277
+ const { url, name } = standalone;
278
+ if (dryRun) {
279
+ logger.info?.(`▸ ${tool}: would download ${url} → ${binDir()}`);
280
+ return { tool, action: "dry-run", command: `download ${url}`, via: "binary" };
281
+ }
282
+ const proceed = yes || await promptYesNo(`Download ${tool} from ${url} into ${binDir()}?`);
283
+ if (!proceed) {
284
+ logger.info?.(`⊘ ${tool}: declined`);
285
+ return { tool, action: "declined", command: `download ${url}` };
286
+ }
287
+ logger.info?.(`▸ ${tool}: downloading static binary...`);
288
+ const r = await downloadBinary({ url, name });
289
+ if (r.ok) {
290
+ logger.info?.(`✓ ${tool}: installed → ${r.dest} (ensure ${binDir()} is on PATH)`);
291
+ return { tool, action: "installed", via: "binary", dest: r.dest };
292
+ }
293
+ logger.warn?.(`✗ ${tool}: download failed (${r.error})`);
294
+ return { tool, action: "failed", via: "binary", error: r.error };
295
+ }
296
+
297
+ /**
298
+ * Plan + (optionally execute) the install of every requested tool.
299
+ *
300
+ * @param {Object} opts
301
+ * @param {string|null} [opts.only] - comma-separated subset
302
+ * @param {boolean} [opts.yes] - skip prompts
303
+ * @param {boolean} [opts.dryRun] - plan-only, no execution
304
+ * @param {Object} [opts.logger]
305
+ * @returns {Promise<{ results: Array<Object>, exitCode: number }>}
306
+ */
307
+ export async function installToolsCommand(opts = {}) {
308
+ const only = parseOnlyList(opts.only);
309
+ const yes = !!opts.yes;
310
+ const dryRun = !!opts.dryRun;
311
+ const logger = opts.logger || console;
312
+
313
+ const targetTools = only || ALL_TOOLS;
314
+ const available = await detectPackageManagers();
315
+ const stack = await detectProjectStack(process.cwd()).catch(() => null);
316
+
317
+ const results = [];
318
+ for (const tool of targetTools) {
319
+ // Stack gating — lighthouse on backend-only projects is skipped
320
+ // UNLESS the user passed --only lighthouse explicitly.
321
+ if (tool === "lighthouse" && !only && !appliesToStack(tool, stack)) {
322
+ results.push({ tool, action: "skipped", reason: "no frontend stack detected" });
323
+ logger.info?.(`⊘ ${tool}: skipped (no frontend stack detected; use --only lighthouse to force)`);
324
+ continue;
325
+ }
326
+
327
+ if (await isInstalled(tool)) {
328
+ results.push({ tool, action: "already-installed" });
329
+ logger.info?.(`✓ ${tool}: already installed`);
330
+ continue;
331
+ }
332
+
333
+ if (tool === "sonar") {
334
+ const result = await handleSonar({ available, dryRun });
335
+ results.push(result);
336
+ if (result.action === "manual") logger.warn?.(`✗ sonar: ${result.reason}`);
337
+ else logger.info?.(`▸ sonar: ${result.command}`);
338
+ if (!dryRun && result.action === "ready") {
339
+ const proceed = yes || await promptYesNo(`Start SonarQube with: ${result.command}?`);
340
+ if (proceed) {
341
+ const r = await runInstall(result.command);
342
+ result.action = r.ok ? "installed" : "failed";
343
+ if (!r.ok) result.error = r.error;
344
+ }
345
+ }
346
+ continue;
347
+ }
348
+
349
+ if (OPTIMIZER_INSTALLERS[tool]) {
350
+ if (dryRun) {
351
+ results.push({ tool, action: "planned", command: `kj install-tools --only ${tool}` });
352
+ logger.info?.(`▸ ${tool}: would install (init installer)`);
353
+ continue;
354
+ }
355
+ const proceed = yes || await promptYesNo(`Install ${tool}?`);
356
+ if (!proceed) { results.push({ tool, action: "declined" }); continue; }
357
+ const install = await OPTIMIZER_INSTALLERS[tool]();
358
+ const r = await install(logger);
359
+ results.push(r.ok
360
+ ? { tool, action: "installed", version: r.version }
361
+ : { tool, action: "failed", error: r.error });
362
+ continue;
363
+ }
364
+
365
+ if (tool === "git") {
366
+ const result = await handleGit({ available, dryRun, yes, logger });
367
+ results.push(result);
368
+ continue;
369
+ }
370
+
371
+ if (tool === "agent-cli") {
372
+ const result = await handleAgentCli({ available, dryRun, yes, logger });
373
+ results.push(result);
374
+ continue;
375
+ }
376
+
377
+ if (tool === "docker") {
378
+ const result = await handleDocker({ available, dryRun, yes, logger });
379
+ results.push(result);
380
+ continue;
381
+ }
382
+
383
+ // semgrep / osv-scanner / lighthouse — package-manager driven.
384
+ const hint = await getInstallHint(tool, available);
385
+ if (!hint.command || !hint.manager) {
386
+ // No package manager matched — try a standalone route (static binary
387
+ // for osv-scanner, a concrete command for semgrep) before giving up.
388
+ const standalone = resolveStandalone(tool, available);
389
+ if (standalone) {
390
+ const result = await handleStandalone({ tool, standalone, manualUrl: hint.manualUrl, dryRun, yes, logger });
391
+ results.push(result);
392
+ continue;
393
+ }
394
+ results.push({ tool, action: "manual", reason: "no compatible package manager found", manualUrl: hint.manualUrl, suggested: hint.command });
395
+ logger.warn?.(`✗ ${tool}: no automatic install route here. Run \`kj doctor\` for options, or install manually: ${hint.manualUrl}`);
396
+ continue;
397
+ }
398
+ if (dryRun) {
399
+ results.push({ tool, action: "dry-run", command: hint.command, manager: hint.manager });
400
+ logger.info?.(`▸ ${tool}: would run \`${hint.command}\` (manager: ${hint.manager})`);
401
+ continue;
402
+ }
403
+ const proceed = yes || await promptYesNo(`Install ${tool} with: ${hint.command}?`);
404
+ if (!proceed) {
405
+ results.push({ tool, action: "declined", command: hint.command });
406
+ logger.info?.(`⊘ ${tool}: declined`);
407
+ continue;
408
+ }
409
+ logger.info?.(`▸ ${tool}: running \`${hint.command}\`...`);
410
+ const r = await runInstall(hint.command);
411
+ if (r.ok) {
412
+ results.push({ tool, action: "installed", command: hint.command, manager: hint.manager });
413
+ logger.info?.(`✓ ${tool}: installed via ${hint.manager}`);
414
+ } else {
415
+ results.push({ tool, action: "failed", command: hint.command, error: r.error });
416
+ logger.warn?.(`✗ ${tool}: install failed (${r.error})`);
417
+ }
418
+ }
419
+
420
+ // KJC-TSK-0659 stop-on-sudo: anything that still needs the user's hands
421
+ // becomes ONE block with the exact per-OS commands, and a distinctive exit
422
+ // code so a driving agent stops and waits instead of continuing degraded.
423
+ const pending = dryRun ? [] : collectPending(results, { yes });
424
+ if (pending.length > 0) {
425
+ logger.warn?.(renderPendingBlock(pending));
426
+ return { results, pending, exitCode: PENDING_EXIT_CODE };
427
+ }
428
+ const failures = results.filter((r) => r.action === "failed").length;
429
+ return { results, pending, exitCode: failures > 0 ? 1 : 0 };
430
+ }
431
+
432
+ export const __test = { parseOnlyList, isInstalled, ALL_TOOLS };
@@ -0,0 +1,94 @@
1
+ /**
2
+ * `kj mutate` (KJC-TSK-0587) — diff-scoped mutation testing. Detects language
3
+ * (M-A registry) → changed lines (M-B diff-scope) → runs the tool (M-C runner)
4
+ * only over what you just touched. `--json` + exit code make it a gate. No
5
+ * silent fallback: unsupported language / unreadable report exit non-zero.
6
+ */
7
+
8
+ import fs from "node:fs/promises";
9
+ import path from "node:path";
10
+ import { detectProjectStack } from "../utils/stack-detect.js";
11
+ import { getMutationTool } from "../mutate/tool-registry.js";
12
+ import { getDiffScope } from "../mutate/diff-scope.js";
13
+ import { runMutation } from "../mutate/runner.js";
14
+
15
+ // Per-tool launch detail: subcommand + JSON report path. Tools without a JSON
16
+ // report leave `report` empty → the run surfaces the tool's own output.
17
+ const INVOCATION = {
18
+ stryker: { base: ["run"], report: "reports/mutation/mutation.json" },
19
+ mutmut: { base: ["run"], report: "mutmut-report.json" },
20
+ infection: { base: [], report: "infection.json" },
21
+ "go-mutesting": { base: [], report: "" },
22
+ pitest: { base: [], report: "" },
23
+ };
24
+
25
+ const DEFAULT_SINCE = "HEAD~1";
26
+
27
+ function makeReadReport(reportFile) {
28
+ if (!reportFile) return undefined;
29
+ return async () => JSON.parse(await fs.readFile(reportFile, "utf8"));
30
+ }
31
+
32
+ /**
33
+ * `deps` ({detectStack, diffScope, run}) is injectable so tests stay pure.
34
+ * `since` diffs against a git ref (default HEAD~1); `path` bypasses the diff.
35
+ * @returns {Promise<number>} process exit code
36
+ */
37
+ export async function mutateCommand({
38
+ projectDir = process.cwd(),
39
+ since,
40
+ path: pathArg,
41
+ json = false,
42
+ maxSurvivors = 0,
43
+ logger = console,
44
+ deps = {},
45
+ } = {}) {
46
+ const detectStack = deps.detectStack ?? detectProjectStack;
47
+ const diffScope = deps.diffScope ?? getDiffScope;
48
+ const run = deps.run ?? runMutation;
49
+ const say = (msg) => logger.info?.(msg);
50
+
51
+ const { language } = await detectStack(projectDir);
52
+ const tool = getMutationTool(language);
53
+ if (!tool.supported) {
54
+ say(`kj mutate: lenguaje no soportado (${language ?? "desconocido"}) — ${tool.reason}`);
55
+ return 2;
56
+ }
57
+
58
+ let scope;
59
+ if (pathArg) {
60
+ const args = tool.scope.flag ? [tool.scope.flag, pathArg] : [pathArg];
61
+ scope = { supported: true, empty: false, args };
62
+ } else {
63
+ scope = await diffScope({ since: since ?? DEFAULT_SINCE, language, projectDir });
64
+ }
65
+ if (scope.empty) {
66
+ say("kj mutate: nada que mutar en el diff.");
67
+ return 0;
68
+ }
69
+
70
+ const invocation = INVOCATION[tool.id] ?? { base: [], report: "" };
71
+ const reportFile = invocation.report ? path.join(projectDir, invocation.report) : "";
72
+ const outcome = await run({
73
+ binary: tool.binary,
74
+ args: [...invocation.base, ...scope.args],
75
+ cwd: projectDir,
76
+ readReport: makeReadReport(reportFile),
77
+ });
78
+
79
+ if (!outcome.result) {
80
+ say(`kj mutate: la herramienta ${tool.id} no produjo un informe legible.`);
81
+ if (outcome.stderr) say(outcome.stderr.trim());
82
+ return 1;
83
+ }
84
+
85
+ const { score, killed, total, survived } = outcome.result;
86
+ if (json) {
87
+ say(JSON.stringify(outcome.result));
88
+ } else {
89
+ say(`kj mutate (${tool.id}): score ${score ?? "n/a"}% — ${killed}/${total} mutantes cazados`);
90
+ for (const s of survived) say(` ✗ superviviente ${s.file}:${s.line} (${s.mutator ?? s.status})`);
91
+ if (survived.length === 0) say(" ✓ sin supervivientes");
92
+ }
93
+ return survived.length > maxSurvivors ? 1 : 0;
94
+ }
@@ -0,0 +1,44 @@
1
+ // KJC-TSK-0437 — `kj ollama [start|stop|status|pull <model>]` subcommand.
2
+ // Thin wrappers around src/rag/ollama-manager.js so the user can manage
3
+ // the RAG embedder lifecycle without touching docker compose.
4
+ import { ollamaUp, ollamaDown, isOllamaReachable, normalizeOllamaConfig } from "../rag/ollama-manager.js";
5
+ import { pullOllamaModel } from "../rag/ollama-capability.js";
6
+
7
+ function embedderCfg(config) {
8
+ return normalizeOllamaConfig(config?.rag?.embedder || {});
9
+ }
10
+
11
+ export async function ollamaStartCommand({ config, logger }) {
12
+ const result = await ollamaUp(config?.rag?.embedder || null);
13
+ if (result.exitCode !== 0) {
14
+ logger.warn(`ollama start failed: ${result.stderr || result.stdout}`);
15
+ return result;
16
+ }
17
+ logger.info(result.reusedHost ? `Reusing Ollama at ${result.reusedHost}` : "Ollama container started.");
18
+ return result;
19
+ }
20
+
21
+ export async function ollamaStopCommand({ config, logger }) {
22
+ const result = await ollamaDown(config?.rag?.embedder || null);
23
+ if (result.exitCode === 0) logger.info(result.stdout || "Ollama stopped.");
24
+ else logger.warn(`ollama stop failed: ${result.stderr || result.stdout}`);
25
+ return result;
26
+ }
27
+
28
+ export async function ollamaStatusCommand({ config, logger }) {
29
+ const cfg = embedderCfg(config);
30
+ const reachable = await isOllamaReachable(cfg.host, cfg.timeouts.healthcheckSeconds);
31
+ const status = { host: cfg.host, container: cfg.containerName, reachable };
32
+ if (reachable) logger.info(`Ollama reachable at ${cfg.host} (container ${cfg.containerName}).`);
33
+ else logger.warn(`Ollama not reachable at ${cfg.host}. Try \`kj ollama start\`.`);
34
+ return status;
35
+ }
36
+
37
+ export async function ollamaPullCommand({ model, config, logger }) {
38
+ if (!model) throw new Error("kj ollama pull: model argument required");
39
+ logger.info(`Pulling ${model} into ${embedderCfg(config).containerName}...`);
40
+ const result = await pullOllamaModel(model, { containerName: embedderCfg(config).containerName });
41
+ if (result.exitCode === 0) logger.info(` -> ${model} ready.`);
42
+ else logger.warn(`pull ${model} failed: ${result.stderr || result.stdout}`);
43
+ return result;
44
+ }
@@ -0,0 +1,45 @@
1
+ // KJC-TSK-0384 PR 2 — `kj onboard` runs collectors + optional LLM synth.
2
+ import { writeFile, mkdir, readFile } from "node:fs/promises";
3
+ import { existsSync } from "node:fs";
4
+ import path from "node:path";
5
+ import { collectAll } from "../onboarder/collectors/index.js";
6
+ import { OnboarderRole } from "../roles/onboarder-role.js";
7
+ import { resolveRole } from "../config.js";
8
+ import { getKarajanHome } from "../utils/paths.js";
9
+ import { getTemplatesRoot } from "../utils/templates-root.js";
10
+
11
+ export function briefPath(projectDir) {
12
+ const slug = path.basename(projectDir).replace(/[^a-zA-Z0-9._-]/g, "-").toLowerCase() || "project";
13
+ return path.join(getKarajanHome(), "onboarding", `${slug}.md`);
14
+ }
15
+
16
+ export async function onboardCommand({ config, logger, flags = {} }) {
17
+ const projectDir = config?.projectDir || process.cwd();
18
+ const out = flags.output || briefPath(projectDir);
19
+ // KJC-BUG-0061 Bug A: Commander maps `--no-synth` to `flags.synth=false`,
20
+ // not `flags.noSynth=true`. Accept both shapes so callers from Commander
21
+ // and direct API callers behave the same.
22
+ const skipSynth = flags.noSynth === true || flags.synth === false;
23
+ logger.info(`[onboard] collecting from ${projectDir}`);
24
+ const bundle = await collectAll(projectDir);
25
+ await mkdir(path.dirname(out), { recursive: true });
26
+ if (skipSynth) {
27
+ await writeFile(out, `# Onboarding bundle — ${projectDir}\n\n\`\`\`json\n${JSON.stringify(bundle, null, 2)}\n\`\`\`\n`, "utf8");
28
+ logger.info(`[onboard] wrote raw bundle to ${out} (--no-synth)`);
29
+ return { path: out, bundle, brief: null };
30
+ }
31
+ const roleCfg = resolveRole(config, "onboarder") || resolveRole(config, "architect");
32
+ const templatePath = path.join(getTemplatesRoot(), "roles", "onboarder.md");
33
+ const instructions = existsSync(templatePath) ? await readFile(templatePath, "utf8") : "";
34
+ const role = new OnboarderRole({ instructions, config, ...roleCfg });
35
+ // KJC-BUG-0061 Bug B: BaseRole.run() refuses to execute unless init()
36
+ // has populated the agent / template context. The plan-generate path
37
+ // already does this; the onboard command was relying on a code path
38
+ // that never ran in isolation.
39
+ await role.init({ projectDir });
40
+ const result = await role.run({ bundle });
41
+ const brief = result?.result?.brief || "# Project is greenfield\n\nNo synth output.\n";
42
+ await writeFile(out, brief, "utf8");
43
+ logger.info(`[onboard] wrote Architecture Brief to ${out}`);
44
+ return { path: out, bundle, brief };
45
+ }
@@ -0,0 +1,55 @@
1
+ // ---- Formatting helpers shared across plan sub-commands ----
2
+
3
+ export function formatPlan(plan) {
4
+ const lines = [];
5
+ if (plan.approach) lines.push("## Approach", plan.approach, "");
6
+ if (plan.steps?.length) {
7
+ lines.push("## Steps");
8
+ for (let i = 0; i < plan.steps.length; i++) {
9
+ const step = plan.steps[i];
10
+ const commit = step.commit ? ` → \`${step.commit}\`` : "";
11
+ lines.push(`${i + 1}. ${step.description}${commit}`);
12
+ }
13
+ lines.push("");
14
+ }
15
+ if (plan.risks?.length) {
16
+ lines.push("## Risks");
17
+ for (const risk of plan.risks) lines.push(`- ${risk}`);
18
+ lines.push("");
19
+ }
20
+ if (plan.outOfScope?.length) {
21
+ lines.push("## Out of scope");
22
+ for (const item of plan.outOfScope) lines.push(`- ${item}`);
23
+ lines.push("");
24
+ }
25
+ return lines.join("\n");
26
+ }
27
+
28
+ /**
29
+ * Convierte el id largo de una dep `hu_<planId>_<NNN>` al short_id de
30
+ * la HU correspondiente cuando lo conoce. Mantiene el id largo si la
31
+ * dep no tiene short_id (HU legacy pre-humanización).
32
+ */
33
+ function displayDep(depId, hus) {
34
+ const hu = hus.find((h) => h.id === depId);
35
+ if (hu?.short_id) return hu.short_id;
36
+ return depId;
37
+ }
38
+
39
+ export function formatHuTable(hus) {
40
+ if (!hus?.length) return " (no HUs)";
41
+ // Humanización IDs: si la HU tiene short_id ("INFRA-001"), úsalo
42
+ // como columna ID; si no, fallback al id largo (HU legacy).
43
+ const idOf = (h) => h.short_id || h.id;
44
+ const lines = [];
45
+ const maxId = Math.min(20, Math.max(...hus.map((h) => idOf(h).length || 0)));
46
+ const maxTitle = Math.min(50, Math.max(...hus.map(h => h.title?.length || 0)));
47
+ lines.push(` ${"ID".padEnd(maxId)} ${"Title".padEnd(maxTitle)} ${"Type".padEnd(10)} ${"Status".padEnd(10)} Deps`);
48
+ lines.push(` ${"─".repeat(maxId)} ${"─".repeat(maxTitle)} ${"─".repeat(10)} ${"─".repeat(10)} ${"─".repeat(15)}`);
49
+ for (const hu of hus) {
50
+ const title = (hu.title || "").slice(0, maxTitle).padEnd(maxTitle);
51
+ const deps = (hu.blocked_by || []).map((d) => displayDep(d, hus)).join(", ") || "—";
52
+ lines.push(` ${idOf(hu).padEnd(maxId)} ${title} ${(hu.task_type || "sw").padEnd(10)} ${(hu.status || "?").padEnd(10)} ${deps}`);
53
+ }
54
+ return lines.join("\n");
55
+ }