@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,219 @@
1
+ import { readdir, readFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+
4
+ const SKILL_DIRS = [".agent/skills", ".claude/skills"];
5
+ const SKILL_FILE = "SKILL.md";
6
+ const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
7
+
8
+ /**
9
+ * Extract the `type` field from SKILL.md frontmatter.
10
+ * Returns "technical" (default) or "domain".
11
+ * @param {string} content — raw SKILL.md content
12
+ * @returns {{type: string, body: string}}
13
+ */
14
+ function parseSkillType(content) {
15
+ const match = content.match(FRONTMATTER_RE);
16
+ if (!match) return { type: "technical", body: content };
17
+
18
+ const frontmatter = match[1];
19
+ const body = match[2];
20
+
21
+ // Simple regex extraction — avoid full YAML parser for a single field
22
+ const typeMatch = frontmatter.match(/^type:\s*(.+)$/m);
23
+ const type = typeMatch ? typeMatch[1].trim().toLowerCase() : "technical";
24
+
25
+ return { type, body };
26
+ }
27
+
28
+ /**
29
+ * Scans known skill directories for SKILL.md files.
30
+ * @param {string} projectDir — absolute path to the project root
31
+ * @param {{type?: string}} [options] — filter by type: "technical" (default), "domain", or undefined (all)
32
+ * @returns {Promise<Array<{name: string, content: string, type: string}>>}
33
+ */
34
+ export async function loadAvailableSkills(projectDir, options = {}) {
35
+ const skills = [];
36
+ const filterType = options.type || null;
37
+
38
+ for (const rel of SKILL_DIRS) {
39
+ const dir = join(projectDir, rel);
40
+ let entries;
41
+ try {
42
+ entries = await readdir(dir, { withFileTypes: true });
43
+ } catch {
44
+ continue; // directory does not exist — skip
45
+ }
46
+
47
+ for (const entry of entries) {
48
+ if (!entry.isDirectory()) continue;
49
+ const skillPath = join(dir, entry.name, SKILL_FILE);
50
+ try {
51
+ const raw = await readFile(skillPath, "utf-8");
52
+ const { type, body } = parseSkillType(raw);
53
+
54
+ // Filter by type if specified
55
+ if (filterType && type !== filterType) continue;
56
+
57
+ // For backward compat: existing callers get content without frontmatter for domain skills,
58
+ // but full content for technical skills (they never had frontmatter before)
59
+ const content = type === "domain" ? body.trim() : raw;
60
+ skills.push({ name: entry.name, content, type });
61
+ } catch {
62
+ // no SKILL.md in this subdirectory — skip
63
+ }
64
+ }
65
+ }
66
+
67
+ return skills;
68
+ }
69
+
70
+ /**
71
+ * Builds a prompt section from loaded skills.
72
+ *
73
+ * For Anthropic (Claude Sonnet 4.5+), skills are delivered as Agent Skills
74
+ * natively (loaded by the runtime from SKILL.md) — duplicating their full
75
+ * content in the prompt wastes tokens and risks drifting from the canonical
76
+ * source. When `provider === "anthropic"`, this function emits a lightweight
77
+ * reference list; Claude will load the SKILL.md files on demand.
78
+ *
79
+ * For every other provider, the full SKILL.md body is inlined (previous
80
+ * behaviour preserved).
81
+ *
82
+ * When `role` is supplied, skills are filtered to those relevant to the role
83
+ * (e.g. a reviewer only sees `*-code-review`, `*-security-*`, and general
84
+ * patterns; see ROLE_SKILL_PATTERNS). Setting role to null/undefined keeps
85
+ * the legacy behavior of injecting every skill (useful for the coder role,
86
+ * which benefits from broad context).
87
+ *
88
+ * @param {Array<{name: string, content: string}>} skills
89
+ * @param {{ provider?: string, role?: string }} [options]
90
+ * @returns {string} prompt section or empty string
91
+ */
92
+ export function buildSkillSection(skills, options = {}) {
93
+ if (!skills || skills.length === 0) return "";
94
+
95
+ const filtered = options.role ? filterSkillsForRole(skills, options.role) : skills;
96
+ if (filtered.length === 0) return "";
97
+
98
+ const providerSlug = normalize(options.provider);
99
+
100
+ if (providerSlug === "anthropic") {
101
+ return buildReferenceSection(filtered);
102
+ }
103
+
104
+ return buildInlineSection(filtered);
105
+ }
106
+
107
+ /**
108
+ * Role → substring patterns that skills must match to be included. A skill
109
+ * matches the role if its name contains ANY of the role's patterns OR if it
110
+ * appears in the role's explicit allowlist. Coder is omitted on purpose: it
111
+ * receives everything.
112
+ *
113
+ * The patterns target the skill NAMING convention shipped by openskills.cc
114
+ * where skills are typically named like `react-patterns`, `java-code-review`,
115
+ * `pytest-patterns`, `owasp-top-10`, etc.
116
+ */
117
+ /**
118
+ * Role → filter config.
119
+ *
120
+ * Two modes:
121
+ * - `includes` present: skill name must contain ANY include AND NOT contain
122
+ * any exclude. `allow` bypasses both checks (explicit allowlist by name).
123
+ * - `includes` absent (or empty) + `excludes` present: skill passes unless
124
+ * its name contains an exclude pattern. This is the "accept broad, trim
125
+ * obvious mismatches" mode used by architect.
126
+ */
127
+ const ROLE_SKILL_PATTERNS = {
128
+ reviewer: {
129
+ includes: ["code-review", "security", "lint", "style", "antipatterns", "owasp"],
130
+ excludes: [],
131
+ allow: ["sql-analysis"],
132
+ },
133
+ architect: {
134
+ // Architect sees everything except test-framework patterns. Architecture
135
+ // decisions touch every domain, so keep the filter permissive.
136
+ includes: null,
137
+ excludes: ["pytest", "vitest", "jest", "playwright", "cypress"],
138
+ allow: [],
139
+ },
140
+ tester: {
141
+ includes: ["test", "pytest", "vitest", "jest", "playwright", "cypress"],
142
+ excludes: [],
143
+ allow: [],
144
+ },
145
+ security: {
146
+ includes: ["security", "owasp", "sast"],
147
+ excludes: [],
148
+ allow: [],
149
+ },
150
+ planner: {
151
+ // Planner gets broad stack context except test frameworks.
152
+ includes: null,
153
+ excludes: ["pytest", "vitest", "jest", "playwright", "cypress"],
154
+ allow: [],
155
+ },
156
+ solomon: {
157
+ includes: ["security", "code-review"],
158
+ excludes: [],
159
+ allow: [],
160
+ },
161
+ };
162
+
163
+ /**
164
+ * Filter skills for a given role using the ROLE_SKILL_PATTERNS map. Unknown
165
+ * roles get ALL skills (conservative default for the coder role and any
166
+ * future addition). Matching is case-insensitive substring.
167
+ *
168
+ * @param {Array<{name: string}>} skills
169
+ * @param {string} role
170
+ * @returns {Array} filtered array
171
+ */
172
+ export function filterSkillsForRole(skills, role) {
173
+ const cfg = ROLE_SKILL_PATTERNS[role];
174
+ if (!cfg) return skills; // unknown role (e.g. coder) → no filter
175
+ const includes = Array.isArray(cfg.includes) ? cfg.includes.map((s) => s.toLowerCase()) : null;
176
+ const excludes = (cfg.excludes || []).map((s) => s.toLowerCase());
177
+ const allow = new Set((cfg.allow || []).map((s) => s.toLowerCase()));
178
+ return skills.filter((s) => {
179
+ const name = (s.name || "").toLowerCase();
180
+ if (allow.has(name)) return true;
181
+ if (excludes.some((pat) => name.includes(pat))) return false;
182
+ if (!includes || includes.length === 0) return true; // exclude-only mode
183
+ return includes.some((pat) => name.includes(pat));
184
+ });
185
+ }
186
+
187
+ function buildInlineSection(skills) {
188
+ const header = [
189
+ "## Domain Skills",
190
+ "",
191
+ "The following domain-specific knowledge is available for this task:"
192
+ ].join("\n");
193
+
194
+ const blocks = skills.map(
195
+ (s) => `### ${s.name}\n${s.content}`
196
+ );
197
+
198
+ return [header, ...blocks].join("\n\n");
199
+ }
200
+
201
+ function buildReferenceSection(skills) {
202
+ const names = skills.map((s) => s.name).join(", ");
203
+ return [
204
+ "## Domain Skills",
205
+ "",
206
+ `Skills available natively via your Agent Skills capability: ${names}.`,
207
+ "Each skill's SKILL.md is already loaded in your runtime; invoke its guidance as needed without re-reading the full content."
208
+ ].join("\n");
209
+ }
210
+
211
+ function normalize(value) {
212
+ if (!value) return null;
213
+ const lc = String(value).toLowerCase();
214
+ if (lc === "claude" || lc === "anthropic") return "anthropic";
215
+ if (lc === "codex" || lc === "openai") return "openai";
216
+ if (lc === "gemini" || lc === "google") return "google";
217
+ if (lc === "opencode") return "opencode";
218
+ return lc;
219
+ }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Local cache for installed OpenSkills, backed by `~/.karajan/skills-cache/`.
3
+ *
4
+ * The cache records WHEN each skill was last installed so we can skip
5
+ * redundant `npx openskills install X` calls during the 7-day TTL window
6
+ * (each call can take 10-30s even when the skill is already present).
7
+ *
8
+ * This is intentionally simple: one directory per skill name with a
9
+ * `meta.json` describing the last install time. Skill content is NOT copied
10
+ * to the cache — OpenSkills owns the real content on disk; we only track
11
+ * freshness.
12
+ */
13
+
14
+ import fs from "node:fs/promises";
15
+ import path from "node:path";
16
+ import { getKarajanHome } from "../utils/paths.js";
17
+
18
+ const CACHE_DIR_NAME = "skills-cache";
19
+ const DEFAULT_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
20
+
21
+ export function getCacheRoot() {
22
+ return path.join(getKarajanHome(), CACHE_DIR_NAME);
23
+ }
24
+
25
+ function sanitize(name) {
26
+ // Allow safe skill name characters, replace everything else with "_".
27
+ return String(name || "").replaceAll(/[^A-Za-z0-9._-]/g, "_");
28
+ }
29
+
30
+ function metaPath(name) {
31
+ return path.join(getCacheRoot(), sanitize(name), "meta.json");
32
+ }
33
+
34
+ /**
35
+ * Read cached metadata for a skill. Returns null if missing or unreadable.
36
+ * @param {string} name
37
+ * @returns {Promise<{cachedAt: string, source?: string}|null>}
38
+ */
39
+ export async function getCachedMeta(name) {
40
+ if (!name) return null;
41
+ try {
42
+ const raw = await fs.readFile(metaPath(name), "utf8");
43
+ const parsed = JSON.parse(raw);
44
+ if (typeof parsed?.cachedAt !== "string") return null;
45
+ return parsed;
46
+ } catch {
47
+ return null;
48
+ }
49
+ }
50
+
51
+ /**
52
+ * True iff the skill was cached more recently than `ttlMs` ago.
53
+ * @param {string} name
54
+ * @param {number} [ttlMs=DEFAULT_TTL_MS]
55
+ * @returns {Promise<boolean>}
56
+ */
57
+ export async function isFresh(name, ttlMs = DEFAULT_TTL_MS) {
58
+ const meta = await getCachedMeta(name);
59
+ if (!meta) return false;
60
+ const cachedTime = Date.parse(meta.cachedAt);
61
+ if (Number.isNaN(cachedTime)) return false;
62
+ return Date.now() - cachedTime < ttlMs;
63
+ }
64
+
65
+ /**
66
+ * Record that a skill was just (re-)installed. Creates the cache subdir if
67
+ * needed.
68
+ * @param {string} name
69
+ * @param {{ source?: string }} [opts]
70
+ * @returns {Promise<void>}
71
+ */
72
+ export async function recordInstall(name, opts = {}) {
73
+ if (!name) return;
74
+ const dir = path.join(getCacheRoot(), sanitize(name));
75
+ await fs.mkdir(dir, { recursive: true });
76
+ const meta = {
77
+ name,
78
+ cachedAt: new Date().toISOString(),
79
+ ...(opts.source ? { source: opts.source } : {}),
80
+ };
81
+ await fs.writeFile(path.join(dir, "meta.json"), JSON.stringify(meta, null, 2), "utf8");
82
+ }
83
+
84
+ /**
85
+ * Remove all cached skill metadata (does not touch real installed skills,
86
+ * only clears the freshness tracker).
87
+ * @returns {Promise<{removed: number}>}
88
+ */
89
+ export async function clearCache() {
90
+ const root = getCacheRoot();
91
+ let removed = 0;
92
+ try {
93
+ const entries = await fs.readdir(root, { withFileTypes: true });
94
+ for (const entry of entries) {
95
+ if (!entry.isDirectory()) continue;
96
+ await fs.rm(path.join(root, entry.name), { recursive: true, force: true });
97
+ removed++;
98
+ }
99
+ } catch { /* cache dir does not exist — nothing to clear */ }
100
+ return { removed };
101
+ }
102
+
103
+ /**
104
+ * List cached skill names with their cachedAt timestamp, sorted oldest first.
105
+ * @returns {Promise<Array<{name: string, cachedAt: string, fresh: boolean}>>}
106
+ */
107
+ export async function listCached(ttlMs = DEFAULT_TTL_MS) {
108
+ const root = getCacheRoot();
109
+ const out = [];
110
+ let entries;
111
+ try {
112
+ entries = await fs.readdir(root, { withFileTypes: true });
113
+ } catch {
114
+ return [];
115
+ }
116
+ for (const entry of entries) {
117
+ if (!entry.isDirectory()) continue;
118
+ const meta = await getCachedMeta(entry.name);
119
+ if (!meta) continue;
120
+ const cachedTime = Date.parse(meta.cachedAt);
121
+ const fresh = !Number.isNaN(cachedTime) && (Date.now() - cachedTime < ttlMs);
122
+ out.push({ name: meta.name || entry.name, cachedAt: meta.cachedAt, fresh });
123
+ }
124
+ out.sort((a, b) => a.cachedAt.localeCompare(b.cachedAt));
125
+ return out;
126
+ }
127
+
128
+ export { DEFAULT_TTL_MS };
@@ -0,0 +1,128 @@
1
+ import { runCommand } from "../utils/process.js";
2
+ import { withRetry } from "../utils/retry.js";
3
+ import { resolveSonarProjectKey } from "./project-key.js";
4
+ import { resolveSonarToken } from "./config-resolver.js";
5
+ import { filterFalsePositives } from "./issue-filter.js";
6
+ import { recoverSonarToken } from "./token-recovery.js";
7
+
8
+ class SonarApiError extends Error {
9
+ constructor(message, { url, httpStatus, hint } = {}) {
10
+ super(message);
11
+ this.name = "SonarApiError";
12
+ this.url = url;
13
+ this.httpStatus = httpStatus;
14
+ this.hint = hint;
15
+ }
16
+ }
17
+
18
+ function tokenFromConfig(config) {
19
+ return resolveSonarToken(config) || "";
20
+ }
21
+
22
+ function parseHttpResponse(stdout) {
23
+ const lines = stdout.split("\n");
24
+ const httpCode = Number.parseInt(lines.pop(), 10) || 0;
25
+ const body = lines.join("\n");
26
+ return { httpCode, body };
27
+ }
28
+
29
+ async function sonarFetchOnce(config, urlPath, { _retriedAfterRecovery = false, logger = null } = {}) {
30
+ const token = tokenFromConfig(config);
31
+ const url = `${config.sonarqube.host}${urlPath}`;
32
+ const res = await runCommand("curl", ["-s", "-w", "\n%{http_code}", "-u", `${token}:`, url]);
33
+
34
+ if (res.exitCode !== 0) {
35
+ const err = new SonarApiError(
36
+ `SonarQube is not reachable at ${config.sonarqube.host}. Check that SonarQube is running ('kj sonar start').`,
37
+ { url, hint: "Run 'kj sonar start' or verify Docker is running." }
38
+ );
39
+ err.httpStatus = 503;
40
+ throw err;
41
+ }
42
+
43
+ const { httpCode, body } = parseHttpResponse(res.stdout);
44
+
45
+ if (httpCode === 401) {
46
+ // KJC-BUG-0057 (v2.19.2): try to bootstrap a fresh token via the Sonar
47
+ // REST API and retry ONCE. If recovery still fails — or this was already
48
+ // the retry — surface the actionable error like before.
49
+ if (!_retriedAfterRecovery) {
50
+ const recovery = await recoverSonarToken(config, logger);
51
+ if (recovery.ok) {
52
+ return sonarFetchOnce(config, urlPath, { _retriedAfterRecovery: true, logger });
53
+ }
54
+ throw new SonarApiError(
55
+ `SonarQube authentication failed (HTTP 401) and auto-recovery failed: ${recovery.reason}. Regenerate the token manually with 'kj init' or save admin user/password in ~/.karajan/sonar-credentials.json.`,
56
+ { url, httpStatus: 401, hint: "Run 'kj init' to regenerate the SonarQube token, or save admin credentials in ~/.karajan/sonar-credentials.json so Karajan can re-bootstrap automatically." }
57
+ );
58
+ }
59
+ throw new SonarApiError(
60
+ `SonarQube authentication failed (HTTP 401) even after auto-recovery. The newly generated token is also being rejected — the Sonar instance may be in an inconsistent state.`,
61
+ { url, httpStatus: 401, hint: "Inspect the Sonar UI directly; consider 'kj sonar restart'." }
62
+ );
63
+ }
64
+
65
+ if (httpCode >= 400) {
66
+ const err = new SonarApiError(
67
+ `SonarQube API returned HTTP ${httpCode} for ${url}.`,
68
+ { url, httpStatus: httpCode }
69
+ );
70
+ err.httpStatus = httpCode;
71
+ throw err;
72
+ }
73
+
74
+ return body;
75
+ }
76
+
77
+ async function sonarFetch(config, urlPath) {
78
+ const maxAttempts = config.sonarqube?.max_scan_retries ?? 3;
79
+ return withRetry(
80
+ () => sonarFetchOnce(config, urlPath),
81
+ { maxAttempts, initialBackoffMs: 2000, maxBackoffMs: 15000 }
82
+ );
83
+ }
84
+
85
+ export async function getQualityGateStatus(config, projectKey = null) {
86
+ const effectiveProjectKey = await resolveSonarProjectKey(config, { projectKey });
87
+ const body = await sonarFetch(config, `/api/qualitygates/project_status?projectKey=${effectiveProjectKey}`);
88
+
89
+ try {
90
+ const parsed = JSON.parse(body);
91
+ return { ok: true, status: parsed.projectStatus?.status || "ERROR", conditions: parsed.projectStatus?.conditions || [], raw: parsed };
92
+ } catch { /* SonarQube response is not valid JSON */
93
+ return { ok: false, status: "ERROR", conditions: [], raw: body };
94
+ }
95
+ }
96
+
97
+ export async function getOpenIssues(config, projectKey = null) {
98
+ const effectiveProjectKey = await resolveSonarProjectKey(config, { projectKey });
99
+ const body = await sonarFetch(config, `/api/issues/search?projectKeys=${effectiveProjectKey}&statuses=OPEN`);
100
+
101
+ let parsedIssues = [];
102
+ let parsedRaw = body;
103
+ try {
104
+ const parsed = JSON.parse(body);
105
+ parsedIssues = parsed.issues || [];
106
+ parsedRaw = parsed;
107
+ } catch { /* SonarQube response is not valid JSON */ }
108
+
109
+ // KJC-TSK-0416: pre-filtro de falsos positivos antes de devolver al
110
+ // caller. El coder, audit y scan consumen lo que pasa el filtro;
111
+ // suppressed se devuelve aparte para observabilidad.
112
+ // v2.17.0: accepts both legacy config.sonar.false_positives (no `tool`
113
+ // field, implicit "sonar") and the new generalised config.audit.false_positives.
114
+ const sonarLegacy = (config?.sonar?.false_positives || []).map((r) => ({ tool: "sonar", ...r }));
115
+ const auditRules = (config?.audit?.false_positives || []).filter((r) => (r.tool || "sonar") === "sonar");
116
+ const taggedIssues = parsedIssues.map((i) => ({ ...i, tool: "sonar" }));
117
+ const { kept, suppressed } = filterFalsePositives(taggedIssues, {
118
+ extraRules: [...sonarLegacy, ...auditRules],
119
+ projectRoot: config?.projectDir,
120
+ });
121
+
122
+ return {
123
+ total: kept.length,
124
+ issues: kept,
125
+ suppressed,
126
+ raw: parsedRaw,
127
+ };
128
+ }
@@ -0,0 +1,78 @@
1
+ import { runCommand } from "../utils/process.js";
2
+ import { resolveSonarProjectKey } from "./project-key.js";
3
+
4
+ function buildCloudScannerArgs(projectKey, config) {
5
+ const sc = config.sonarcloud || {};
6
+ const scanner = sc.scanner || {};
7
+ const host = sc.host || "https://sonarcloud.io";
8
+ const organization = process.env.KJ_SONARCLOUD_ORG || sc.organization;
9
+
10
+ const args = [
11
+ `-Dsonar.host.url=${host}`,
12
+ `-Dsonar.projectKey=${projectKey}`
13
+ ];
14
+
15
+ // Token passed via SONAR_TOKEN env var, not CLI args (invisible in `ps aux`)
16
+ if (organization) args.push(`-Dsonar.organization=${organization}`);
17
+ if (scanner.sources) args.push(`-Dsonar.sources=${scanner.sources}`);
18
+ if (scanner.exclusions) args.push(`-Dsonar.exclusions=${scanner.exclusions}`);
19
+ if (scanner.test_inclusions) args.push(`-Dsonar.test.inclusions=${scanner.test_inclusions}`);
20
+
21
+ return args;
22
+ }
23
+
24
+ export async function runSonarCloudScan(config, projectKey = null) {
25
+ const sc = config.sonarcloud || {};
26
+ const token = process.env.KJ_SONARCLOUD_TOKEN || sc.token;
27
+ const organization = sc.organization || process.env.KJ_SONARCLOUD_ORG;
28
+
29
+ if (!token) {
30
+ return {
31
+ ok: false,
32
+ projectKey: null,
33
+ stdout: "",
34
+ stderr: "SonarCloud token not configured. Set sonarcloud.token in kj.config.yml or KJ_SONARCLOUD_TOKEN env var.",
35
+ exitCode: 1
36
+ };
37
+ }
38
+
39
+ if (!organization) {
40
+ return {
41
+ ok: false,
42
+ projectKey: null,
43
+ stdout: "",
44
+ stderr: "SonarCloud organization not configured. Set sonarcloud.organization in kj.config.yml or KJ_SONARCLOUD_ORG env var.",
45
+ exitCode: 1
46
+ };
47
+ }
48
+
49
+ let effectiveProjectKey;
50
+ try {
51
+ effectiveProjectKey = projectKey || sc.project_key || await resolveSonarProjectKey(config, { projectKey });
52
+ } catch (error) {
53
+ return {
54
+ ok: false,
55
+ projectKey: null,
56
+ stdout: "",
57
+ stderr: error?.message || String(error),
58
+ exitCode: 1
59
+ };
60
+ }
61
+
62
+ const scannerTimeout = 15 * 60 * 1000;
63
+ const args = buildCloudScannerArgs(effectiveProjectKey, config);
64
+
65
+ // Pass token via process env, not CLI args — invisible in `ps aux`
66
+ const env = { ...process.env };
67
+ if (token) env.SONAR_TOKEN = token;
68
+
69
+ const result = await runCommand("npx", ["@sonar/scan", ...args], { timeout: scannerTimeout, env });
70
+
71
+ return {
72
+ ok: result.exitCode === 0,
73
+ projectKey: effectiveProjectKey,
74
+ stdout: result.stdout,
75
+ stderr: result.stderr,
76
+ exitCode: result.exitCode
77
+ };
78
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Centralized SonarQube configuration resolution.
3
+ *
4
+ * Single source of truth for host URL, token, and admin credentials.
5
+ * Consumers: scanner.js, preflight-checks.js, api.js.
6
+ */
7
+
8
+ import { loadSonarCredentials } from "./credentials.js";
9
+
10
+ const DEFAULT_HOST = "http://localhost:9000";
11
+
12
+ /**
13
+ * Normalize SonarQube host URL for API calls (replace Docker-internal hostname).
14
+ * @param {string|undefined} rawHost - Raw host from config or env
15
+ * @returns {string} Normalized host URL without trailing slash
16
+ */
17
+ export function resolveSonarHost(rawHost) {
18
+ const host = String(rawHost || DEFAULT_HOST)
19
+ .replaceAll(/host\.docker\.internal/g, "localhost")
20
+ .replace(/\/+$/, "");
21
+ return host;
22
+ }
23
+
24
+ /**
25
+ * Resolve SonarQube auth token from (in priority order):
26
+ * 1. KJ_SONAR_TOKEN env var
27
+ * 2. config.sonarqube.token
28
+ * 3. SONAR_TOKEN env var
29
+ *
30
+ * For async resolution (including credentials file), use resolveSonarTokenAsync.
31
+ *
32
+ * @param {object} [config={}] - Karajan config object
33
+ * @returns {string|null} Token string or null if none found
34
+ */
35
+ export function resolveSonarToken(config = {}) {
36
+ const token =
37
+ process.env.KJ_SONAR_TOKEN ||
38
+ config.sonarqube?.token ||
39
+ process.env.SONAR_TOKEN ||
40
+ null;
41
+ return token || null;
42
+ }
43
+
44
+ /**
45
+ * Async version of resolveSonarToken that also checks the credentials file
46
+ * for previously generated tokens.
47
+ *
48
+ * @param {object} [config={}] - Karajan config object
49
+ * @returns {Promise<string|null>} Token string or null if none found
50
+ */
51
+ export async function resolveSonarTokenAsync(config = {}) {
52
+ const syncToken = resolveSonarToken(config);
53
+ if (syncToken) return syncToken;
54
+
55
+ // Check credentials file for persisted token
56
+ const fileCreds = (await loadSonarCredentials()) || {};
57
+ return fileCreds.token || null;
58
+ }
59
+
60
+ /**
61
+ * Resolve SonarQube admin credentials from (in priority order):
62
+ * 1. Env vars: KJ_SONAR_ADMIN_USER / KJ_SONAR_ADMIN_PASSWORD
63
+ * 2. Config: sonarqube.admin_user / sonarqube.admin_password
64
+ * 3. Credentials file: ~/.karajan/sonar-credentials.json
65
+ *
66
+ * Returns all candidate passwords (de-duped) so callers can try each.
67
+ *
68
+ * @param {object} [config={}] - Karajan config object
69
+ * @returns {Promise<{ user: string|null, passwords: string[] }>}
70
+ */
71
+ export async function resolveSonarCredentials(config = {}) {
72
+ const fileCreds = (await loadSonarCredentials()) || {};
73
+
74
+ const user =
75
+ process.env.KJ_SONAR_ADMIN_USER ||
76
+ config.sonarqube?.admin_user ||
77
+ fileCreds.user ||
78
+ "admin"; // Default for fresh SonarQube installs
79
+
80
+ const candidates = [
81
+ process.env.KJ_SONAR_ADMIN_PASSWORD,
82
+ config.sonarqube?.admin_password,
83
+ fileCreds.password,
84
+ "admin", // Default password for fresh SonarQube installs (zero-config)
85
+ ].filter(Boolean);
86
+
87
+ return { user, passwords: [...new Set(candidates)] };
88
+ }