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,691 @@
1
+ // PipelineEngine — the core orchestration layer for Forge projects.
2
+ //
3
+ // The engine owns all project + phase lifecycle operations:
4
+ // - Creating projects
5
+ // - Phase transition validation and persistence (atomic: event + project update)
6
+ // - Trajectory recording (start/step/complete)
7
+ // - Phase output persistence
8
+ // - Claim lifecycle for parallel implementation dispatch
9
+ // - History reconstruction from event log
10
+ // - Cycle count computation from event log
11
+ //
12
+ // All database writes are synchronous (better-sqlite3). External operations
13
+ // (embedding, Qdrant, knowledge extraction) are the responsibility of MCP tool
14
+ // handlers — this engine knows nothing about them.
15
+ import { randomUUID } from 'node:crypto';
16
+ // ---------------------------------------------------------------------------
17
+ // Phase type bridge
18
+ // ---------------------------------------------------------------------------
19
+ // The storage layer uses PipelinePhase (agent-role names) in the TypeScript
20
+ // interface types, but the actual DB values are Phase (workflow-stage names).
21
+ // These cast helpers make the boundary explicit without scattering `as unknown`.
22
+ /** Cast a workflow-stage Phase to the PipelinePhase expected by event factories. */
23
+ function pp(phase) {
24
+ return phase;
25
+ }
26
+ /** Cast a PipelinePhase DB value back to a workflow-stage Phase. */
27
+ function wp(phase) {
28
+ return phase;
29
+ }
30
+ // ---------------------------------------------------------------------------
31
+ // Parallel phase pairs
32
+ // ---------------------------------------------------------------------------
33
+ // Phases that can run concurrently. When starting a phase whose parallel
34
+ // sibling is (or leads to) the current phase, we allow the start without
35
+ // advancing currentPhase, so both agents can work and submit independently.
36
+ const PARALLEL_PAIRS = new Map([
37
+ ['architecture', 'design'],
38
+ ['design', 'architecture'],
39
+ ]);
40
+ /**
41
+ * Check if `toPhase` can be started as a parallel sibling of the current phase.
42
+ * Returns true when:
43
+ * - `toPhase` has a parallel sibling, AND
44
+ * - `fromPhase` is the sibling itself, OR `fromPhase` can validly transition
45
+ * to the sibling (so both can start from the same predecessor).
46
+ */
47
+ function isParallelStart(fromPhase, toPhase, tier) {
48
+ const sibling = PARALLEL_PAIRS.get(toPhase);
49
+ if (!sibling)
50
+ return false;
51
+ return fromPhase === sibling || isValidTransition(fromPhase, sibling, tier);
52
+ }
53
+ import { isValidTransition, getAvailableTransitions, isCycleTransition, getCycleKey, canCycle, getForcedAdvancePhase, } from './state-machine.js';
54
+ import { phaseTransitionEvent, agentStartedEvent, agentCompletedEvent, projectCreatedEvent, projectCompletedEvent, claimCreatedEvent, claimCompletedEvent, claimFailedEvent, eventToParams, rowToEvent, } from './events.js';
55
+ import { logger } from '../util/logger.js';
56
+ export class PipelineEngineError extends Error {
57
+ code;
58
+ details;
59
+ constructor(code, message, details = {}) {
60
+ super(message);
61
+ this.name = 'PipelineEngineError';
62
+ this.code = code;
63
+ this.details = details;
64
+ }
65
+ }
66
+ // ---------------------------------------------------------------------------
67
+ // PipelineEngine
68
+ // ---------------------------------------------------------------------------
69
+ export class PipelineEngine {
70
+ db;
71
+ constructor(db) {
72
+ this.db = db;
73
+ }
74
+ // ---------------------------------------------------------------------------
75
+ // Project lifecycle
76
+ // ---------------------------------------------------------------------------
77
+ createProject(opts) {
78
+ const id = randomUUID();
79
+ const now = Date.now();
80
+ const tier = opts.tier ?? 'full';
81
+ this.db.run(`INSERT INTO projects (id, repo_id, name, description, current_phase, tier, status, created_at, updated_at, metadata)
82
+ VALUES (?, ?, ?, ?, 'idle', ?, 'active', ?, ?, ?)`, [
83
+ id,
84
+ opts.repoId,
85
+ opts.name,
86
+ opts.description ?? null,
87
+ tier,
88
+ now,
89
+ now,
90
+ JSON.stringify(opts.metadata ?? {}),
91
+ ]);
92
+ // Emit project_created event
93
+ const event = projectCreatedEvent(id, {
94
+ name: opts.name,
95
+ tier,
96
+ repo_id: opts.repoId,
97
+ });
98
+ this.db.run(`INSERT INTO pipeline_events (project_id, event_type, from_phase, to_phase, agent, payload, created_at)
99
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, Object.values(eventToParams(event)));
100
+ logger.info('PipelineEngine: project created', { id, name: opts.name, tier });
101
+ return this.getProject(id);
102
+ }
103
+ getProject(projectId) {
104
+ const row = this.db.get(`SELECT * FROM projects WHERE id = ?`, [projectId]);
105
+ if (!row)
106
+ return null;
107
+ return this._rowToProject(row);
108
+ }
109
+ /**
110
+ * Lists projects with optional repo and status filters.
111
+ * Ordered by updated_at DESC (most recently modified first).
112
+ */
113
+ listProjects(repoId, status) {
114
+ const conditions = [];
115
+ const params = [];
116
+ if (repoId !== undefined) {
117
+ conditions.push('repo_id = ?');
118
+ params.push(repoId);
119
+ }
120
+ if (status !== undefined) {
121
+ conditions.push('status = ?');
122
+ params.push(status);
123
+ }
124
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
125
+ const rows = this.db.all(`SELECT * FROM projects ${where} ORDER BY updated_at DESC`, params);
126
+ return rows.map((r) => this._rowToProject(r));
127
+ }
128
+ /**
129
+ * Returns the most recently updated active project for a repo, or null when
130
+ * there is no active project for that repo.
131
+ */
132
+ getActiveProject(repoId) {
133
+ const row = this.db.get(`SELECT * FROM projects WHERE repo_id = ? AND status = 'active' ORDER BY updated_at DESC LIMIT 1`, [repoId]);
134
+ return row ? this._rowToProject(row) : null;
135
+ }
136
+ // ---------------------------------------------------------------------------
137
+ // Task-spec core API
138
+ // ---------------------------------------------------------------------------
139
+ /**
140
+ * Returns the full current state of a project: project data, full event
141
+ * history, active claims, derived cycle counts, and available transitions.
142
+ *
143
+ * Throws PipelineEngineError with code PROJECT_NOT_FOUND when the project
144
+ * does not exist.
145
+ */
146
+ getState(projectId) {
147
+ const project = this._requireProject(projectId);
148
+ const history = this.getEventHistory(projectId);
149
+ const activeClaims = this.getActiveClaims(projectId);
150
+ const cycleCounts = this._getCycleCounts(projectId);
151
+ const currentPhase = wp(project.currentPhase);
152
+ const availableTransitions = getAvailableTransitions(currentPhase, project.tier);
153
+ return { project, history, activeClaims, cycleCounts, availableTransitions };
154
+ }
155
+ /**
156
+ * Atomically advances a project to `toPhase` in a single SQLite transaction:
157
+ * 1. Validates the transition (throws on illegal transitions).
158
+ * 2. Checks cycle limits (throws CYCLE_LIMIT_EXCEEDED; details include the
159
+ * forced advance phase so callers can decide how to proceed).
160
+ * 3. Inserts a phase_transition event.
161
+ * 4. Updates projects.current_phase and updated_at.
162
+ * 5. If toPhase === 'completed', marks the project status 'complete' and
163
+ * emits an additional project_completed event.
164
+ *
165
+ * Throws PipelineEngineError for invalid transitions and cycle limit exceeded.
166
+ * All writes are in a single better-sqlite3 transaction.
167
+ */
168
+ advancePhase(projectId, toPhase, agent, payload) {
169
+ const project = this._requireProject(projectId);
170
+ if (project.status !== 'active') {
171
+ throw new PipelineEngineError('PROJECT_NOT_ACTIVE', `Project '${projectId}' has status '${project.status}' and cannot be advanced`, { currentPhase: project.currentPhase, status: project.status });
172
+ }
173
+ const fromPhase = wp(project.currentPhase);
174
+ if (!isValidTransition(fromPhase, toPhase, project.tier)) {
175
+ const available = getAvailableTransitions(fromPhase, project.tier);
176
+ throw new PipelineEngineError('INVALID_TRANSITION', `Cannot transition from '${fromPhase}' to '${toPhase}' for tier '${project.tier}'`, { currentPhase: fromPhase, attempted: toPhase, legalTransitions: available });
177
+ }
178
+ // Cycle limit check for backward edges.
179
+ if (isCycleTransition(fromPhase, toPhase)) {
180
+ const key = getCycleKey(fromPhase, toPhase);
181
+ const cycleCounts = this._getCycleCounts(projectId);
182
+ const count = cycleCounts[key] ?? 0;
183
+ if (!canCycle(fromPhase, toPhase, count)) {
184
+ const forcedPhase = getForcedAdvancePhase(fromPhase, toPhase);
185
+ throw new PipelineEngineError('CYCLE_LIMIT_EXCEEDED', `Cycle '${key}' has reached the maximum of ${count} iterations. Force advance to '${forcedPhase ?? 'next'}'`, { cycleKey: key, cycleCount: count, forcedAdvanceTo: forcedPhase });
186
+ }
187
+ }
188
+ const now = Date.now();
189
+ const isTerminal = toPhase === 'completed';
190
+ const newStatus = isTerminal ? 'complete' : 'active';
191
+ const transEvent = phaseTransitionEvent(projectId, pp(fromPhase), pp(toPhase), agent ?? null, payload ?? {});
192
+ this.db.db.transaction(() => {
193
+ // 1. Insert phase_transition event
194
+ this.db.run(`INSERT INTO pipeline_events (project_id, event_type, from_phase, to_phase, agent, payload, created_at)
195
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, Object.values(eventToParams(transEvent)));
196
+ // 2. Update project
197
+ this.db.run(`UPDATE projects SET current_phase = ?, updated_at = ?, status = ? WHERE id = ?`, [toPhase, now, newStatus, projectId]);
198
+ // 3. Emit project_completed if terminal
199
+ if (isTerminal) {
200
+ const completedEvent = projectCompletedEvent(projectId, { agent: agent ?? null });
201
+ this.db.run(`INSERT INTO pipeline_events (project_id, event_type, from_phase, to_phase, agent, payload, created_at)
202
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, Object.values(eventToParams(completedEvent)));
203
+ }
204
+ })();
205
+ logger.info('PipelineEngine: advancePhase', { projectId, from: fromPhase, to: toPhase, agent });
206
+ }
207
+ /**
208
+ * Persists a phase output to phase_outputs. Multiple outputs per phase are
209
+ * allowed (e.g., vision doc, refined vision doc, etc.).
210
+ *
211
+ * Throws PipelineEngineError with code PROJECT_NOT_FOUND when the project
212
+ * does not exist.
213
+ */
214
+ recordOutput(projectId, phase, outputType, content, filePath) {
215
+ this._requireProject(projectId);
216
+ this.db.run(`INSERT INTO phase_outputs (project_id, phase, output_type, content, file_path, created_at)
217
+ VALUES (?, ?, ?, ?, ?, ?)`, [projectId, phase, outputType, content, filePath ?? null, Date.now()]);
218
+ }
219
+ /**
220
+ * Returns phase outputs for a project, optionally filtered to a specific
221
+ * phase. Ordered by created_at ASC.
222
+ */
223
+ getOutputs(projectId, phase) {
224
+ this._requireProject(projectId);
225
+ if (phase !== undefined) {
226
+ return this.db.all(`SELECT * FROM phase_outputs WHERE project_id = ? AND phase = ? ORDER BY created_at ASC`, [projectId, phase]).map((r) => this._rowToPhaseOutput(r));
227
+ }
228
+ return this.getAllPhaseOutputs(projectId);
229
+ }
230
+ /**
231
+ * Returns all events for a project ordered by created_at ASC.
232
+ * This is the raw event sourcing log used for history reconstruction.
233
+ */
234
+ getEventHistory(projectId) {
235
+ return this.db.all(`SELECT * FROM pipeline_events WHERE project_id = ? ORDER BY created_at ASC`, [projectId]).map(rowToEvent);
236
+ }
237
+ /**
238
+ * Marks a project as 'archived'. Emits a project_completed event with an
239
+ * 'abandoned' flag. Idempotent if already archived.
240
+ *
241
+ * Throws PipelineEngineError with code PROJECT_NOT_FOUND when the project
242
+ * does not exist.
243
+ */
244
+ abandonProject(projectId) {
245
+ const project = this._requireProject(projectId);
246
+ if (project.status === 'archived') {
247
+ return; // Idempotent.
248
+ }
249
+ const now = Date.now();
250
+ const completedEvent = projectCompletedEvent(projectId, { abandoned: true });
251
+ this.db.db.transaction(() => {
252
+ this.db.run(`UPDATE projects SET status = 'archived', updated_at = ? WHERE id = ?`, [now, projectId]);
253
+ this.db.run(`INSERT INTO pipeline_events (project_id, event_type, from_phase, to_phase, agent, payload, created_at)
254
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, Object.values(eventToParams(completedEvent)));
255
+ })();
256
+ logger.info('PipelineEngine: project abandoned', { projectId });
257
+ }
258
+ // ---------------------------------------------------------------------------
259
+ // Phase transitions (extended API — used by MCP tools)
260
+ // ---------------------------------------------------------------------------
261
+ /**
262
+ * Transition a project to a new phase.
263
+ * Validates the transition against the state machine, emits events,
264
+ * starts a trajectory, and returns the result.
265
+ */
266
+ startPhase(projectId, toPhase, agent, extra) {
267
+ const project = this.getProject(projectId);
268
+ if (!project) {
269
+ return { error: 'PROJECT_NOT_FOUND', current_phase: 'idle', legal_transitions: [] };
270
+ }
271
+ // Cast from PipelinePhase (domain type) to Phase (state-machine type) — the
272
+ // DB stores Phase values in the current_phase column; the Project interface
273
+ // types it as PipelinePhase for legacy compatibility.
274
+ const fromPhase = project.currentPhase;
275
+ const tier = project.tier;
276
+ const cycleCounts = this._getCycleCounts(projectId);
277
+ // Validate transition
278
+ const validTransition = isValidTransition(fromPhase, toPhase, tier);
279
+ const parallelStart = !validTransition && isParallelStart(fromPhase, toPhase, tier);
280
+ if (!validTransition && !parallelStart) {
281
+ const legal = getAvailableTransitions(fromPhase, tier);
282
+ return {
283
+ error: 'INVALID_TRANSITION',
284
+ current_phase: fromPhase,
285
+ legal_transitions: legal,
286
+ };
287
+ }
288
+ // For parallel siblings (architecture + design), don't change currentPhase —
289
+ // both agents run concurrently and the submit handlers coordinate advancement.
290
+ // Skip only when the current phase IS the sibling (both are running from the
291
+ // same starting point) or when we allowed a parallelStart.
292
+ const sibling = PARALLEL_PAIRS.get(toPhase);
293
+ const skipPhaseUpdate = parallelStart || (validTransition && sibling !== undefined && fromPhase === sibling);
294
+ // Check cycle limits (only for non-parallel forward transitions)
295
+ if (!skipPhaseUpdate && isCycleTransition(fromPhase, toPhase)) {
296
+ const key = getCycleKey(fromPhase, toPhase);
297
+ const count = cycleCounts[key] ?? 0;
298
+ if (!canCycle(fromPhase, toPhase, count)) {
299
+ const forcedPhase = getForcedAdvancePhase(fromPhase, toPhase);
300
+ logger.warn('PipelineEngine: cycle limit exceeded, forcing advance', {
301
+ projectId,
302
+ from: fromPhase,
303
+ to: toPhase,
304
+ forced: forcedPhase,
305
+ count,
306
+ });
307
+ // Force advance to the prescribed phase instead
308
+ if (forcedPhase) {
309
+ return this.startPhase(projectId, forcedPhase, agent, {
310
+ ...extra,
311
+ forced: true,
312
+ reason: 'cycle_limit_exceeded',
313
+ });
314
+ }
315
+ }
316
+ // Increment cycle counter
317
+ cycleCounts[key] = (cycleCounts[key] ?? 0) + 1;
318
+ }
319
+ const now = Date.now();
320
+ // Update project phase (skip for parallel siblings)
321
+ if (!skipPhaseUpdate) {
322
+ this.db.run(`UPDATE projects SET current_phase = ?, updated_at = ? WHERE id = ?`, [toPhase, now, projectId]);
323
+ }
324
+ // Emit transition event (bridge Phase -> PipelinePhase for event factory)
325
+ const transEvent = phaseTransitionEvent(projectId, pp(fromPhase), pp(toPhase), agent, extra ?? {});
326
+ this.db.run(`INSERT INTO pipeline_events (project_id, event_type, from_phase, to_phase, agent, payload, created_at)
327
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, Object.values(eventToParams(transEvent)));
328
+ // Emit agent_started event
329
+ const startedEvent = agentStartedEvent(projectId, pp(toPhase), agent, extra ?? {});
330
+ this.db.run(`INSERT INTO pipeline_events (project_id, event_type, from_phase, to_phase, agent, payload, created_at)
331
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, Object.values(eventToParams(startedEvent)));
332
+ // Start trajectory
333
+ const trajectoryId = randomUUID();
334
+ this.db.run(`INSERT INTO trajectories (id, project_id, phase, agent, status, started_at)
335
+ VALUES (?, ?, ?, ?, 'active', ?)`, [trajectoryId, projectId, toPhase, agent, now]);
336
+ logger.info('PipelineEngine: phase started', { projectId, from: fromPhase, to: toPhase, agent, parallel: skipPhaseUpdate });
337
+ return {
338
+ projectId,
339
+ fromPhase,
340
+ toPhase,
341
+ trajectoryId,
342
+ cycleCounts,
343
+ availableTransitions: getAvailableTransitions(toPhase, tier),
344
+ };
345
+ }
346
+ /**
347
+ * Record completion of a phase: persist output, complete the trajectory,
348
+ * and optionally advance the state machine.
349
+ */
350
+ completePhase(projectId, phase, outputType, content, opts) {
351
+ const project = this.getProject(projectId);
352
+ if (!project) {
353
+ throw new Error(`Project not found: ${projectId}`);
354
+ }
355
+ const now = Date.now();
356
+ // Persist phase output
357
+ const result = this.db.run(`INSERT INTO phase_outputs (project_id, phase, output_type, content, file_path, created_at)
358
+ VALUES (?, ?, ?, ?, ?, ?)`, [projectId, phase, outputType, content, opts.filePath ?? null, now]);
359
+ // Complete active trajectory for this phase
360
+ if (opts.trajectoryId) {
361
+ this.db.run(`UPDATE trajectories SET status = 'complete', completed_at = ?, success = 1 WHERE id = ?`, [now, opts.trajectoryId]);
362
+ // Record trajectory step for the output
363
+ this.db.run(`INSERT INTO trajectory_steps (trajectory_id, action, result, quality_score, metadata, created_at)
364
+ VALUES (?, ?, ?, ?, ?, ?)`, [
365
+ opts.trajectoryId,
366
+ `submit_${phase}`,
367
+ content.slice(0, 500),
368
+ opts.qualityScore ?? null,
369
+ JSON.stringify(opts.metadata ?? {}),
370
+ now,
371
+ ]);
372
+ }
373
+ // Emit agent_completed event
374
+ const completedEvent = agentCompletedEvent(projectId, pp(phase), opts.agent, {
375
+ output_type: outputType,
376
+ content_length: content.length,
377
+ ...opts.metadata,
378
+ });
379
+ this.db.run(`INSERT INTO pipeline_events (project_id, event_type, from_phase, to_phase, agent, payload, created_at)
380
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, Object.values(eventToParams(completedEvent)));
381
+ // Advance state if requested
382
+ if (opts.advanceTo) {
383
+ this.db.run(`UPDATE projects SET current_phase = ?, updated_at = ? WHERE id = ?`, [opts.advanceTo, now, projectId]);
384
+ const transEvent = phaseTransitionEvent(projectId, pp(phase), pp(opts.advanceTo), opts.agent, {});
385
+ this.db.run(`INSERT INTO pipeline_events (project_id, event_type, from_phase, to_phase, agent, payload, created_at)
386
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, Object.values(eventToParams(transEvent)));
387
+ logger.info('PipelineEngine: phase completed, advanced', {
388
+ projectId,
389
+ from: phase,
390
+ to: opts.advanceTo,
391
+ });
392
+ }
393
+ return {
394
+ outputId: Number(result.lastInsertRowid),
395
+ nextPhase: opts.advanceTo ?? null,
396
+ };
397
+ }
398
+ // ---------------------------------------------------------------------------
399
+ // Trajectory helpers
400
+ // ---------------------------------------------------------------------------
401
+ startTrajectory(projectId, phase, agent) {
402
+ const id = randomUUID();
403
+ this.db.run(`INSERT INTO trajectories (id, project_id, phase, agent, status, started_at)
404
+ VALUES (?, ?, ?, ?, 'active', ?)`, [id, projectId, phase, agent, Date.now()]);
405
+ return id;
406
+ }
407
+ recordTrajectoryStep(trajectoryId, action, result, qualityScore, metadata) {
408
+ this.db.run(`INSERT INTO trajectory_steps (trajectory_id, action, result, quality_score, metadata, created_at)
409
+ VALUES (?, ?, ?, ?, ?, ?)`, [
410
+ trajectoryId,
411
+ action,
412
+ result ?? null,
413
+ qualityScore ?? null,
414
+ JSON.stringify(metadata ?? {}),
415
+ Date.now(),
416
+ ]);
417
+ }
418
+ completeTrajectory(trajectoryId, success, feedback) {
419
+ this.db.run(`UPDATE trajectories SET status = ?, completed_at = ?, success = ?, feedback = ? WHERE id = ?`, [success ? 'complete' : 'failed', Date.now(), success ? 1 : 0, feedback ?? null, trajectoryId]);
420
+ }
421
+ // ---------------------------------------------------------------------------
422
+ // Claims (parallel implementation dispatch)
423
+ // ---------------------------------------------------------------------------
424
+ createClaim(projectId, moduleName, agent) {
425
+ const id = randomUUID();
426
+ const now = Date.now();
427
+ this.db.run(`INSERT INTO claims (id, project_id, module_name, agent, status, claimed_at)
428
+ VALUES (?, ?, ?, ?, 'claimed', ?)`, [id, projectId, moduleName, agent, now]);
429
+ const event = claimCreatedEvent(projectId, id, moduleName, agent);
430
+ this.db.run(`INSERT INTO pipeline_events (project_id, event_type, from_phase, to_phase, agent, payload, created_at)
431
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, Object.values(eventToParams(event)));
432
+ return {
433
+ id,
434
+ projectId,
435
+ moduleName,
436
+ agent,
437
+ branch: null,
438
+ status: 'claimed',
439
+ claimedAt: now,
440
+ completedAt: null,
441
+ result: null,
442
+ };
443
+ }
444
+ completeClaim(claimId, result) {
445
+ const row = this.db.get(`SELECT * FROM claims WHERE id = ?`, [claimId]);
446
+ if (!row)
447
+ throw new Error(`Claim not found: ${claimId}`);
448
+ const now = Date.now();
449
+ this.db.run(`UPDATE claims SET status = 'complete', completed_at = ?, result = ? WHERE id = ?`, [now, result ?? null, claimId]);
450
+ const event = claimCompletedEvent(row.project_id, claimId, row.module_name, row.agent, result);
451
+ this.db.run(`INSERT INTO pipeline_events (project_id, event_type, from_phase, to_phase, agent, payload, created_at)
452
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, Object.values(eventToParams(event)));
453
+ const remaining = this.db.all(`SELECT id FROM claims WHERE project_id = ? AND status NOT IN ('complete', 'failed')`, [row.project_id]);
454
+ return {
455
+ remainingCount: remaining.length,
456
+ allComplete: remaining.length === 0,
457
+ };
458
+ }
459
+ failClaim(claimId, reason) {
460
+ const row = this.db.get(`SELECT * FROM claims WHERE id = ?`, [claimId]);
461
+ if (!row)
462
+ throw new Error(`Claim not found: ${claimId}`);
463
+ this.db.run(`UPDATE claims SET status = 'failed', completed_at = ? WHERE id = ?`, [Date.now(), claimId]);
464
+ const event = claimFailedEvent(row.project_id, claimId, row.module_name, row.agent, reason);
465
+ this.db.run(`INSERT INTO pipeline_events (project_id, event_type, from_phase, to_phase, agent, payload, created_at)
466
+ VALUES (?, ?, ?, ?, ?, ?, ?)`, Object.values(eventToParams(event)));
467
+ }
468
+ getActiveClaims(projectId) {
469
+ const rows = this.db.all(`SELECT * FROM claims WHERE project_id = ? AND status NOT IN ('complete', 'failed')`, [projectId]);
470
+ return rows.map((r) => this._rowToClaim(r));
471
+ }
472
+ getClaims(projectId, status) {
473
+ if (status !== undefined) {
474
+ const rows = this.db.all(`SELECT * FROM claims WHERE project_id = ? AND status = ? ORDER BY claimed_at ASC`, [projectId, status]);
475
+ return rows.map((r) => this._rowToClaim(r));
476
+ }
477
+ const rows = this.db.all(`SELECT * FROM claims WHERE project_id = ? ORDER BY claimed_at ASC`, [projectId]);
478
+ return rows.map((r) => this._rowToClaim(r));
479
+ }
480
+ getClaim(claimId) {
481
+ const row = this.db.get(`SELECT * FROM claims WHERE id = ?`, [claimId]);
482
+ return row ? this._rowToClaim(row) : null;
483
+ }
484
+ getClaimByModule(projectId, moduleName) {
485
+ const row = this.db.get(`SELECT * FROM claims WHERE project_id = ? AND module_name = ? ORDER BY claimed_at DESC LIMIT 1`, [projectId, moduleName]);
486
+ return row ? this._rowToClaim(row) : null;
487
+ }
488
+ // ---------------------------------------------------------------------------
489
+ // Phase outputs
490
+ // ---------------------------------------------------------------------------
491
+ getPhaseOutput(projectId, phase, outputType) {
492
+ const sql = outputType
493
+ ? `SELECT * FROM phase_outputs WHERE project_id = ? AND phase = ? AND output_type = ? ORDER BY created_at DESC LIMIT 1`
494
+ : `SELECT * FROM phase_outputs WHERE project_id = ? AND phase = ? ORDER BY created_at DESC LIMIT 1`;
495
+ const params = outputType ? [projectId, phase, outputType] : [projectId, phase];
496
+ const row = this.db.get(sql, params);
497
+ if (!row)
498
+ return null;
499
+ return this._rowToPhaseOutput(row);
500
+ }
501
+ /**
502
+ * Retrieve metadata stored in the agent_completed event for a phase.
503
+ * This includes flags like requires_qa, requires_designer, modules, etc.
504
+ * that are passed via opts.metadata in completePhase().
505
+ */
506
+ getPhaseMetadata(projectId, phase) {
507
+ const row = this.db.get(`SELECT payload FROM pipeline_events
508
+ WHERE project_id = ? AND event_type = 'agent_completed' AND from_phase = ?
509
+ ORDER BY created_at DESC LIMIT 1`, [projectId, phase]);
510
+ if (!row)
511
+ return null;
512
+ try {
513
+ return JSON.parse(row.payload);
514
+ }
515
+ catch {
516
+ return null;
517
+ }
518
+ }
519
+ /**
520
+ * Record a broadcast event in the pipeline_events table.
521
+ * Used by phase tools to auto-broadcast module completions for swarm coordination.
522
+ */
523
+ recordBroadcast(projectId, content, severity = 'info') {
524
+ this.db.run(`INSERT INTO pipeline_events (project_id, event_type, from_phase, to_phase, agent, payload, created_at)
525
+ VALUES (?, ?, NULL, NULL, NULL, ?, ?)`, [
526
+ projectId,
527
+ 'agent_broadcast',
528
+ JSON.stringify({ content, severity }),
529
+ Date.now(),
530
+ ]);
531
+ }
532
+ getAllPhaseOutputs(projectId) {
533
+ const rows = this.db.all(`SELECT * FROM phase_outputs WHERE project_id = ? ORDER BY created_at ASC`, [projectId]);
534
+ return rows.map((r) => this._rowToPhaseOutput(r));
535
+ }
536
+ // ---------------------------------------------------------------------------
537
+ // History
538
+ // ---------------------------------------------------------------------------
539
+ getHistory(projectId) {
540
+ const events = this.db.all(`SELECT * FROM pipeline_events WHERE project_id = ? ORDER BY created_at ASC`, [projectId]);
541
+ const outputs = this.getAllPhaseOutputs(projectId);
542
+ // Build phase history from agent_started / agent_completed pairs.
543
+ // ev.fromPhase is PipelinePhase (domain type), PhaseHistory.phase is Phase.
544
+ const history = [];
545
+ const startedMap = new Map();
546
+ for (const row of events) {
547
+ const ev = rowToEvent(row);
548
+ if (ev.eventType === 'agent_started' && ev.fromPhase) {
549
+ startedMap.set(`${ev.fromPhase}-${ev.agent}`, {
550
+ phase: wp(ev.fromPhase),
551
+ agent: ev.agent ?? 'unknown',
552
+ startedAt: ev.createdAt,
553
+ });
554
+ }
555
+ else if (ev.eventType === 'agent_completed' && ev.fromPhase) {
556
+ const key = `${ev.fromPhase}-${ev.agent}`;
557
+ const started = startedMap.get(key);
558
+ if (started) {
559
+ history.push({
560
+ phase: started.phase,
561
+ agent: started.agent,
562
+ startedAt: new Date(started.startedAt).toISOString(),
563
+ completedAt: new Date(ev.createdAt).toISOString(),
564
+ success: true,
565
+ });
566
+ startedMap.delete(key);
567
+ }
568
+ }
569
+ }
570
+ // Flush any in-progress phases
571
+ for (const [, started] of startedMap) {
572
+ history.push({
573
+ phase: started.phase,
574
+ agent: started.agent,
575
+ startedAt: new Date(started.startedAt).toISOString(),
576
+ });
577
+ }
578
+ return {
579
+ projectId,
580
+ events: events.map(rowToEvent),
581
+ outputs,
582
+ history,
583
+ };
584
+ }
585
+ // ---------------------------------------------------------------------------
586
+ // Cycle tracking
587
+ // ---------------------------------------------------------------------------
588
+ getCycleCounts(projectId) {
589
+ return this._getCycleCounts(projectId);
590
+ }
591
+ _getCycleCounts(projectId) {
592
+ const rows = this.db.all(`SELECT from_phase, to_phase, COUNT(*) as count
593
+ FROM pipeline_events
594
+ WHERE project_id = ? AND event_type = 'phase_transition' AND from_phase IS NOT NULL AND to_phase IS NOT NULL
595
+ GROUP BY from_phase, to_phase`, [projectId]);
596
+ const counts = {};
597
+ for (const row of rows) {
598
+ const key = `${row.from_phase}_to_${row.to_phase}`;
599
+ // Only include recognised cycle keys
600
+ if (getCycleKey(row.from_phase, row.to_phase)) {
601
+ counts[key] = row.count;
602
+ }
603
+ }
604
+ return counts;
605
+ }
606
+ // ---------------------------------------------------------------------------
607
+ // Internal helpers
608
+ // ---------------------------------------------------------------------------
609
+ /**
610
+ * Retrieves a project by id and throws a typed PipelineEngineError when not
611
+ * found. Used by every method that requires an existing project.
612
+ */
613
+ _requireProject(projectId) {
614
+ const row = this.db.get(`SELECT * FROM projects WHERE id = ?`, [projectId]);
615
+ if (!row) {
616
+ throw new PipelineEngineError('PROJECT_NOT_FOUND', `Project '${projectId}' does not exist`, { projectId });
617
+ }
618
+ return this._rowToProject(row);
619
+ }
620
+ // ---------------------------------------------------------------------------
621
+ // Row mappers
622
+ // ---------------------------------------------------------------------------
623
+ _rowToProject(row) {
624
+ let metadata;
625
+ try {
626
+ metadata = JSON.parse(row.metadata);
627
+ }
628
+ catch {
629
+ metadata = {};
630
+ }
631
+ return {
632
+ id: row.id,
633
+ repoId: row.repo_id,
634
+ name: row.name,
635
+ description: row.description,
636
+ // The DB stores Phase (workflow-stage) values in current_phase, but
637
+ // Project.currentPhase is typed as PipelinePhase. We bridge here so
638
+ // callers use wp() to get back to Phase when needed.
639
+ currentPhase: row.current_phase,
640
+ tier: row.tier,
641
+ status: row.status,
642
+ createdAt: row.created_at,
643
+ updatedAt: row.updated_at,
644
+ metadata,
645
+ };
646
+ }
647
+ _rowToClaim(row) {
648
+ return {
649
+ id: row.id,
650
+ projectId: row.project_id,
651
+ moduleName: row.module_name,
652
+ agent: row.agent,
653
+ branch: row.branch,
654
+ status: row.status,
655
+ claimedAt: row.claimed_at,
656
+ completedAt: row.completed_at,
657
+ result: row.result,
658
+ };
659
+ }
660
+ _rowToPhaseOutput(row) {
661
+ return {
662
+ id: row.id,
663
+ projectId: row.project_id,
664
+ // Bridge: Phase values stored in DB, PhaseOutput.phase typed as PipelinePhase.
665
+ phase: row.phase,
666
+ outputType: row.output_type,
667
+ content: row.content,
668
+ filePath: row.file_path,
669
+ createdAt: row.created_at,
670
+ };
671
+ }
672
+ // Expose getTrajectory for phase tools
673
+ getActiveTrajectory(projectId, phase) {
674
+ const row = this.db.get(`SELECT * FROM trajectories WHERE project_id = ? AND phase = ? AND status = 'active' ORDER BY started_at DESC LIMIT 1`, [projectId, phase]);
675
+ if (!row)
676
+ return null;
677
+ return {
678
+ id: row.id,
679
+ projectId: row.project_id,
680
+ // Bridge: Phase stored in DB, Trajectory.phase typed as PipelinePhase.
681
+ phase: row.phase,
682
+ agent: row.agent,
683
+ status: row.status,
684
+ startedAt: row.started_at,
685
+ completedAt: row.completed_at,
686
+ success: row.success === null ? null : row.success === 1,
687
+ feedback: row.feedback,
688
+ };
689
+ }
690
+ }
691
+ //# sourceMappingURL=engine.js.map