@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,281 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { execFile } from "node:child_process";
4
+ import { promisify } from "node:util";
5
+ import { getKarajanHome } from "../utils/paths.js";
6
+ import { sniffManifests, summariseStack } from "../utils/manifest-sniff.js";
7
+
8
+ const execFileAsync = promisify(execFile);
9
+
10
+ const SOURCE_EXTENSIONS = new Set([
11
+ ".js", ".mjs", ".cjs", ".ts", ".mts", ".cts", ".tsx", ".jsx",
12
+ ".py", ".rb", ".go", ".rs", ".java", ".kt", ".swift", ".c", ".cpp", ".h",
13
+ ".vue", ".svelte", ".astro", ".php", ".cs", ".sh"
14
+ ]);
15
+
16
+ const EXCLUDE_DIRS = new Set([
17
+ "node_modules", ".git", "dist", "build", "coverage", ".next",
18
+ ".nuxt", ".output", "__pycache__", ".cache", ".parcel-cache"
19
+ ]);
20
+
21
+ function slugify(projectDir) {
22
+ return path.basename(projectDir).replaceAll(/[^a-zA-Z0-9_-]/g, "_");
23
+ }
24
+
25
+ async function walkSourceFiles(dir) {
26
+ // Use git ls-files for speed (instant vs recursive walk)
27
+ try {
28
+ const { stdout } = await execFileAsync("git", ["ls-files", "--cached", "--others", "--exclude-standard"], { cwd: dir, encoding: "utf8", timeout: 5000 });
29
+ return stdout.trim().split("\n")
30
+ .filter(f => f && SOURCE_EXTENSIONS.has(path.extname(f)) && !EXCLUDE_DIRS.has(f.split("/")[0]))
31
+ .map(f => path.join(dir, f));
32
+ } catch {
33
+ // Fallback to recursive walk if not a git repo
34
+ const files = [];
35
+ let entries;
36
+ try {
37
+ entries = await fs.readdir(dir, { withFileTypes: true });
38
+ } catch { /* directory not readable */
39
+ return files;
40
+ }
41
+ for (const entry of entries) {
42
+ if (EXCLUDE_DIRS.has(entry.name)) continue;
43
+ const full = path.join(dir, entry.name);
44
+ if (entry.isDirectory()) {
45
+ files.push(...await walkSourceFiles(full));
46
+ } else if (SOURCE_EXTENSIONS.has(path.extname(entry.name))) {
47
+ files.push(full);
48
+ }
49
+ }
50
+ return files;
51
+ }
52
+ }
53
+
54
+ async function countLines(filePath) {
55
+ try {
56
+ const content = await fs.readFile(filePath, "utf8");
57
+ return content.split("\n").length;
58
+ } catch {
59
+ return 0;
60
+ }
61
+ }
62
+
63
+ function countDependenciesFromStack(stack) {
64
+ // Suma deps + devDeps de TODOS los manifests detectados (multi-stack).
65
+ // Si no hay manifests, devuelve ceros — coherente con repos sin paquete.
66
+ let deps = 0;
67
+ let devDeps = 0;
68
+ for (const m of stack.manifests) {
69
+ deps += m.deps;
70
+ devDeps += m.devDeps;
71
+ }
72
+ return { dependencies: deps, devDependencies: devDeps, total: deps + devDeps };
73
+ }
74
+
75
+ function runDepcheck(projectDir) {
76
+ return new Promise((resolve) => {
77
+ execFile("npx", ["depcheck", "--json"], { cwd: projectDir, timeout: 30000 }, (err, stdout) => {
78
+ if (err && !stdout) {
79
+ resolve({ unused: [], note: "depcheck not available or failed" });
80
+ return;
81
+ }
82
+ try {
83
+ const result = JSON.parse(stdout);
84
+ const unused = [
85
+ ...(result.dependencies || []),
86
+ ...(result.devDependencies || [])
87
+ ];
88
+ resolve({ unused });
89
+ } catch {
90
+ resolve({ unused: [], note: "depcheck output not parseable" });
91
+ }
92
+ });
93
+ });
94
+ }
95
+
96
+ // Regexes hoisted to module scope so they're compiled once instead of per file.
97
+ // Naming: *Re catches static/syntactic patterns; we still rely on the import set
98
+ // being a superset (false negative > false positive in audit context).
99
+ const EXPORT_RE = /export\s+(?:default\s+)?(?:function|class|const|let|var|async\s+function)\s+(\w+)/g;
100
+ const NAMED_EXPORT_RE = /export\s*\{([^}]+)\}/g;
101
+ const STATIC_NAMED_IMPORT_RE = /import\s+\{([^}]+)\}\s+from/g;
102
+ const DEFAULT_IMPORT_RE = /import\s+(\w+)\s+from/g;
103
+ // Namespace import (`import * as ns from <path>`) — knip-equivalent: mark
104
+ // the target file as fully-consumed because we cannot statically know
105
+ // which `ns.X` accesses happen.
106
+ const NAMESPACE_IMPORT_RE = /import\s+\*\s+as\s+\w+\s+from\s+["']([^"']+)["']/g;
107
+ // Any dynamic `await import(<path>)` — destructured, late-bound, or
108
+ // member-accessed. Conservatively mark the target as fully-consumed:
109
+ // trying to enumerate downstream destructuring/member-access patterns is
110
+ // fragile (the same test may use parenthesised assignment with no const,
111
+ // or bind first and `.foo`-access later). A false-negative here (one
112
+ // extra "live" export we can't prove dead) is always cheaper than a
113
+ // false-positive in audit context.
114
+ const DYNAMIC_IMPORT_RE = /\bawait\s+import\s*\(\s*["']([^"']+)["']\s*\)/g;
115
+ // Re-export bridges (`export { x } from <path>` and `export * from <path>`)
116
+ // — both consume from the target file (the re-exporter is itself another
117
+ // file consumer downstream).
118
+ const RE_EXPORT_NAMED_RE = /export\s*\{([^}]+)\}\s*from\s*["']([^"']+)["']/g;
119
+ const RE_EXPORT_STAR_RE = /export\s*\*\s*from\s*["']([^"']+)["']/g;
120
+ // Export preceded by a JSDoc block with `@internal` is opt-out: documented as
121
+ // dynamic-import-only or test-fixture surface that the regex passes can't see.
122
+ const INTERNAL_EXPORT_RE = /\/\*\*[\s\S]*?@internal[\s\S]*?\*\/\s*export\s+(?:default\s+)?(?:function|class|const|let|var|async\s+function)\s+(\w+)/g;
123
+
124
+ function resolveImportSpecifier(fromFile, specifier) {
125
+ // Only resolve project-relative paths; bare specifiers (npm packages) and
126
+ // node: builtins are not in our exportMap and don't matter here.
127
+ if (!specifier.startsWith(".") && !specifier.startsWith("/")) return null;
128
+ const base = path.resolve(path.dirname(fromFile), specifier);
129
+ // Caller will probe both with and without explicit extensions.
130
+ return base.endsWith(".js") || base.endsWith(".mjs") || base.endsWith(".cjs")
131
+ || base.endsWith(".ts") || base.endsWith(".tsx")
132
+ ? base
133
+ : `${base}.js`;
134
+ }
135
+
136
+ // Strip every string literal (template, double, single) from the content
137
+ // before scanning for export declarations. Test files routinely embed
138
+ // sample source code inside fixtures (template literals for plugin
139
+ // modules, double-quoted diff lines for prompt tests, etc.). At top level
140
+ // `export ...` is a statement, never an expression, so it can never
141
+ // legitimately appear inside a quoted string in real code — meaning the
142
+ // strip is safe for export detection. We do NOT use this stripped view
143
+ // for import detection: the import regexes anchor on `import {...} from`
144
+ // regardless of the path string, but the namespace/dynamic-import passes
145
+ // need the actual path inside the quotes to resolve the target module.
146
+ function stripStringLiterals(content) {
147
+ return content
148
+ .replaceAll(/`[^`]*`/g, "``")
149
+ .replaceAll(/"(?:\\.|[^"\\])*"/g, '""')
150
+ .replaceAll(/'(?:\\.|[^'\\])*'/g, "''");
151
+ }
152
+
153
+ async function findDeadExports(sourceFiles) {
154
+ const exportMap = new Map(); // file -> [exportName]
155
+ const internalExportsByFile = new Map(); // file -> Set<exportName>
156
+ const importedNames = new Set();
157
+ const fullyConsumedFiles = new Set(); // resolved file paths reached via `import *` or bound dynamic import
158
+
159
+ for (const file of sourceFiles) {
160
+ let content;
161
+ try {
162
+ content = await fs.readFile(file, "utf8");
163
+ } catch {
164
+ continue;
165
+ }
166
+ const exportContent = stripStringLiterals(content);
167
+
168
+ const exports = [];
169
+ for (const match of exportContent.matchAll(EXPORT_RE)) {
170
+ exports.push(match[1]);
171
+ }
172
+ for (const match of exportContent.matchAll(NAMED_EXPORT_RE)) {
173
+ const names = match[1].split(",").map(n => n.trim().split(/\s+as\s+/).pop().trim());
174
+ exports.push(...names);
175
+ }
176
+ if (exports.length > 0) {
177
+ exportMap.set(file, exports);
178
+ }
179
+
180
+ // Collect `@internal`-tagged exports up front so the dead-pass can skip them.
181
+ const internalSet = new Set();
182
+ for (const match of exportContent.matchAll(INTERNAL_EXPORT_RE)) {
183
+ internalSet.add(match[1]);
184
+ }
185
+ if (internalSet.size > 0) {
186
+ internalExportsByFile.set(file, internalSet);
187
+ }
188
+
189
+ for (const match of content.matchAll(STATIC_NAMED_IMPORT_RE)) {
190
+ const names = match[1].split(",").map(n => n.trim().split(/\s+as\s+/)[0].trim());
191
+ for (const name of names) importedNames.add(name);
192
+ }
193
+ for (const match of content.matchAll(DEFAULT_IMPORT_RE)) {
194
+ importedNames.add(match[1]);
195
+ }
196
+ for (const match of content.matchAll(NAMESPACE_IMPORT_RE)) {
197
+ const resolved = resolveImportSpecifier(file, match[1]);
198
+ if (resolved) fullyConsumedFiles.add(resolved);
199
+ }
200
+ for (const match of content.matchAll(DYNAMIC_IMPORT_RE)) {
201
+ const resolved = resolveImportSpecifier(file, match[1]);
202
+ if (resolved) fullyConsumedFiles.add(resolved);
203
+ }
204
+ for (const match of content.matchAll(RE_EXPORT_NAMED_RE)) {
205
+ const names = match[1].split(",").map(n => n.trim().split(/\s+as\s+/)[0].trim());
206
+ for (const name of names) importedNames.add(name);
207
+ }
208
+ for (const match of content.matchAll(RE_EXPORT_STAR_RE)) {
209
+ const resolved = resolveImportSpecifier(file, match[1]);
210
+ if (resolved) fullyConsumedFiles.add(resolved);
211
+ }
212
+ }
213
+
214
+ const dead = [];
215
+ for (const [file, exports] of exportMap) {
216
+ if (fullyConsumedFiles.has(file)) continue; // module imported via * or bound await import — can't tell which exports are used
217
+ const internalSet = internalExportsByFile.get(file);
218
+ for (const name of exports) {
219
+ if (internalSet?.has(name)) continue; // explicitly opted out via @internal
220
+ if (!importedNames.has(name)) {
221
+ dead.push({ file, name });
222
+ }
223
+ }
224
+ }
225
+ return dead;
226
+ }
227
+
228
+ export async function measureBasalCost(projectDir) {
229
+ const sourceFiles = await walkSourceFiles(projectDir);
230
+ let totalLines = 0;
231
+ for (const f of sourceFiles) {
232
+ totalLines += await countLines(f);
233
+ }
234
+
235
+ const stack = summariseStack(await sniffManifests(projectDir));
236
+ const depInfo = countDependenciesFromStack(stack);
237
+ // depcheck solo aplica a Node; en stacks puros no-Node lo saltamos.
238
+ const hasNode = stack.manifests.some((m) => m.stack === "node");
239
+ const depcheck = hasNode ? await runDepcheck(projectDir) : { unused: [], note: "depcheck skipped (no package.json)" };
240
+ const deadExports = await findDeadExports(sourceFiles);
241
+
242
+ return {
243
+ totalLines,
244
+ totalFiles: sourceFiles.length,
245
+ dependencies: depInfo,
246
+ unusedDependencies: depcheck,
247
+ deadExports,
248
+ stack
249
+ };
250
+ }
251
+
252
+ function auditDir() {
253
+ return path.join(getKarajanHome(), "audits");
254
+ }
255
+
256
+ export async function loadPreviousAudit(projectDir) {
257
+ const file = path.join(auditDir(), slugify(projectDir), "latest.json");
258
+ try {
259
+ return JSON.parse(await fs.readFile(file, "utf8"));
260
+ } catch {
261
+ return null;
262
+ }
263
+ }
264
+
265
+ export async function saveAuditSnapshot(projectDir, metrics) {
266
+ const dir = path.join(auditDir(), slugify(projectDir));
267
+ await fs.mkdir(dir, { recursive: true });
268
+ const snapshot = { ...metrics, timestamp: new Date().toISOString() };
269
+ await fs.writeFile(path.join(dir, "latest.json"), JSON.stringify(snapshot, null, 2));
270
+ return snapshot;
271
+ }
272
+
273
+ export function computeGrowthDelta(current, previous) {
274
+ if (!previous) return null;
275
+ return {
276
+ lines: current.totalLines - previous.totalLines,
277
+ files: current.totalFiles - previous.totalFiles,
278
+ deps: current.dependencies.total - (previous.dependencies?.total ?? 0),
279
+ since: previous.timestamp || null
280
+ };
281
+ }
@@ -0,0 +1,144 @@
1
+ // Circular import-deps collector for `kj audit` (KJC-TSK — v2.17).
2
+ //
3
+ // Deterministic, stack-aware. Uses madge programmatically; activates only
4
+ // when the project's stack has JS/TS sources. Anything else returns
5
+ // { available: false, reason }.
6
+ //
7
+ // Same best-effort shape as sonar-findings / osv-findings / semgrep-findings:
8
+ // - missing dep or timeout returns { available: false, reason }
9
+ // - findings are passed through audit/issue-filter.js so the user can
10
+ // suppress known cycles via config.audit.false_positives or inline
11
+ // `// karajan-audit-ignore: madge:circular-import` markers.
12
+
13
+ import path from "node:path";
14
+ import fs from "node:fs/promises";
15
+ import { filterFalsePositives } from "./issue-filter.js";
16
+
17
+ // madge is loaded dynamically: in the SEA standalone binary it is excluded
18
+ // from the bundle (see scripts/esbuild-sea.config.mjs) and a require()
19
+ // failure here cleanly degrades to available:false, while npm installs work
20
+ // normally. Keeping the import inside collectCircularDeps() also avoids
21
+ // paying the load cost for non-JS projects.
22
+
23
+ const SCAN_TIMEOUT_MS = 60_000;
24
+ const MAX_CYCLES_REPORTED = 50;
25
+
26
+ // Extensions madge will follow. Python/Go/etc. are not supported and we
27
+ // short-circuit on stack detection — listing only JS/TS variants here is a
28
+ // safety net so we never accidentally scan unrelated files.
29
+ const JS_TS_EXTENSIONS = ["js", "mjs", "cjs", "jsx", "ts", "tsx"];
30
+
31
+ function hasJsTsStack(stack) {
32
+ if (!stack) return false;
33
+ const lang = (stack.language || "").toLowerCase();
34
+ if (lang === "javascript" || lang === "typescript") return true;
35
+ const langs = Array.isArray(stack.languages) ? stack.languages : [];
36
+ return langs.some((l) => /^(javascript|typescript|jsx?|tsx?)$/i.test(String(l)));
37
+ }
38
+
39
+ async function findTsConfig(projectDir) {
40
+ for (const name of ["tsconfig.json", "jsconfig.json"]) {
41
+ const p = path.join(projectDir, name);
42
+ try { await fs.access(p); return p; } catch { /* not present */ }
43
+ }
44
+ return undefined;
45
+ }
46
+
47
+ function pickEntrypoint(projectDir) {
48
+ // madge needs an entrypoint. We prefer common conventions; if none
49
+ // exists we fall back to scanning `src/` which covers ~95% of JS/TS
50
+ // projects we've seen.
51
+ return path.join(projectDir, "src");
52
+ }
53
+
54
+ /**
55
+ * Collect circular-import findings for a project.
56
+ *
57
+ * @param {string} projectDir
58
+ * @param {object} stack - shape returned by detectProjectStack()
59
+ * @param {object} [config] - kj config (for config.audit.false_positives)
60
+ * @param {object} [logger]
61
+ * @returns {Promise<{available: boolean, reason?: string, total?: number, cycles?: object[], suppressed?: object[]}>}
62
+ */
63
+ export async function collectCircularDeps(projectDir, stack, config = {}, logger = null) {
64
+ if (!hasJsTsStack(stack)) {
65
+ return { available: false, reason: "no JS/TS sources detected — circular-dep scan skipped" };
66
+ }
67
+
68
+ const entry = pickEntrypoint(projectDir);
69
+ try { await fs.access(entry); } catch {
70
+ return { available: false, reason: `entrypoint ${path.relative(projectDir, entry)} not found` };
71
+ }
72
+
73
+ let madge;
74
+ try {
75
+ ({ default: madge } = await import("madge"));
76
+ } catch (err) {
77
+ return { available: false, reason: `madge not loadable (${err?.message || err}). In the SEA binary install karajan-code via npm to enable.` };
78
+ }
79
+
80
+ const tsConfig = await findTsConfig(projectDir);
81
+ const opts = {
82
+ fileExtensions: JS_TS_EXTENSIONS,
83
+ excludeRegExp: [/node_modules/, /\.test\.[jt]sx?$/, /\.spec\.[jt]sx?$/, /dist\//, /build\//, /coverage\//],
84
+ detectiveOptions: { es6: { mixedImports: true } },
85
+ };
86
+ if (tsConfig) opts.tsConfig = tsConfig;
87
+
88
+ let cycles;
89
+ try {
90
+ const scan = await Promise.race([
91
+ madge(entry, opts),
92
+ new Promise((_, rej) => setTimeout(() => rej(new Error("madge timeout")), SCAN_TIMEOUT_MS)),
93
+ ]);
94
+ cycles = scan.circular() || [];
95
+ } catch (err) {
96
+ logger?.warn?.(`audit/circular-deps: madge failed (${err?.message || err})`);
97
+ return { available: false, reason: `madge run failed: ${err?.message || err}` };
98
+ }
99
+
100
+ // madge returns paths relative to the entry dir; normalize to paths
101
+ // relative to projectDir so they match how the rest of the audit (Sonar,
102
+ // Semgrep) reports locations and so config.audit.false_positives can
103
+ // target them with familiar globs like "src/".
104
+ const relPrefix = path.relative(projectDir, entry);
105
+ const normalizedCycles = cycles.map((cycle) =>
106
+ cycle.map((p) => (relPrefix ? path.join(relPrefix, p) : p)),
107
+ );
108
+
109
+ const issues = normalizedCycles.slice(0, MAX_CYCLES_REPORTED).map((cycle, idx) => ({
110
+ tool: "madge",
111
+ rule: "circular-import",
112
+ component: cycle[0],
113
+ path: cycle[0],
114
+ line: 1,
115
+ severity: cycle.length >= 4 ? "MAJOR" : "MINOR",
116
+ message: `Circular import chain (${cycle.length} files): ${cycle.join(" → ")} → ${cycle[0]}`,
117
+ cycle,
118
+ index: idx,
119
+ }));
120
+
121
+ const extraRules = (config?.audit?.false_positives || []).filter((r) => r.tool === "madge");
122
+ const { kept, suppressed } = filterFalsePositives(issues, { extraRules, projectRoot: projectDir });
123
+
124
+ return {
125
+ available: true,
126
+ total: kept.length,
127
+ suppressedCount: suppressed.length,
128
+ cycles: kept,
129
+ suppressed,
130
+ truncated: normalizedCycles.length > MAX_CYCLES_REPORTED ? normalizedCycles.length - MAX_CYCLES_REPORTED : 0,
131
+ };
132
+ }
133
+
134
+ /**
135
+ * Group cycles by severity for the deterministic summary renderer.
136
+ */
137
+ export function groupCyclesBySeverity(cycles) {
138
+ const groups = { MAJOR: [], MINOR: [] };
139
+ for (const c of cycles || []) {
140
+ const sev = c.severity || "MINOR";
141
+ if (groups[sev]) groups[sev].push(c);
142
+ }
143
+ return groups;
144
+ }
@@ -0,0 +1,186 @@
1
+ // Dead exports collector for `kj audit` (KJC-TSK v2.17, code-quality dim).
2
+ //
3
+ // Wraps knip via subprocess. Reports unused exports + unused files —
4
+ // findings consumed deterministically by the LLM phase so the coder
5
+ // stops shipping new code without noticing dead code piling up.
6
+ //
7
+ // Same best-effort pattern as madge / semgrep / osv / sonar:
8
+ // - missing project signal (no package.json, no JS/TS) → available:false
9
+ // - missing knip binary → available:false (degrades cleanly in SEA)
10
+ // - timeout / parse error → available:false with reason
11
+ //
12
+ // Findings pass through audit/issue-filter.js so users can suppress
13
+ // false positives via config.audit.false_positives (tool="knip") or
14
+ // inline `// karajan-audit-ignore: knip:<ruleId>`.
15
+
16
+ import { execFile } from "node:child_process";
17
+ import { promisify } from "node:util";
18
+ import { createRequire } from "node:module";
19
+ import path from "node:path";
20
+ import fs from "node:fs/promises";
21
+ import { filterFalsePositives } from "./issue-filter.js";
22
+
23
+ const execFileAsync = promisify(execFile);
24
+
25
+ const SCAN_TIMEOUT_MS = 120_000;
26
+ const MAX_DEAD_EXPORTS_REPORTED = 100;
27
+ const MAX_UNUSED_FILES_REPORTED = 50;
28
+
29
+ function hasJsTsStack(stack) {
30
+ if (!stack) return false;
31
+ const lang = (stack.language || "").toLowerCase();
32
+ if (lang === "javascript" || lang === "typescript") return true;
33
+ const langs = Array.isArray(stack.languages) ? stack.languages : [];
34
+ return langs.some((l) => /^(javascript|typescript|jsx?|tsx?)$/i.test(String(l)));
35
+ }
36
+
37
+ async function hasPackageJson(projectDir) {
38
+ try { await fs.access(path.join(projectDir, "package.json")); return true; } catch { return false; }
39
+ }
40
+
41
+ /**
42
+ * Resolve the knip binary path relative to this package's node_modules.
43
+ * In SEA builds knip is `external` and require.resolve throws — we let
44
+ * the caller catch it and return available:false.
45
+ *
46
+ * knip restricts its subpath exports to "." and "./session", so we can't
47
+ * directly resolve "knip/bin/knip.js" or even "knip/package.json".
48
+ * Instead we resolve "knip" (the main entry, dist/index.js), walk up to
49
+ * its package root, and locate bin/knip.js from there.
50
+ */
51
+ function resolveKnipBin() {
52
+ const require = createRequire(import.meta.url);
53
+ const entry = require.resolve("knip"); // e.g. .../node_modules/knip/dist/index.js
54
+ // entry is .../node_modules/knip/dist/index.js — the package root is two levels up
55
+ const knipRoot = path.dirname(path.dirname(entry));
56
+ return path.join(knipRoot, "bin", "knip.js");
57
+ }
58
+
59
+ /**
60
+ * Collect dead-exports findings for a project.
61
+ *
62
+ * @param {string} projectDir
63
+ * @param {object} stack
64
+ * @param {object} [config]
65
+ * @param {object} [logger]
66
+ * @returns {Promise<{available: boolean, reason?: string, total?: number, exports?: object[], files?: object[], suppressed?: object[]}>}
67
+ */
68
+ export async function collectDeadExports(projectDir, stack, config = {}, logger = null) {
69
+ if (!hasJsTsStack(stack)) {
70
+ return { available: false, reason: "no JS/TS sources detected — dead-export scan skipped" };
71
+ }
72
+ if (!(await hasPackageJson(projectDir))) {
73
+ return { available: false, reason: "no package.json found — knip needs one to detect entry points" };
74
+ }
75
+
76
+ let knipBin;
77
+ try { knipBin = resolveKnipBin(); } catch (err) {
78
+ return { available: false, reason: `knip binary not resolvable (${err?.message || err}). In the SEA binary install karajan-code via npm to enable.` };
79
+ }
80
+
81
+ let raw;
82
+ try {
83
+ const { stdout } = await execFileAsync(
84
+ process.execPath,
85
+ [knipBin, "--reporter", "json", "--no-progress", "--no-exit-code"],
86
+ { cwd: projectDir, timeout: SCAN_TIMEOUT_MS, maxBuffer: 16 * 1024 * 1024 },
87
+ );
88
+ raw = stdout;
89
+ } catch (err) {
90
+ // knip exits non-zero when issues are found; with --no-exit-code that
91
+ // should not happen, but old configs might still trigger it. If the
92
+ // process produced JSON on stdout we still try to parse it.
93
+ raw = err?.stdout || "";
94
+ if (!raw) {
95
+ logger?.warn?.(`audit/dead-exports: knip failed (${err?.message || err})`);
96
+ return { available: false, reason: `knip run failed: ${err?.shortMessage || err?.message || err}` };
97
+ }
98
+ }
99
+
100
+ let report;
101
+ try {
102
+ report = JSON.parse(raw);
103
+ } catch (err) {
104
+ return { available: false, reason: `knip output not parseable as JSON (${err?.message || err})` };
105
+ }
106
+
107
+ // knip --reporter json shape: { files: [paths], issues: [{ file, exports?, types?, ... }], ... }
108
+ const unusedFiles = Array.isArray(report.files) ? report.files : [];
109
+ const fileIssues = Array.isArray(report.issues) ? report.issues : [];
110
+
111
+ const deadExportIssues = [];
112
+ for (const fi of fileIssues) {
113
+ const file = fi.file || fi.filePath || "";
114
+ if (!file) continue;
115
+ for (const ex of fi.exports || []) {
116
+ deadExportIssues.push({
117
+ tool: "knip",
118
+ rule: "unused-exports",
119
+ component: file,
120
+ path: file,
121
+ line: ex.line || 1,
122
+ col: ex.col,
123
+ name: ex.name,
124
+ severity: "MINOR",
125
+ message: `Unused export \`${ex.name}\` in ${file}`,
126
+ });
127
+ }
128
+ for (const t of fi.types || []) {
129
+ deadExportIssues.push({
130
+ tool: "knip",
131
+ rule: "unused-types",
132
+ component: file,
133
+ path: file,
134
+ line: t.line || 1,
135
+ name: t.name,
136
+ severity: "MINOR",
137
+ message: `Unused type \`${t.name}\` in ${file}`,
138
+ });
139
+ }
140
+ }
141
+
142
+ const unusedFileIssues = unusedFiles.map((f) => ({
143
+ tool: "knip",
144
+ rule: "unused-files",
145
+ component: f,
146
+ path: f,
147
+ line: 1,
148
+ severity: "MAJOR",
149
+ message: `Unused file: ${f}`,
150
+ }));
151
+
152
+ const allIssues = [
153
+ ...deadExportIssues.slice(0, MAX_DEAD_EXPORTS_REPORTED),
154
+ ...unusedFileIssues.slice(0, MAX_UNUSED_FILES_REPORTED),
155
+ ];
156
+ const extraRules = (config?.audit?.false_positives || []).filter((r) => r.tool === "knip");
157
+ const { kept, suppressed } = filterFalsePositives(allIssues, { extraRules, projectRoot: projectDir });
158
+
159
+ const keptExports = kept.filter((i) => i.rule === "unused-exports" || i.rule === "unused-types");
160
+ const keptFiles = kept.filter((i) => i.rule === "unused-files");
161
+
162
+ return {
163
+ available: true,
164
+ total: kept.length,
165
+ suppressedCount: suppressed.length,
166
+ exports: keptExports,
167
+ files: keptFiles,
168
+ suppressed,
169
+ truncated: {
170
+ exports: deadExportIssues.length > MAX_DEAD_EXPORTS_REPORTED ? deadExportIssues.length - MAX_DEAD_EXPORTS_REPORTED : 0,
171
+ files: unusedFiles.length > MAX_UNUSED_FILES_REPORTED ? unusedFiles.length - MAX_UNUSED_FILES_REPORTED : 0,
172
+ },
173
+ };
174
+ }
175
+
176
+ /**
177
+ * Group dead-export issues by severity for the deterministic summary.
178
+ */
179
+ export function groupDeadExportsBySeverity(items) {
180
+ const groups = { MAJOR: [], MINOR: [] };
181
+ for (const i of items || []) {
182
+ const sev = i.severity || "MINOR";
183
+ if (groups[sev]) groups[sev].push(i);
184
+ }
185
+ return groups;
186
+ }