@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,438 @@
1
+ import express from 'express';
2
+ import helmet from 'helmet';
3
+ import rateLimit from 'express-rate-limit';
4
+ import { join, dirname } from 'node:path';
5
+ import { fileURLToPath, pathToFileURL } from 'node:url';
6
+ import { writeFileSync, rmSync, mkdirSync, realpathSync } from 'node:fs';
7
+ import { initDb, closeDb } from './db.js';
8
+ import { fullScan, startWatcher } from './sync.js';
9
+ import apiRoutes from './routes/api.js';
10
+ import pipelineRoutes from './routes/pipeline.js';
11
+ import ragRoutes from './routes/rag.js';
12
+ import wikiRoutes from './routes/wiki.js';
13
+ import { authMiddleware } from './auth.js';
14
+ import { getOrCreateToken, getTokenPath } from './token-store.js';
15
+ import { reapZombieSessions } from './zombie-reaper.js';
16
+ import { reapZombieHus } from './hu-zombie-reaper.js';
17
+ import { setHuStatus as setHuStatusPlanMutation, setHuFailResult as setHuFailResultPlanMutation } from './plan-mutations.js';
18
+ import { cleanupEphemeralProjects } from './ephemeral-cleaner.js';
19
+ import { findAvailablePort as findAvailablePortBase } from 'karajan-core/port-check';
20
+
21
+ /**
22
+ * Path to the PID file the CLI's `kj board start` / `kj board stop`
23
+ * also use. Pre-v2.7.5 this was written ONLY by the CLI launcher,
24
+ * so a self-respawn (the 🔁 button on the board) left a stale PID
25
+ * pointing at a dead process — and the next `kj plan` saw that, gave
26
+ * up on the existing board, and spawned a new one on port 4001.
27
+ *
28
+ * Now the server writes its own PID on every start so both the CLI
29
+ * launch and the in-place restart keep the file accurate.
30
+ */
31
+ // KJC-TSK-0420: delegate to db.js::getKjHome so KARAJAN_HOME / KJ_HOME /
32
+ // VITEST all share one precedence chain.
33
+ import { getKjHome } from './db.js';
34
+ const PID_FILE = join(getKjHome(), 'hu-board.pid');
35
+
36
+ const __dirname = dirname(fileURLToPath(import.meta.url));
37
+ const PUBLIC_DIR = join(__dirname, '..', 'public');
38
+
39
+ /**
40
+ * Cache-Control headers that tell the browser to revalidate every fetch.
41
+ * Applied to HTML / JS / CSS so a server restart can't be hidden by stale
42
+ * in-memory cache. Assets with content-hash in filename can override this
43
+ * later with `immutable` if we adopt a build step.
44
+ */
45
+ function noStoreHeaders(res, filePath) {
46
+ if (/\.(html|js|css)$/i.test(filePath)) {
47
+ res.setHeader('Cache-Control', 'no-store, must-revalidate');
48
+ }
49
+ }
50
+
51
+ /** Default bind: loopback only. Override with BIND_HOST=0.0.0.0 (or any IP). */
52
+ const DEFAULT_BIND_HOST = '127.0.0.1';
53
+
54
+ /** Loopback addresses that don't need a security warning at startup. */
55
+ const LOOPBACK_ADDRESSES = new Set(['127.0.0.1', 'localhost', '::1']);
56
+
57
+ /**
58
+ * Finds an available port starting from the given port, logging each busy
59
+ * hop. Delegates to the shared util in src/utils/port-check.js.
60
+ * @param {number} startPort
61
+ * @param {number} maxAttempts
62
+ * @returns {Promise<number>}
63
+ */
64
+ function findAvailablePort(startPort, maxAttempts = 11) {
65
+ return findAvailablePortBase(startPort, maxAttempts, (port) => {
66
+ console.log(`[server] Port ${port} is busy, trying ${port + 1}...`);
67
+ });
68
+ }
69
+
70
+ /**
71
+ * Parses the desired port from env var or --port flag.
72
+ * @returns {number}
73
+ */
74
+ function getDesiredPort() {
75
+ const portArg = process.argv.find((arg, i, arr) => arr[i - 1] === '--port');
76
+ if (portArg) return parseInt(portArg, 10);
77
+ return parseInt(process.env.PORT || '4000', 10);
78
+ }
79
+
80
+ /**
81
+ * Resolve the bind host. Precedence:
82
+ * 1. `BIND_HOST` env var (set by `kj board start --bind <host>`)
83
+ * 2. Default: 127.0.0.1 (loopback only)
84
+ * @returns {string}
85
+ */
86
+ function getBindHost() {
87
+ return process.env.BIND_HOST || DEFAULT_BIND_HOST;
88
+ }
89
+
90
+ /**
91
+ * Build the security middleware stack used before any route handler.
92
+ * Exposed as a function so tests can mount the same stack on a
93
+ * disposable Express app.
94
+ * @returns {import('express').RequestHandler[]}
95
+ */
96
+ export function buildSecurityMiddleware() {
97
+ const stack = [
98
+ // Default helmet config sets X-Content-Type-Options, X-DNS-Prefetch-Control,
99
+ // X-Frame-Options, Strict-Transport-Security (when over HTTPS), and a
100
+ // conservative Content-Security-Policy. We keep CSP loose enough for the
101
+ // existing inline scripts in public/index.html to run.
102
+ helmet({
103
+ contentSecurityPolicy: {
104
+ useDefaults: true,
105
+ directives: {
106
+ // Allow inline scripts, styles AND inline event handlers
107
+ // (onclick="..."). Helmet's default puts `script-src-attr
108
+ // 'none'` which silently blocks every onclick in app.js —
109
+ // detected during the 2026-05-07 dogfooding session when
110
+ // `kj board` Sessions cards became un-clickable.
111
+ //
112
+ // Tightening this is a follow-up: app.js would need to
113
+ // migrate every inline handler to addEventListener.
114
+ "script-src": ["'self'", "'unsafe-inline'"],
115
+ "script-src-attr": ["'unsafe-inline'"],
116
+ // Google Fonts (CSS + woff2) used by index.html.
117
+ "style-src": ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
118
+ "font-src": ["'self'", "https://fonts.gstatic.com", "data:"],
119
+ "img-src": ["'self'", "data:"],
120
+ },
121
+ },
122
+ // The board is a same-origin SPA; cross-origin embedders should
123
+ // never load it, but COEP/CORP defaults sometimes break dev tools
124
+ // proxies. Keep them off until there's a concrete need.
125
+ crossOriginEmbedderPolicy: false,
126
+ }),
127
+ ];
128
+ return stack;
129
+ }
130
+
131
+ /**
132
+ * Build the rate-limit middleware for `/api`.
133
+ *
134
+ * Default is 600 req/min per IP (raised from 300 in KJC-BUG-0039 —
135
+ * the first-load fanout of the dashboard + multiple tabs + SSE
136
+ * reconnects after a refresh could push past 300/min and hit the user
137
+ * with a 429 on the very first page they see).
138
+ *
139
+ * Override via env var `HU_BOARD_RATE_LIMIT=<n>` for unusual setups
140
+ * (load tests, multi-board farms behind a shared NAT).
141
+ *
142
+ * SSE (`/api/events`) is exempt — a single persistent stream should
143
+ * not count toward a per-minute request budget, and tab refreshes
144
+ * legitimately create new SSE connections.
145
+ *
146
+ * @returns {import('express').RequestHandler}
147
+ */
148
+ export function buildRateLimiter() {
149
+ const raw = Number(process.env.HU_BOARD_RATE_LIMIT);
150
+ const limit = Number.isFinite(raw) && raw > 0 ? raw : 600;
151
+ return rateLimit({
152
+ windowMs: 60 * 1000,
153
+ limit,
154
+ standardHeaders: 'draft-7', // RateLimit-* headers
155
+ legacyHeaders: false, // drop X-RateLimit-*
156
+ skip: (req) => req.path === '/events' || req.path.startsWith('/events/'),
157
+ message: {
158
+ error: 'Too Many Requests',
159
+ message: 'Rate limit exceeded. Try again in a few seconds.',
160
+ },
161
+ });
162
+ }
163
+
164
+ /**
165
+ * Main entry point: initializes database, syncs data, and starts the server.
166
+ */
167
+ async function main() {
168
+ // Initialize SQLite. better-sqlite3 is a native module — when its
169
+ // prebuilt binary is missing or ABI-mismatched (Node was upgraded),
170
+ // initDb() throws. Surface an actionable message instead of a bare
171
+ // stack trace so the user knows exactly what to run.
172
+ console.log('[server] Initializing database...');
173
+ try {
174
+ initDb();
175
+ } catch (err) {
176
+ console.error('[server] FATAL: could not initialize the database.');
177
+ console.error('[server] The native module better-sqlite3 failed to load, or the DB file could not be opened.');
178
+ console.error('[server] Fix: run `npm rebuild better-sqlite3` (or reinstall karajan-code).');
179
+ console.error('[server] Original error:', err?.stack || String(err));
180
+ process.exit(1);
181
+ }
182
+
183
+ // Full scan of existing files BEFORE the reaper runs. Otherwise
184
+ // fullScan would read every session.json from disk and overwrite
185
+ // the reap-failed status the reaper just set, resurrecting the
186
+ // zombie on every boot. Order matters here.
187
+ console.log('[server] Running full scan of JSON files...');
188
+ fullScan();
189
+
190
+ // Reap any session that the orchestrator left in a non-terminal
191
+ // state. Updates BOTH the DB row AND the session.json on disk so
192
+ // the next boot's fullScan sees a stable `failed` status and never
193
+ // resurrects the zombi.
194
+ try {
195
+ const dbHandle = (await import('./db.js')).getDb();
196
+ const reaped = reapZombieSessions({ db: dbHandle });
197
+ if (reaped.length > 0) {
198
+ console.log(`[zombie-reaper] reaped ${reaped.length} stale session(s):`);
199
+ for (const r of reaped) {
200
+ const diskNote = r.disk_persisted ? '' : ' (disk write failed — DB-only)';
201
+ console.log(` - ${r.id} (was ${r.status_before}; ${r.reason})${diskNote}`);
202
+ }
203
+ } else {
204
+ console.log('[zombie-reaper] no zombie sessions detected');
205
+ }
206
+ } catch (err) {
207
+ // Never block startup on the reaper. Log and move on.
208
+ console.warn(`[zombie-reaper] skipped: ${err.message}`);
209
+ }
210
+
211
+ // KJC-TSK-0394 step 3 — resetear a `pending` HUs colgadas en
212
+ // coding/reviewing >30min (orquestador crashed mid-iteration). NO
213
+ // a `failed` porque no sabemos el outcome; `result` se preserva.
214
+ try {
215
+ const dbHandle = (await import('./db.js')).getDb();
216
+ const reapedHus = reapZombieHus({
217
+ db: dbHandle,
218
+ setHuStatus: setHuStatusPlanMutation,
219
+ setHuFailResult: setHuFailResultPlanMutation,
220
+ });
221
+ if (reapedHus.length > 0) {
222
+ console.log(`[hu-zombie-reaper] reset ${reapedHus.length} stuck HU(s) to pending:`);
223
+ for (const r of reapedHus) {
224
+ const planNote = r.plan_persisted ? '' : ' (plan write failed — DB-only)';
225
+ console.log(` - ${r.id} (was ${r.status_before}; ${r.reason})${planNote}`);
226
+ }
227
+ } else {
228
+ console.log('[hu-zombie-reaper] no stuck HUs detected');
229
+ }
230
+ } catch (err) {
231
+ console.warn(`[hu-zombie-reaper] skipped: ${err.message}`);
232
+ }
233
+
234
+ // KJC-TSK-0414 PR2 — reconciliación de sesiones hibernadas al arrancar.
235
+ // Para cada session en ~/.kj/standby/<id>.json:
236
+ // - cooldownUntil <= now → spawn `kj resume <id>` inmediato
237
+ // - cooldownUntil > now → re-programa setTimeout que dispara en cooldown
238
+ // Idempotente (lockfile per session). Cero polling.
239
+ try {
240
+ const { reconcileAll } = await import('karajan-core/standby-scheduler');
241
+ const { spawn } = await import('node:child_process');
242
+ const { join, dirname } = await import('node:path');
243
+ const { fileURLToPath } = await import('node:url');
244
+ const KJ_CLI = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'src', 'cli.js');
245
+ const onResume = (sessionId) => {
246
+ console.log(`[standby] reanudando sesión ${sessionId} (spawn kj standby resume)`);
247
+ try {
248
+ const child = spawn(process.execPath, [KJ_CLI, 'standby', 'resume', sessionId], {
249
+ detached: true, stdio: 'ignore', env: process.env,
250
+ });
251
+ child.unref();
252
+ } catch (err) {
253
+ console.warn(`[standby] spawn resume falló para ${sessionId}: ${err.message}`);
254
+ }
255
+ };
256
+ const r = reconcileAll({ onResume, logger: { info: console.log.bind(console), warn: console.warn.bind(console) } });
257
+ if (r.total > 0) console.log(`[standby] reconcile: ${r.total} session(es) — ${r.immediate} resume inmediato, ${r.scheduled} programadas`);
258
+ } catch (err) {
259
+ console.warn(`[standby] reconcile skipped: ${err.message}`);
260
+ }
261
+
262
+ // KJC-TSK-0371 (board polish #3) — clean up ephemeral projects
263
+ // (`/tmp/`-rooted, `tmp_*`/`test_*`/`demo_*`/`kj-test-*` prefixed)
264
+ // that have been inactive for >24h. Runs AFTER the zombie-reaper
265
+ // so any session that just got marked failed counts toward
266
+ // last_activity correctly. The user can override per-project via
267
+ // PATCH /api/projects/:id/is-test.
268
+ try {
269
+ const dbHandle = (await import('./db.js')).getDb();
270
+ const cleaned = cleanupEphemeralProjects({ db: dbHandle });
271
+ if (cleaned.length > 0) {
272
+ console.log(`[ephemeral-cleaner] removed ${cleaned.length} ephemeral project(s):`);
273
+ for (const c of cleaned) {
274
+ console.log(` - ${c.id} (${c.reason}; ${c.stories_deleted} stories, ${c.sessions_deleted} sessions cascaded)`);
275
+ }
276
+ } else {
277
+ console.log('[ephemeral-cleaner] no ephemeral projects detected');
278
+ }
279
+ } catch (err) {
280
+ console.warn(`[ephemeral-cleaner] skipped: ${err.message}`);
281
+ }
282
+
283
+ // Start file watcher
284
+ const watcher = startWatcher();
285
+
286
+ // Bootstrap the auth token before mounting routes. The token is
287
+ // only ENFORCED for non-loopback peers (see auth.js), but we set
288
+ // it unconditionally so flipping `--bind 0.0.0.0` later doesn't
289
+ // require a manual file fiddle.
290
+ if (!process.env.HU_BOARD_TOKEN) {
291
+ const token = getOrCreateToken();
292
+ if (token) process.env.HU_BOARD_TOKEN = token;
293
+ }
294
+
295
+ // Create Express app
296
+ const app = express();
297
+ app.use(...buildSecurityMiddleware());
298
+ app.use(express.json());
299
+ app.use(express.static(PUBLIC_DIR, { setHeaders: noStoreHeaders, etag: false, lastModified: false }));
300
+ app.use('/api', buildRateLimiter(), authMiddleware(), apiRoutes);
301
+ app.use('/api/pipeline', authMiddleware(), pipelineRoutes);
302
+ app.use('/api/rag', authMiddleware(), ragRoutes);
303
+ app.use('/api/wiki', authMiddleware(), wikiRoutes);
304
+
305
+ // SPA fallback: serve index.html for non-API, non-static routes
306
+ app.get('/{*splat}', (_req, res) => {
307
+ res.setHeader('Cache-Control', 'no-store, must-revalidate');
308
+ res.sendFile(join(PUBLIC_DIR, 'index.html'));
309
+ });
310
+
311
+ // Find available port (always probed against loopback so the
312
+ // result is meaningful regardless of bind host).
313
+ const desiredPort = getDesiredPort();
314
+ const port = await findAvailablePort(desiredPort);
315
+ const bindHost = getBindHost();
316
+ const isLoopback = LOOPBACK_ADDRESSES.has(bindHost);
317
+
318
+ const server = app.listen(port, bindHost, () => {
319
+ // Write the PID file so the CLI's `kj board status / stop` and
320
+ // the next `kj plan`'s startBoard() check find this server. The
321
+ // file used to be written only by `kj board start`'s launcher,
322
+ // which meant the 🔁 self-respawn left a stale PID and the next
323
+ // auto-start spawned a duplicate board on the next free port.
324
+ try {
325
+ mkdirSync(dirname(PID_FILE), { recursive: true });
326
+ writeFileSync(PID_FILE, String(process.pid), 'utf8');
327
+ } catch (err) {
328
+ console.warn(`[server] could not write PID file: ${err.message}`);
329
+ }
330
+ const visibleHost = isLoopback ? 'localhost' : bindHost;
331
+ console.log(`\n Karajan HU Board`);
332
+ console.log(` -----------------`);
333
+ console.log(` PID: ${process.pid}`);
334
+ console.log(` Running at: http://${visibleHost}:${port}`);
335
+ console.log(` Data dir: ${process.env.KJ_HOME || '~/.karajan'}`);
336
+ if (!isLoopback) {
337
+ const token = process.env.HU_BOARD_TOKEN;
338
+ console.log('');
339
+ console.log(' ⚠️ Bound to non-loopback interface.');
340
+ console.log(` Token required for non-loopback peers: ${getTokenPath()}`);
341
+ if (token) {
342
+ console.log(` URL with token: http://${bindHost}:${port}/?token=${token}`);
343
+ }
344
+ }
345
+ console.log('');
346
+ });
347
+
348
+ // Graceful shutdown
349
+ //
350
+ // Two pre-2026-05-07 footguns this fixes:
351
+ //
352
+ // 1) `server.close()` was called LAST, so for ~1s after `kj board
353
+ // stop` the HTTP server stayed up while watcher/DB cleanup ran.
354
+ // A browser refresh during that window — exactly the user's
355
+ // "1 segundo después se carga el board" report — saw a working
356
+ // board because the old keep-alive connections were still
357
+ // being served. Now we tell the server to stop accepting new
358
+ // connections IMMEDIATELY.
359
+ //
360
+ // 2) `server.close()` only waits for active connections to drain.
361
+ // The browser's HTTP/1.1 keep-alive connections can stay open
362
+ // for minutes, so the daemon process never actually exited
363
+ // after stop. `closeAllConnections()` (Node 18.2+) force-kills
364
+ // every active socket so the daemon can exit promptly.
365
+ //
366
+ // 3) A 5s hard deadline guarantees exit even if something goes
367
+ // wrong with the cleanup. SIGKILL-by-the-OS is the cure if
368
+ // the user really wants out, but a clean exit is the goal.
369
+ let shuttingDown = false;
370
+ const shutdown = () => {
371
+ if (shuttingDown) return;
372
+ shuttingDown = true;
373
+ console.log('\n[server] Shutting down...');
374
+ // 1. Stop accepting new connections + force-close existing ones.
375
+ server.close(() => {
376
+ // The cleanup happens in the close callback so we don't run it
377
+ // before the server has detached from its listening socket.
378
+ try { watcher.close(); } catch { /* ignore */ }
379
+ try { closeDb(); } catch { /* ignore */ }
380
+ try { rmSync(PID_FILE, { force: true }); } catch { /* ignore */ }
381
+ console.log('[server] Shutdown complete.');
382
+ process.exit(0);
383
+ });
384
+ if (typeof server.closeAllConnections === 'function') {
385
+ server.closeAllConnections();
386
+ }
387
+ // 5s hard deadline: if the close callback hasn't fired by then,
388
+ // something is wedged — exit anyway. unref so this timer doesn't
389
+ // itself keep the loop alive.
390
+ setTimeout(() => {
391
+ console.warn('[server] Forced exit after 5s shutdown deadline.');
392
+ process.exit(1);
393
+ }, 5000).unref();
394
+ };
395
+
396
+ process.on('SIGINT', shutdown);
397
+ process.on('SIGTERM', shutdown);
398
+ }
399
+
400
+ /**
401
+ * Whether this module runs as the board daemon. The legacy check
402
+ * `import.meta.url === ` + "`file://${process.argv[1]}`" + ` silently
403
+ * broke the daemon on Windows (backslashes), linked / global installs
404
+ * (import.meta.url resolves symlinks, process.argv[1] does not) and
405
+ * paths with spaces (percent-encoding): main() never ran, the process
406
+ * exited 0 and nothing ever reached hu-board.log. The launcher now
407
+ * sets KJ_BOARD_DAEMON=1, which we trust first; the normalised URL
408
+ * comparison (symlinks resolved on both sides) is the fallback.
409
+ * Exported so tests can exercise it without spawning a process.
410
+ * @returns {boolean}
411
+ */
412
+ export function isDaemonEntryPoint() {
413
+ if (process.env.KJ_BOARD_DAEMON === '1') return true;
414
+ try {
415
+ return import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href;
416
+ } catch {
417
+ return false;
418
+ }
419
+ }
420
+
421
+ // Only run main() when this file is the entry point. Tests import
422
+ // the helpers above without spinning up the server.
423
+ if (isDaemonEntryPoint()) {
424
+ // A dying daemon must never die in silence: the launcher wires
425
+ // stderr to hu-board.log, so log the cause before exiting non-zero.
426
+ process.on('uncaughtException', (err) => {
427
+ console.error('[server] FATAL uncaught exception:', err?.stack || String(err));
428
+ process.exit(1);
429
+ });
430
+ process.on('unhandledRejection', (reason) => {
431
+ console.error('[server] FATAL unhandled rejection:', reason?.stack || String(reason));
432
+ process.exit(1);
433
+ });
434
+ main().catch((err) => {
435
+ console.error('[server] Fatal error:', err?.stack || err);
436
+ process.exit(1);
437
+ });
438
+ }