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,565 @@
1
+ // KnowledgeSearch — semantic search over the Qdrant knowledge collection.
2
+ //
3
+ // Architecture notes (ADR-2):
4
+ // - ALL knowledge search goes through Qdrant. There is no SQLite FTS fallback.
5
+ // - When Qdrant is unavailable the search returns [] with a logged warning —
6
+ // it does NOT throw. The pipeline continues; agents just receive no gotchas.
7
+ // - Cross-repo search is opt-in (include_cross_repo defaults to false at the
8
+ // tool layer). Results from the caller's repo get a small score boost to
9
+ // surface local knowledge first without completely filtering out cross-repo
10
+ // items when cross-repo is enabled.
11
+ // - stack_tags filtering uses a Qdrant "should" condition (any tag matches)
12
+ // rather than "must" (all tags must match), because a gotcha tagged with
13
+ // ["nestjs", "swc"] is equally relevant to a query from a project that uses
14
+ // either nestjs or swc.
15
+ //
16
+ // Qdrant filter shape reference:
17
+ // must → all conditions must be satisfied (AND)
18
+ // should→ at least one condition must be satisfied (OR)
19
+ // Values indexed as 'keyword' support match: { value: "..." } for exact match
20
+ // Values indexed as 'float' support range: { gte: 0.3 }
21
+
22
+ import { logger } from '../util/logger.js';
23
+ import { embedText } from '../ingestion/embedder.js';
24
+ import type { QdrantVectorStore } from '../storage/qdrant-store.js';
25
+ import type { KnowledgeCategory, KnowledgeItem, KnowledgePayload } from '../util/types.js';
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Public types (spec-matching interface)
29
+ // ---------------------------------------------------------------------------
30
+
31
+ /**
32
+ * Result shape returned from KnowledgeSearch.search().
33
+ * Uses snake_case field names to match the MCP tool response contract.
34
+ */
35
+ export interface KnowledgeSearchResult {
36
+ id: string;
37
+ category: string;
38
+ title: string;
39
+ content: string;
40
+ confidence: number;
41
+ stack_tags: string[];
42
+ source_repo: string;
43
+ relevance_score: number;
44
+ }
45
+
46
+ // Legacy camelCase interface retained for backward compat with earlier callers.
47
+ // New callers should use KnowledgeSearchResult (snake_case).
48
+ export interface KnowledgeSearchOptions {
49
+ repoId?: string;
50
+ category?: KnowledgeCategory;
51
+ stackTags?: string[];
52
+ minConfidence?: number;
53
+ limit?: number;
54
+ scoreThreshold?: number;
55
+ }
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // Score boost applied to results from the same repo as the caller.
59
+ // Set conservatively: local items should surface first but not crowd out
60
+ // highly relevant cross-repo items.
61
+ // ---------------------------------------------------------------------------
62
+ const SAME_REPO_SCORE_BOOST = 0.05;
63
+
64
+ // ---------------------------------------------------------------------------
65
+ // KnowledgeSearch
66
+ // ---------------------------------------------------------------------------
67
+
68
+ export class KnowledgeSearch {
69
+ constructor(private readonly vectorStore: QdrantVectorStore) {}
70
+
71
+ // ---------------------------------------------------------------------------
72
+ // Primary search method (spec B4 signature)
73
+ // ---------------------------------------------------------------------------
74
+
75
+ /**
76
+ * Semantic search across knowledge items stored in the Qdrant knowledge
77
+ * collection.
78
+ *
79
+ * Options:
80
+ * category — filter to a single KnowledgeCategory
81
+ * stack_tags — filter to items that share at least one tag (OR match)
82
+ * repo_id — used for same-repo scoring boost; also hard-filters
83
+ * when include_cross_repo is false
84
+ * limit — max results to return (default 10)
85
+ * include_cross_repo— include items from other repos (default true)
86
+ * min_confidence — minimum confidence score (default 0.3)
87
+ *
88
+ * Returns results sorted descending by relevance_score.
89
+ * Returns [] when Qdrant is unavailable — never throws.
90
+ */
91
+ async search(
92
+ query: string,
93
+ options?: {
94
+ category?: KnowledgeCategory;
95
+ stack_tags?: string[];
96
+ repo_id?: string;
97
+ limit?: number;
98
+ include_cross_repo?: boolean;
99
+ min_confidence?: number;
100
+ },
101
+ ): Promise<KnowledgeSearchResult[]> {
102
+ const {
103
+ category,
104
+ stack_tags,
105
+ repo_id,
106
+ limit = 10,
107
+ include_cross_repo = true,
108
+ min_confidence = 0.3,
109
+ } = options ?? {};
110
+
111
+ // Check Qdrant availability first — avoid embedding model init overhead
112
+ // when Qdrant is down.
113
+ const healthy = await this.vectorStore.isHealthy();
114
+ if (!healthy) {
115
+ logger.warn('KnowledgeSearch.search: Qdrant unavailable, returning empty results', {
116
+ query: query.slice(0, 80),
117
+ });
118
+ return [];
119
+ }
120
+
121
+ let queryVector: number[];
122
+ try {
123
+ queryVector = await embedText(query);
124
+ } catch (err) {
125
+ logger.warn('KnowledgeSearch.search: embedding failed, returning empty results', {
126
+ error: String(err),
127
+ });
128
+ return [];
129
+ }
130
+
131
+ // Build Qdrant filter.
132
+ // When include_cross_repo is false, hard-filter to repo_id only.
133
+ // When include_cross_repo is true, only include cross-repo items that have
134
+ // sharing = 'team' or 'public' (private repos never leak into other repos'
135
+ // results). Items from the caller's own repo are always included regardless
136
+ // of sharing setting.
137
+ const qdrantFilter = this._buildQdrantFilter({
138
+ category,
139
+ stack_tags,
140
+ repo_id: include_cross_repo ? undefined : repo_id,
141
+ caller_repo_id: include_cross_repo ? repo_id : undefined,
142
+ min_confidence,
143
+ });
144
+
145
+ let rawResults: Array<{ id: string; score: number; payload: Record<string, unknown> }>;
146
+ try {
147
+ // Fetch more than requested to allow re-ranking after same-repo boost.
148
+ const fetchLimit = Math.min(limit * 3, 100);
149
+
150
+ const results = await this.vectorStore.searchKnowledge(queryVector, {
151
+ limit: fetchLimit,
152
+ // We handle min_confidence via the filter's range condition on the
153
+ // confidence payload field rather than scoreThreshold (which applies
154
+ // to the cosine similarity score, a different axis).
155
+ scoreThreshold: 0.0,
156
+ filter: qdrantFilter as Record<string, unknown>,
157
+ });
158
+
159
+ rawResults = results.map((r) => ({
160
+ id: r.id,
161
+ score: r.score,
162
+ payload: r.payload as unknown as Record<string, unknown>,
163
+ }));
164
+ } catch (err) {
165
+ logger.warn('KnowledgeSearch.search: Qdrant search failed, returning empty results', {
166
+ error: String(err),
167
+ });
168
+ return [];
169
+ }
170
+
171
+ // Apply same-repo boost, re-rank, trim to limit.
172
+ const boosted = rawResults.map((r) => {
173
+ const payload = r.payload as Partial<KnowledgePayload>;
174
+ const boost = repo_id && payload.repo_id === repo_id ? SAME_REPO_SCORE_BOOST : 0;
175
+ return { ...r, boostedScore: r.score + boost };
176
+ });
177
+
178
+ boosted.sort((a, b) => b.boostedScore - a.boostedScore);
179
+
180
+ const output: KnowledgeSearchResult[] = [];
181
+ for (const r of boosted) {
182
+ if (output.length >= limit) break;
183
+
184
+ const p = r.payload as Partial<KnowledgePayload>;
185
+ if (!p.id || !p.title || !p.content) continue;
186
+
187
+ output.push({
188
+ id: p.id,
189
+ category: p.category ?? 'pattern',
190
+ title: p.title,
191
+ content: p.content,
192
+ confidence: p.confidence ?? 0,
193
+ stack_tags: Array.isArray(p.stack_tags) ? p.stack_tags : [],
194
+ source_repo: p.repo_id ?? '',
195
+ relevance_score: r.boostedScore,
196
+ });
197
+ }
198
+
199
+ logger.debug('KnowledgeSearch.search: completed', {
200
+ query: query.slice(0, 80),
201
+ resultCount: output.length,
202
+ category,
203
+ hadSameRepoBoost: !!repo_id,
204
+ });
205
+
206
+ return output;
207
+ }
208
+
209
+ // ---------------------------------------------------------------------------
210
+ // Confidence boost
211
+ // ---------------------------------------------------------------------------
212
+
213
+ /**
214
+ * Increment access_count and update accessed_at for a knowledge item.
215
+ * Optionally bumps the confidence score by boostAmount.
216
+ *
217
+ * Called when an agent actively consumes a knowledge item — access signals
218
+ * genuine utility and nudges confidence upward.
219
+ * Silent on Qdrant failure (non-fatal side effect).
220
+ */
221
+ async boostConfidence(knowledgeId: string, boostAmount = 0.01): Promise<void> {
222
+ const healthy = await this.vectorStore.isHealthy();
223
+ if (!healthy) {
224
+ logger.debug('KnowledgeSearch.boostConfidence: Qdrant unavailable, skip', { knowledgeId });
225
+ return;
226
+ }
227
+
228
+ try {
229
+ // Use setKnowledgePayload if the store exposes it; otherwise fall back
230
+ // to a no-op so callers never see an exception from a best-effort call.
231
+ const store = this.vectorStore as QdrantVectorStore & {
232
+ getKnowledge?: (id: string) => Promise<Record<string, unknown> | null>;
233
+ setKnowledgePayload?: (id: string, patch: Record<string, unknown>) => Promise<void>;
234
+ };
235
+
236
+ let currentConfidence = 0;
237
+ let currentAccessCount = 0;
238
+
239
+ if (typeof store.getKnowledge === 'function') {
240
+ const existing = await store.getKnowledge(knowledgeId);
241
+ if (existing) {
242
+ currentConfidence = (existing['confidence'] as number | undefined) ?? 0;
243
+ currentAccessCount = (existing['access_count'] as number | undefined) ?? 0;
244
+ }
245
+ }
246
+
247
+ const patch: Record<string, unknown> = {
248
+ accessed_at: Date.now(),
249
+ access_count: currentAccessCount + 1,
250
+ confidence: Math.min(1.0, currentConfidence + boostAmount),
251
+ };
252
+
253
+ if (typeof store.setKnowledgePayload === 'function') {
254
+ await store.setKnowledgePayload(knowledgeId, patch);
255
+ logger.debug('KnowledgeSearch.boostConfidence: boosted', {
256
+ knowledgeId,
257
+ newAccessCount: patch['access_count'],
258
+ newConfidence: patch['confidence'],
259
+ });
260
+ } else {
261
+ logger.debug(
262
+ 'KnowledgeSearch.boostConfidence: setKnowledgePayload not available, skipping',
263
+ { knowledgeId },
264
+ );
265
+ }
266
+ } catch (err) {
267
+ logger.warn('KnowledgeSearch.boostConfidence: failed (non-fatal)', {
268
+ knowledgeId,
269
+ error: String(err),
270
+ });
271
+ }
272
+ }
273
+
274
+ // ---------------------------------------------------------------------------
275
+ // Typed accessors
276
+ // ---------------------------------------------------------------------------
277
+
278
+ /**
279
+ * Return gotcha items optionally filtered by stack tags.
280
+ * Uses Qdrant filter (category=gotcha) plus optional stack_tags OR match.
281
+ * Results sorted by confidence descending.
282
+ * Gracefully returns [] on Qdrant unavailability.
283
+ */
284
+ async getGotchas(
285
+ stack?: string[],
286
+ limit = 20,
287
+ minConfidence = 0.3,
288
+ ): Promise<KnowledgeItem[]> {
289
+ return this._getByCategory('gotcha', stack, limit, minConfidence);
290
+ }
291
+
292
+ /**
293
+ * Return pattern items optionally filtered by domain.
294
+ * `domain` is treated as a stack tag (single-element array) for consistency.
295
+ * Results sorted by confidence descending.
296
+ * Gracefully returns [] on Qdrant unavailability.
297
+ */
298
+ async getPatterns(
299
+ domain?: string,
300
+ limit = 20,
301
+ minConfidence = 0.3,
302
+ ): Promise<KnowledgeItem[]> {
303
+ const tags = domain ? [domain] : undefined;
304
+ return this._getByCategory('pattern', tags, limit, minConfidence);
305
+ }
306
+
307
+ // ---------------------------------------------------------------------------
308
+ // Backward-compat helpers (camelCase signature from the prior implementation)
309
+ // ---------------------------------------------------------------------------
310
+
311
+ /** Convenience: search with category=gotcha. Backward-compat camelCase API. */
312
+ async searchGotchas(
313
+ query: string,
314
+ opts?: Omit<KnowledgeSearchOptions, 'category'>,
315
+ ): Promise<KnowledgeSearchResult[]> {
316
+ return this.search(query, {
317
+ category: 'gotcha',
318
+ stack_tags: opts?.stackTags,
319
+ repo_id: opts?.repoId,
320
+ limit: opts?.limit,
321
+ min_confidence: opts?.minConfidence,
322
+ });
323
+ }
324
+
325
+ /** Convenience: search with category=decision. Backward-compat camelCase API. */
326
+ async searchDecisions(
327
+ query: string,
328
+ opts?: Omit<KnowledgeSearchOptions, 'category'>,
329
+ ): Promise<KnowledgeSearchResult[]> {
330
+ return this.search(query, {
331
+ category: 'decision',
332
+ stack_tags: opts?.stackTags,
333
+ repo_id: opts?.repoId,
334
+ limit: opts?.limit,
335
+ min_confidence: opts?.minConfidence,
336
+ });
337
+ }
338
+
339
+ /** Convenience: search with category=pattern. Backward-compat camelCase API. */
340
+ async searchPatterns(
341
+ query: string,
342
+ opts?: Omit<KnowledgeSearchOptions, 'category'>,
343
+ ): Promise<KnowledgeSearchResult[]> {
344
+ return this.search(query, {
345
+ category: 'pattern',
346
+ stack_tags: opts?.stackTags,
347
+ repo_id: opts?.repoId,
348
+ limit: opts?.limit,
349
+ min_confidence: opts?.minConfidence,
350
+ });
351
+ }
352
+
353
+ /** Convenience: search with category=convention. Backward-compat camelCase API. */
354
+ async searchConventions(
355
+ query: string,
356
+ opts?: Omit<KnowledgeSearchOptions, 'category'>,
357
+ ): Promise<KnowledgeSearchResult[]> {
358
+ return this.search(query, {
359
+ category: 'convention',
360
+ stack_tags: opts?.stackTags,
361
+ repo_id: opts?.repoId,
362
+ limit: opts?.limit,
363
+ min_confidence: opts?.minConfidence,
364
+ });
365
+ }
366
+
367
+ /** True when the vector store is configured and healthy. */
368
+ async isAvailable(): Promise<boolean> {
369
+ return this.vectorStore.isHealthy();
370
+ }
371
+
372
+ // ---------------------------------------------------------------------------
373
+ // Private helpers
374
+ // ---------------------------------------------------------------------------
375
+
376
+ /**
377
+ * Retrieve knowledge items by category using Qdrant filter scroll (no
378
+ * semantic query). Falls back to semantic search when filterKnowledge is
379
+ * not yet available on the store.
380
+ */
381
+ private async _getByCategory(
382
+ category: KnowledgeCategory,
383
+ stack?: string[],
384
+ limit = 20,
385
+ minConfidence = 0.3,
386
+ ): Promise<KnowledgeItem[]> {
387
+ const healthy = await this.vectorStore.isHealthy();
388
+ if (!healthy) {
389
+ logger.warn(`KnowledgeSearch._getByCategory: Qdrant unavailable`, { category });
390
+ return [];
391
+ }
392
+
393
+ try {
394
+ const qdrantFilter = this._buildQdrantFilter({
395
+ category,
396
+ stack_tags: stack,
397
+ min_confidence: minConfidence,
398
+ });
399
+
400
+ // Use filterKnowledge (scroll-based, no semantic ranking) if available.
401
+ const store = this.vectorStore as QdrantVectorStore & {
402
+ filterKnowledge?: (
403
+ filter: Record<string, unknown>,
404
+ limit: number,
405
+ ) => Promise<Array<{ id: string; payload: Record<string, unknown> }>>;
406
+ };
407
+
408
+ if (typeof store.filterKnowledge === 'function') {
409
+ const rawItems = await store.filterKnowledge(
410
+ qdrantFilter as Record<string, unknown>,
411
+ limit,
412
+ );
413
+ return rawItems
414
+ .map((r) => this._payloadToKnowledgeItem(r.payload))
415
+ .filter((item): item is KnowledgeItem => item !== null)
416
+ .sort((a, b) => b.confidence - a.confidence);
417
+ }
418
+
419
+ // Fallback: semantic search using the category name as query.
420
+ // Less accurate but functional until filterKnowledge lands.
421
+ logger.debug(
422
+ `KnowledgeSearch._getByCategory: filterKnowledge unavailable, ` +
423
+ `falling back to semantic search`,
424
+ { category },
425
+ );
426
+ const results = await this.search(category, {
427
+ category: category as KnowledgeCategory,
428
+ stack_tags: stack,
429
+ limit,
430
+ min_confidence: minConfidence,
431
+ });
432
+ return results.map((r) => this._resultToKnowledgeItem(r));
433
+ } catch (err) {
434
+ logger.warn(`KnowledgeSearch._getByCategory: failed`, {
435
+ category,
436
+ error: String(err),
437
+ });
438
+ return [];
439
+ }
440
+ }
441
+
442
+ /**
443
+ * Build the Qdrant filter object from search option components.
444
+ *
445
+ * We construct the full Qdrant filter shape directly here (not relying on
446
+ * QdrantVectorStore.buildFilter) because we need both "must" (AND) and
447
+ * "should" (OR) conditions which the simple flat-map builder cannot express.
448
+ *
449
+ * Qdrant filter reference:
450
+ * { must: [...], should: [...], must_not: [...] }
451
+ * Each condition: { key: string, match: { value } } or { key, range: { gte } }
452
+ */
453
+ private _buildQdrantFilter(opts: {
454
+ category?: KnowledgeCategory;
455
+ stack_tags?: string[];
456
+ repo_id?: string;
457
+ caller_repo_id?: string;
458
+ min_confidence?: number;
459
+ }): Record<string, unknown> | undefined {
460
+ const must: unknown[] = [];
461
+ const should: unknown[] = [];
462
+
463
+ if (opts.category) {
464
+ must.push({ key: 'category', match: { value: opts.category } });
465
+ }
466
+
467
+ // Hard-filter to a single repo (used when include_cross_repo is false).
468
+ if (opts.repo_id) {
469
+ must.push({ key: 'repo_id', match: { value: opts.repo_id } });
470
+ }
471
+
472
+ // Cross-repo sharing enforcement: when searching across repos, only include
473
+ // items that are either (a) from the caller's own repo, or (b) have sharing
474
+ // set to 'team' or 'public'. Private repos never leak into other repos.
475
+ //
476
+ // Qdrant filter logic:
477
+ // must: [{ should: [
478
+ // { repo_id == caller_repo_id }, ← own repo: always included
479
+ // { sharing == 'team' }, ← team-shared repos
480
+ // { sharing == 'public' }, ← public repos
481
+ // ]}]
482
+ if (opts.caller_repo_id && !opts.repo_id) {
483
+ must.push({
484
+ should: [
485
+ { key: 'repo_id', match: { value: opts.caller_repo_id } },
486
+ { key: 'sharing', match: { value: 'team' } },
487
+ { key: 'sharing', match: { value: 'public' } },
488
+ ],
489
+ });
490
+ } else if (!opts.repo_id && !opts.caller_repo_id) {
491
+ // No caller repo context — only allow team/public items to prevent
492
+ // accidental exposure of private repo knowledge.
493
+ must.push({
494
+ should: [
495
+ { key: 'sharing', match: { value: 'team' } },
496
+ { key: 'sharing', match: { value: 'public' } },
497
+ ],
498
+ });
499
+ }
500
+
501
+ if (opts.min_confidence !== undefined && opts.min_confidence > 0) {
502
+ must.push({ key: 'confidence', range: { gte: opts.min_confidence } });
503
+ }
504
+
505
+ // stack_tags: require at least one matching tag (OR semantics).
506
+ if (opts.stack_tags && opts.stack_tags.length > 0) {
507
+ for (const tag of opts.stack_tags) {
508
+ should.push({ key: 'stack_tags', match: { value: tag } });
509
+ }
510
+ }
511
+
512
+ if (must.length === 0 && should.length === 0) {
513
+ return undefined;
514
+ }
515
+
516
+ const filter: Record<string, unknown> = {};
517
+ if (must.length > 0) filter['must'] = must;
518
+ if (should.length > 0) filter['should'] = should;
519
+
520
+ return filter;
521
+ }
522
+
523
+ /**
524
+ * Convert a KnowledgePayload from Qdrant into a KnowledgeItem.
525
+ * Returns null if required fields are missing.
526
+ */
527
+ private _payloadToKnowledgeItem(
528
+ payload: Record<string, unknown>,
529
+ ): KnowledgeItem | null {
530
+ const p = payload as Partial<KnowledgePayload>;
531
+ if (!p.id || !p.title || !p.content) return null;
532
+
533
+ return {
534
+ id: p.id,
535
+ title: p.title,
536
+ content: p.content,
537
+ stack_tags: Array.isArray(p.stack_tags) ? p.stack_tags : [],
538
+ confidence: p.confidence ?? 0,
539
+ source: (p.source as KnowledgeItem['source']) ?? 'manual',
540
+ source_phase: (p.source_phase as KnowledgeItem['source_phase']) ?? null,
541
+ source_agent: p.source_agent ?? null,
542
+ created_at: p.created_at ?? Date.now(),
543
+ updated_at: p.updated_at ?? Date.now(),
544
+ };
545
+ }
546
+
547
+ /**
548
+ * Convert a KnowledgeSearchResult back to a KnowledgeItem shape.
549
+ * Used as a fallback when filterKnowledge is not available on the store.
550
+ */
551
+ private _resultToKnowledgeItem(result: KnowledgeSearchResult): KnowledgeItem {
552
+ return {
553
+ id: result.id,
554
+ title: result.title,
555
+ content: result.content,
556
+ stack_tags: result.stack_tags,
557
+ confidence: result.confidence,
558
+ source: 'manual',
559
+ source_phase: null,
560
+ source_agent: null,
561
+ created_at: Date.now(),
562
+ updated_at: Date.now(),
563
+ };
564
+ }
565
+ }