@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,111 @@
1
+ /**
2
+ * `kj policy add "<regla hablada>"` (PL-C, KJC-TSK-0735) — el usuario habla,
3
+ * kj escribe: un agente traduce a UNA regla candidata del vocabulario
4
+ * CERRADO, parsePolicy valida el merge (lo intraducible se RECHAZA, jamás
5
+ * se inventa — el YAML es artefacto, no interfaz), se propone el diff y NO
6
+ * se aplica sin confirmación explícita (--yes).
7
+ */
8
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
9
+ import { join } from "node:path";
10
+ import yaml from "js-yaml";
11
+ import { parsePolicy } from "@karajan-family/governance";
12
+ import { createAgent } from "../agents/index.js";
13
+ import { resolveRole } from "../config/role-resolver.js";
14
+
15
+ const PROMPT = (text) => `Traduce esta regla de proyecto hablada a un FRAGMENTO YAML del vocabulario
16
+ cerrado v1 de policy de Karajan. Vocabulario COMPLETO (nada fuera de esto):
17
+ roles.<rol>.write|shell con allow/deny (listas de globs o patrones de
18
+ comando), enforcement (warn|deny), class (security); invariants con
19
+ {id, kind: diff-threshold, metric: net_lines_added, max, enforcement}.
20
+ Responde SOLO el fragmento YAML (claves raíz: roles y/o invariants), sin
21
+ explicación ni fences. Si la regla NO cabe en el vocabulario, responde
22
+ exactamente: NO_TRADUCIBLE.
23
+
24
+ Regla: ${text}`;
25
+
26
+ async function defaultTranslate({ text, config, logger }) {
27
+ const role = resolveRole(config, "reviewer");
28
+ const res = await createAgent(role.provider, config, logger).reviewTask({ prompt: PROMPT(text), role: "reviewer" });
29
+ if (!res?.ok) throw new Error(`el agente traductor (${role.provider}) falló: ${res?.error || "sin salida"}`);
30
+ return String(res.output || "").replace(/^```[a-z]*\n?|```\s*$/g, "").trim();
31
+ }
32
+
33
+ // Merge conservador: las listas se CONCATENAN (añadir jamás borra), los
34
+ // escalares nuevos solo entran donde no había valor.
35
+ function mergeFragment(doc, frag) {
36
+ const out = { version: 1, ...doc };
37
+ for (const [role, caps] of Object.entries(frag.roles || {})) {
38
+ out.roles ||= {};
39
+ out.roles[role] ||= {};
40
+ for (const [cap, spec] of Object.entries(caps || {})) {
41
+ const cur = (out.roles[role][cap] ||= {});
42
+ for (const [k, v] of Object.entries(spec || {})) {
43
+ cur[k] = Array.isArray(v) ? [...new Set([...(cur[k] || []), ...v])] : (cur[k] ?? v);
44
+ }
45
+ }
46
+ }
47
+ if (frag.invariants) out.invariants = [...(doc.invariants || []), ...frag.invariants];
48
+ return out;
49
+ }
50
+
51
+ const naiveDiff = (oldText, newText) => {
52
+ const oldLines = new Set(oldText.split("\n"));
53
+ const newLines = new Set(newText.split("\n"));
54
+ return [
55
+ ...[...oldLines].filter((l) => l.trim() && !newLines.has(l)).map((l) => `- ${l}`),
56
+ ...[...newLines].filter((l) => l.trim() && !oldLines.has(l)).map((l) => `+ ${l}`),
57
+ ].join("\n");
58
+ };
59
+
60
+ export async function policyAddCommand({ text, config = {}, flags = {}, logger = console, deps = {} }) {
61
+ const projectDir = config?.projectDir || process.cwd();
62
+ if (!text?.trim()) {
63
+ logger.error?.('policy add: falta la regla — kj policy add "el coder no toca los workflows"');
64
+ return 1;
65
+ }
66
+ const { translateFn = defaultTranslate } = deps;
67
+ const raw = await translateFn({ text, config, logger });
68
+ if (raw.includes("NO_TRADUCIBLE")) {
69
+ logger.error?.("policy add: la regla no cabe en el vocabulario cerrado v1 — no se inventa nada; exprésala como write/shell/diff-threshold o queda como instrucción de prompt");
70
+ return 1;
71
+ }
72
+ let frag;
73
+ try {
74
+ frag = yaml.load(raw);
75
+ } catch (err) {
76
+ logger.error?.(`policy add: el agente no devolvió YAML válido (${err.message}) — nada que aplicar`);
77
+ return 1;
78
+ }
79
+ if (typeof frag !== "object" || frag === null || Object.keys(frag).some((k) => !["roles", "invariants"].includes(k))) {
80
+ logger.error?.("policy add: el fragmento solo puede declarar roles y/o invariants — rechazado (vocabulario cerrado)");
81
+ return 1;
82
+ }
83
+
84
+ const file = join(projectDir, ".karajan", "policy.yml");
85
+ let current = "";
86
+ try {
87
+ current = readFileSync(file, "utf8");
88
+ } catch { /* sin policy previa: se propone crearla */ }
89
+ const merged = mergeFragment(current ? yaml.load(current) || {} : {}, frag);
90
+ const proposed = yaml.dump(merged, { lineWidth: 100 });
91
+ const { errors } = parsePolicy(proposed);
92
+ if (errors.length > 0) {
93
+ for (const e of errors) logger.error?.(`✗ ${e}`);
94
+ logger.error?.("policy add: la regla traducida no valida contra el vocabulario del motor — rechazada, no se inventa");
95
+ return 1;
96
+ }
97
+
98
+ logger.info?.(`policy add — diff propuesto para .karajan/policy.yml:\n${naiveDiff(current, proposed)}`);
99
+ if (current.includes("#")) {
100
+ logger.warn?.("⚠ policy add: el fichero actual tiene comentarios y el rewrite NO los preserva — revisa el diff antes de confirmar");
101
+ }
102
+ if (!flags.yes) {
103
+ logger.info?.("policy add: NADA aplicado — re-ejecuta con --yes para escribir (la confirmación es tuya, no del agente)");
104
+ return 0;
105
+ }
106
+ // Primer run sin .karajan/ (catch de codex): crear el padre, no reventar.
107
+ mkdirSync(join(projectDir, ".karajan"), { recursive: true });
108
+ writeFileSync(file, proposed, "utf8");
109
+ logger.info?.("✓ policy add: regla aplicada — los gates (review, pre-commit, tier C) la aplican desde ya");
110
+ return 0;
111
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Adaptador del registro de decisiones para karajan-code (GOV-C,
3
+ * KJC-TSK-0747): almacén `.karajan/policy-decisions.jsonl`; policy_hash =
4
+ * sha256 del policy.yml CRUDO (null sin policy — la decisión lo dice, no
5
+ * lo inventa). Append no atómico entre procesos: aceptado, el chokepoint
6
+ * de commit es secuencial.
7
+ */
8
+ import { appendFileSync, mkdirSync, readFileSync } from "node:fs";
9
+ import { createHash } from "node:crypto";
10
+ import { join } from "node:path";
11
+ import { recordDecision } from "@karajan-family/governance";
12
+
13
+ const filePath = (projectDir) => join(projectDir, ".karajan", "policy-decisions.jsonl");
14
+
15
+ export function policyFileHash(projectDir) {
16
+ try {
17
+ return createHash("sha256").update(readFileSync(join(projectDir, ".karajan", "policy.yml"), "utf8"), "utf8").digest("hex");
18
+ } catch {
19
+ return null;
20
+ }
21
+ }
22
+
23
+ /** @returns {object} the sealed record. */
24
+ export function recordGateDecision(projectDir, entry) {
25
+ return recordDecision({
26
+ entry,
27
+ deps: {
28
+ lastLine: () => {
29
+ try {
30
+ const lines = readFileSync(filePath(projectDir), "utf8").split("\n").filter((l) => l.trim());
31
+ return lines.at(-1) ?? null;
32
+ } catch {
33
+ return null;
34
+ }
35
+ },
36
+ append: (line) => {
37
+ mkdirSync(join(projectDir, ".karajan"), { recursive: true });
38
+ appendFileSync(filePath(projectDir), `${line}\n`, "utf8");
39
+ },
40
+ },
41
+ });
42
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * karajan-code policy ADAPTER (GOV-A, KJC-TSK-0745, ADR 0003). El motor
3
+ * vive en @karajan-family/governance (packages/governance): aquí queda SOLO lo
4
+ * que es de este dominio — dónde vive el fichero de policy, los defaults
5
+ * inexcepcionables del supervisor del Sentinel, y el mapeo de tools del
6
+ * harness (Write/Edit/Bash…) a capabilities del kernel. La superficie
7
+ * pública de este módulo no cambia: loadPolicy/evalToolCall/checkStagedDiff
8
+ * siguen siendo lo que los gates de PL-B consumen.
9
+ */
10
+ import { readFileSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import {
13
+ ALLOW_VERDICT,
14
+ checkArtifacts,
15
+ denyVerdict,
16
+ evalShell,
17
+ evalWrite,
18
+ parsePolicy,
19
+ DEFAULT_POLICY as KERNEL_DEFAULT_POLICY,
20
+ } from "@karajan-family/governance";
21
+
22
+ // Defaults inexcepcionables de karajan-code: los ficheros del supervisor
23
+ // (antes hardcodeados en el PRETOOL del Sentinel — PL-B). Misma semántica
24
+ // textual: nombrarlos deniega, con o sin root, y la policy no los debilita.
25
+ export const SUPERVISOR_DEFAULTS = [
26
+ {
27
+ id: "defaults.supervisor.write",
28
+ pattern: /\.claude\/settings\.json\b|\.karajan\/(hooks|harness)\//,
29
+ message: "nombra ficheros del supervisor — solo el humano los modifica, fuera de la sesión",
30
+ },
31
+ ];
32
+
33
+ // Tools del harness: mapeo acción → capability del kernel. Registro cerrado
34
+ // (fallo de solomon: lo desconocido no se permite a un rol DECLARADO).
35
+ const WRITE_TOOLS = new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
36
+ const READONLY_TOOLS = new Set(["Read", "Grep", "Glob", "LS", "WebFetch", "WebSearch"]);
37
+
38
+ export const DEFAULT_POLICY = KERNEL_DEFAULT_POLICY;
39
+
40
+ function defaultReadFile(projectDir) {
41
+ try {
42
+ return readFileSync(join(projectDir, ".karajan", "policy.yml"), "utf8");
43
+ } catch (err) {
44
+ // Solo ENOENT es "sin policy"; EACCES/EIO seria un fallback silencioso.
45
+ if (err.code === "ENOENT") return null;
46
+ throw err;
47
+ }
48
+ }
49
+
50
+ /**
51
+ * @returns {{policy: object, errors: string[]}} errors non-empty ⇒ the file
52
+ * declares something the engine cannot enforce — callers must surface it.
53
+ */
54
+ export function loadPolicy({ projectDir = process.cwd(), deps = {} } = {}) {
55
+ const { readFile = defaultReadFile } = deps;
56
+ let raw;
57
+ try {
58
+ raw = readFile(projectDir);
59
+ } catch (err) {
60
+ return {
61
+ policy: { ...KERNEL_DEFAULT_POLICY, defaults: SUPERVISOR_DEFAULTS },
62
+ errors: [`policy.yml: ilegible (${err.code || err.message}) — presente pero no leible NO es "sin policy"`],
63
+ };
64
+ }
65
+ return parsePolicy(raw, { defaults: SUPERVISOR_DEFAULTS });
66
+ }
67
+
68
+ /** One tool call against the policy. `root` permite evaluar file_path
69
+ * absolutos. Un rol DECLARADO solo usa tools del registro: lo desconocido
70
+ * es no-verificable ⇒ deny (roles sin declarar quedan como hoy). */
71
+ export function evalToolCall(policy, { role = "coder", tool, input = {}, root = null }) {
72
+ if (WRITE_TOOLS.has(tool)) return evalWrite(policy, role, input.file_path || input.notebook_path, root);
73
+ if (tool === "Bash") return evalShell(policy, role, input.command);
74
+ if (READONLY_TOOLS.has(tool) || !policy.roles?.[role]) return ALLOW_VERDICT;
75
+ return denyVerdict(`roles.${role}.tools`, `tool "${tool}" fuera del registro de la policy — no verificable para el rol declarado ${role}`);
76
+ }
77
+
78
+ /** El gate del RESULTADO en este dominio: los artefactos son los ficheros
79
+ * del diff staged y la métrica sale de numstat. */
80
+ export function checkStagedDiff(policy, facts) {
81
+ return checkArtifacts(policy, facts);
82
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Adaptador de excepciones de karajan-code (GOV-A, KJC-TSK-0745). El
3
+ * registro vive en el kernel (@karajan-family/governance); aquí solo lo que es de
4
+ * este dominio: la identidad (git user + usuario del SO — DECLARADA, no
5
+ * autenticada) y el destino append-only `.karajan/policy-exceptions.jsonl`.
6
+ */
7
+ import { appendFileSync, mkdirSync, readFileSync } from "node:fs";
8
+ import { join } from "node:path";
9
+ import { userInfo } from "node:os";
10
+ import { spawnSync } from "node:child_process";
11
+ import { recordPolicyException as kernelRecord } from "@karajan-family/governance";
12
+
13
+ function defaultIdentity(projectDir) {
14
+ const git = (args) => spawnSync("git", ["-C", projectDir, "config", ...args], { encoding: "utf8" }).stdout?.trim() || null;
15
+ // grade DECLARADA: git+os es atribución, no autenticación — el registro
16
+ // no debe venderse como más evidencia de la que es (GOV-B).
17
+ return { git: `${git(["user.name"]) ?? "?"} <${git(["user.email"]) ?? "?"}>`, os: userInfo().username, grade: "declarada" };
18
+ }
19
+
20
+ /**
21
+ * Standing exceptions del dominio code: las permanentes del jsonl. Parse
22
+ * TOLERANTE — una línea corrupta se descarta CONTÁNDOLA (el gate la
23
+ * reporta), jamás rompe la evaluación. Las puntuales no dan standing.
24
+ * @returns {{standing: object[], discarded: number}}
25
+ */
26
+ export function loadStandingExceptions(projectDir) {
27
+ let raw;
28
+ try {
29
+ raw = readFileSync(join(projectDir, ".karajan", "policy-exceptions.jsonl"), "utf8");
30
+ } catch {
31
+ return { standing: [], discarded: 0 };
32
+ }
33
+ const standing = [];
34
+ let discarded = 0;
35
+ for (const line of raw.split("\n")) {
36
+ if (!line.trim()) continue;
37
+ try {
38
+ const rec = JSON.parse(line);
39
+ // JSON válido pero no-objeto (null, número…) es tan corrupto como el
40
+ // que no parsea: se descarta contando (catch de codex, explícito).
41
+ if (typeof rec !== "object" || rec === null) discarded += 1;
42
+ else if (rec.scopeKind === "permanente") standing.push(rec);
43
+ } catch {
44
+ discarded += 1;
45
+ }
46
+ }
47
+ return { standing, discarded };
48
+ }
49
+
50
+ function defaultAppend(projectDir, line) {
51
+ const dir = join(projectDir, ".karajan");
52
+ mkdirSync(dir, { recursive: true });
53
+ appendFileSync(join(dir, "policy-exceptions.jsonl"), line, "utf8");
54
+ }
55
+
56
+ /** @returns {object} the recorded entry (with ts + who resolved). */
57
+ export function recordPolicyException({ projectDir, entry, deps = {} }) {
58
+ const { append = defaultAppend, identity = defaultIdentity } = deps;
59
+ return kernelRecord({ projectDir, entry, deps: { append, identity } });
60
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * privacy onboarding (KJC-TSK-0707, PV-D) — the user never fills YAML by
3
+ * hand: `kj env install` asks and kj writes `~/.karajan/privacy.yml`.
4
+ * Every question skips on Enter; answers are NEVER echoed back.
5
+ */
6
+
7
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
8
+ import { dirname } from "node:path";
9
+ import { stringify as stringifyYaml } from "yaml";
10
+ import { privacyConfigPath } from "./scan.js";
11
+
12
+ export async function ensurePrivacyList({ wizard = null, logger = console, isTTY = process.stdout.isTTY } = {}) {
13
+ const target = privacyConfigPath();
14
+ if (existsSync(target)) return { created: false, reason: "present" };
15
+ if (!isTTY || !wizard) {
16
+ logger.info?.(`⚠ privacy: no personal denylist yet — re-run interactively or create ${target} (personal:/allow:) so YOUR data blocks at every outbound boundary`);
17
+ return { created: false, reason: "non-interactive" };
18
+ }
19
+ logger.info?.("privacy: let's protect your personal data at every outbound boundary (Enter skips any question).");
20
+ const csv = (s) => String(s || "").split(",").map((x) => x.trim()).filter(Boolean);
21
+ const personal = [
22
+ ...csv(await wizard.input("Personal emails that must NEVER be published (comma-separated):")),
23
+ ...csv(await wizard.input("Personal phone / ID numbers to block (comma-separated, optional):")),
24
+ ...csv(await wizard.input("Full name to block (optional):")),
25
+ ];
26
+ const allow = csv(await wizard.input("Public identities that are FINE to publish (handles, comma-separated):"));
27
+ if (personal.length === 0 && allow.length === 0) {
28
+ logger.info?.("privacy: skipped — generic PII detection still warns; create the denylist later to make YOUR data block");
29
+ return { created: false, reason: "skipped" };
30
+ }
31
+ mkdirSync(dirname(target), { recursive: true });
32
+ writeFileSync(target, `# kj privacy denylist — GLOBAL, never commit this file to any repo.\n${stringifyYaml({ personal, allow })}`);
33
+ logger.info?.(`✓ privacy: denylist written to ${target} (${personal.length} entrada(s), values not echoed)`);
34
+ return { created: true, entries: personal.length };
35
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * privacy — the outbound boundary scanner (KJC-TSK-0704, epic KJC-PCS-0070).
3
+ * Born from a real incident (personal emails published on a landing):
4
+ * outputs get sanitized at boundaries like inputs do. Engine: karajan-rag's
5
+ * audited redactPII line by line — detects AND masks in one pass, so a
6
+ * finding never echoes the datum. On top, the user's denylist from
7
+ * `~/.karajan/privacy.yml` — GLOBAL, never in a repo: the list is sensitive.
8
+ */
9
+
10
+ import { existsSync, lstatSync, readFileSync, readdirSync } from "node:fs";
11
+ import os from "node:os";
12
+ import { extname, join } from "node:path";
13
+ import { parse as parseYaml } from "yaml";
14
+ import { redactPII } from "karajan-rag";
15
+
16
+ const SKIP_DIRS = new Set(["node_modules", ".git", ".kj"]);
17
+ const BINARY_EXT = new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".ico", ".pdf", ".zip", ".gz", ".tgz", ".woff", ".woff2", ".ttf", ".eot", ".mp4", ".db", ".sqlite"]);
18
+ const MAX_FILE_BYTES = 512 * 1024;
19
+
20
+ export function privacyConfigPath(home = os.homedir()) {
21
+ // KJ_PRIVACY_CONFIG: test/CI override of the global denylist location.
22
+ return process.env.KJ_PRIVACY_CONFIG || join(home, ".karajan", "privacy.yml");
23
+ }
24
+
25
+ /** Personal denylist + public-identity allowlist. Absent file → generics only. */
26
+ export function loadPrivacyList({ home = os.homedir() } = {}) {
27
+ try {
28
+ const raw = parseYaml(readFileSync(privacyConfigPath(home), "utf8")) || {};
29
+ const clean = (xs) => (Array.isArray(xs) ? xs.map(String).map((s) => s.trim()).filter(Boolean) : []);
30
+ return { personal: clean(raw.personal), allow: clean(raw.allow), present: true };
31
+ } catch {
32
+ return { personal: [], allow: [], present: false };
33
+ }
34
+ }
35
+
36
+ const maskValue = (v) => (v.length <= 4 ? "***" : `${v.slice(0, 2)}***${v.slice(-2)}`);
37
+
38
+ // KJC-TSK-0708 — hardcoded secrets. Known token shapes are never legit in
39
+ // an outbound artifact → block; heuristics (credential assignments and
40
+ // user:pass@ connection strings) warn toward .env / a secret manager.
41
+ const SECRET_SHAPES = [
42
+ { type: "github-token", re: /\bgh[pousr]_[A-Za-z0-9]{20,}\b/g },
43
+ { type: "openai-key", re: /\bsk-[A-Za-z0-9_-]{20,}\b/g },
44
+ { type: "aws-key", re: /\bAKIA[0-9A-Z]{16}\b/g },
45
+ { type: "slack-token", re: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g },
46
+ { type: "google-key", re: /\bAIza[0-9A-Za-z_-]{35}\b/g },
47
+ { type: "private-key", re: /-----BEGIN [A-Z ]*PRIVATE KEY-----/g },
48
+ { type: "jwt", re: /\beyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g },
49
+ ];
50
+ const SECRET_HEURISTICS = [
51
+ { type: "secret-assignment", re: /\b(?:password|passwd|pwd|secret|token|api[_-]?key|apikey|auth)["']?\s*[:=]\s*["']([^"']{6,})["']/gi },
52
+ { type: "conn-string", re: /\b[a-z][a-z0-9+.-]*:\/\/[^\s:@/]+:([^\s@/]+)@/gi },
53
+ ];
54
+
55
+ /**
56
+ * Scan a text. Returns findings — denylist hits as `severity:"block"`
57
+ * (masked value), generic PII as `severity:"warn"` (line already redacted).
58
+ */
59
+ export function scanText(text, { list = loadPrivacyList(), source = "<text>" } = {}) {
60
+ const findings = [];
61
+ String(text).split("\n").forEach((line, i) => {
62
+ let probe = line;
63
+ for (const a of list.allow) probe = probe.split(a).join(" ");
64
+ for (const p of list.personal) {
65
+ let at = probe.toLowerCase().indexOf(p.toLowerCase());
66
+ if (at === -1) continue;
67
+ findings.push({ severity: "block", type: "denylist", source, line: i + 1, masked: maskValue(p) });
68
+ // Blank every occurrence so the generics don't double-report it.
69
+ while (at !== -1) { probe = probe.slice(0, at) + " ".repeat(p.length) + probe.slice(at + p.length); at = probe.toLowerCase().indexOf(p.toLowerCase()); }
70
+ }
71
+ for (const { type, re } of SECRET_SHAPES) {
72
+ re.lastIndex = 0;
73
+ let m;
74
+ while ((m = re.exec(probe)) !== null) {
75
+ findings.push({ severity: "block", type, source, line: i + 1, masked: maskValue(m[0]) });
76
+ probe = probe.slice(0, m.index) + " ".repeat(m[0].length) + probe.slice(m.index + m[0].length);
77
+ re.lastIndex = m.index + m[0].length;
78
+ }
79
+ }
80
+ for (const { type, re } of SECRET_HEURISTICS) {
81
+ re.lastIndex = 0;
82
+ let m;
83
+ while ((m = re.exec(probe)) !== null) {
84
+ findings.push({ severity: "warn", type, source, line: i + 1, masked: `${maskValue(m[1])} — move it to .env or a secret manager` });
85
+ probe = probe.slice(0, m.index) + " ".repeat(m[0].length) + probe.slice(m.index + m[0].length);
86
+ re.lastIndex = m.index + m[0].length;
87
+ }
88
+ }
89
+ const red = redactPII(probe);
90
+ if (red.total > 0) {
91
+ for (const [type, n] of Object.entries(red.counts)) {
92
+ if (n > 0) findings.push({ severity: "warn", type, source, line: i + 1, masked: red.text.trim().slice(0, 120) });
93
+ }
94
+ }
95
+ });
96
+ return findings;
97
+ }
98
+
99
+ /** Scan files/dirs recursively (skips node_modules/.git, binaries, big files). */
100
+ export function scanPaths(paths, { list = loadPrivacyList() } = {}) {
101
+ const findings = [];
102
+ const visit = (p) => {
103
+ if (!existsSync(p)) return;
104
+ // Skip symlinks: an ancestor link recurses forever; an outside link ships nothing.
105
+ const st = lstatSync(p);
106
+ if (st.isSymbolicLink()) return;
107
+ if (st.isDirectory()) {
108
+ for (const name of readdirSync(p)) {
109
+ if (!SKIP_DIRS.has(name)) visit(join(p, name));
110
+ }
111
+ return;
112
+ }
113
+ if (BINARY_EXT.has(extname(p).toLowerCase()) || st.size > MAX_FILE_BYTES) return;
114
+ const content = readFileSync(p);
115
+ if (content.includes(0)) return; // binary sniff: NUL byte
116
+ findings.push(...scanText(content.toString("utf8"), { list, source: p }));
117
+ };
118
+ for (const p of paths) visit(p);
119
+ return findings;
120
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Arbiter prompt (KJC-TSK-0573, design in docs/specs/autonomous-delivery.md §6).
3
+ *
4
+ * The Arbiter is Karajan's autonomous decision authority: given a conflict, it
5
+ * picks the LEAST-BAD option from a closed set and moves forward — no human.
6
+ */
7
+
8
+ /** The closed set of conflict-resolution verdicts. BLOCK_AND_CONTINUE is safe. */
9
+ export const VERDICTS = Object.freeze([
10
+ "ACCEPT_WITH_DEFECT",
11
+ "RETRY_DIFFERENT_APPROACH",
12
+ "DESCOPE_HU",
13
+ "BLOCK_AND_CONTINUE",
14
+ "PROCEED",
15
+ ]);
16
+
17
+ export function buildArbiterPrompt({ question, options = [], context = null } = {}) {
18
+ const allowed = options.length ? options.map((o) => o.value) : VERDICTS;
19
+ return [
20
+ "You are the Arbiter — Karajan's autonomous decision authority. There is no human to ask.",
21
+ "Resolve the conflict below by choosing the LEAST-BAD option and moving forward.",
22
+ "",
23
+ "Ground-truth order (what wins when signals disagree):",
24
+ " 1. Acceptance tests passing — beats any reviewer opinion.",
25
+ " 2. Must-fix issues (security, correctness, data) — beat style.",
26
+ " 3. Nice-to-have (style, optional refactors) — never block; record as a defect if dropped.",
27
+ "Goal: maximize 'the ask is met', minimize irreversible risk. Imperfect-but-delivered beats blocked.",
28
+ "",
29
+ `Conflict: ${question}`,
30
+ context ? `Context: ${JSON.stringify(context)}` : "",
31
+ "",
32
+ `Choose exactly one verdict from: ${allowed.join(", ")}`,
33
+ "Respond with ONLY this JSON (no prose):",
34
+ '{"verdict":"<one of the allowed>","confidence":0.0,"rationale":"why it is the least-bad option","defect":"residual debt if any, else null","directive":"instruction to the coder when RETRY_DIFFERENT_APPROACH, else null"}',
35
+ ]
36
+ .filter(Boolean)
37
+ .join("\n");
38
+ }
@@ -0,0 +1,103 @@
1
+ import { loadAvailableSkills, buildSkillSection } from "../skills/skill-loader.js";
2
+ import { getLanguageInstruction } from "../utils/locale.js";
3
+ import { section, buildPromptLayout, joinLayout, STABLE, VOLATILE } from "./prompt-layout.js";
4
+
5
+ const SUBAGENT_PREAMBLE = [
6
+ "IMPORTANT: You are running as a Karajan sub-agent.",
7
+ "Do NOT ask about using Karajan, do NOT mention Karajan, do NOT suggest orchestration.",
8
+ "Do NOT use any MCP tools. Focus only on designing the architecture for the task."
9
+ ].join(" ");
10
+
11
+ export const VALID_VERDICTS = new Set(["ready", "needs_clarification"]);
12
+
13
+ /**
14
+ * Build the architect prompt as a { stable, volatile } layout (Φ1,
15
+ * KJC-PCS-0057): instructions, guidelines, schema, contexts and skills
16
+ * are identical across invocations on the same project; research
17
+ * context and the task itself go last.
18
+ */
19
+ export async function buildArchitectPromptLayout({ task, instructions, researchContext = null, productContext = null, domainContext = null, projectDir = null, language = "en", provider = null }) {
20
+ const langInstruction = getLanguageInstruction(language);
21
+ let skillSection = null;
22
+ if (projectDir) {
23
+ const skills = await loadAvailableSkills(projectDir);
24
+ skillSection = buildSkillSection(skills, { provider, role: "architect" });
25
+ }
26
+
27
+ return buildPromptLayout([
28
+ section(SUBAGENT_PREAMBLE, STABLE),
29
+ section(langInstruction, STABLE),
30
+ section(instructions, STABLE),
31
+ section("You are the architect in a multi-role AI pipeline.", STABLE),
32
+ section("Analyze the task and produce a concrete architecture design including layers, patterns, data model, API contracts, dependencies, and tradeoffs.", STABLE),
33
+ section("## Architecture Guidelines", STABLE),
34
+ section([
35
+ "- Identify the architecture type (layered, microservices, event-driven, etc.)",
36
+ "- Define the layers and their responsibilities",
37
+ "- Identify design patterns to apply",
38
+ "- Define the data model with entities",
39
+ "- Specify API contracts (endpoints, events, interfaces)",
40
+ "- List external and internal dependencies",
41
+ "- Document tradeoffs and their rationale",
42
+ "- If critical decisions cannot be made without more information, list clarifying questions"
43
+ ].join("\n"), STABLE),
44
+ section("Return a single valid JSON object and nothing else.", STABLE),
45
+ section('JSON schema: {"verdict":"ready|needs_clarification","architecture":{"type":string,"layers":[string],"patterns":[string],"dataModel":{"entities":[string]},"apiContracts":[string],"dependencies":[string],"tradeoffs":[string]},"questions":[string],"summary":string}', STABLE),
46
+ section(productContext ? `## Product Context\n${productContext}` : null, STABLE),
47
+ section(domainContext ? `## Domain Context\n${domainContext}` : null, STABLE),
48
+ section(skillSection, STABLE),
49
+ section(researchContext ? `## Research Context\n${researchContext}` : null, VOLATILE),
50
+ section(`## Task\n${task}`, VOLATILE),
51
+ ]);
52
+ }
53
+
54
+ export async function buildArchitectPrompt(options) {
55
+ return joinLayout(await buildArchitectPromptLayout(options));
56
+ }
57
+
58
+ function filterStrings(arr) {
59
+ if (!Array.isArray(arr)) return [];
60
+ return arr.filter((item) => typeof item === "string");
61
+ }
62
+
63
+ export function parseArchitectOutput(raw) {
64
+ const text = raw?.trim() || "";
65
+ const jsonMatch = /\{[\s\S]*\}/.exec(text);
66
+ if (!jsonMatch) return null;
67
+
68
+ let parsed;
69
+ try {
70
+ parsed = JSON.parse(jsonMatch[0]);
71
+ } catch { /* architect output is not valid JSON */
72
+ return null;
73
+ }
74
+
75
+ const verdict = VALID_VERDICTS.has(parsed.verdict)
76
+ ? parsed.verdict
77
+ : "needs_clarification";
78
+
79
+ const arch = parsed.architecture && typeof parsed.architecture === "object"
80
+ ? parsed.architecture
81
+ : {};
82
+
83
+ const dataModel = arch.dataModel && typeof arch.dataModel === "object"
84
+ ? arch.dataModel
85
+ : {};
86
+
87
+ return {
88
+ verdict,
89
+ architecture: {
90
+ type: typeof arch.type === "string" ? arch.type : "",
91
+ layers: filterStrings(arch.layers),
92
+ patterns: filterStrings(arch.patterns),
93
+ dataModel: {
94
+ entities: filterStrings(dataModel.entities)
95
+ },
96
+ apiContracts: filterStrings(arch.apiContracts),
97
+ dependencies: filterStrings(arch.dependencies),
98
+ tradeoffs: filterStrings(arch.tradeoffs)
99
+ },
100
+ questions: filterStrings(parsed.questions),
101
+ summary: parsed.summary || ""
102
+ };
103
+ }