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,545 @@
1
+ /**
2
+ * Git history analyzer for co-modification edges (Phase 2 full implementation).
3
+ * Runs `git log --name-only` to find files changed together frequently,
4
+ * creates CO_MODIFIED edges, and detects test file relationships.
5
+ *
6
+ * Phase 3 additions:
7
+ * - analyzeFileStats(): per-file commit count, stability, velocity
8
+ * - extractCommitRecords(): parse git log into CommitRecord[]
9
+ */
10
+
11
+ import { exec } from 'child_process';
12
+ import { promisify } from 'util';
13
+ import { logger } from '../util/logger.js';
14
+ import type { GraphStore } from '../storage/interfaces.js';
15
+ import type { FileChangeStats, CommitRecord } from '../util/types.js';
16
+ import { UPSERT_CO_MODIFIED } from '../query/graph-queries.js';
17
+
18
+ const execAsync = promisify(exec);
19
+
20
+ export interface CoModificationPair {
21
+ fileA: string;
22
+ fileB: string;
23
+ count: number;
24
+ lastCommit: string;
25
+ }
26
+
27
+ export interface GitAnalysisResult {
28
+ coModifications: CoModificationPair[];
29
+ testPairs: Array<{ testFile: string; sourceFile: string }>;
30
+ totalCommitsAnalyzed: number;
31
+ errors: string[];
32
+ }
33
+
34
+ /**
35
+ * Analyze git history to find files frequently changed together.
36
+ * Parses `git log --name-only` output for the last `historyDepth` commits,
37
+ * counts co-occurrence pairs, and returns pairs with count >= minComodCount.
38
+ */
39
+ export async function analyzeCoModifications(
40
+ repoPath: string,
41
+ historyDepth: number = 500,
42
+ minComodCount: number = 3
43
+ ): Promise<CoModificationPair[]> {
44
+ logger.debug('git-analyzer: starting co-modification analysis', { repoPath, historyDepth });
45
+
46
+ let stdout: string;
47
+ try {
48
+ const { stdout: out } = await execAsync(
49
+ `git -C "${repoPath}" log --name-only --format="%H" -n ${historyDepth}`,
50
+ { maxBuffer: 50 * 1024 * 1024 } // 50 MB buffer for large repos
51
+ );
52
+ stdout = out;
53
+ } catch (err) {
54
+ logger.warn('git log failed — not a git repo or git not available', {
55
+ repoPath,
56
+ error: String(err),
57
+ });
58
+ return [];
59
+ }
60
+
61
+ const commitGroups = parseGitLog(stdout);
62
+ const pairCounts = new Map<string, { count: number; lastCommit: string }>();
63
+
64
+ for (const { hash, files } of commitGroups) {
65
+ // Only consider commits with 2-50 files (ignore mega-commits like reformats)
66
+ if (files.length < 2 || files.length > 50) continue;
67
+
68
+ // Count every ordered pair (lexicographically sorted to ensure canonical key)
69
+ for (let i = 0; i < files.length; i++) {
70
+ for (let j = i + 1; j < files.length; j++) {
71
+ const [a, b] = [files[i], files[j]].sort();
72
+ const key = `${a}\x00${b}`;
73
+ const existing = pairCounts.get(key);
74
+ if (existing) {
75
+ existing.count++;
76
+ existing.lastCommit = hash;
77
+ } else {
78
+ pairCounts.set(key, { count: 1, lastCommit: hash });
79
+ }
80
+ }
81
+ }
82
+ }
83
+
84
+ const result: CoModificationPair[] = [];
85
+ for (const [key, { count, lastCommit }] of pairCounts) {
86
+ if (count >= minComodCount) {
87
+ const parts = key.split('\x00');
88
+ const fileA = parts[0] ?? '';
89
+ const fileB = parts[1] ?? '';
90
+ result.push({ fileA, fileB, count, lastCommit });
91
+ }
92
+ }
93
+
94
+ logger.debug('git-analyzer: co-modification analysis complete', {
95
+ repoPath,
96
+ totalCommits: commitGroups.length,
97
+ uniquePairs: pairCounts.size,
98
+ qualifyingPairs: result.length,
99
+ });
100
+
101
+ return result;
102
+ }
103
+
104
+ /**
105
+ * Parse `git log --name-only --format="%H"` output into commit groups.
106
+ * Each group contains a commit hash and the list of files changed.
107
+ */
108
+ export function parseGitLog(
109
+ stdout: string
110
+ ): Array<{ hash: string; files: string[] }> {
111
+ const groups: Array<{ hash: string; files: string[] }> = [];
112
+ const lines = stdout.split('\n');
113
+
114
+ let currentHash = '';
115
+ let currentFiles: string[] = [];
116
+
117
+ for (const rawLine of lines) {
118
+ const line = rawLine.trim();
119
+ if (!line) {
120
+ // Blank line separates commits; flush current group
121
+ if (currentHash && currentFiles.length > 0) {
122
+ groups.push({ hash: currentHash, files: currentFiles });
123
+ currentFiles = [];
124
+ }
125
+ continue;
126
+ }
127
+
128
+ // A commit hash is exactly 40 hex chars
129
+ if (/^[0-9a-f]{40}$/i.test(line)) {
130
+ // Flush previous group before starting a new commit
131
+ if (currentHash && currentFiles.length > 0) {
132
+ groups.push({ hash: currentHash, files: currentFiles });
133
+ currentFiles = [];
134
+ }
135
+ currentHash = line;
136
+ } else {
137
+ // This is a file name line
138
+ if (currentHash) {
139
+ currentFiles.push(line);
140
+ }
141
+ }
142
+ }
143
+
144
+ // Flush final group
145
+ if (currentHash && currentFiles.length > 0) {
146
+ groups.push({ hash: currentHash, files: currentFiles });
147
+ }
148
+
149
+ return groups;
150
+ }
151
+
152
+ /**
153
+ * Detect test files and their corresponding source files.
154
+ * Returns pairs: { testFile, sourceFile }
155
+ *
156
+ * Handles patterns:
157
+ * foo.spec.ts -> foo.ts
158
+ * foo.test.ts -> foo.ts
159
+ * __tests__/foo.ts -> ../foo.ts (sibling detection)
160
+ */
161
+ export async function detectTestFiles(
162
+ files: string[]
163
+ ): Promise<Array<{ testFile: string; sourceFile: string }>> {
164
+ const fileSet = new Set(files);
165
+ const pairs: Array<{ testFile: string; sourceFile: string }> = [];
166
+
167
+ for (const file of files) {
168
+ // Pattern 1: foo.spec.{ext} -> foo.{ext}
169
+ const specMatch = file.match(/^(.+)\.spec\.(ts|tsx|js|jsx|mts|cts)$/);
170
+ if (specMatch) {
171
+ const [, base, ext] = specMatch;
172
+ const candidate = `${base}.${ext}`;
173
+ if (fileSet.has(candidate)) {
174
+ pairs.push({ testFile: file, sourceFile: candidate });
175
+ }
176
+ continue;
177
+ }
178
+
179
+ // Pattern 2: foo.test.{ext} -> foo.{ext}
180
+ const testMatch = file.match(/^(.+)\.test\.(ts|tsx|js|jsx|mts|cts)$/);
181
+ if (testMatch) {
182
+ const [, base, ext] = testMatch;
183
+ const candidate = `${base}.${ext}`;
184
+ if (fileSet.has(candidate)) {
185
+ pairs.push({ testFile: file, sourceFile: candidate });
186
+ }
187
+ continue;
188
+ }
189
+
190
+ // Pattern 3: __tests__/foo.ts -> ../foo.ts (try parent dir)
191
+ const testsMatch = file.match(/^(.*\/)__tests__\/([^/]+)$/);
192
+ if (testsMatch) {
193
+ const [, dir, filename] = testsMatch;
194
+ const candidate = `${dir}${filename}`;
195
+ if (fileSet.has(candidate)) {
196
+ pairs.push({ testFile: file, sourceFile: candidate });
197
+ }
198
+ }
199
+ }
200
+
201
+ return pairs;
202
+ }
203
+
204
+ /**
205
+ * Run full git analysis for a repo: co-modifications + test file detection.
206
+ * Then write the edges to FalkorDB.
207
+ */
208
+ export async function runGitAnalysis(
209
+ repoPath: string,
210
+ repoId: string,
211
+ allFiles: string[],
212
+ graphStore: GraphStore,
213
+ historyDepth: number = 500,
214
+ minComodCount: number = 3
215
+ ): Promise<GitAnalysisResult> {
216
+ const errors: string[] = [];
217
+
218
+ // 1. Analyze co-modifications from git log
219
+ let coModifications: CoModificationPair[] = [];
220
+ let totalCommitsAnalyzed = 0;
221
+
222
+ try {
223
+ coModifications = await analyzeCoModifications(repoPath, historyDepth, minComodCount);
224
+
225
+ // Count commits (re-parse log for count only)
226
+ try {
227
+ const { stdout } = await execAsync(
228
+ `git -C "${repoPath}" rev-list --count HEAD -n ${historyDepth}`,
229
+ { maxBuffer: 1024 * 1024 }
230
+ );
231
+ totalCommitsAnalyzed = parseInt(stdout.trim(), 10) || 0;
232
+ } catch {
233
+ totalCommitsAnalyzed = 0;
234
+ }
235
+ } catch (err) {
236
+ errors.push(`Co-modification analysis failed: ${String(err)}`);
237
+ }
238
+
239
+ // 2. Write CO_MODIFIED edges to graph
240
+ for (const pair of coModifications) {
241
+ try {
242
+ await graphStore.query(
243
+ UPSERT_CO_MODIFIED(pair.fileA, pair.fileB, repoId, pair.count, pair.lastCommit)
244
+ );
245
+ } catch (err) {
246
+ logger.warn('Failed to write CO_MODIFIED edge', {
247
+ fileA: pair.fileA,
248
+ fileB: pair.fileB,
249
+ error: String(err),
250
+ });
251
+ }
252
+ }
253
+
254
+ // 3. Detect test pairs
255
+ const testPairs = await detectTestFiles(allFiles);
256
+
257
+ // 4. Write TESTS edges to graph
258
+ for (const { testFile, sourceFile } of testPairs) {
259
+ try {
260
+ await graphStore.upsertEdge(
261
+ 'File', { path: testFile, repo_id: repoId },
262
+ 'TESTS',
263
+ {},
264
+ 'File', { path: sourceFile, repo_id: repoId }
265
+ );
266
+ } catch (err) {
267
+ logger.warn('Failed to write TESTS edge', {
268
+ testFile,
269
+ sourceFile,
270
+ error: String(err),
271
+ });
272
+ }
273
+ }
274
+
275
+ logger.info('git analysis complete', {
276
+ repoId,
277
+ coModPairs: coModifications.length,
278
+ testPairs: testPairs.length,
279
+ errors: errors.length,
280
+ });
281
+
282
+ return { coModifications, testPairs, totalCommitsAnalyzed, errors };
283
+ }
284
+
285
+ // ============================================================
286
+ // Phase 3: Git History Enrichment
287
+ // ============================================================
288
+
289
+ /**
290
+ * Analyze per-file change statistics from git history.
291
+ * Runs a second git log with `%H|%s|%an|%aI` format (hash, subject, author, ISO date)
292
+ * plus `--name-only` to map commits to files.
293
+ *
294
+ * Computes per-file:
295
+ * - commitCount: total commits touching this file
296
+ * - stabilityScore: 0-1, higher = fewer recent changes relative to history
297
+ * - changeVelocity: -1 to +1, positive = accelerating changes
298
+ * - lastCommitHash, lastCommitTs, recentMessages (last 5)
299
+ */
300
+ export async function analyzeFileStats(
301
+ repoPath: string,
302
+ historyDepth: number = 500
303
+ ): Promise<Map<string, FileChangeStats>> {
304
+ const normalizedPath = repoPath.replace(/\\/g, '/');
305
+ logger.debug('git-analyzer: starting file stats analysis', { repoPath: normalizedPath, historyDepth });
306
+
307
+ let stdout: string;
308
+ try {
309
+ const { stdout: out } = await execAsync(
310
+ `git -C "${normalizedPath}" log --format="%H|%s|%an|%aI" --name-only -n ${historyDepth}`,
311
+ { maxBuffer: 50 * 1024 * 1024 }
312
+ );
313
+ stdout = out;
314
+ } catch (err) {
315
+ logger.warn('git log (file stats) failed', { repoPath: normalizedPath, error: String(err) });
316
+ return new Map();
317
+ }
318
+
319
+ // Parse into commit records with files
320
+ const commits = parseStatsGitLog(stdout);
321
+
322
+ // Build per-file stats
323
+ const fileStatsMap = new Map<string, {
324
+ commitCount: number;
325
+ commitTimestamps: number[];
326
+ lastCommitHash: string;
327
+ lastCommitTs: number;
328
+ recentMessages: string[];
329
+ }>();
330
+
331
+ for (const commit of commits) {
332
+ const ts = commit.timestamp;
333
+
334
+ for (const file of commit.files) {
335
+ const existing = fileStatsMap.get(file);
336
+ if (existing) {
337
+ existing.commitCount++;
338
+ existing.commitTimestamps.push(ts);
339
+ if (ts > existing.lastCommitTs) {
340
+ existing.lastCommitHash = commit.hash;
341
+ existing.lastCommitTs = ts;
342
+ }
343
+ // Keep only last 5 messages
344
+ if (existing.recentMessages.length < 5) {
345
+ existing.recentMessages.push(commit.message);
346
+ }
347
+ } else {
348
+ fileStatsMap.set(file, {
349
+ commitCount: 1,
350
+ commitTimestamps: [ts],
351
+ lastCommitHash: commit.hash,
352
+ lastCommitTs: ts,
353
+ recentMessages: [commit.message],
354
+ });
355
+ }
356
+ }
357
+ }
358
+
359
+ // Compute stability and velocity for each file
360
+ const result = new Map<string, FileChangeStats>();
361
+ const now = Date.now();
362
+
363
+ for (const [filePath, raw] of fileStatsMap) {
364
+ const stability = computeStabilityScore(raw.commitTimestamps, now);
365
+ const velocity = computeChangeVelocity(raw.commitTimestamps);
366
+
367
+ result.set(filePath, {
368
+ commitCount: raw.commitCount,
369
+ stabilityScore: stability,
370
+ changeVelocity: velocity,
371
+ lastCommitHash: raw.lastCommitHash,
372
+ lastCommitTs: raw.lastCommitTs,
373
+ recentMessages: raw.recentMessages,
374
+ });
375
+ }
376
+
377
+ logger.debug('git-analyzer: file stats analysis complete', {
378
+ repoPath: normalizedPath,
379
+ filesWithStats: result.size,
380
+ totalCommits: commits.length,
381
+ });
382
+
383
+ return result;
384
+ }
385
+
386
+ /**
387
+ * Extract commit records from git history.
388
+ * Parses git log into CommitRecord[], filtering merge commits and
389
+ * bulk reformats (commits touching >50 files).
390
+ */
391
+ export async function extractCommitRecords(
392
+ repoPath: string,
393
+ historyDepth: number = 500
394
+ ): Promise<CommitRecord[]> {
395
+ const normalizedPath = repoPath.replace(/\\/g, '/');
396
+ logger.debug('git-analyzer: extracting commit records', { repoPath: normalizedPath, historyDepth });
397
+
398
+ let stdout: string;
399
+ try {
400
+ const { stdout: out } = await execAsync(
401
+ `git -C "${normalizedPath}" log --format="%H|%s|%an|%aI" --name-only -n ${historyDepth}`,
402
+ { maxBuffer: 50 * 1024 * 1024 }
403
+ );
404
+ stdout = out;
405
+ } catch (err) {
406
+ logger.warn('git log (commit records) failed', { repoPath: normalizedPath, error: String(err) });
407
+ return [];
408
+ }
409
+
410
+ const allCommits = parseStatsGitLog(stdout);
411
+
412
+ // Filter out merge commits and bulk reformats (>50 files)
413
+ const filtered = allCommits.filter(c => {
414
+ // Skip merge commits (common patterns)
415
+ const lowerMsg = c.message.toLowerCase();
416
+ if (lowerMsg.startsWith('merge ') || lowerMsg.startsWith('merge:')) return false;
417
+
418
+ // Skip bulk reformats (>50 files changed)
419
+ if (c.files.length > 50) return false;
420
+
421
+ // Skip commits with no files (empty commits)
422
+ if (c.files.length === 0) return false;
423
+
424
+ return true;
425
+ });
426
+
427
+ logger.debug('git-analyzer: commit records extracted', {
428
+ repoPath: normalizedPath,
429
+ totalCommits: allCommits.length,
430
+ filteredCommits: filtered.length,
431
+ });
432
+
433
+ return filtered;
434
+ }
435
+
436
+ /**
437
+ * Parse `git log --format="%H|%s|%an|%aI" --name-only` output into commit records.
438
+ * Each commit block is: header line (hash|subject|author|date) followed by blank + file lines.
439
+ */
440
+ function parseStatsGitLog(stdout: string): CommitRecord[] {
441
+ const commits: CommitRecord[] = [];
442
+ const lines = stdout.split('\n');
443
+
444
+ let currentCommit: CommitRecord | null = null;
445
+
446
+ for (const rawLine of lines) {
447
+ const line = rawLine.trim();
448
+
449
+ if (!line) {
450
+ // Blank line separates commit header from files, or between commits
451
+ continue;
452
+ }
453
+
454
+ // Try to parse as a commit header (hash|subject|author|date)
455
+ const pipeIndex = line.indexOf('|');
456
+ if (pipeIndex === 40 && /^[0-9a-f]{40}$/i.test(line.slice(0, 40))) {
457
+ // Flush previous commit
458
+ if (currentCommit) {
459
+ commits.push(currentCommit);
460
+ }
461
+
462
+ const parts = line.split('|');
463
+ const hash = parts[0] ?? '';
464
+ const message = parts[1] ?? '';
465
+ const author = parts[2] ?? '';
466
+ const dateStr = parts.slice(3).join('|'); // ISO date may contain no pipes but be safe
467
+
468
+ currentCommit = {
469
+ hash,
470
+ message,
471
+ author,
472
+ timestamp: dateStr ? new Date(dateStr).getTime() : 0,
473
+ files: [],
474
+ };
475
+ } else if (currentCommit) {
476
+ // This is a file path line
477
+ currentCommit.files.push(line);
478
+ }
479
+ }
480
+
481
+ // Flush final commit
482
+ if (currentCommit) {
483
+ commits.push(currentCommit);
484
+ }
485
+
486
+ return commits;
487
+ }
488
+
489
+ /**
490
+ * Compute stability score (0-1). Higher = more stable (fewer recent changes).
491
+ * Uses the ratio of commits in the recent half of the time window vs total commits.
492
+ * A file with all changes in the distant past is stable (score ~1.0).
493
+ * A file with all changes recently is unstable (score ~0.0).
494
+ */
495
+ function computeStabilityScore(timestamps: number[], now: number): number {
496
+ if (timestamps.length <= 1) return 1.0;
497
+
498
+ const sorted = [...timestamps].sort((a, b) => a - b);
499
+ const oldest = sorted[0]!;
500
+ const timeRange = now - oldest;
501
+ if (timeRange <= 0) return 1.0;
502
+
503
+ const midpoint = oldest + timeRange / 2;
504
+ const recentCount = sorted.filter(t => t > midpoint).length;
505
+ const recentRatio = recentCount / sorted.length;
506
+
507
+ // Invert: fewer recent changes = more stable
508
+ return Math.max(0, Math.min(1, 1.0 - recentRatio));
509
+ }
510
+
511
+ /**
512
+ * Compute change velocity (-1 to +1).
513
+ * Compares the rate of changes in the first half vs second half of the commit history.
514
+ * Positive = changes are accelerating. Negative = changes are decelerating.
515
+ */
516
+ function computeChangeVelocity(timestamps: number[]): number {
517
+ if (timestamps.length < 4) return 0;
518
+
519
+ const sorted = [...timestamps].sort((a, b) => a - b);
520
+ const mid = Math.floor(sorted.length / 2);
521
+ const firstHalf = sorted.slice(0, mid);
522
+ const secondHalf = sorted.slice(mid);
523
+
524
+ // Compute average inter-commit interval for each half
525
+ const avgInterval = (arr: number[]): number => {
526
+ if (arr.length < 2) return Infinity;
527
+ let totalGap = 0;
528
+ for (let i = 1; i < arr.length; i++) {
529
+ totalGap += arr[i]! - arr[i - 1]!;
530
+ }
531
+ return totalGap / (arr.length - 1);
532
+ };
533
+
534
+ const firstAvg = avgInterval(firstHalf);
535
+ const secondAvg = avgInterval(secondHalf);
536
+
537
+ if (firstAvg === Infinity && secondAvg === Infinity) return 0;
538
+ if (firstAvg === Infinity) return 1; // No first-half activity, all second-half
539
+ if (secondAvg === Infinity) return -1; // No second-half activity
540
+
541
+ // Shorter intervals in second half = accelerating
542
+ // Normalize to -1..+1 range
543
+ const ratio = (firstAvg - secondAvg) / Math.max(firstAvg, secondAvg);
544
+ return Math.max(-1, Math.min(1, ratio));
545
+ }