@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,98 @@
1
+ // KJC-TSK-0435 (RAG Auto-Bootstrap PR1) — Ollama-in-Docker manager.
2
+ // Mirrors src/sonar/manager.js: write docker-compose, start/stop the
3
+ // container, discover a free port, wait for readiness. Next PR wires
4
+ // it to `kj init`.
5
+ import fs from "node:fs/promises";
6
+ import net from "node:net";
7
+ import { ensureDir } from "../utils/fs.js";
8
+ import { runCommand } from "../utils/process.js";
9
+ import { getKarajanHome, getOllamaComposePath } from "../utils/paths.js";
10
+
11
+ const DEFAULTS = { port: 11434, hcS: 5, upMs: 300000, ctrlMs: 120000, readyMs: 90000 };
12
+ const pos = (v, d) => (Number(v) > 0 ? Number(v) : d);
13
+
14
+ export function normalizeOllamaConfig(ollama = {}) {
15
+ const t = ollama.timeouts || {};
16
+ const port = Number(ollama.port) || DEFAULTS.port;
17
+ return {
18
+ host: ollama.host || `http://localhost:${port}`,
19
+ port,
20
+ external: ollama.external === true,
21
+ containerName: ollama.container_name || "kj-ollama",
22
+ volume: ollama.volume || "kj_ollama_data",
23
+ timeouts: {
24
+ healthcheckSeconds: pos(t.healthcheck_seconds, DEFAULTS.hcS),
25
+ composeUpMs: pos(t.compose_up_ms, DEFAULTS.upMs),
26
+ composeControlMs: pos(t.compose_control_ms, DEFAULTS.ctrlMs),
27
+ readyMs: pos(t.ready_ms, DEFAULTS.readyMs),
28
+ },
29
+ };
30
+ }
31
+
32
+ export function buildComposeTemplate(cfg) {
33
+ return `services:
34
+ ollama:
35
+ image: ollama/ollama:latest
36
+ container_name: ${cfg.containerName}
37
+ ports:
38
+ - "${cfg.port}:11434"
39
+ volumes:
40
+ - ${cfg.volume}:/root/.ollama
41
+ restart: unless-stopped
42
+
43
+ volumes:
44
+ ${cfg.volume}:
45
+ `;
46
+ }
47
+
48
+ export async function ensureComposeFile(ollama = null) {
49
+ const cfg = normalizeOllamaConfig(ollama || {});
50
+ await ensureDir(getKarajanHome());
51
+ await fs.writeFile(getOllamaComposePath(), buildComposeTemplate(cfg), "utf8");
52
+ return getOllamaComposePath();
53
+ }
54
+
55
+ export async function isOllamaReachable(host, healthcheckSeconds = DEFAULTS.hcS) {
56
+ const t = String(pos(healthcheckSeconds, DEFAULTS.hcS));
57
+ const r = await runCommand("curl", ["-s", "-o", "/dev/null", "-w", "%{http_code}", "--max-time", t, `${host}/api/tags`]);
58
+ return r.exitCode === 0 && r.stdout.trim().startsWith("2");
59
+ }
60
+
61
+ export async function findAvailableOllamaPort(start = DEFAULTS.port, span = 10) {
62
+ for (let p = start; p < start + span; p++) {
63
+ const free = await new Promise((res) => {
64
+ const s = net.createServer();
65
+ s.once("error", () => res(false));
66
+ s.once("listening", () => s.close(() => res(true)));
67
+ s.listen(p, "127.0.0.1");
68
+ });
69
+ if (free) return p;
70
+ }
71
+ throw new Error(`No free port in [${start}, ${start + span})`);
72
+ }
73
+
74
+ export async function waitForOllamaReady(host, timeoutMs = DEFAULTS.readyMs) {
75
+ const dl = Date.now() + timeoutMs;
76
+ while (Date.now() < dl) {
77
+ if (await isOllamaReachable(host, 2)) return true;
78
+ await new Promise((r) => setTimeout(r, 1500));
79
+ }
80
+ return false;
81
+ }
82
+
83
+ export async function ollamaUp(ollama = null) {
84
+ const cfg = normalizeOllamaConfig(ollama || {});
85
+ if (await isOllamaReachable(cfg.host, cfg.timeouts.healthcheckSeconds)) {
86
+ return { exitCode: 0, stdout: `Ollama already reachable at ${cfg.host}, skipping container start.`, stderr: "", reusedHost: cfg.host };
87
+ }
88
+ if (cfg.external) return { exitCode: 1, stdout: "", stderr: `Configured external Ollama is not reachable at ${cfg.host}.` };
89
+ const compose = await ensureComposeFile(ollama);
90
+ return runCommand("docker", ["compose", "-f", compose, "up", "-d"], { timeout: cfg.timeouts.composeUpMs });
91
+ }
92
+
93
+ export async function ollamaDown(ollama = null) {
94
+ const cfg = normalizeOllamaConfig(ollama || {});
95
+ if (cfg.external) return { exitCode: 0, stdout: "ollama.external=true, skipping Docker stop.", stderr: "" };
96
+ const compose = await ensureComposeFile(ollama);
97
+ return runCommand("docker", ["compose", "-f", compose, "stop"], { timeout: cfg.timeouts.composeControlMs });
98
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * ONNX embedder fallback (KJC-TSK-0683) — limited machines where installing
3
+ * Ollama/Docker is not viable still get a working RAG: the built-in ONNX
4
+ * embedder (all-MiniLM via @huggingface/transformers, in-process, CPU).
5
+ * The choice is PERSISTED in the project config: onnx (384) and ollama
6
+ * (768) have different vector dims, so the election must be sticky —
7
+ * an index can never be written by one and queried by the other.
8
+ */
9
+ import fs from "node:fs/promises";
10
+ import { existsSync, rmSync } from "node:fs";
11
+ import path from "node:path";
12
+ import Database from "better-sqlite3";
13
+ import { parseDocument } from "yaml";
14
+ import { getProjectConfigPath } from "../config/loader.js";
15
+ import { dbPath } from "./vec-store.js";
16
+
17
+ const ONNX_EMBEDDER = { provider: "onnx", dim: 384 };
18
+
19
+ /**
20
+ * KJC-BUG-0128: the hasRagIndex probe could leave behind an EMPTY store
21
+ * whose vec table was created at the default dim (768) — ONNX inserts (384)
22
+ * then failed on every chunk. Before the fallback indexes, delete an empty
23
+ * store (plus WAL siblings) so it is recreated at the right dim. A store
24
+ * that already HAS chunks is never touched — switching dims there would
25
+ * destroy other projects' indexes.
26
+ */
27
+ export function resetEmptyStore() {
28
+ const storePath = dbPath();
29
+ if (!existsSync(storePath)) return true;
30
+ // Read-only, schema-agnostic inspection: no DDL, no vec extension, no
31
+ // dimension assumptions — the store is only ever OPENED to be counted.
32
+ const db = new Database(storePath, { readonly: true });
33
+ try {
34
+ const hasChunksTable = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='chunks'").get();
35
+ if (hasChunksTable && db.prepare("SELECT COUNT(*) AS n FROM chunks").get().n > 0) return false;
36
+ } finally {
37
+ db.close();
38
+ }
39
+ for (const suffix of ["", "-wal", "-shm"]) rmSync(storePath + suffix, { force: true });
40
+ return true;
41
+ }
42
+
43
+ /** The same config, with the embedder swapped to the built-in ONNX. */
44
+ export function onnxConfig(config) {
45
+ return {
46
+ ...config,
47
+ rag: { ...(config?.rag || {}), embedder: { ...(config?.rag?.embedder || {}), ...ONNX_EMBEDDER } },
48
+ };
49
+ }
50
+
51
+ /**
52
+ * Persist the ONNX election in the PROJECT config file. Surgical edit via
53
+ * the yaml Document API: only rag.embedder.{provider,dim} change — the
54
+ * user's comments, ordering and every other key survive untouched.
55
+ */
56
+ export async function persistOnnxChoice(projectDir) {
57
+ const configPath = getProjectConfigPath(projectDir);
58
+ let source = "";
59
+ try {
60
+ source = await fs.readFile(configPath, "utf8");
61
+ } catch { /* no project config yet — create it */ }
62
+ const doc = parseDocument(source || "{}");
63
+ for (const [key, value] of Object.entries(ONNX_EMBEDDER)) {
64
+ doc.setIn(["rag", "embedder", key], value);
65
+ }
66
+ await fs.mkdir(path.dirname(configPath), { recursive: true });
67
+ await fs.writeFile(configPath, doc.toString(), "utf8");
68
+ return configPath;
69
+ }
@@ -0,0 +1,4 @@
1
+ // Shim: rerank now lives in karajan-core/rag so the hu-board workspace
2
+ // can consume it without a relative dep on the CLI src tree.
3
+ // KJC-TSK-0632 PR2.
4
+ export { RerankError, _resetPipeline, rerank } from "karajan-core/rag/rerank";
@@ -0,0 +1,2 @@
1
+ // Shim: retriever now lives in karajan-core/rag (KJC-TSK-0632 PR3).
2
+ export * from "karajan-core/rag/retriever";
@@ -0,0 +1,17 @@
1
+ // Shim: vec-store primitives now live in karajan-core/vec-store so
2
+ // the hu-board workspace can consume them without a relative dep on the
3
+ // CLI src tree. KJC-TSK-0511 PR4.
4
+ export {
5
+ dbPath,
6
+ openVecStore,
7
+ insertChunk,
8
+ searchSimilar,
9
+ projectSlug,
10
+ deleteChunksBySource,
11
+ getLastIndexedCommit,
12
+ setLastIndexedCommit,
13
+ getEmbeddingsByIds,
14
+ findChunkByHash,
15
+ countChunks,
16
+ searchBM25,
17
+ } from "karajan-core/vec-store";
@@ -0,0 +1,62 @@
1
+ // KJC-TSK-0441 (RAG v2.28.0 PR1) — chokidar watcher. Live re-index of plans
2
+ // + onboarding + (opt) sources, debounced. PID file arbitrates a single daemon.
3
+ // KJC-TSK-0482 — source matcher derivado del registry (multi-lang: JS/Py/Rust/Go/Java).
4
+ import { writeFileSync, readFileSync, existsSync, unlinkSync } from "node:fs";
5
+ import { extname, join } from "node:path";
6
+ import chokidar from "chokidar";
7
+ import { openVecStore, deleteChunksBySource, projectSlug } from "./vec-store.js";
8
+ import { makeGovernedEmbedder } from "./governed-embedder.js";
9
+ import { indexFile } from "./indexer.js";
10
+ import { getKarajanHome } from "../utils/paths.js";
11
+ import { getAllCodeExtensions } from "../lang/registry.js";
12
+
13
+ const PIDFILE = () => join(getKarajanHome(), "watcher.pid");
14
+ const DEFAULT_DEBOUNCE_MS = 1000;
15
+ const SKIP_SEGMENTS = new Set(["node_modules", ".git", "dist", "build", "coverage", ".karajan", ".next", ".kj", "_diet"]);
16
+ const SOURCE_EXTS = new Set(getAllCodeExtensions().map((e) => e.toLowerCase()));
17
+
18
+ export function isWatchable(path) {
19
+ if (path.includes("/.karajan/onboarding/")) return /\.md$/.test(path);
20
+ if (path.includes("/.karajan/plans/")) return /\.json$/.test(path);
21
+ if (path.split("/").some((seg) => SKIP_SEGMENTS.has(seg))) return false;
22
+ return SOURCE_EXTS.has(extname(path).toLowerCase());
23
+ }
24
+
25
+ export function startWatcher({ projectDir, config, logger = console, debounceMs = DEFAULT_DEBOUNCE_MS, withSources = false } = {}) {
26
+ if (!projectDir) throw new Error("startWatcher: projectDir is required");
27
+ const slug = projectSlug(projectDir);
28
+ const dim = config?.rag?.embedder?.dim || 768;
29
+ const db = openVecStore({ dim });
30
+ const embedder = makeGovernedEmbedder(config);
31
+ const paths = [join(getKarajanHome(), "onboarding"), join(getKarajanHome(), "plans")];
32
+ if (withSources) paths.push(projectDir);
33
+ const watcher = chokidar.watch(paths, { ignoreInitial: true, persistent: true });
34
+ const pending = new Map();
35
+ const flush = async (p) => {
36
+ pending.delete(p);
37
+ if (!existsSync(p)) { deleteChunksBySource(db, p); logger.info?.(`[rag-watcher] removed → ${p}`); return; }
38
+ if (!isWatchable(p)) return;
39
+ try { const r = await indexFile(p, { db, embedder, logger, project: slug }); logger.info?.(`[rag-watcher] reindexed ${p} → ${r.indexed} chunk(s)`); }
40
+ catch (err) { logger.warn?.(`[rag-watcher] reindex failed for ${p}: ${err.message}`); }
41
+ };
42
+ const schedule = (p) => { if (pending.has(p)) clearTimeout(pending.get(p)); pending.set(p, setTimeout(() => flush(p), debounceMs)); };
43
+ watcher.on("add", schedule).on("change", schedule).on("unlink", schedule).on("error", (err) => logger.warn?.(`[rag-watcher] ${err.message}`));
44
+ return async function stop() {
45
+ for (const t of pending.values()) clearTimeout(t);
46
+ pending.clear();
47
+ await watcher.close();
48
+ try { db.close(); } catch { /* already closed */ }
49
+ };
50
+ }
51
+
52
+ export function writePidFile(pid = process.pid) { writeFileSync(PIDFILE(), String(pid), "utf8"); }
53
+ export function clearPidFile() { if (existsSync(PIDFILE())) unlinkSync(PIDFILE()); }
54
+ export function readPidFile() {
55
+ if (!existsSync(PIDFILE())) return null;
56
+ const pid = Number(readFileSync(PIDFILE(), "utf8").trim());
57
+ return Number.isInteger(pid) && pid > 0 ? pid : null;
58
+ }
59
+ export function isPidAlive(pid) {
60
+ if (!pid) return false;
61
+ try { process.kill(pid, 0); return true; } catch { return false; }
62
+ }
@@ -0,0 +1,4 @@
1
+ // Shim: where-parser now lives in karajan-core/rag so the hu-board
2
+ // workspace can consume it without a relative dep on the CLI src tree.
3
+ // KJC-TSK-0632 PR2.
4
+ export { parseWhere, buildWhereSql } from "karajan-core/rag/where-parser";
@@ -0,0 +1,88 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ const DEFAULT_THRESHOLD = 2;
4
+
5
+ function normalizeIssueKey(value) {
6
+ if (value === null || value === undefined) return "";
7
+ return String(value).trim();
8
+ }
9
+
10
+ function buildIssueSignature(issues, keySelector) {
11
+ const counts = new Map();
12
+ for (const issue of issues || []) {
13
+ const key = normalizeIssueKey(keySelector(issue));
14
+ if (!key) continue;
15
+ counts.set(key, (counts.get(key) || 0) + 1);
16
+ }
17
+ if (counts.size === 0) return "";
18
+ return Array.from(counts.entries())
19
+ .sort(([a], [b]) => a.localeCompare(b))
20
+ .map(([key, count]) => `${key}:${count}`)
21
+ .join("|");
22
+ }
23
+
24
+ function hashSignature(signature) {
25
+ if (!signature) return "";
26
+ return createHash("sha256").update(signature).digest("hex");
27
+ }
28
+
29
+ function updateRepeatState(state, signatureHash) {
30
+ if (!signatureHash) {
31
+ return { lastHash: null, repeatCount: 0 };
32
+ }
33
+ const nextCount = signatureHash === state.lastHash ? state.repeatCount + 1 : 1;
34
+ return { lastHash: signatureHash, repeatCount: nextCount };
35
+ }
36
+
37
+ function parseThreshold(value) {
38
+ const parsed = Number(value);
39
+ if (Number.isFinite(parsed) && parsed > 0) return parsed;
40
+ return DEFAULT_THRESHOLD;
41
+ }
42
+
43
+ export function getRepeatThreshold(config) {
44
+ const raw =
45
+ config?.failFast?.repeatThreshold ??
46
+ config?.session?.repeat_detection_threshold ??
47
+ config?.session?.fail_fast_repeats ??
48
+ 2;
49
+ const value = Number(raw);
50
+ if (Number.isFinite(value) && value > 0) return value;
51
+ return 2;
52
+ }
53
+
54
+ export class RepeatDetector {
55
+ constructor({ threshold = DEFAULT_THRESHOLD } = {}) {
56
+ this.threshold = parseThreshold(threshold);
57
+ this.sonar = { lastHash: null, repeatCount: 0 };
58
+ this.reviewer = { lastHash: null, repeatCount: 0 };
59
+ }
60
+
61
+ addIteration(sonarIssues, reviewerIssues) {
62
+ const sonarSignature = buildIssueSignature(
63
+ sonarIssues || [],
64
+ (issue) => issue?.rule || issue?.message
65
+ );
66
+ const reviewerSignature = buildIssueSignature(
67
+ reviewerIssues || [],
68
+ (issue) => issue?.description || issue?.content || issue?.id
69
+ );
70
+
71
+ this.sonar = updateRepeatState(this.sonar, hashSignature(sonarSignature));
72
+ this.reviewer = updateRepeatState(this.reviewer, hashSignature(reviewerSignature));
73
+ }
74
+
75
+ isStalled() {
76
+ if (this.sonar.repeatCount >= this.threshold) {
77
+ return { stalled: true, reason: "sonar_repeat" };
78
+ }
79
+ if (this.reviewer.repeatCount >= this.threshold) {
80
+ return { stalled: true, reason: "reviewer_repeat" };
81
+ }
82
+ return { stalled: false, reason: "" };
83
+ }
84
+
85
+ getRepeatCounts() {
86
+ return { sonar: this.sonar.repeatCount, reviewer: this.reviewer.repeatCount };
87
+ }
88
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Card-first gate (KJC-TSK-0686, MG-A of épica KJC-PCS-0068): the rule
3
+ * that work starts from a card moves from playbook text into the gate.
4
+ * hu-board → the branch must reference a LIVE card (block by default);
5
+ * planning-game/external → card-shaped reference required (warn default,
6
+ * block via method_gates.card_first). Exemptions explicit and visible:
7
+ * base branch, configured prefixes (releases), KJ_ALLOW_NO_CARD=1.
8
+ */
9
+ import { listPlans, loadPlan } from "../plan/plan-store.js";
10
+ import { escapeRegExp } from "../utils/escape-regexp.js";
11
+ import { verifyCardAlive } from "./card-verify.js";
12
+
13
+ const LIVE_STATUSES = new Set(["pending", "running", "failed"]);
14
+ // Card-shaped reference: LIN-123, bb-002 and multi-segment ids like
15
+ // KJC-TSK-0684 (optional middle segments) — tight enough to skip slugs.
16
+ // Shared with the method report (MG-D): one pattern, one truth.
17
+ export const CARD_REF_RE = /\b[a-z][a-z0-9]{1,9}(?:-[a-z][a-z0-9]{1,9})*-\d{1,6}\b/i;
18
+ const DEFAULT_EXEMPT_PREFIXES = ["chore/release-"];
19
+
20
+ // Token-boundary match: BB-002 must not satisfy a branch that actually
21
+ // references BB-0022 (reviewer catch — substring matching allowed work
22
+ // onto the wrong card).
23
+ function refInBranch(ref, branchLower) {
24
+ return new RegExp(`(^|[^a-z0-9])${escapeRegExp(String(ref).toLowerCase())}([^a-z0-9]|$)`).test(branchLower);
25
+ }
26
+
27
+ async function findLiveCardInBranch(projectDir, branch) {
28
+ const needle = branch.toLowerCase();
29
+ // Scan EVERY match before deciding: any live reference satisfies the
30
+ // gate; a closed one is only reported when no live match exists
31
+ // (reviewer catch — first-match could reject a branch that also
32
+ // references a live card, depending on plan iteration order).
33
+ let closedHit = null;
34
+ for (const meta of await listPlans(projectDir)) {
35
+ const plan = await loadPlan(projectDir, meta.planId);
36
+ for (const h of plan.hus || []) {
37
+ for (const ref of [h.short_id, h.id]) {
38
+ if (ref && refInBranch(ref, needle)) {
39
+ if (LIVE_STATUSES.has(h.status)) return { ref, live: true, status: h.status };
40
+ closedHit = { ref, live: false, status: h.status };
41
+ }
42
+ }
43
+ }
44
+ }
45
+ return closedHit;
46
+ }
47
+
48
+ export async function checkCardFirst({ config = {}, projectDir = process.cwd(), branch, env = process.env, deps = {} }) {
49
+ if (env.KJ_ALLOW_NO_CARD === "1") {
50
+ return { ok: true, mode: "exempt", reason: "KJ_ALLOW_NO_CARD=1 (explicit escape hatch)" };
51
+ }
52
+ const exempt = config.method_gates?.card_first_exempt_branches || DEFAULT_EXEMPT_PREFIXES;
53
+ if (exempt.some((p) => branch.startsWith(p))) {
54
+ return { ok: true, mode: "exempt", reason: `branch prefix exempt (${branch.split("/")[0]}/…)` };
55
+ }
56
+ // The base branch belongs to the branch-first gate — card-first here
57
+ // would only double-report (and HEAD covers detached checkouts in CI).
58
+ if ([config.base_branch || "main", "main", "master", "HEAD"].includes(branch)) {
59
+ return { ok: true, mode: "exempt", reason: "base branch — the branch-first gate owns this case" };
60
+ }
61
+
62
+ const backend = config.state_backend || "hu-board";
63
+ const policy = config.method_gates?.card_first || "auto";
64
+
65
+ if (backend === "hu-board") {
66
+ const hit = await findLiveCardInBranch(projectDir, branch);
67
+ if (hit?.live) return { ok: true, mode: "pass", ref: hit.ref };
68
+ const effective = policy === "auto" ? "block" : policy;
69
+ const reason = hit
70
+ ? `branch references ${hit.ref} but its card is "${hit.status}" — card-first needs a live card (reopen it or create one: kj hu add)`
71
+ : `no live card referenced in branch "${branch}" — create it first (kj hu add "…" --id <REF>) and name the branch after it`;
72
+ return effective === "block"
73
+ ? { ok: false, mode: "block", reason }
74
+ : { ok: true, mode: "warn", reason };
75
+ }
76
+
77
+ // planning-game / external: presence check, upgraded to LIVENESS against
78
+ // the real tracker when board.verify_cmd declares an adapter
79
+ // (KJC-TSK-0732, issue #1371). Unverifiable degrades honestly — the
80
+ // result names its level so nobody mistakes branch-ref for tracker.
81
+ const boardName = config.board?.name || backend;
82
+ const effective = policy === "auto" ? "warn" : policy;
83
+ if (CARD_REF_RE.test(branch)) {
84
+ const ref = branch.match(CARD_REF_RE)[0];
85
+ const { verifyFn = verifyCardAlive } = deps;
86
+ const v = await verifyFn({ ref, verifyCmd: config.board?.verify_cmd, projectDir });
87
+ if (v.verified === false) {
88
+ const reason = `branch references ${ref} but ${boardName} reports it "${v.status}" — card-first needs a live card in the tracker (create/reopen it there)`;
89
+ return effective === "block"
90
+ ? { ok: false, mode: "block", reason, level: v.level }
91
+ : { ok: true, mode: "warn", reason, level: v.level };
92
+ }
93
+ return { ok: true, mode: "pass", ref, level: v.level, ...(v.note ? { note: v.note } : {}) };
94
+ }
95
+ const reason = `no card reference in branch "${branch}" — card-first on ${boardName}: create the card there and name the branch after it (e.g. feat/ABC-123-summary)`;
96
+ return effective === "block"
97
+ ? { ok: false, mode: "block", reason }
98
+ : { ok: true, mode: "warn", reason };
99
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * card-verify — KJC-TSK-0732 (issue #1371). With a declared tracker,
3
+ * card-first upgrades "the branch names a card" to "the tracker says that
4
+ * card is ALIVE". Transport-agnostic: `board.verify_cmd` is the USER'S
5
+ * adapter (their CLI/MCP wrapper — credentials never enter kj) printing
6
+ * {exists, live, status?} JSON on stdout. Unverifiable NEVER blocks: it
7
+ * degrades to branch-ref level SAYING SO — a gate must not claim a check
8
+ * it didn't make, in either direction. Definitive verdicts are cached
9
+ * briefly; degradations never are, so recovery is immediate.
10
+ */
11
+ import { exec } from "node:child_process";
12
+ import { createHash } from "node:crypto";
13
+ import { promisify } from "node:util";
14
+ import fs from "node:fs";
15
+ import path from "node:path";
16
+ import { extractFirstJson } from "../utils/json-extract.js";
17
+
18
+ const execAsync = promisify(exec);
19
+ export const VERIFY_TIMEOUT_MS = 4000;
20
+ const CACHE_TTL_MS = 10 * 60 * 1000;
21
+ // The ref lands inside a shell command (the user's adapter template), so it
22
+ // must be inert BY CONSTRUCTION — card-ref alphabet only, anchored (codex
23
+ // catch: same injection class as the tournament coder names).
24
+ const SAFE_REF_RE = /^[a-z0-9][a-z0-9-]{0,63}$/i;
25
+
26
+ const defaultExec = async (cmd, { cwd }) =>
27
+ execAsync(cmd, { cwd, timeout: VERIFY_TIMEOUT_MS, maxBuffer: 1024 * 1024 });
28
+
29
+ function cachePath(projectDir) {
30
+ return path.join(projectDir, ".kj", "cache", "card-verify.json");
31
+ }
32
+
33
+ /** @returns {Promise<{level: "tracker"|"branch-ref", verified: boolean|null, status?: string|null, note?: string}>}
34
+ * verified: true = alive · false = definitively not · null = degraded. */
35
+ export async function verifyCardAlive({ ref, verifyCmd, projectDir = process.cwd(), deps = {} }) {
36
+ const { execFn = defaultExec, now = Date.now } = deps;
37
+ if (!verifyCmd) {
38
+ return {
39
+ level: "branch-ref", verified: null,
40
+ note: "card-first checked the branch reference only — set board.verify_cmd to verify the card against your tracker",
41
+ };
42
+ }
43
+
44
+ if (!SAFE_REF_RE.test(String(ref))) {
45
+ return {
46
+ level: "branch-ref", verified: null,
47
+ note: `tracker verification skipped — ref "${String(ref).slice(0, 30)}" is outside the card-ref alphabet`,
48
+ };
49
+ }
50
+ let cache = {};
51
+ try {
52
+ cache = JSON.parse(fs.readFileSync(cachePath(projectDir), "utf8"));
53
+ } catch { /* no cache yet */ }
54
+ // Keyed by adapter AND ref (reviewer catch): a verdict from one
55
+ // verify_cmd must never answer for a different one.
56
+ const key = `${createHash("sha256").update(verifyCmd).digest("hex").slice(0, 12)}:${ref}`;
57
+ const hit = cache[key];
58
+ if (hit && now() - hit.at < CACHE_TTL_MS) return hit.result;
59
+
60
+ const cmd = verifyCmd.includes("{ref}") ? verifyCmd.replaceAll("{ref}", ref) : `${verifyCmd} ${ref}`;
61
+ try {
62
+ const { stdout } = await execFn(cmd, { cwd: projectDir });
63
+ const parsed = extractFirstJson(String(stdout));
64
+ if (!parsed || typeof parsed.exists !== "boolean") {
65
+ throw new Error("adapter must print JSON {exists, live, status} on stdout");
66
+ }
67
+ const alive = parsed.exists && parsed.live !== false;
68
+ const result = alive
69
+ ? { level: "tracker", verified: true, status: parsed.status ?? null }
70
+ : { level: "tracker", verified: false, status: parsed.status ?? (parsed.exists ? "not-live" : "not-found") };
71
+ try {
72
+ fs.mkdirSync(path.dirname(cachePath(projectDir)), { recursive: true });
73
+ fs.writeFileSync(cachePath(projectDir), JSON.stringify({ ...cache, [key]: { at: now(), result } }));
74
+ } catch { /* cache is best-effort — the verdict stands without it */ }
75
+ return result;
76
+ } catch (err) {
77
+ const why = String(err.message || err).split("\n")[0].slice(0, 120);
78
+ return {
79
+ level: "branch-ref", verified: null,
80
+ note: `tracker verification degraded to branch-ref (${why}) — fix board.verify_cmd or its credentials`,
81
+ };
82
+ }
83
+ }
@@ -0,0 +1,112 @@
1
+ import { runCommand } from "../utils/process.js";
2
+ import { generateSnapshotDiff } from "./snapshot-diff.js";
3
+ import { getRunContext } from "../utils/run-context.js";
4
+
5
+ // Module-scope back-compat state for callers outside a `withRunContext`
6
+ // scope. When inside a pipeline, `getRunContext()` provides per-run
7
+ // isolation (TSK-0338) — the module-level values below are only consulted
8
+ // as a last resort.
9
+
10
+ /** @type {((command: string, args?: string[], options?: object) => Promise<object>)|null} */
11
+ let _runner = null;
12
+
13
+ /** @type {Map<string, string>|null} — pre-coder filesystem snapshot for git-free diff */
14
+ let _snapshot = null;
15
+
16
+ /** @type {string|null} — project directory scope for git diff (prevents leaking unrelated changes) */
17
+ let _projectDir = null;
18
+
19
+ /** Retained for back-compat; pipelines should use `withRunContext({ projectDir })` for isolation. */
20
+ export function setProjectDir(dir) {
21
+ _projectDir = dir;
22
+ }
23
+
24
+ /** Retained for back-compat; pipelines should use `withRunContext({ snapshot })` for isolation. */
25
+ export function setSnapshot(snapshot) {
26
+ _snapshot = snapshot;
27
+ }
28
+
29
+ /** Retained for back-compat; pipelines should use `withRunContext({ runner })` for isolation. */
30
+ export function setRunner(runner) {
31
+ _runner = runner;
32
+ }
33
+
34
+ function resolveRunner() {
35
+ return getRunContext()?.runner || _runner || runCommand;
36
+ }
37
+
38
+ function resolveProjectDir() {
39
+ const ctx = getRunContext();
40
+ if (ctx && "projectDir" in ctx) return ctx.projectDir;
41
+ return _projectDir;
42
+ }
43
+
44
+ function resolveSnapshot() {
45
+ const ctx = getRunContext();
46
+ if (ctx && "snapshot" in ctx) return ctx.snapshot;
47
+ return _snapshot;
48
+ }
49
+
50
+ function run(command, args, ...rest) {
51
+ return resolveRunner()(command, args, ...rest);
52
+ }
53
+
54
+ export async function computeBaseRef({ baseBranch = "main", baseRef = null }) {
55
+ if (baseRef) return baseRef;
56
+
57
+ try {
58
+ const mergeBase = await run("git", ["merge-base", "HEAD", `origin/${baseBranch}`]);
59
+ if (mergeBase.exitCode === 0) return mergeBase.stdout.trim();
60
+
61
+ const fallback = await run("git", ["rev-parse", "HEAD~1"]);
62
+ if (fallback.exitCode === 0) return fallback.stdout.trim();
63
+
64
+ // Empty repo (zero commits): diff against the empty tree
65
+ const emptyTree = await run("git", ["hash-object", "-t", "tree", "/dev/null"]);
66
+ if (emptyTree.exitCode === 0) return emptyTree.stdout.trim();
67
+ } catch { /* git not available */ }
68
+
69
+ // No git or all strategies failed — return sentinel for snapshot-based diff
70
+ return "__snapshot__";
71
+ }
72
+
73
+ export async function generateDiff({ baseRef, stageNewFiles = false, projectDir = null }) {
74
+ // Auto-resolve projectDir from run context (TSK-0338) or module fallback
75
+ const scopeDir = projectDir || resolveProjectDir() || null;
76
+ const snapshot = resolveSnapshot();
77
+
78
+ // Try git diff first
79
+ try {
80
+ if (stageNewFiles) {
81
+ const addArgs = scopeDir ? ["-A", scopeDir] : ["-A"];
82
+ await run("git", ["add", ...addArgs]);
83
+ }
84
+ const diffArgs = ["diff", stageNewFiles ? "--cached" : "", `${baseRef}`].filter(Boolean);
85
+ // Scope diff to projectDir when it's a subdirectory (prevents leaking unrelated changes)
86
+ if (scopeDir) diffArgs.push("--", scopeDir);
87
+ const result = await run("git", diffArgs);
88
+ if (result.exitCode === 0) {
89
+ return result.stdout;
90
+ }
91
+ } catch { /* git not available or failed */ }
92
+
93
+ // Fallback: snapshot-based diff (no git needed)
94
+ if (snapshot) {
95
+ const dir = projectDir || process.cwd();
96
+ return generateSnapshotDiff(snapshot, null, dir);
97
+ }
98
+
99
+ // No git and no snapshot — generate diff of all files as new.
100
+ // TSK-0340: dropped a stray `const { takeSnapshot } = await import(...)`
101
+ // that was never used in this branch (we synthesize an empty Map and
102
+ // let generateSnapshotDiff do the file walk itself).
103
+ const dir = projectDir || process.cwd();
104
+ const emptySnapshot = new Map();
105
+ return generateSnapshotDiff(emptySnapshot, null, dir);
106
+ }
107
+
108
+ export async function getUntrackedFiles(cwd = null) {
109
+ const result = await run("git", ["ls-files", "--others", "--exclude-standard"], cwd ? { cwd } : {});
110
+ if (result.exitCode !== 0) return [];
111
+ return result.stdout.trim().split("\n").filter(Boolean);
112
+ }