@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,307 @@
1
+ import fs from "node:fs";
2
+ import fsPromises from "node:fs/promises";
3
+ import path from "node:path";
4
+ import os from "node:os";
5
+ import { runCommand } from "../utils/process.js";
6
+ import { sonarUp } from "./manager.js";
7
+ import { resolveSonarProjectKey } from "./project-key.js";
8
+ import {
9
+ resolveSonarHost,
10
+ resolveSonarToken as resolveToken,
11
+ resolveSonarCredentials,
12
+ } from "./config-resolver.js";
13
+
14
+ async function findNativeSonarScanner() {
15
+ const which = await runCommand("sh", ["-c", "command -v sonar-scanner"]);
16
+ if (which.exitCode === 0) {
17
+ const bin = String(which.stdout || "").trim();
18
+ if (bin) return bin;
19
+ }
20
+ return null;
21
+ }
22
+
23
+ export async function pickSonarScanner(scannerConfig = {}, deps = {}) {
24
+ const find = deps.findNativeSonarScanner || findNativeSonarScanner;
25
+ const arch = deps.arch || os.arch();
26
+ const forced = String(scannerConfig.command || "").trim().toLowerCase();
27
+ if (forced === "docker") return { type: "docker" };
28
+ if (forced === "native" || forced === "sonar-scanner" || forced.endsWith("/sonar-scanner")) {
29
+ const bin = (forced === "native" || forced === "sonar-scanner") ? await find() : forced;
30
+ if (bin) return { type: "native", bin };
31
+ return { type: "docker", reason: "native sonar-scanner not found on PATH" };
32
+ }
33
+ if (arch === "arm64") {
34
+ const bin = await find();
35
+ if (bin) return { type: "native", bin, reason: "ARM64 host — preferring native binary" };
36
+ }
37
+ return { type: "docker" };
38
+ }
39
+
40
+ export function buildScannerOpts(projectKey, scanner = {}) {
41
+ const opts = [`-Dsonar.projectKey=${projectKey}`];
42
+ if (scanner.sources) opts.push(`-Dsonar.sources=${scanner.sources}`);
43
+ if (scanner.exclusions) opts.push(`-Dsonar.exclusions=${scanner.exclusions}`);
44
+ if (scanner.test_inclusions) opts.push(`-Dsonar.test.inclusions=${scanner.test_inclusions}`);
45
+ if (scanner.coverage_exclusions) opts.push(`-Dsonar.coverage.exclusions=${scanner.coverage_exclusions}`);
46
+ if (scanner.javascript_lcov_report_paths) {
47
+ opts.push(`-Dsonar.javascript.lcov.reportPaths=${scanner.javascript_lcov_report_paths}`);
48
+ }
49
+ const rules = scanner.disabled_rules || [];
50
+ if (rules.length > 0) {
51
+ // KJC-BUG-0139: the list property is declared ONCE — one `multicriteria=`
52
+ // per rule was last-wins, silently un-ignoring every rule but the final one.
53
+ opts.push(`-Dsonar.issue.ignore.multicriteria=${rules.map((_, i) => `e${i + 1}`).join(",")}`);
54
+ rules.forEach((rule, i) => {
55
+ opts.push(
56
+ `-Dsonar.issue.ignore.multicriteria.e${i + 1}.ruleKey=${rule}`,
57
+ `-Dsonar.issue.ignore.multicriteria.e${i + 1}.resourceKey=**/*`
58
+ );
59
+ });
60
+ }
61
+ return opts.join(" ");
62
+ }
63
+
64
+ function normalizeScannerConfig(scanner = {}) {
65
+ const out = { ...scanner };
66
+ if (typeof out.sources === "string" && out.sources.trim()) {
67
+ const existing = out.sources
68
+ .split(",")
69
+ .map((s) => s.trim())
70
+ .filter(Boolean)
71
+ .filter((entry) => fs.existsSync(entry));
72
+
73
+ if (existing.length > 0) {
74
+ out.sources = existing.join(",");
75
+ }
76
+ }
77
+ return out;
78
+ }
79
+
80
+ function parseJsonSafe(text) {
81
+ try {
82
+ return JSON.parse(text);
83
+ } catch { /* invalid JSON */
84
+ return null;
85
+ }
86
+ }
87
+
88
+ async function validateAdminCredentials(host, user, password) {
89
+ const res = await runCommand("curl", [
90
+ "-sS",
91
+ "-u",
92
+ `${user}:${password}`,
93
+ `${host}/api/authentication/validate`
94
+ ]);
95
+ if (res.exitCode !== 0) return false;
96
+ const parsed = parseJsonSafe(res.stdout);
97
+ return Boolean(parsed?.valid);
98
+ }
99
+
100
+ async function generateUserToken(host, user, password) {
101
+ const tokenName = `karajan-${Date.now()}`;
102
+ const res = await runCommand("curl", [
103
+ "-sS",
104
+ "-u",
105
+ `${user}:${password}`,
106
+ "-X",
107
+ "POST",
108
+ "--data-urlencode",
109
+ `name=${tokenName}`,
110
+ `${host}/api/user_tokens/generate`
111
+ ]);
112
+ if (res.exitCode !== 0) return null;
113
+ const parsed = parseJsonSafe(res.stdout);
114
+ return parsed?.token || null;
115
+ }
116
+
117
+ function coverageConfig(config) {
118
+ return config?.sonarqube?.coverage || {};
119
+ }
120
+
121
+ function checkLcovExists(lcovPath, blockOnFailure, stdout = "") {
122
+ if (!lcovPath) return { ok: true, scannerPatch: {} };
123
+ if (fs.existsSync(lcovPath)) {
124
+ return { ok: true, scannerPatch: { javascript_lcov_report_paths: lcovPath } };
125
+ }
126
+ if (blockOnFailure) {
127
+ return { ok: false, exitCode: 1, stdout, stderr: `Configured lcov report path does not exist: ${lcovPath}` };
128
+ }
129
+ return { ok: true, scannerPatch: {} };
130
+ }
131
+
132
+ function handleCoverageNoCommand(lcovPath, blockOnFailure) {
133
+ if (!lcovPath) {
134
+ return {
135
+ ok: false, exitCode: 1, stdout: "",
136
+ stderr: "Sonar coverage is enabled but neither coverage.command nor coverage.lcov_report_path is configured."
137
+ };
138
+ }
139
+ return checkLcovExists(lcovPath, blockOnFailure);
140
+ }
141
+
142
+ async function maybeRunCoverage(config) {
143
+ const coverage = coverageConfig(config);
144
+ if (!coverage.enabled) return { ok: true, scannerPatch: {} };
145
+
146
+ const lcovPath = String(coverage.lcov_report_path || "").trim();
147
+ const blockOnFailure = coverage.block_on_failure !== false;
148
+
149
+ if (!String(coverage.command || "").trim()) {
150
+ return handleCoverageNoCommand(lcovPath, blockOnFailure);
151
+ }
152
+
153
+ const command = String(coverage.command || "").trim();
154
+ const timeout = Number(coverage.timeout_ms) > 0 ? Number(coverage.timeout_ms) : 5 * 60 * 1000;
155
+ const run = await runCommand("bash", ["-lc", command], { timeout });
156
+
157
+ if (run.exitCode !== 0) {
158
+ if (blockOnFailure) {
159
+ return { ok: false, exitCode: run.exitCode, stdout: run.stdout || "", stderr: run.stderr || "Coverage command failed" };
160
+ }
161
+ return { ok: true, scannerPatch: {} };
162
+ }
163
+
164
+ return checkLcovExists(lcovPath, blockOnFailure, run.stdout || "");
165
+ }
166
+
167
+ async function resolveSonarTokenWithFallback(config, apiHost) {
168
+ const explicitToken = resolveToken(config);
169
+ if (explicitToken) return explicitToken;
170
+
171
+ const { user: adminUser, passwords } = await resolveSonarCredentials(config);
172
+ if (!adminUser || passwords.length === 0) return null;
173
+
174
+ for (const password of passwords) {
175
+ const valid = await validateAdminCredentials(apiHost, adminUser, password);
176
+ if (!valid) continue;
177
+ const token = await generateUserToken(apiHost, adminUser, password);
178
+ if (token) return token;
179
+ }
180
+
181
+ return null;
182
+ }
183
+
184
+ async function ensureSonarProjectProperties(cwd = process.cwd()) {
185
+ const propsPath = path.join(cwd, "sonar-project.properties");
186
+ try {
187
+ await fsPromises.access(propsPath);
188
+ return; // already exists
189
+ } catch {
190
+ // Auto-generate based on project structure
191
+ let pkg = {};
192
+ try {
193
+ const raw = await fsPromises.readFile(path.join(cwd, "package.json"), "utf8");
194
+ pkg = JSON.parse(raw);
195
+ } catch {
196
+ // no package.json or invalid JSON — use defaults
197
+ }
198
+ const projectKey = (pkg.name || path.basename(cwd)).replaceAll(/[^a-zA-Z0-9_.-]/g, "_");
199
+ const props = [
200
+ `sonar.projectKey=${projectKey}`,
201
+ `sonar.projectName=${pkg.name || path.basename(cwd)}`,
202
+ `sonar.sources=src`,
203
+ `sonar.tests=tests`,
204
+ `sonar.javascript.lcov.reportPaths=coverage/lcov.info`,
205
+ `sonar.exclusions=**/node_modules/**,**/dist/**,**/build/**,**/coverage/**`,
206
+ ].join("\n");
207
+ await fsPromises.writeFile(propsPath, props + "\n", "utf8");
208
+ }
209
+ }
210
+
211
+ export async function runSonarScan(config, projectKey = null) {
212
+ let effectiveProjectKey;
213
+ try {
214
+ effectiveProjectKey = await resolveSonarProjectKey(config, { projectKey });
215
+ } catch (error) {
216
+ return {
217
+ ok: false,
218
+ projectKey: null,
219
+ stdout: "",
220
+ stderr: error?.message || String(error),
221
+ exitCode: 1
222
+ };
223
+ }
224
+ const sonarConfig = config?.sonarqube || {};
225
+ const rawHost = sonarConfig.host || "http://localhost:9000";
226
+ const isExternalSonar = sonarConfig.external === true;
227
+ const scannerTimeout = Number(sonarConfig?.timeouts?.scanner_ms) > 0
228
+ ? Number(sonarConfig.timeouts.scanner_ms)
229
+ : 15 * 60 * 1000;
230
+ const sonarNetwork = sonarConfig.network || "karajan_sonar_net";
231
+ const apiHost = resolveSonarHost(rawHost);
232
+ const isLocalHost = /localhost|127\.0\.0\.1/.test(rawHost);
233
+ const host = isLocalHost ? rawHost.replaceAll(/localhost|127\.0\.0\.1/g, "host.docker.internal") : rawHost;
234
+
235
+ const start = await sonarUp(rawHost);
236
+ if (start.exitCode !== 0) {
237
+ return {
238
+ ok: false,
239
+ stdout: start.stdout || "",
240
+ stderr: start.stderr || "Failed to start SonarQube service",
241
+ exitCode: start.exitCode
242
+ };
243
+ }
244
+ await ensureSonarProjectProperties();
245
+ const token = await resolveSonarTokenWithFallback(config, apiHost);
246
+ if (!token) {
247
+ return {
248
+ ok: false,
249
+ stdout: "",
250
+ stderr:
251
+ "Unable to resolve Sonar token. Fix: (1) run 'kj init' to configure it, (2) set KJ_SONAR_TOKEN env var, or (3) add sonarqube.token to ~/.karajan/kj.config.yml.",
252
+ exitCode: 1
253
+ };
254
+ }
255
+ process.env.KJ_SONAR_TOKEN = token;
256
+ const coverage = await maybeRunCoverage(config);
257
+ if (!coverage.ok) {
258
+ return {
259
+ ok: false,
260
+ stdout: coverage.stdout || "",
261
+ stderr: coverage.stderr || "Failed to generate coverage report for SonarQube",
262
+ exitCode: coverage.exitCode || 1
263
+ };
264
+ }
265
+ const scannerConfig = normalizeScannerConfig({
266
+ ...sonarConfig.scanner,
267
+ ...coverage.scannerPatch
268
+ });
269
+
270
+ const pick = await pickSonarScanner(sonarConfig.scanner);
271
+ const env = {
272
+ ...process.env,
273
+ SONAR_HOST_URL: pick.type === "native" ? rawHost : host,
274
+ SONAR_TOKEN: token || "",
275
+ SONAR_SCANNER_OPTS: buildScannerOpts(effectiveProjectKey, scannerConfig)
276
+ };
277
+
278
+ let cmd, args;
279
+ if (pick.type === "native") {
280
+ cmd = pick.bin;
281
+ args = [];
282
+ } else {
283
+ cmd = "docker";
284
+ args = [
285
+ "run",
286
+ "--rm",
287
+ "-v",
288
+ `${process.cwd()}:/usr/src`,
289
+ ...(isLocalHost ? ["--add-host", "host.docker.internal:host-gateway"] : []),
290
+ ...(isLocalHost || isExternalSonar ? [] : ["--network", sonarNetwork]),
291
+ "-e", "SONAR_HOST_URL",
292
+ "-e", "SONAR_TOKEN",
293
+ "-e", "SONAR_SCANNER_OPTS",
294
+ "sonarsource/sonar-scanner-cli"
295
+ ];
296
+ }
297
+
298
+ const result = await runCommand(cmd, args, { timeout: scannerTimeout, env });
299
+ return {
300
+ ok: result.exitCode === 0,
301
+ projectKey: effectiveProjectKey,
302
+ scanner: pick.type,
303
+ stdout: result.stdout,
304
+ stderr: result.stderr,
305
+ exitCode: result.exitCode
306
+ };
307
+ }
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Sonar token bootstrap (KJC-TSK-0367).
3
+ *
4
+ * On `kj init`, after the SonarQube container is up, try to generate the
5
+ * analysis token via the REST API instead of asking the user to walk
6
+ * through the web UI manually. The bootstrapper:
7
+ *
8
+ * 1. Probes the host with admin/admin (the container default).
9
+ * 2. If login succeeds AND the password is still default, it rotates
10
+ * the password to a freshly generated value and persists that to
11
+ * `~/.karajan/sonar.admin-password` (mode 0600). This avoids
12
+ * leaving the deployment with the well-known default credentials.
13
+ * 3. Generates a Global Analysis Token named `karajan-cli` via
14
+ * `POST /api/user_tokens/generate` and returns it.
15
+ * 4. Persists the token to `~/.karajan/sonar.token` (mode 0600). The
16
+ * caller writes it into `config.sonarqube.token` separately.
17
+ *
18
+ * On any HTTP error, we return `{ ok: false, reason }` and let the
19
+ * caller fall back to the manual-flow instructions that existed before
20
+ * this card. We never throw — the wizard must keep going.
21
+ */
22
+
23
+ import { writeFileSync, chmodSync, mkdirSync } from "node:fs";
24
+ import { dirname } from "node:path";
25
+ import { randomBytes } from "node:crypto";
26
+ import { getKarajanHome } from "../utils/paths.js";
27
+ import path from "node:path";
28
+
29
+ const DEFAULT_USER = "admin";
30
+ const DEFAULT_PASS = "admin";
31
+ const TOKEN_NAME = "karajan-cli";
32
+
33
+ /**
34
+ * @param {string} host e.g. http://localhost:9000
35
+ * @param {string} pathname "/api/..."
36
+ * @param {object} opts
37
+ * @param {"GET"|"POST"} [opts.method="GET"]
38
+ * @param {Record<string,string>} [opts.form] form-urlencoded body
39
+ * @param {string} opts.user
40
+ * @param {string} opts.pass
41
+ * @returns {Promise<{ status: number, body: any }>}
42
+ */
43
+ async function call(host, pathname, opts) {
44
+ const url = host.replace(/\/$/, "") + pathname;
45
+ const headers = {
46
+ Authorization: "Basic " + Buffer.from(`${opts.user}:${opts.pass}`).toString("base64"),
47
+ };
48
+ let body;
49
+ if (opts.method === "POST" && opts.form) {
50
+ headers["Content-Type"] = "application/x-www-form-urlencoded";
51
+ body = new URLSearchParams(opts.form).toString();
52
+ }
53
+ const res = await fetch(url, { method: opts.method || "GET", headers, body });
54
+ let parsed = null;
55
+ const text = await res.text();
56
+ if (text) {
57
+ try { parsed = JSON.parse(text); } catch { parsed = text; }
58
+ }
59
+ return { status: res.status, body: parsed };
60
+ }
61
+
62
+ /**
63
+ * Generate a strong (32-byte hex) random password. We never re-use the
64
+ * default `admin` password and never persist `admin` to disk.
65
+ */
66
+ function generatePassword() {
67
+ return randomBytes(24).toString("base64url");
68
+ }
69
+
70
+ /**
71
+ * Persist a value at `~/.karajan/<filename>` with mode 0600.
72
+ */
73
+ function persistSecret(filename, value) {
74
+ const home = getKarajanHome();
75
+ const target = path.join(home, filename);
76
+ mkdirSync(dirname(target), { recursive: true });
77
+ writeFileSync(target, value, { mode: 0o600 });
78
+ try { chmodSync(target, 0o600); } catch { /* best-effort on Windows */ }
79
+ return target;
80
+ }
81
+
82
+ /**
83
+ * Bootstrap the Sonar analysis token. Idempotent: if the
84
+ * `karajan-cli` token already exists Sonar will refuse to create it
85
+ * again (HTTP 400 with `"errors":[{"msg":"A user token...exists"}]`).
86
+ * In that case we delete-and-recreate so kj init always returns a
87
+ * usable token.
88
+ *
89
+ * @param {object} opts
90
+ * @param {string} opts.host Default http://localhost:9000
91
+ * @returns {Promise<{
92
+ * ok: boolean,
93
+ * token?: string,
94
+ * savedTo?: string,
95
+ * passwordRotated?: boolean,
96
+ * passwordSavedTo?: string,
97
+ * reason?: string,
98
+ * }>}
99
+ */
100
+ export async function bootstrapSonarToken({ host = "http://localhost:9000" } = {}) {
101
+ let user = DEFAULT_USER;
102
+ let pass = DEFAULT_PASS;
103
+ let passwordRotated = false;
104
+ let passwordSavedTo = null;
105
+ // KJC-BUG-0035: capture rotation failures so the caller can log them
106
+ // instead of swallowing the error silently. Pre-fix the rotation
107
+ // returned status 403/500/network were ignored and the user never knew.
108
+ let passwordRotationError = null;
109
+
110
+ // 1) Probe: does admin/admin still work?
111
+ const probe = await call(host, "/api/authentication/validate", { user, pass }).catch((err) => ({ status: 0, body: err.message }));
112
+ if (probe.status === 0) {
113
+ return { ok: false, reason: `network error reaching ${host}: ${probe.body}` };
114
+ }
115
+ if (probe.status === 401 || (probe.body && probe.body.valid === false)) {
116
+ return { ok: false, reason: "admin/admin login failed (password may already be customized — set sonarqube.token manually)" };
117
+ }
118
+
119
+ // 2) Rotate the default password if it's still in place. This stops
120
+ // the well-known credentials from being a stable attack surface
121
+ // on this user's machine.
122
+ const newPass = generatePassword();
123
+ const change = await call(host, "/api/users/change_password", {
124
+ method: "POST",
125
+ user,
126
+ pass,
127
+ form: { login: user, previousPassword: pass, password: newPass },
128
+ }).catch((err) => ({ status: 0, body: err.message }));
129
+
130
+ if (change.status === 204 || change.status === 200) {
131
+ pass = newPass;
132
+ passwordRotated = true;
133
+ passwordSavedTo = persistSecret("sonar.admin-password", pass);
134
+ } else if (change.status === 400 && Array.isArray(change.body?.errors)
135
+ && change.body.errors.some(e => /default/i.test(e.msg))) {
136
+ // Sonar complained the password matches a default — rare. Try with
137
+ // a longer one.
138
+ const longerPass = generatePassword() + generatePassword();
139
+ const retry = await call(host, "/api/users/change_password", {
140
+ method: "POST",
141
+ user,
142
+ pass,
143
+ form: { login: user, previousPassword: pass, password: longerPass },
144
+ }).catch(() => ({ status: 0 }));
145
+ if (retry.status === 204 || retry.status === 200) {
146
+ pass = longerPass;
147
+ passwordRotated = true;
148
+ passwordSavedTo = persistSecret("sonar.admin-password", pass);
149
+ } else {
150
+ passwordRotationError = `rotate-retry HTTP ${retry.status}: ${detailFromBody(retry.body)}`;
151
+ }
152
+ } else if (change.status === 401) {
153
+ // Expected: admin already changed password — not a failure.
154
+ } else if (change.status !== 0) {
155
+ // KJC-BUG-0035: ANY other non-success status (403, 500, 400-without-default, …)
156
+ // means the rotation didn't happen and admin/admin is still live on disk.
157
+ // Surface it via the result so the caller can warn the user.
158
+ passwordRotationError = `rotate HTTP ${change.status}: ${detailFromBody(change.body)}`;
159
+ } else {
160
+ // status === 0: network error before getting any HTTP response.
161
+ passwordRotationError = `rotate network error: ${detailFromBody(change.body)}`;
162
+ }
163
+
164
+ // 3) Token already exists? Revoke it so we can re-create cleanly.
165
+ await call(host, "/api/user_tokens/revoke", {
166
+ method: "POST",
167
+ user,
168
+ pass,
169
+ form: { name: TOKEN_NAME },
170
+ }).catch(() => { /* token may not exist — that's fine */ });
171
+
172
+ // 4) Generate the analysis token.
173
+ const gen = await call(host, "/api/user_tokens/generate", {
174
+ method: "POST",
175
+ user,
176
+ pass,
177
+ form: { name: TOKEN_NAME, type: "GLOBAL_ANALYSIS_TOKEN" },
178
+ }).catch((err) => ({ status: 0, body: err.message }));
179
+
180
+ if (gen.status !== 200 || !gen.body?.token) {
181
+ const detail = gen.body?.errors?.[0]?.msg || gen.body || `HTTP ${gen.status}`;
182
+ return { ok: false, reason: `token generation failed: ${detail}`, passwordRotated, passwordSavedTo };
183
+ }
184
+
185
+ const token = gen.body.token;
186
+ const savedTo = persistSecret("sonar.token", token);
187
+
188
+ return {
189
+ ok: true,
190
+ token,
191
+ savedTo,
192
+ passwordRotated,
193
+ passwordSavedTo: passwordRotated ? passwordSavedTo : null,
194
+ passwordRotationError,
195
+ };
196
+ }
197
+
198
+ function detailFromBody(body) {
199
+ if (!body) return "no body";
200
+ if (typeof body === "string") return body.slice(0, 200);
201
+ if (Array.isArray(body.errors)) {
202
+ return body.errors.map(e => e?.msg || JSON.stringify(e)).join("; ").slice(0, 200);
203
+ }
204
+ try { return JSON.stringify(body).slice(0, 200); } catch { return "(unserialisable)"; }
205
+ }
@@ -0,0 +1,67 @@
1
+ // Sonar 401 auto-recovery — KJC-BUG-0057 / v2.19.2.
2
+ //
3
+ // When the Sonar API returns 401 (token missing / invalid / expired / revoked)
4
+ // and we still have admin/admin (or a user/pass we saved at `kj init` time),
5
+ // regenerate the analysis token VIA THE REST API and persist it. The caller
6
+ // retries the original request once with the new token.
7
+ //
8
+ // Programmatic, no LLM involvement. The whole point of the user feedback was
9
+ // that this is plumbing, not a decision: Karajan has the credentials to
10
+ // re-bootstrap and should do it without asking.
11
+
12
+ import { bootstrapSonarToken } from "./token-bootstrap.js";
13
+ import { saveSonarToken } from "./credentials.js";
14
+
15
+ // Per-process latch: a single Sonar run that 401s on every endpoint must
16
+ // only trigger ONE bootstrap attempt — not one per endpoint. The latch
17
+ // resets when the process exits.
18
+ let _recoveryAttempted = false;
19
+
20
+ /**
21
+ * @internal Reset the latch between tests (mirrors __resetKjHomeWarningForTests).
22
+ */
23
+ export function __resetSonarRecoveryLatchForTests() {
24
+ _recoveryAttempted = false;
25
+ }
26
+
27
+ /**
28
+ * Attempt to re-bootstrap the Sonar token after a 401. Mutates `config`
29
+ * in place when successful so the immediate retry picks up the new token.
30
+ *
31
+ * @param {object} config Karajan config (will be mutated on success).
32
+ * @param {object} [logger]
33
+ * @returns {Promise<{ ok: boolean, reason?: string, newToken?: string, savedTo?: string, passwordRotated?: boolean }>}
34
+ */
35
+ export async function recoverSonarToken(config, logger = null) {
36
+ if (_recoveryAttempted) {
37
+ return { ok: false, reason: "recovery already attempted in this process" };
38
+ }
39
+ _recoveryAttempted = true;
40
+
41
+ const host = config?.sonarqube?.host || "http://localhost:9000";
42
+ logger?.warn?.(`[sonar-recovery] 401 from ${host} — attempting token re-bootstrap`);
43
+
44
+ const result = await bootstrapSonarToken({ host });
45
+
46
+ if (!result.ok) {
47
+ logger?.warn?.(`[sonar-recovery] bootstrap failed: ${result.reason}`);
48
+ return { ok: false, reason: result.reason };
49
+ }
50
+
51
+ // Mutate the in-memory config so the caller's retry sees the new token.
52
+ config.sonarqube = config.sonarqube || {};
53
+ config.sonarqube.token = result.token;
54
+
55
+ // Also persist to ~/.karajan/sonar-credentials.json so future processes
56
+ // pick it up via the normal config-resolver chain instead of triggering
57
+ // recovery again. token-bootstrap.js already wrote sonar.token; this
58
+ // mirrors it into the structured credentials file the resolver reads.
59
+ try {
60
+ await saveSonarToken(result.token);
61
+ } catch (err) {
62
+ logger?.warn?.(`[sonar-recovery] persist to sonar-credentials.json failed (non-blocking): ${err.message}`);
63
+ }
64
+
65
+ logger?.info?.(`[sonar-recovery] new token persisted to ${result.savedTo}${result.passwordRotated ? " (admin password rotated)" : ""} — retrying original request`);
66
+ return { ok: true, newToken: result.token, savedTo: result.savedTo, passwordRotated: result.passwordRotated };
67
+ }
@@ -0,0 +1,58 @@
1
+ // Refine loop for the spec-reviewer (KJC-PCS-0048 PR 3). Called from
2
+ // run-spec-review.js when the user picks [r]efine. Asks the role for a
3
+ // rewritten v2, persists v1.md / v2.md, opens $EDITOR for the user to
4
+ // tweak, uses a SHA-256 hash diff to flag whether the v2 needs a fresh
5
+ // review pass (user modified it) or can proceed as-is.
6
+
7
+ import fs from "node:fs/promises";
8
+ import path from "node:path";
9
+ import crypto from "node:crypto";
10
+ import { spawnSync } from "node:child_process";
11
+
12
+ const sha256 = (s) => crypto.createHash("sha256").update(String(s || ""), "utf8").digest("hex");
13
+
14
+ function openEditor(filePath, { logger, openEditorFn = null } = {}) {
15
+ if (openEditorFn) return openEditorFn(filePath);
16
+ const raw = process.env.VISUAL || process.env.EDITOR || "vi"; // same precedence as src/commands/config.js
17
+ const parts = raw.split(/\s+/);
18
+ logger?.info?.(`Opening ${parts[0]} on ${filePath}`);
19
+ return spawnSync(parts[0], [...parts.slice(1), filePath], { stdio: "inherit" }).status === 0;
20
+ }
21
+
22
+ /**
23
+ * Single refine iteration. The outer loop in run-spec-review.js decides
24
+ * whether to re-invoke us based on the returned `hashChanged` flag
25
+ * (re-review the v2 because the user modified it) or accept the v2 as
26
+ * the final spec (`hashChanged === false`).
27
+ *
28
+ * @returns {Promise<{ok, refinedSpec?, hashChanged, error?, v2Path?}>}
29
+ */
30
+ export async function runRefinePass({ spec, findings, role, projectDir, taskFile, logger, fs: fsOverride, openEditorFn }) {
31
+ const _fs = fsOverride || fs;
32
+ const refineRes = await role.refineSpec({ spec, findings });
33
+ if (!refineRes.ok) return { ok: false, error: refineRes.error || "refine failed", hashChanged: false };
34
+
35
+ let v2Path;
36
+ try {
37
+ const root = projectDir || process.cwd();
38
+ const ts = new Date().toISOString().replaceAll(/[:.]/g, "-");
39
+ const dir = path.join(root, ".reviews", `spec-review-${ts}`);
40
+ await _fs.mkdir(dir, { recursive: true });
41
+ await _fs.writeFile(path.join(dir, "spec-v1.md"), spec, "utf8");
42
+ v2Path = path.join(dir, "spec-v2.md");
43
+ await _fs.writeFile(v2Path, refineRes.refinedSpec, "utf8");
44
+ if (taskFile) {
45
+ // Mirror the v2 next to the user's --task-file so they find it
46
+ // without diving into .reviews/.
47
+ const p = path.parse(taskFile);
48
+ await _fs.writeFile(path.join(p.dir, `${p.name}.v2${p.ext || ".md"}`), refineRes.refinedSpec, "utf8").catch(() => {});
49
+ }
50
+ } catch (err) { return { ok: false, error: `persist failed: ${err.message}`, hashChanged: false }; }
51
+
52
+ const before = sha256(refineRes.refinedSpec);
53
+ await openEditor(v2Path, { logger, openEditorFn });
54
+ let after;
55
+ try { after = await _fs.readFile(v2Path, "utf8"); }
56
+ catch (err) { return { ok: false, error: `read v2 failed: ${err.message}`, hashChanged: false }; }
57
+ return { ok: true, refinedSpec: after, hashChanged: before !== sha256(after), v2Path };
58
+ }