forge-server 0.1.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 (412) hide show
  1. package/.claude/hooks/worktree-create.sh +64 -0
  2. package/.claude/hooks/worktree-remove.sh +57 -0
  3. package/.claude/settings.local.json +29 -0
  4. package/.forge/knowledge/conventions.yaml +1 -0
  5. package/.forge/knowledge/decisions.yaml +1 -0
  6. package/.forge/knowledge/gotchas.yaml +1 -0
  7. package/.forge/knowledge/patterns.yaml +1 -0
  8. package/.forge/manifest.yaml +6 -0
  9. package/CLAUDE.md +144 -0
  10. package/bin/setup-forge.sh +132 -0
  11. package/dist/cli.d.ts +3 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +553 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/context/codebase.d.ts +57 -0
  16. package/dist/context/codebase.d.ts.map +1 -0
  17. package/dist/context/codebase.js +301 -0
  18. package/dist/context/codebase.js.map +1 -0
  19. package/dist/context/injector.d.ts +147 -0
  20. package/dist/context/injector.d.ts.map +1 -0
  21. package/dist/context/injector.js +533 -0
  22. package/dist/context/injector.js.map +1 -0
  23. package/dist/context/memory.d.ts +32 -0
  24. package/dist/context/memory.d.ts.map +1 -0
  25. package/dist/context/memory.js +140 -0
  26. package/dist/context/memory.js.map +1 -0
  27. package/dist/context/session-index.d.ts +54 -0
  28. package/dist/context/session-index.d.ts.map +1 -0
  29. package/dist/context/session-index.js +265 -0
  30. package/dist/context/session-index.js.map +1 -0
  31. package/dist/context/session.d.ts +42 -0
  32. package/dist/context/session.d.ts.map +1 -0
  33. package/dist/context/session.js +121 -0
  34. package/dist/context/session.js.map +1 -0
  35. package/dist/index.d.ts +3 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +37 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/ingestion/chunker.d.ts +19 -0
  40. package/dist/ingestion/chunker.d.ts.map +1 -0
  41. package/dist/ingestion/chunker.js +189 -0
  42. package/dist/ingestion/chunker.js.map +1 -0
  43. package/dist/ingestion/embedder.d.ts +45 -0
  44. package/dist/ingestion/embedder.d.ts.map +1 -0
  45. package/dist/ingestion/embedder.js +152 -0
  46. package/dist/ingestion/embedder.js.map +1 -0
  47. package/dist/ingestion/git-analyzer.d.ts +77 -0
  48. package/dist/ingestion/git-analyzer.d.ts.map +1 -0
  49. package/dist/ingestion/git-analyzer.js +437 -0
  50. package/dist/ingestion/git-analyzer.js.map +1 -0
  51. package/dist/ingestion/indexer.d.ts +79 -0
  52. package/dist/ingestion/indexer.d.ts.map +1 -0
  53. package/dist/ingestion/indexer.js +766 -0
  54. package/dist/ingestion/indexer.js.map +1 -0
  55. package/dist/ingestion/markdown-chunker.d.ts +19 -0
  56. package/dist/ingestion/markdown-chunker.d.ts.map +1 -0
  57. package/dist/ingestion/markdown-chunker.js +243 -0
  58. package/dist/ingestion/markdown-chunker.js.map +1 -0
  59. package/dist/ingestion/markdown-knowledge.d.ts +21 -0
  60. package/dist/ingestion/markdown-knowledge.d.ts.map +1 -0
  61. package/dist/ingestion/markdown-knowledge.js +129 -0
  62. package/dist/ingestion/markdown-knowledge.js.map +1 -0
  63. package/dist/ingestion/parser.d.ts +20 -0
  64. package/dist/ingestion/parser.d.ts.map +1 -0
  65. package/dist/ingestion/parser.js +429 -0
  66. package/dist/ingestion/parser.js.map +1 -0
  67. package/dist/ingestion/watcher.d.ts +28 -0
  68. package/dist/ingestion/watcher.d.ts.map +1 -0
  69. package/dist/ingestion/watcher.js +147 -0
  70. package/dist/ingestion/watcher.js.map +1 -0
  71. package/dist/knowledge/hydrator.d.ts +37 -0
  72. package/dist/knowledge/hydrator.d.ts.map +1 -0
  73. package/dist/knowledge/hydrator.js +220 -0
  74. package/dist/knowledge/hydrator.js.map +1 -0
  75. package/dist/knowledge/registry.d.ts +129 -0
  76. package/dist/knowledge/registry.d.ts.map +1 -0
  77. package/dist/knowledge/registry.js +361 -0
  78. package/dist/knowledge/registry.js.map +1 -0
  79. package/dist/knowledge/search.d.ts +114 -0
  80. package/dist/knowledge/search.d.ts.map +1 -0
  81. package/dist/knowledge/search.js +428 -0
  82. package/dist/knowledge/search.js.map +1 -0
  83. package/dist/knowledge/store.d.ts +76 -0
  84. package/dist/knowledge/store.d.ts.map +1 -0
  85. package/dist/knowledge/store.js +230 -0
  86. package/dist/knowledge/store.js.map +1 -0
  87. package/dist/learning/confidence.d.ts +30 -0
  88. package/dist/learning/confidence.d.ts.map +1 -0
  89. package/dist/learning/confidence.js +165 -0
  90. package/dist/learning/confidence.js.map +1 -0
  91. package/dist/learning/patterns.d.ts +52 -0
  92. package/dist/learning/patterns.d.ts.map +1 -0
  93. package/dist/learning/patterns.js +290 -0
  94. package/dist/learning/patterns.js.map +1 -0
  95. package/dist/learning/trajectory.d.ts +55 -0
  96. package/dist/learning/trajectory.d.ts.map +1 -0
  97. package/dist/learning/trajectory.js +200 -0
  98. package/dist/learning/trajectory.js.map +1 -0
  99. package/dist/memory/memory-compat.d.ts +100 -0
  100. package/dist/memory/memory-compat.d.ts.map +1 -0
  101. package/dist/memory/memory-compat.js +146 -0
  102. package/dist/memory/memory-compat.js.map +1 -0
  103. package/dist/memory/observation-store.d.ts +57 -0
  104. package/dist/memory/observation-store.d.ts.map +1 -0
  105. package/dist/memory/observation-store.js +154 -0
  106. package/dist/memory/observation-store.js.map +1 -0
  107. package/dist/memory/session-tracker.d.ts +81 -0
  108. package/dist/memory/session-tracker.d.ts.map +1 -0
  109. package/dist/memory/session-tracker.js +262 -0
  110. package/dist/memory/session-tracker.js.map +1 -0
  111. package/dist/pipeline/engine.d.ts +179 -0
  112. package/dist/pipeline/engine.d.ts.map +1 -0
  113. package/dist/pipeline/engine.js +691 -0
  114. package/dist/pipeline/engine.js.map +1 -0
  115. package/dist/pipeline/events.d.ts +54 -0
  116. package/dist/pipeline/events.d.ts.map +1 -0
  117. package/dist/pipeline/events.js +157 -0
  118. package/dist/pipeline/events.js.map +1 -0
  119. package/dist/pipeline/parallel.d.ts +83 -0
  120. package/dist/pipeline/parallel.d.ts.map +1 -0
  121. package/dist/pipeline/parallel.js +277 -0
  122. package/dist/pipeline/parallel.js.map +1 -0
  123. package/dist/pipeline/state-machine.d.ts +65 -0
  124. package/dist/pipeline/state-machine.d.ts.map +1 -0
  125. package/dist/pipeline/state-machine.js +176 -0
  126. package/dist/pipeline/state-machine.js.map +1 -0
  127. package/dist/query/graph-queries.d.ts +84 -0
  128. package/dist/query/graph-queries.d.ts.map +1 -0
  129. package/dist/query/graph-queries.js +216 -0
  130. package/dist/query/graph-queries.js.map +1 -0
  131. package/dist/query/hybrid-search.d.ts +34 -0
  132. package/dist/query/hybrid-search.d.ts.map +1 -0
  133. package/dist/query/hybrid-search.js +263 -0
  134. package/dist/query/hybrid-search.js.map +1 -0
  135. package/dist/query/intent-detector.d.ts +35 -0
  136. package/dist/query/intent-detector.d.ts.map +1 -0
  137. package/dist/query/intent-detector.js +115 -0
  138. package/dist/query/intent-detector.js.map +1 -0
  139. package/dist/query/ranking.d.ts +57 -0
  140. package/dist/query/ranking.d.ts.map +1 -0
  141. package/dist/query/ranking.js +109 -0
  142. package/dist/query/ranking.js.map +1 -0
  143. package/dist/server.d.ts +3 -0
  144. package/dist/server.d.ts.map +1 -0
  145. package/dist/server.js +291 -0
  146. package/dist/server.js.map +1 -0
  147. package/dist/storage/falkordb-store.d.ts +73 -0
  148. package/dist/storage/falkordb-store.d.ts.map +1 -0
  149. package/dist/storage/falkordb-store.js +346 -0
  150. package/dist/storage/falkordb-store.js.map +1 -0
  151. package/dist/storage/file-cache.d.ts +32 -0
  152. package/dist/storage/file-cache.d.ts.map +1 -0
  153. package/dist/storage/file-cache.js +115 -0
  154. package/dist/storage/file-cache.js.map +1 -0
  155. package/dist/storage/interfaces.d.ts +151 -0
  156. package/dist/storage/interfaces.d.ts.map +1 -0
  157. package/dist/storage/interfaces.js +7 -0
  158. package/dist/storage/interfaces.js.map +1 -0
  159. package/dist/storage/qdrant-store.d.ts +110 -0
  160. package/dist/storage/qdrant-store.d.ts.map +1 -0
  161. package/dist/storage/qdrant-store.js +467 -0
  162. package/dist/storage/qdrant-store.js.map +1 -0
  163. package/dist/storage/schema.d.ts +4 -0
  164. package/dist/storage/schema.d.ts.map +1 -0
  165. package/dist/storage/schema.js +136 -0
  166. package/dist/storage/schema.js.map +1 -0
  167. package/dist/storage/sqlite.d.ts +35 -0
  168. package/dist/storage/sqlite.d.ts.map +1 -0
  169. package/dist/storage/sqlite.js +132 -0
  170. package/dist/storage/sqlite.js.map +1 -0
  171. package/dist/tools/collaboration-tools.d.ts +111 -0
  172. package/dist/tools/collaboration-tools.d.ts.map +1 -0
  173. package/dist/tools/collaboration-tools.js +174 -0
  174. package/dist/tools/collaboration-tools.js.map +1 -0
  175. package/dist/tools/context-tools.d.ts +293 -0
  176. package/dist/tools/context-tools.d.ts.map +1 -0
  177. package/dist/tools/context-tools.js +437 -0
  178. package/dist/tools/context-tools.js.map +1 -0
  179. package/dist/tools/graph-tools.d.ts +129 -0
  180. package/dist/tools/graph-tools.d.ts.map +1 -0
  181. package/dist/tools/graph-tools.js +237 -0
  182. package/dist/tools/graph-tools.js.map +1 -0
  183. package/dist/tools/ingestion-tools.d.ts +96 -0
  184. package/dist/tools/ingestion-tools.d.ts.map +1 -0
  185. package/dist/tools/ingestion-tools.js +90 -0
  186. package/dist/tools/ingestion-tools.js.map +1 -0
  187. package/dist/tools/learning-tools.d.ts +168 -0
  188. package/dist/tools/learning-tools.d.ts.map +1 -0
  189. package/dist/tools/learning-tools.js +158 -0
  190. package/dist/tools/learning-tools.js.map +1 -0
  191. package/dist/tools/memory-tools.d.ts +183 -0
  192. package/dist/tools/memory-tools.d.ts.map +1 -0
  193. package/dist/tools/memory-tools.js +197 -0
  194. package/dist/tools/memory-tools.js.map +1 -0
  195. package/dist/tools/phase-tools.d.ts +954 -0
  196. package/dist/tools/phase-tools.d.ts.map +1 -0
  197. package/dist/tools/phase-tools.js +1215 -0
  198. package/dist/tools/phase-tools.js.map +1 -0
  199. package/dist/tools/pipeline-tools.d.ts +140 -0
  200. package/dist/tools/pipeline-tools.d.ts.map +1 -0
  201. package/dist/tools/pipeline-tools.js +162 -0
  202. package/dist/tools/pipeline-tools.js.map +1 -0
  203. package/dist/tools/registration-tools.d.ts +220 -0
  204. package/dist/tools/registration-tools.d.ts.map +1 -0
  205. package/dist/tools/registration-tools.js +391 -0
  206. package/dist/tools/registration-tools.js.map +1 -0
  207. package/dist/util/circuit-breaker.d.ts +75 -0
  208. package/dist/util/circuit-breaker.d.ts.map +1 -0
  209. package/dist/util/circuit-breaker.js +159 -0
  210. package/dist/util/circuit-breaker.js.map +1 -0
  211. package/dist/util/config.d.ts +23 -0
  212. package/dist/util/config.d.ts.map +1 -0
  213. package/dist/util/config.js +164 -0
  214. package/dist/util/config.js.map +1 -0
  215. package/dist/util/logger.d.ts +13 -0
  216. package/dist/util/logger.d.ts.map +1 -0
  217. package/dist/util/logger.js +45 -0
  218. package/dist/util/logger.js.map +1 -0
  219. package/dist/util/token-counter.d.ts +24 -0
  220. package/dist/util/token-counter.d.ts.map +1 -0
  221. package/dist/util/token-counter.js +48 -0
  222. package/dist/util/token-counter.js.map +1 -0
  223. package/dist/util/types.d.ts +525 -0
  224. package/dist/util/types.d.ts.map +1 -0
  225. package/dist/util/types.js +5 -0
  226. package/dist/util/types.js.map +1 -0
  227. package/docker-compose.yml +20 -0
  228. package/docs/plans/2026-02-27-swarm-coordination/architecture.md +203 -0
  229. package/docs/plans/2026-02-27-swarm-coordination/vision.md +57 -0
  230. package/docs/plans/completed/2026-02-26-forge-plugin-bundling/architecture.md +1 -0
  231. package/docs/plans/completed/2026-02-26-forge-plugin-bundling/vision.md +300 -0
  232. package/docs/plans/completed/2026-02-27-forge-swarm-learning/architecture.md +480 -0
  233. package/docs/plans/completed/2026-02-27-forge-swarm-learning/verification-checklist.md +462 -0
  234. package/docs/plans/completed/2026-02-27-git-history-atlassian/git-jira-plan.md +181 -0
  235. package/package.json +39 -0
  236. package/plugin/.claude-plugin/plugin.json +8 -0
  237. package/plugin/.mcp.json +15 -0
  238. package/plugin/README.md +134 -0
  239. package/plugin/agents/architect.md +367 -0
  240. package/plugin/agents/backend-specialist.md +263 -0
  241. package/plugin/agents/brainstormer.md +122 -0
  242. package/plugin/agents/data-specialist.md +266 -0
  243. package/plugin/agents/designer.md +408 -0
  244. package/plugin/agents/frontend-specialist.md +241 -0
  245. package/plugin/agents/inspector.md +406 -0
  246. package/plugin/agents/knowledge-keeper.md +443 -0
  247. package/plugin/agents/platform-engineer.md +326 -0
  248. package/plugin/agents/product-manager.md +268 -0
  249. package/plugin/agents/product-owner.md +438 -0
  250. package/plugin/agents/pulse-checker.md +73 -0
  251. package/plugin/agents/qa-strategist.md +500 -0
  252. package/plugin/agents/self-improver.md +310 -0
  253. package/plugin/agents/strategist.md +360 -0
  254. package/plugin/agents/supervisor.md +380 -0
  255. package/plugin/commands/brainstorm.md +25 -0
  256. package/plugin/commands/forge.md +88 -0
  257. package/plugin/docs/atlassian-integration.md +110 -0
  258. package/plugin/docs/workflow.md +126 -0
  259. package/plugin/skills/agent-development/.skillfish.json +10 -0
  260. package/plugin/skills/agent-development/SKILL.md +415 -0
  261. package/plugin/skills/agent-development/examples/agent-creation-prompt.md +238 -0
  262. package/plugin/skills/agent-development/examples/complete-agent-examples.md +427 -0
  263. package/plugin/skills/agent-development/references/agent-creation-system-prompt.md +207 -0
  264. package/plugin/skills/agent-development/references/system-prompt-design.md +411 -0
  265. package/plugin/skills/agent-development/references/triggering-examples.md +491 -0
  266. package/plugin/skills/agent-development/scripts/validate-agent.sh +217 -0
  267. package/plugin/skills/agent-handoff/SKILL.md +335 -0
  268. package/plugin/skills/anti-stub/SKILL.md +317 -0
  269. package/plugin/skills/brainstorm/SKILL.md +31 -0
  270. package/plugin/skills/debugging/SKILL.md +276 -0
  271. package/plugin/skills/fix/SKILL.md +62 -0
  272. package/plugin/skills/frontend-design/.skillfish.json +10 -0
  273. package/plugin/skills/frontend-design/SKILL.md +42 -0
  274. package/plugin/skills/gotchas/SKILL.md +61 -0
  275. package/plugin/skills/graph-orchestrator/SKILL.md +38 -0
  276. package/plugin/skills/history/SKILL.md +58 -0
  277. package/plugin/skills/impact/SKILL.md +59 -0
  278. package/plugin/skills/implementation-execution/SKILL.md +291 -0
  279. package/plugin/skills/index-repo/SKILL.md +55 -0
  280. package/plugin/skills/interviewing/SKILL.md +225 -0
  281. package/plugin/skills/knowledge-curation/SKILL.md +393 -0
  282. package/plugin/skills/learn/SKILL.md +69 -0
  283. package/plugin/skills/mcp-integration/.skillfish.json +10 -0
  284. package/plugin/skills/mcp-integration/SKILL.md +554 -0
  285. package/plugin/skills/mcp-integration/examples/http-server.json +20 -0
  286. package/plugin/skills/mcp-integration/examples/sse-server.json +19 -0
  287. package/plugin/skills/mcp-integration/examples/stdio-server.json +26 -0
  288. package/plugin/skills/mcp-integration/references/authentication.md +549 -0
  289. package/plugin/skills/mcp-integration/references/server-types.md +536 -0
  290. package/plugin/skills/mcp-integration/references/tool-usage.md +538 -0
  291. package/plugin/skills/nestjs/.skillfish.json +10 -0
  292. package/plugin/skills/nestjs/SKILL.md +669 -0
  293. package/plugin/skills/nestjs/drizzle-reference.md +1894 -0
  294. package/plugin/skills/nestjs/reference.md +1447 -0
  295. package/plugin/skills/nestjs/workflow-optimization.md +229 -0
  296. package/plugin/skills/parallel-dispatch/SKILL.md +308 -0
  297. package/plugin/skills/project-discovery/SKILL.md +304 -0
  298. package/plugin/skills/search/SKILL.md +56 -0
  299. package/plugin/skills/security-audit/SKILL.md +362 -0
  300. package/plugin/skills/skill-development/.skillfish.json +10 -0
  301. package/plugin/skills/skill-development/SKILL.md +637 -0
  302. package/plugin/skills/skill-development/references/skill-creator-original.md +209 -0
  303. package/plugin/skills/tdd/SKILL.md +273 -0
  304. package/plugin/skills/terminal-presentation/SKILL.md +395 -0
  305. package/plugin/skills/test-strategy/SKILL.md +365 -0
  306. package/plugin/skills/verification-protocol/SKILL.md +256 -0
  307. package/plugin/skills/visual-explainer/CHANGELOG.md +97 -0
  308. package/plugin/skills/visual-explainer/LICENSE +21 -0
  309. package/plugin/skills/visual-explainer/README.md +137 -0
  310. package/plugin/skills/visual-explainer/SKILL.md +352 -0
  311. package/plugin/skills/visual-explainer/banner.png +0 -0
  312. package/plugin/skills/visual-explainer/package.json +11 -0
  313. package/plugin/skills/visual-explainer/prompts/diff-review.md +68 -0
  314. package/plugin/skills/visual-explainer/prompts/fact-check.md +63 -0
  315. package/plugin/skills/visual-explainer/prompts/generate-slides.md +18 -0
  316. package/plugin/skills/visual-explainer/prompts/generate-web-diagram.md +10 -0
  317. package/plugin/skills/visual-explainer/prompts/plan-review.md +86 -0
  318. package/plugin/skills/visual-explainer/prompts/project-recap.md +61 -0
  319. package/plugin/skills/visual-explainer/references/css-patterns.md +1188 -0
  320. package/plugin/skills/visual-explainer/references/libraries.md +470 -0
  321. package/plugin/skills/visual-explainer/references/responsive-nav.md +212 -0
  322. package/plugin/skills/visual-explainer/references/slide-patterns.md +1403 -0
  323. package/plugin/skills/visual-explainer/templates/architecture.html +596 -0
  324. package/plugin/skills/visual-explainer/templates/data-table.html +540 -0
  325. package/plugin/skills/visual-explainer/templates/mermaid-flowchart.html +435 -0
  326. package/plugin/skills/visual-explainer/templates/slide-deck.html +913 -0
  327. package/src/cli.ts +655 -0
  328. package/src/context/.gitkeep +0 -0
  329. package/src/context/codebase.ts +393 -0
  330. package/src/context/injector.ts +797 -0
  331. package/src/context/memory.ts +187 -0
  332. package/src/context/session-index.ts +327 -0
  333. package/src/context/session.ts +152 -0
  334. package/src/index.ts +47 -0
  335. package/src/ingestion/.gitkeep +0 -0
  336. package/src/ingestion/chunker.ts +277 -0
  337. package/src/ingestion/embedder.ts +167 -0
  338. package/src/ingestion/git-analyzer.ts +545 -0
  339. package/src/ingestion/indexer.ts +984 -0
  340. package/src/ingestion/markdown-chunker.ts +337 -0
  341. package/src/ingestion/markdown-knowledge.ts +175 -0
  342. package/src/ingestion/parser.ts +475 -0
  343. package/src/ingestion/watcher.ts +182 -0
  344. package/src/knowledge/.gitkeep +0 -0
  345. package/src/knowledge/hydrator.ts +246 -0
  346. package/src/knowledge/registry.ts +463 -0
  347. package/src/knowledge/search.ts +565 -0
  348. package/src/knowledge/store.ts +262 -0
  349. package/src/learning/.gitkeep +0 -0
  350. package/src/learning/confidence.ts +193 -0
  351. package/src/learning/patterns.ts +360 -0
  352. package/src/learning/trajectory.ts +268 -0
  353. package/src/memory/.gitkeep +0 -0
  354. package/src/memory/memory-compat.ts +233 -0
  355. package/src/memory/observation-store.ts +224 -0
  356. package/src/memory/session-tracker.ts +332 -0
  357. package/src/pipeline/.gitkeep +0 -0
  358. package/src/pipeline/engine.ts +1139 -0
  359. package/src/pipeline/events.ts +253 -0
  360. package/src/pipeline/parallel.ts +394 -0
  361. package/src/pipeline/state-machine.ts +199 -0
  362. package/src/query/.gitkeep +0 -0
  363. package/src/query/graph-queries.ts +262 -0
  364. package/src/query/hybrid-search.ts +337 -0
  365. package/src/query/intent-detector.ts +131 -0
  366. package/src/query/ranking.ts +161 -0
  367. package/src/server.ts +352 -0
  368. package/src/storage/.gitkeep +0 -0
  369. package/src/storage/falkordb-store.ts +388 -0
  370. package/src/storage/file-cache.ts +141 -0
  371. package/src/storage/interfaces.ts +201 -0
  372. package/src/storage/qdrant-store.ts +557 -0
  373. package/src/storage/schema.ts +139 -0
  374. package/src/storage/sqlite.ts +168 -0
  375. package/src/tools/.gitkeep +0 -0
  376. package/src/tools/collaboration-tools.ts +208 -0
  377. package/src/tools/context-tools.ts +493 -0
  378. package/src/tools/graph-tools.ts +295 -0
  379. package/src/tools/ingestion-tools.ts +122 -0
  380. package/src/tools/learning-tools.ts +181 -0
  381. package/src/tools/memory-tools.ts +234 -0
  382. package/src/tools/phase-tools.ts +1452 -0
  383. package/src/tools/pipeline-tools.ts +188 -0
  384. package/src/tools/registration-tools.ts +450 -0
  385. package/src/util/.gitkeep +0 -0
  386. package/src/util/circuit-breaker.ts +193 -0
  387. package/src/util/config.ts +177 -0
  388. package/src/util/logger.ts +53 -0
  389. package/src/util/token-counter.ts +52 -0
  390. package/src/util/types.ts +710 -0
  391. package/tests/context/.gitkeep +0 -0
  392. package/tests/integration/.gitkeep +0 -0
  393. package/tests/knowledge/.gitkeep +0 -0
  394. package/tests/learning/.gitkeep +0 -0
  395. package/tests/pipeline/.gitkeep +0 -0
  396. package/tests/tools/.gitkeep +0 -0
  397. package/tsconfig.json +21 -0
  398. package/vitest.config.ts +10 -0
  399. package/vscode-extension/.vscodeignore +7 -0
  400. package/vscode-extension/README.md +43 -0
  401. package/vscode-extension/out/edge-collector.js +274 -0
  402. package/vscode-extension/out/edge-collector.js.map +1 -0
  403. package/vscode-extension/out/extension.js +264 -0
  404. package/vscode-extension/out/extension.js.map +1 -0
  405. package/vscode-extension/out/forge-client.js +318 -0
  406. package/vscode-extension/out/forge-client.js.map +1 -0
  407. package/vscode-extension/package-lock.json +59 -0
  408. package/vscode-extension/package.json +71 -0
  409. package/vscode-extension/src/edge-collector.ts +320 -0
  410. package/vscode-extension/src/extension.ts +269 -0
  411. package/vscode-extension/src/forge-client.ts +364 -0
  412. package/vscode-extension/tsconfig.json +19 -0
@@ -0,0 +1,797 @@
1
+ // ContextInjector — B6
2
+ //
3
+ // The central orchestrator for phase-start context assembly.
4
+ // Called by every forge.start_* tool handler before returning to the agent.
5
+ //
6
+ // For each phase, the injector:
7
+ // 1. Looks up the project and its repo from SQLite
8
+ // 2. Reads the repo's stack tags
9
+ // 3. Searches knowledge relevant to this phase
10
+ // 4. Gets codebase context when architecturally useful (architecture, implementation)
11
+ // 5. Gets memories from recent sessions when relevant
12
+ // 6. Reads previous phase outputs from SQLite
13
+ // 7. Assembles and returns the typed context object
14
+ //
15
+ // Every method handles unavailable backends gracefully and NEVER throws —
16
+ // a missing Qdrant or FalkorDB must not block a phase from starting.
17
+
18
+ import type { PipelineDB } from '../storage/sqlite.js';
19
+ import type { KnowledgeSearch } from '../knowledge/search.js';
20
+ import type { CodebaseContext } from './codebase.js';
21
+ import type { MemoryBridge } from './memory.js';
22
+ import type { ConfidenceManager } from '../learning/confidence.js';
23
+ import type { QdrantVectorStore } from '../storage/qdrant-store.js';
24
+ import type {
25
+ ProjectRow,
26
+ RepoRow,
27
+ PhaseOutputRow,
28
+ Phase,
29
+ } from '../util/types.js';
30
+ import { logger } from '../util/logger.js';
31
+
32
+ // ---------------------------------------------------------------------------
33
+ // Interview context
34
+ // ---------------------------------------------------------------------------
35
+
36
+ export interface InterviewContext {
37
+ similarProjects: Array<{
38
+ name: string;
39
+ repo: string;
40
+ relevance: number;
41
+ outcome: string;
42
+ }>;
43
+ relevantGotchas: Array<{
44
+ title: string;
45
+ content: string;
46
+ confidence: number;
47
+ sourceRepo: string;
48
+ }>;
49
+ interviewTemplate: string;
50
+ }
51
+
52
+ // ---------------------------------------------------------------------------
53
+ // Architecture context
54
+ // ---------------------------------------------------------------------------
55
+
56
+ export interface ArchitectureContextModules {
57
+ name: string;
58
+ path: string;
59
+ purpose: string;
60
+ }
61
+
62
+ export interface ArchitectureCodebaseContext {
63
+ modules: ArchitectureContextModules[];
64
+ patterns: string[];
65
+ dependencies: string[];
66
+ fileCount: number;
67
+ }
68
+
69
+ export interface ArchitectureContext {
70
+ codebaseContext: ArchitectureCodebaseContext;
71
+ relevantDecisions: Array<{
72
+ title: string;
73
+ content: string;
74
+ confidence: number;
75
+ sourceRepo: string;
76
+ }>;
77
+ relevantGotchas: Array<{
78
+ title: string;
79
+ content: string;
80
+ stackTags: string[];
81
+ confidence: number;
82
+ }>;
83
+ stackConstraints: string[];
84
+ }
85
+
86
+ // ---------------------------------------------------------------------------
87
+ // Implementation context (per module)
88
+ // ---------------------------------------------------------------------------
89
+
90
+ export interface ModuleContext {
91
+ // Stable (cacheable across module dispatches)
92
+ conventions: Array<{ title: string; content: string }>;
93
+ gotchas: Array<{ title: string; content: string }>;
94
+ siblingModules: Array<{ name: string; agentType: string; description?: string }>;
95
+ advisory: AdvisoryContext;
96
+ // Volatile (per-module, changes each dispatch)
97
+ relevantCode: string;
98
+ recalledContext: string[];
99
+ recentBroadcasts: Array<{ content: string; severity: string; created_at: number }>;
100
+ }
101
+
102
+ // ---------------------------------------------------------------------------
103
+ // Inspection context
104
+ // ---------------------------------------------------------------------------
105
+
106
+ export interface InspectionContext {
107
+ checklist: string;
108
+ pastFindings: Array<{
109
+ description: string;
110
+ severity: string;
111
+ resolution: string;
112
+ sourceRepo: string;
113
+ }>;
114
+ antiStubPatterns: string[];
115
+ cycleCount: number;
116
+ maxCycles: number;
117
+ broadcasts: Array<{ content: string; severity: string; created_at: number }>;
118
+ advisory: AdvisoryContext;
119
+ }
120
+
121
+ // ---------------------------------------------------------------------------
122
+ // Advisory context (injected into implementation + inspection phases)
123
+ // ---------------------------------------------------------------------------
124
+
125
+ export interface AdvisoryContext {
126
+ visionSummary: string;
127
+ architectureSummary: string;
128
+ designSummary: string;
129
+ activeDirectives: Array<{ content: string; from: string; created_at: number }>;
130
+ }
131
+
132
+ // ---------------------------------------------------------------------------
133
+ // Generic phase context
134
+ // ---------------------------------------------------------------------------
135
+
136
+ export interface PhaseContext {
137
+ relevantKnowledge: Array<{
138
+ title: string;
139
+ content: string;
140
+ category: string;
141
+ confidence: number;
142
+ }>;
143
+ previousOutputs: Array<{
144
+ phase: string;
145
+ type: string;
146
+ preview: string;
147
+ }>;
148
+ warnings: string[];
149
+ recentBroadcasts: Array<{
150
+ content: string;
151
+ severity: string;
152
+ created_at: number;
153
+ }>;
154
+ }
155
+
156
+ // ---------------------------------------------------------------------------
157
+ // ContextInjector
158
+ // ---------------------------------------------------------------------------
159
+
160
+ export class ContextInjector {
161
+ // CACHE-FRIENDLY ORDERING PRINCIPLE:
162
+ // All context injection methods emit fields from MOST STABLE to MOST VOLATILE.
163
+ // This maximizes Anthropic's automatic prompt caching — cached prefix tokens
164
+ // get a 90% input cost discount. Stable content (conventions, gotchas, advisory
165
+ // summaries) comes first and stays cached across agent dispatches. Volatile
166
+ // content (broadcasts, per-module code) comes last.
167
+
168
+ private readonly confidenceManager?: ConfidenceManager;
169
+ private readonly vectorStore?: QdrantVectorStore;
170
+
171
+ constructor(
172
+ private readonly knowledgeSearch: KnowledgeSearch,
173
+ private readonly codebaseContext: CodebaseContext,
174
+ private readonly memoryBridge: MemoryBridge,
175
+ private readonly db: PipelineDB,
176
+ confidenceManager?: ConfidenceManager,
177
+ vectorStore?: QdrantVectorStore,
178
+ ) {
179
+ this.confidenceManager = confidenceManager;
180
+ this.vectorStore = vectorStore;
181
+ }
182
+
183
+ // ---------------------------------------------------------------------------
184
+ // Fire-and-forget confidence boost for knowledge items returned by searches.
185
+ // NEVER blocks the caller — errors are logged and swallowed.
186
+ // ---------------------------------------------------------------------------
187
+
188
+ private boostKnowledgeResults(
189
+ results: Array<{ id: string }>,
190
+ ): void {
191
+ if (!this.confidenceManager || !this.vectorStore) return;
192
+ const cm = this.confidenceManager;
193
+ const vs = this.vectorStore;
194
+ for (const result of results) {
195
+ cm.boost(vs, result.id).catch((err) => {
196
+ logger.debug('ContextInjector.boostKnowledgeResults: boost failed (non-fatal)', {
197
+ id: result.id,
198
+ error: String(err),
199
+ });
200
+ });
201
+ }
202
+ }
203
+
204
+ // ---------------------------------------------------------------------------
205
+ // Broadcast injection helper.
206
+ // Retrieves recent agent broadcasts for a project from observations.
207
+ // Used to enrich phase context with cross-agent findings.
208
+ // ---------------------------------------------------------------------------
209
+
210
+ private async getRecentBroadcasts(
211
+ projectId: string,
212
+ limit: number = 5,
213
+ ): Promise<Array<{ content: string; severity: string; created_at: number }>> {
214
+ if (!this.vectorStore) return [];
215
+ try {
216
+ const filter: Record<string, unknown> = {
217
+ must: [
218
+ { key: 'category', match: { value: 'agent_broadcast' } },
219
+ { key: 'tags', match: { value: projectId } },
220
+ ],
221
+ };
222
+ const results = await this.vectorStore.filterObservations(filter, limit);
223
+ return results.map((r) => {
224
+ const payload = r.payload as unknown as Record<string, unknown>;
225
+ return {
226
+ content: (payload['content'] as string) ?? '',
227
+ severity: (payload['importance'] as number) >= 0.7 ? 'critical' : (payload['importance'] as number) >= 0.5 ? 'warning' : 'info',
228
+ created_at: (payload['created_at'] as number) ?? 0,
229
+ };
230
+ });
231
+ } catch (err) {
232
+ logger.debug('ContextInjector.getRecentBroadcasts: failed (non-fatal)', { error: String(err) });
233
+ return [];
234
+ }
235
+ }
236
+
237
+ // ---------------------------------------------------------------------------
238
+ // Advisory context (shared across implementation + inspection phases)
239
+ // ---------------------------------------------------------------------------
240
+
241
+ async injectAdvisoryContext(projectId: string): Promise<AdvisoryContext> {
242
+ const defaultCtx: AdvisoryContext = {
243
+ visionSummary: '',
244
+ architectureSummary: '',
245
+ designSummary: '',
246
+ activeDirectives: [],
247
+ };
248
+ try {
249
+ // Get phase outputs for vision, architecture, design
250
+ const phases = ['interview', 'architecture', 'design'] as const;
251
+ const summaries: Record<string, string> = {};
252
+ for (const phase of phases) {
253
+ const row = this.db.get<PhaseOutputRow>(
254
+ `SELECT content FROM phase_outputs WHERE project_id = ? AND phase = ? ORDER BY created_at DESC LIMIT 1`,
255
+ [projectId, phase],
256
+ );
257
+ summaries[phase] = row ? row.content.slice(0, 1500) : '';
258
+ }
259
+
260
+ // Get active advisory directives from observations
261
+ let activeDirectives: AdvisoryContext['activeDirectives'] = [];
262
+ if (this.vectorStore) {
263
+ try {
264
+ const filter = {
265
+ must: [
266
+ { key: 'category', match: { value: 'advisory_checkpoint' } },
267
+ { key: 'tags', match: { value: projectId } },
268
+ ],
269
+ };
270
+ const results = await this.vectorStore.filterObservations(filter, 10);
271
+ activeDirectives = results.map((r) => {
272
+ const payload = r.payload as unknown as Record<string, unknown>;
273
+ return {
274
+ content: (payload['content'] as string) ?? '',
275
+ from: ((payload['tags'] as string[]) ?? []).find(t => t !== projectId && t !== 'advisory_checkpoint') ?? 'unknown',
276
+ created_at: (payload['created_at'] as number) ?? 0,
277
+ };
278
+ });
279
+ } catch (err) {
280
+ logger.debug('injectAdvisoryContext: directive search failed (non-fatal)', { error: String(err) });
281
+ }
282
+ }
283
+
284
+ return {
285
+ visionSummary: summaries['interview'] ?? '',
286
+ architectureSummary: summaries['architecture'] ?? '',
287
+ designSummary: summaries['design'] ?? '',
288
+ activeDirectives,
289
+ };
290
+ } catch (err) {
291
+ logger.debug('injectAdvisoryContext: failed (non-fatal)', { error: String(err) });
292
+ return defaultCtx;
293
+ }
294
+ }
295
+
296
+ // ---------------------------------------------------------------------------
297
+ // Interview phase
298
+ // ---------------------------------------------------------------------------
299
+
300
+ async injectInterviewContext(
301
+ projectId: string,
302
+ initialRequest?: string,
303
+ ): Promise<InterviewContext> {
304
+ const { project, repo } = this.loadProjectAndRepo(projectId);
305
+ const stackTags = repo ? parseStack(repo.stack) : [];
306
+ const query = initialRequest ?? project?.name ?? 'project';
307
+
308
+ // Search for relevant gotchas related to the request
309
+ const gotchas = await this.knowledgeSearch.searchGotchas(query, {
310
+ stackTags,
311
+ limit: 5,
312
+ minConfidence: 0.4,
313
+ });
314
+
315
+ // Fire-and-forget confidence boost for consumed knowledge
316
+ this.boostKnowledgeResults(gotchas);
317
+
318
+ // Search memories for similar past projects
319
+ const memories = await this.memoryBridge.searchMemories(
320
+ `project similar to: ${query}`,
321
+ { repoId: repo?.id, limit: 5 },
322
+ );
323
+
324
+ // Derive similar projects from memories that look like project summaries
325
+ const similarProjects = memories
326
+ .filter((m) => m.content.length > 30)
327
+ .slice(0, 3)
328
+ .map((m, i) => ({
329
+ name: `Past project ${i + 1}`,
330
+ repo: repo?.name ?? 'unknown',
331
+ relevance: m.relevanceScore,
332
+ outcome: m.content.slice(0, 120),
333
+ }));
334
+
335
+ return {
336
+ similarProjects,
337
+ relevantGotchas: gotchas.map((g) => ({
338
+ title: g.title,
339
+ content: g.content,
340
+ confidence: g.confidence,
341
+ sourceRepo: g.source_repo,
342
+ })),
343
+ interviewTemplate: buildInterviewTemplate(stackTags),
344
+ };
345
+ }
346
+
347
+ // ---------------------------------------------------------------------------
348
+ // Architecture phase
349
+ // ---------------------------------------------------------------------------
350
+
351
+ async injectArchitectureContext(
352
+ projectId: string,
353
+ visionSummary?: string,
354
+ ): Promise<ArchitectureContext> {
355
+ const { project, repo } = this.loadProjectAndRepo(projectId);
356
+ const stackTags = repo ? parseStack(repo.stack) : [];
357
+ const query = visionSummary ?? project?.name ?? 'architecture';
358
+
359
+ // Run knowledge search and codebase context in parallel
360
+ const [decisions, gotchas, codebaseResult] = await Promise.all([
361
+ this.knowledgeSearch.searchDecisions(query, {
362
+ stackTags,
363
+ limit: 5,
364
+ minConfidence: 0.4,
365
+ }),
366
+ this.knowledgeSearch.searchGotchas(query, {
367
+ stackTags,
368
+ limit: 5,
369
+ minConfidence: 0.4,
370
+ }),
371
+ repo
372
+ ? this.codebaseContext.getContext(query, {
373
+ repoId: repo.id,
374
+ detail: 'skeleton',
375
+ maxResults: 8,
376
+ maxTokens: 3000,
377
+ })
378
+ : Promise.resolve(null),
379
+ ]);
380
+
381
+ // Fire-and-forget confidence boost for consumed knowledge
382
+ this.boostKnowledgeResults(decisions);
383
+ this.boostKnowledgeResults(gotchas);
384
+
385
+ // Build a coarse module list from skeleton file paths
386
+ const modules = (codebaseResult?.pivotFiles ?? []).map((f) => ({
387
+ name: basename(f.filePath),
388
+ path: f.filePath,
389
+ purpose: f.matchReason,
390
+ }));
391
+
392
+ // Derive patterns and dependencies from skeleton content
393
+ const skeletonContent = (codebaseResult?.supportingSkeletons ?? [])
394
+ .map((s) => s.skeleton)
395
+ .join('\n');
396
+
397
+ const patterns = extractPatterns(skeletonContent, stackTags);
398
+ const dependencies = extractDependencies(skeletonContent);
399
+
400
+ // Stack constraints come from the manifest stack tags
401
+ const stackConstraints = stackTags.map(
402
+ (tag) => `Stack: ${tag} — apply ${tag}-specific patterns and gotchas`,
403
+ );
404
+
405
+ return {
406
+ codebaseContext: {
407
+ modules,
408
+ patterns,
409
+ dependencies,
410
+ fileCount: codebaseResult?.pivotFiles.length ?? 0,
411
+ },
412
+ relevantDecisions: decisions.map((d) => ({
413
+ title: d.title,
414
+ content: d.content,
415
+ confidence: d.confidence,
416
+ sourceRepo: d.source_repo,
417
+ })),
418
+ relevantGotchas: gotchas.map((g) => ({
419
+ title: g.title,
420
+ content: g.content,
421
+ stackTags: g.stack_tags,
422
+ confidence: g.confidence,
423
+ })),
424
+ stackConstraints,
425
+ };
426
+ }
427
+
428
+ // ---------------------------------------------------------------------------
429
+ // Implementation phase (per-module)
430
+ // ---------------------------------------------------------------------------
431
+
432
+ async injectImplementationContext(
433
+ projectId: string,
434
+ modules: Array<{ name: string; agentType: string; description?: string }>,
435
+ ): Promise<Record<string, ModuleContext>> {
436
+ const { repo } = this.loadProjectAndRepo(projectId);
437
+ const stackTags = repo ? parseStack(repo.stack) : [];
438
+
439
+ // Fetch project-wide broadcasts once (shared across all modules)
440
+ const recentBroadcasts = await this.getRecentBroadcasts(projectId, 15);
441
+
442
+ // Fetch advisory context once (shared across all modules)
443
+ const advisory = await this.injectAdvisoryContext(projectId);
444
+
445
+ const result: Record<string, ModuleContext> = {};
446
+
447
+ // Process each module, fanning out searches in parallel
448
+ await Promise.all(
449
+ modules.map(async (mod) => {
450
+ const query = `${mod.agentType} ${mod.name}`;
451
+
452
+ const [gotchas, conventions, code] = await Promise.all([
453
+ this.knowledgeSearch.searchGotchas(query, {
454
+ stackTags,
455
+ limit: 4,
456
+ minConfidence: 0.4,
457
+ }),
458
+ this.knowledgeSearch.searchConventions(query, {
459
+ stackTags,
460
+ limit: 3,
461
+ minConfidence: 0.3,
462
+ }),
463
+ repo
464
+ ? this.codebaseContext.getContext(query, {
465
+ repoId: repo.id,
466
+ detail: 'full',
467
+ maxResults: 4,
468
+ maxTokens: 4000,
469
+ })
470
+ : Promise.resolve(null),
471
+ ]);
472
+
473
+ // Fire-and-forget confidence boost for consumed knowledge
474
+ this.boostKnowledgeResults(gotchas);
475
+ this.boostKnowledgeResults(conventions);
476
+
477
+ const relevantCode = code
478
+ ? code.pivotFiles
479
+ .map((f) => `// ${f.filePath}\n${f.content}`)
480
+ .join('\n\n')
481
+ : '';
482
+
483
+ // Build sibling list: all modules EXCEPT this one
484
+ const siblingModules = modules
485
+ .filter((m) => m.name !== mod.name)
486
+ .map((m) => ({
487
+ name: m.name,
488
+ agentType: m.agentType,
489
+ description: m.description,
490
+ }));
491
+
492
+ result[mod.name] = {
493
+ // Stable (cacheable)
494
+ conventions: conventions.map((c) => ({
495
+ title: c.title,
496
+ content: c.content,
497
+ } satisfies { title: string; content: string })),
498
+ gotchas: gotchas.map((g) => ({
499
+ title: g.title,
500
+ content: g.content,
501
+ } satisfies { title: string; content: string })),
502
+ siblingModules,
503
+ advisory,
504
+ // Volatile
505
+ relevantCode,
506
+ recalledContext: [], // populated by session index when available
507
+ recentBroadcasts,
508
+ };
509
+ }),
510
+ );
511
+
512
+ return result;
513
+ }
514
+
515
+ // ---------------------------------------------------------------------------
516
+ // Inspection phase
517
+ // ---------------------------------------------------------------------------
518
+
519
+ async injectInspectionContext(projectId: string): Promise<InspectionContext> {
520
+ const { repo } = this.loadProjectAndRepo(projectId);
521
+ const stackTags = repo ? parseStack(repo.stack) : [];
522
+
523
+ // Look up how many inspection cycles have occurred for this project
524
+ const cycleCount = this.countInspectionCycles(projectId);
525
+
526
+ const gotchas = await this.knowledgeSearch.searchGotchas(
527
+ 'code quality anti-patterns stubs incomplete implementation',
528
+ { stackTags, limit: 6, minConfidence: 0.35 },
529
+ );
530
+
531
+ // Fire-and-forget confidence boost for consumed knowledge
532
+ this.boostKnowledgeResults(gotchas);
533
+
534
+ // Past findings from inspection outputs stored in phase_outputs
535
+ const pastOutputRows = this.db.all<PhaseOutputRow>(
536
+ `SELECT * FROM phase_outputs WHERE project_id = ? AND phase = ? ORDER BY created_at DESC LIMIT 5`,
537
+ [projectId, 'inspection'],
538
+ );
539
+
540
+ const pastFindings = pastOutputRows.map((row) => ({
541
+ description: row.content.slice(0, 200),
542
+ severity: 'unknown',
543
+ resolution: '',
544
+ sourceRepo: repo?.name ?? 'current',
545
+ }));
546
+
547
+ // Fetch all broadcasts from this project for interface compatibility checks
548
+ const broadcasts = await this.getRecentBroadcasts(projectId, 30);
549
+
550
+ const advisory = await this.injectAdvisoryContext(projectId);
551
+
552
+ return {
553
+ checklist: buildInspectionChecklist(stackTags),
554
+ pastFindings,
555
+ antiStubPatterns: ANTI_STUB_PATTERNS,
556
+ cycleCount,
557
+ maxCycles: 3,
558
+ broadcasts,
559
+ advisory,
560
+ };
561
+ }
562
+
563
+ // ---------------------------------------------------------------------------
564
+ // Generic phase context
565
+ // ---------------------------------------------------------------------------
566
+
567
+ async injectPhaseContext(
568
+ projectId: string,
569
+ phase: Phase,
570
+ query?: string,
571
+ ): Promise<PhaseContext> {
572
+ const { project, repo } = this.loadProjectAndRepo(projectId);
573
+ const stackTags = repo ? parseStack(repo.stack) : [];
574
+ const effectiveQuery =
575
+ query ?? PHASE_QUERY_HINTS[phase] ?? project?.name ?? phase;
576
+
577
+ const warnings: string[] = [];
578
+
579
+ // Generic knowledge search
580
+ const knowledge = await this.knowledgeSearch.search(effectiveQuery, {
581
+ stack_tags: stackTags,
582
+ limit: 6,
583
+ min_confidence: 0.3,
584
+ });
585
+
586
+ // Fire-and-forget confidence boost for consumed knowledge
587
+ this.boostKnowledgeResults(knowledge);
588
+
589
+ if (knowledge.length === 0) {
590
+ warnings.push('No relevant knowledge found — knowledge base may be empty');
591
+ }
592
+
593
+ // Previous phase outputs for this project
594
+ const outputRows = this.db.all<PhaseOutputRow>(
595
+ `SELECT phase, output_type, content FROM phase_outputs
596
+ WHERE project_id = ? ORDER BY created_at DESC LIMIT 10`,
597
+ [projectId],
598
+ );
599
+
600
+ const previousOutputs = outputRows.map((row) => ({
601
+ phase: row.phase,
602
+ type: row.output_type,
603
+ preview: row.content.slice(0, 300),
604
+ }));
605
+
606
+ // Fetch recent agent broadcasts for this project
607
+ const recentBroadcasts = await this.getRecentBroadcasts(projectId);
608
+
609
+ return {
610
+ relevantKnowledge: knowledge.map((k) => ({
611
+ title: k.title,
612
+ content: k.content,
613
+ category: k.category,
614
+ confidence: k.confidence,
615
+ } satisfies { title: string; content: string; category: string; confidence: number })),
616
+ previousOutputs,
617
+ warnings,
618
+ recentBroadcasts,
619
+ };
620
+ }
621
+
622
+ // ---------------------------------------------------------------------------
623
+ // Private helpers
624
+ // ---------------------------------------------------------------------------
625
+
626
+ private loadProjectAndRepo(projectId: string): {
627
+ project: ProjectRow | undefined;
628
+ repo: RepoRow | undefined;
629
+ } {
630
+ const project = this.db.get<ProjectRow>(
631
+ `SELECT * FROM projects WHERE id = ?`,
632
+ [projectId],
633
+ );
634
+ const repo = project
635
+ ? this.db.get<RepoRow>(`SELECT * FROM repos WHERE id = ?`, [
636
+ project.repo_id,
637
+ ])
638
+ : undefined;
639
+ return { project, repo };
640
+ }
641
+
642
+ /**
643
+ * Count how many times this project has had an inspection -> implementation
644
+ * cycle (i.e., how many times inspection produced a "fail" verdict).
645
+ */
646
+ private countInspectionCycles(projectId: string): number {
647
+ const row = this.db.get<{ cnt: number }>(
648
+ `SELECT COUNT(*) as cnt FROM pipeline_events
649
+ WHERE project_id = ? AND from_phase = 'inspection' AND to_phase = 'implementation'`,
650
+ [projectId],
651
+ );
652
+ return row?.cnt ?? 0;
653
+ }
654
+ }
655
+
656
+ // ---------------------------------------------------------------------------
657
+ // Utilities
658
+ // ---------------------------------------------------------------------------
659
+
660
+ function parseStack(stackJson: string): string[] {
661
+ try {
662
+ const parsed = JSON.parse(stackJson);
663
+ return Array.isArray(parsed) ? (parsed as string[]) : [];
664
+ } catch {
665
+ return [];
666
+ }
667
+ }
668
+
669
+ function basename(filePath: string): string {
670
+ const parts = filePath.replace(/\\/g, '/').split('/');
671
+ return parts[parts.length - 1] ?? filePath;
672
+ }
673
+
674
+ function extractPatterns(content: string, stackTags: string[]): string[] {
675
+ const patterns: string[] = [];
676
+ if (content.includes('Module') && stackTags.includes('nestjs')) {
677
+ patterns.push('NestJS module pattern detected');
678
+ }
679
+ if (content.includes('Drizzle') || content.includes('drizzle')) {
680
+ patterns.push('Drizzle ORM pattern detected');
681
+ }
682
+ if (content.includes('interface ') || content.includes('type ')) {
683
+ patterns.push('TypeScript interface-driven design');
684
+ }
685
+ if (content.includes('export class')) {
686
+ patterns.push('Class-based service pattern');
687
+ }
688
+ return patterns;
689
+ }
690
+
691
+ function extractDependencies(content: string): string[] {
692
+ const deps: string[] = [];
693
+ const importRegex = /from ['"](@?[^'"./][^'"]*)['"]/g;
694
+ let match: RegExpExecArray | null;
695
+ const seen = new Set<string>();
696
+ while ((match = importRegex.exec(content)) !== null) {
697
+ const dep = match[1];
698
+ if (dep && !seen.has(dep)) {
699
+ seen.add(dep);
700
+ deps.push(dep);
701
+ }
702
+ }
703
+ return deps.slice(0, 10);
704
+ }
705
+
706
+ function buildInterviewTemplate(stackTags: string[]): string {
707
+ const stackList = stackTags.length > 0 ? stackTags.join(', ') : 'unspecified';
708
+ return `## Strategist Interview Template
709
+
710
+ **Tech Stack**: ${stackList}
711
+
712
+ ### Questions to ask:
713
+ 1. What is the core problem this feature solves?
714
+ 2. Who are the primary users and what is their workflow?
715
+ 3. What are the success criteria? How will we know it is done?
716
+ 4. What are the constraints (timeline, performance, compatibility)?
717
+ 5. Are there existing patterns or modules in the codebase we should follow?
718
+ 6. What are the out-of-scope items for this iteration?
719
+
720
+ ### Vision document to produce:
721
+ - Problem statement (1 paragraph)
722
+ - User stories (numbered list)
723
+ - Success criteria (measurable)
724
+ - Out of scope (explicit list)
725
+ - Known risks or dependencies`;
726
+ }
727
+
728
+ function buildInspectionChecklist(stackTags: string[]): string {
729
+ const lines = [
730
+ '## Inspector Checklist',
731
+ '',
732
+ '### Build',
733
+ '- [ ] TypeScript compiles without errors',
734
+ '- [ ] No missing imports or unresolved modules',
735
+ '- [ ] No dangling providers or unregistered services',
736
+ '',
737
+ '### Anti-Stub',
738
+ '- [ ] No TODO comments left in implementation',
739
+ '- [ ] No hardcoded return values (stub responses)',
740
+ '- [ ] Every service method performs real work',
741
+ '- [ ] Every guard performs real auth checks',
742
+ '',
743
+ '### Wiring',
744
+ '- [ ] HTTP request traces end-to-end through controller -> service -> DB',
745
+ '- [ ] All error cases handled (not just happy path)',
746
+ '- [ ] Response shapes match specified interfaces',
747
+ '',
748
+ '### Quality',
749
+ '- [ ] Input validation on all endpoints',
750
+ '- [ ] Appropriate HTTP status codes',
751
+ '- [ ] No bare catch blocks that swallow errors',
752
+ ];
753
+
754
+ if (stackTags.includes('nestjs')) {
755
+ lines.push(
756
+ '',
757
+ '### NestJS-specific',
758
+ '- [ ] All providers registered in their module',
759
+ '- [ ] All module imports declared',
760
+ '- [ ] DTOs have class-validator decorators',
761
+ '- [ ] Guards are applied with @UseGuards()',
762
+ );
763
+ }
764
+
765
+ if (stackTags.includes('drizzle')) {
766
+ lines.push(
767
+ '',
768
+ '### Drizzle-specific',
769
+ '- [ ] Migrations generated and applied (db:generate then db:migrate)',
770
+ '- [ ] No raw SQL where Drizzle query builder is appropriate',
771
+ '- [ ] Exported interfaces for service return types (TS4053 prevention)',
772
+ );
773
+ }
774
+
775
+ return lines.join('\n');
776
+ }
777
+
778
+ const ANTI_STUB_PATTERNS = [
779
+ 'return { success: true } without side effects',
780
+ '// TODO: implement later',
781
+ '// TODO: add real logic',
782
+ 'throw new Error("Not implemented")',
783
+ 'return null // placeholder',
784
+ 'console.log("implement me")',
785
+ 'return [] // stub',
786
+ 'return {} // placeholder',
787
+ ];
788
+
789
+ const PHASE_QUERY_HINTS: Partial<Record<Phase, string>> = {
790
+ interview: 'project scoping vision requirements user stories',
791
+ architecture: 'module design service boundaries database schema API contracts',
792
+ design: 'UI design component hierarchy user experience layout',
793
+ qa_strategy: 'test plan testing strategy coverage integration tests',
794
+ implementation: 'implementation code services controllers database',
795
+ inspection: 'code review quality check anti-patterns stubs completeness',
796
+ knowledge_collection: 'learnings gotchas patterns decisions retrospective',
797
+ };