@winspan/claude-forge 8.41.0 → 8.50.6

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 (804) hide show
  1. package/.claude/CLAUDE.md +17 -0
  2. package/.eslintrc.js +23 -0
  3. package/.prettierrc +8 -0
  4. package/ARCHITECTURE_ISSUES.md +249 -0
  5. package/CLAUDE.md +265 -0
  6. package/CLAUDE.md.backup +488 -0
  7. package/DEVELOPMENT.md +310 -0
  8. package/dist/claudemd/claudemd-generator.d.ts +38 -3
  9. package/dist/claudemd/claudemd-generator.d.ts.map +1 -1
  10. package/dist/claudemd/claudemd-generator.js +629 -11
  11. package/dist/claudemd/claudemd-generator.js.map +1 -1
  12. package/dist/claudemd/index.d.ts +2 -2
  13. package/dist/claudemd/index.d.ts.map +1 -1
  14. package/dist/claudemd/index.js.map +1 -1
  15. package/dist/claudemd/resume-manager.d.ts.map +1 -1
  16. package/dist/claudemd/resume-manager.js +5 -2
  17. package/dist/claudemd/resume-manager.js.map +1 -1
  18. package/dist/claudemd/tech-detector.d.ts +1 -0
  19. package/dist/claudemd/tech-detector.d.ts.map +1 -1
  20. package/dist/claudemd/tech-detector.js +53 -0
  21. package/dist/claudemd/tech-detector.js.map +1 -1
  22. package/dist/cli/commands/claudemd.js +2 -2
  23. package/dist/cli/commands/claudemd.js.map +1 -1
  24. package/dist/cli/commands/daemon.d.ts +28 -0
  25. package/dist/cli/commands/daemon.d.ts.map +1 -1
  26. package/dist/cli/commands/daemon.js +200 -8
  27. package/dist/cli/commands/daemon.js.map +1 -1
  28. package/dist/cli/commands/init.d.ts.map +1 -1
  29. package/dist/cli/commands/init.js +3 -35
  30. package/dist/cli/commands/init.js.map +1 -1
  31. package/dist/cli/commands/menu.js +10 -10
  32. package/dist/cli/commands/menu.js.map +1 -1
  33. package/dist/cli/commands/skills.d.ts.map +1 -1
  34. package/dist/cli/commands/skills.js +8 -2
  35. package/dist/cli/commands/skills.js.map +1 -1
  36. package/dist/cli/commands/stats.d.ts.map +1 -1
  37. package/dist/cli/commands/stats.js +0 -17
  38. package/dist/cli/commands/stats.js.map +1 -1
  39. package/dist/cli/commands/trace.d.ts +9 -0
  40. package/dist/cli/commands/trace.d.ts.map +1 -0
  41. package/dist/cli/commands/trace.js +137 -0
  42. package/dist/cli/commands/trace.js.map +1 -0
  43. package/dist/cli/index.js +2 -4
  44. package/dist/cli/index.js.map +1 -1
  45. package/dist/core/ai/provider.d.ts +10 -2
  46. package/dist/core/ai/provider.d.ts.map +1 -1
  47. package/dist/core/ai/provider.js.map +1 -1
  48. package/dist/core/ai/types.d.ts +1 -19
  49. package/dist/core/ai/types.d.ts.map +1 -1
  50. package/dist/core/ai/types.js +1 -1
  51. package/dist/core/config.d.ts +2 -1
  52. package/dist/core/config.d.ts.map +1 -1
  53. package/dist/core/config.js +23 -6
  54. package/dist/core/config.js.map +1 -1
  55. package/dist/core/constants.d.ts +2 -2
  56. package/dist/core/constants.js +2 -2
  57. package/dist/core/constants.js.map +1 -1
  58. package/dist/core/queue/index.d.ts +52 -0
  59. package/dist/core/queue/index.d.ts.map +1 -0
  60. package/dist/core/queue/index.js +176 -0
  61. package/dist/core/queue/index.js.map +1 -0
  62. package/dist/core/storage/base.d.ts +33 -0
  63. package/dist/core/storage/base.d.ts.map +1 -0
  64. package/dist/core/storage/base.js +211 -0
  65. package/dist/core/storage/base.js.map +1 -0
  66. package/dist/core/storage/events.d.ts +52 -0
  67. package/dist/core/storage/events.d.ts.map +1 -0
  68. package/dist/core/storage/events.js +201 -0
  69. package/dist/core/storage/events.js.map +1 -0
  70. package/dist/core/storage/injections.d.ts +27 -0
  71. package/dist/core/storage/injections.d.ts.map +1 -0
  72. package/dist/core/storage/injections.js +51 -0
  73. package/dist/core/storage/injections.js.map +1 -0
  74. package/dist/core/storage/maintenance.d.ts +21 -0
  75. package/dist/core/storage/maintenance.d.ts.map +1 -0
  76. package/dist/core/storage/maintenance.js +52 -0
  77. package/dist/core/storage/maintenance.js.map +1 -0
  78. package/dist/core/storage/routing.d.ts +71 -0
  79. package/dist/core/storage/routing.d.ts.map +1 -0
  80. package/dist/core/storage/routing.js +141 -0
  81. package/dist/core/storage/routing.js.map +1 -0
  82. package/dist/core/storage/rows.d.ts +0 -47
  83. package/dist/core/storage/rows.d.ts.map +1 -1
  84. package/dist/core/storage/schema.sql +74 -136
  85. package/dist/core/storage/sessions.d.ts +34 -0
  86. package/dist/core/storage/sessions.d.ts.map +1 -0
  87. package/dist/core/storage/sessions.js +78 -0
  88. package/dist/core/storage/sessions.js.map +1 -0
  89. package/dist/core/storage/skills.d.ts +40 -0
  90. package/dist/core/storage/skills.d.ts.map +1 -0
  91. package/dist/core/storage/skills.js +107 -0
  92. package/dist/core/storage/skills.js.map +1 -0
  93. package/dist/core/storage/sqlite.d.ts +63 -265
  94. package/dist/core/storage/sqlite.d.ts.map +1 -1
  95. package/dist/core/storage/sqlite.js +102 -759
  96. package/dist/core/storage/sqlite.js.map +1 -1
  97. package/dist/core/storage/tasks.d.ts +64 -0
  98. package/dist/core/storage/tasks.d.ts.map +1 -0
  99. package/dist/core/storage/tasks.js +134 -0
  100. package/dist/core/storage/tasks.js.map +1 -0
  101. package/dist/core/storage/token-usage.d.ts +36 -0
  102. package/dist/core/storage/token-usage.d.ts.map +1 -0
  103. package/dist/core/storage/token-usage.js +59 -0
  104. package/dist/core/storage/token-usage.js.map +1 -0
  105. package/dist/core/types.d.ts +60 -4
  106. package/dist/core/types.d.ts.map +1 -1
  107. package/dist/core/types.js +24 -1
  108. package/dist/core/types.js.map +1 -1
  109. package/dist/core/utils/format.d.ts +28 -0
  110. package/dist/core/utils/format.d.ts.map +1 -0
  111. package/dist/core/utils/format.js +68 -0
  112. package/dist/core/utils/format.js.map +1 -0
  113. package/dist/core/utils/logger.d.ts +6 -1
  114. package/dist/core/utils/logger.d.ts.map +1 -1
  115. package/dist/core/utils/logger.js +72 -2
  116. package/dist/core/utils/logger.js.map +1 -1
  117. package/dist/core/utils/session.d.ts +16 -0
  118. package/dist/core/utils/session.d.ts.map +1 -0
  119. package/dist/core/utils/session.js +25 -0
  120. package/dist/core/utils/session.js.map +1 -0
  121. package/dist/core/utils/time.d.ts +22 -0
  122. package/dist/core/utils/time.d.ts.map +1 -0
  123. package/dist/core/utils/time.js +38 -0
  124. package/dist/core/utils/time.js.map +1 -0
  125. package/dist/daemon/handlers/history-exporter.d.ts.map +1 -1
  126. package/dist/daemon/handlers/history-exporter.js +6 -4
  127. package/dist/daemon/handlers/history-exporter.js.map +1 -1
  128. package/dist/daemon/handlers/post-tool-use.d.ts +5 -12
  129. package/dist/daemon/handlers/post-tool-use.d.ts.map +1 -1
  130. package/dist/daemon/handlers/post-tool-use.js +21 -79
  131. package/dist/daemon/handlers/post-tool-use.js.map +1 -1
  132. package/dist/daemon/handlers/stop.d.ts +24 -12
  133. package/dist/daemon/handlers/stop.d.ts.map +1 -1
  134. package/dist/daemon/handlers/stop.js +141 -42
  135. package/dist/daemon/handlers/stop.js.map +1 -1
  136. package/dist/daemon/handlers/user-prompt.d.ts +18 -19
  137. package/dist/daemon/handlers/user-prompt.d.ts.map +1 -1
  138. package/dist/daemon/handlers/user-prompt.js +103 -227
  139. package/dist/daemon/handlers/user-prompt.js.map +1 -1
  140. package/dist/daemon/index.d.ts +6 -2
  141. package/dist/daemon/index.d.ts.map +1 -1
  142. package/dist/daemon/index.js +76 -120
  143. package/dist/daemon/index.js.map +1 -1
  144. package/dist/daemon/launchd/com.claude-forge.daemon.plist.template +47 -0
  145. package/dist/daemon/launchd-installer.d.ts +61 -0
  146. package/dist/daemon/launchd-installer.d.ts.map +1 -0
  147. package/dist/daemon/launchd-installer.js +182 -0
  148. package/dist/daemon/launchd-installer.js.map +1 -0
  149. package/dist/daemon/lifecycle.d.ts +11 -0
  150. package/dist/daemon/lifecycle.d.ts.map +1 -1
  151. package/dist/daemon/lifecycle.js +44 -0
  152. package/dist/daemon/lifecycle.js.map +1 -1
  153. package/dist/daemon/router.d.ts +9 -2
  154. package/dist/daemon/router.d.ts.map +1 -1
  155. package/dist/daemon/router.js +27 -3
  156. package/dist/daemon/router.js.map +1 -1
  157. package/dist/daemon/server.d.ts.map +1 -1
  158. package/dist/daemon/server.js +6 -5
  159. package/dist/daemon/server.js.map +1 -1
  160. package/dist/daemon/services/anti-pattern-detector.d.ts +50 -0
  161. package/dist/daemon/services/anti-pattern-detector.d.ts.map +1 -0
  162. package/dist/daemon/services/anti-pattern-detector.js +357 -0
  163. package/dist/daemon/services/anti-pattern-detector.js.map +1 -0
  164. package/dist/daemon/services/drift-detector.d.ts +64 -0
  165. package/dist/daemon/services/drift-detector.d.ts.map +1 -0
  166. package/dist/daemon/services/drift-detector.js +201 -0
  167. package/dist/daemon/services/drift-detector.js.map +1 -0
  168. package/dist/{intelligence → daemon/services}/task-segmenter.d.ts +7 -1
  169. package/dist/daemon/services/task-segmenter.d.ts.map +1 -0
  170. package/dist/{intelligence → daemon/services}/task-segmenter.js +29 -6
  171. package/dist/daemon/services/task-segmenter.js.map +1 -0
  172. package/dist/daemon/services/weekly-report.d.ts +91 -0
  173. package/dist/daemon/services/weekly-report.d.ts.map +1 -0
  174. package/dist/daemon/services/weekly-report.js +327 -0
  175. package/dist/daemon/services/weekly-report.js.map +1 -0
  176. package/dist/hooks/hook-lib.sh +81 -0
  177. package/dist/hooks/notification.sh +7 -3
  178. package/dist/hooks/post-tool-use.sh +8 -4
  179. package/dist/hooks/pre-tool-use.sh +7 -4
  180. package/dist/hooks/stop.sh +1 -1
  181. package/dist/hooks/user-prompt-submit.sh +8 -9
  182. package/dist/mcp/server.d.ts +2 -2
  183. package/dist/mcp/server.d.ts.map +1 -1
  184. package/dist/mcp/server.js +71 -11
  185. package/dist/mcp/server.js.map +1 -1
  186. package/dist/skills/invocation-guard.d.ts +20 -0
  187. package/dist/skills/invocation-guard.d.ts.map +1 -1
  188. package/dist/skills/invocation-guard.js +63 -0
  189. package/dist/skills/invocation-guard.js.map +1 -1
  190. package/dist/skills/matcher.d.ts.map +1 -1
  191. package/dist/skills/matcher.js +12 -3
  192. package/dist/skills/matcher.js.map +1 -1
  193. package/dist/skills/official/code-simplifier.md +16 -0
  194. package/dist/skills/official/find-skills.md +23 -0
  195. package/dist/skills/official/official-api-design.md +17 -0
  196. package/dist/skills/official/official-architecture-decision.md +20 -0
  197. package/dist/skills/official/official-bmad.md +118 -0
  198. package/dist/skills/official/official-db-schema-design.md +16 -0
  199. package/dist/skills/official/official-debug.md +17 -0
  200. package/dist/skills/official/official-doc-driven.md +31 -0
  201. package/dist/skills/official/official-harness-engineering.md +108 -0
  202. package/dist/skills/official/official-performance-optimization.md +30 -0
  203. package/dist/skills/official/official-pr-review.md +35 -0
  204. package/dist/skills/official/official-release-checklist.md +30 -0
  205. package/dist/skills/official/official-security-hardening.md +26 -0
  206. package/dist/skills/official/official-spec-driven-design.md +31 -0
  207. package/dist/skills/official/planning-with-files.md +37 -0
  208. package/dist/skills/official/ui-ux-pro-max.md +18 -0
  209. package/dist/skills/official/webapp-testing.md +12 -0
  210. package/dist/skills/official-skills.d.ts +8 -4
  211. package/dist/skills/official-skills.d.ts.map +1 -1
  212. package/dist/skills/official-skills.js +48 -704
  213. package/dist/skills/official-skills.js.map +1 -1
  214. package/dist/skills/registry.d.ts +5 -0
  215. package/dist/skills/registry.d.ts.map +1 -1
  216. package/dist/skills/registry.js +48 -15
  217. package/dist/skills/registry.js.map +1 -1
  218. package/dist/skills/tools/pipeline-suggest.d.ts +30 -0
  219. package/dist/skills/tools/pipeline-suggest.d.ts.map +1 -0
  220. package/dist/skills/tools/pipeline-suggest.js +178 -0
  221. package/dist/skills/tools/pipeline-suggest.js.map +1 -0
  222. package/dist/web/routes/ai.d.ts.map +1 -1
  223. package/dist/web/routes/ai.js +16 -22
  224. package/dist/web/routes/ai.js.map +1 -1
  225. package/dist/web/routes/drift.d.ts +10 -0
  226. package/dist/web/routes/drift.d.ts.map +1 -0
  227. package/dist/web/routes/drift.js +21 -0
  228. package/dist/web/routes/drift.js.map +1 -0
  229. package/dist/web/routes/error-handler.d.ts +43 -0
  230. package/dist/web/routes/error-handler.d.ts.map +1 -0
  231. package/dist/web/routes/error-handler.js +99 -0
  232. package/dist/web/routes/error-handler.js.map +1 -0
  233. package/dist/web/routes/insights.d.ts +9 -0
  234. package/dist/web/routes/insights.d.ts.map +1 -0
  235. package/dist/web/routes/insights.js +34 -0
  236. package/dist/web/routes/insights.js.map +1 -0
  237. package/dist/web/routes/patch.js +2 -2
  238. package/dist/web/routes/patch.js.map +1 -1
  239. package/dist/web/routes/reports.d.ts +10 -0
  240. package/dist/web/routes/reports.d.ts.map +1 -0
  241. package/dist/web/routes/reports.js +27 -0
  242. package/dist/web/routes/reports.js.map +1 -0
  243. package/dist/web/routes/rules.d.ts +10 -3
  244. package/dist/web/routes/rules.d.ts.map +1 -1
  245. package/dist/web/routes/rules.js +80 -95
  246. package/dist/web/routes/rules.js.map +1 -1
  247. package/dist/web/routes/sessions.d.ts +1 -2
  248. package/dist/web/routes/sessions.d.ts.map +1 -1
  249. package/dist/web/routes/sessions.js +27 -39
  250. package/dist/web/routes/sessions.js.map +1 -1
  251. package/dist/web/routes/skill-stats.d.ts.map +1 -1
  252. package/dist/web/routes/skill-stats.js +38 -0
  253. package/dist/web/routes/skill-stats.js.map +1 -1
  254. package/dist/web/routes/skills.d.ts.map +1 -1
  255. package/dist/web/routes/skills.js +34 -0
  256. package/dist/web/routes/skills.js.map +1 -1
  257. package/dist/web/routes/stats.d.ts +7 -0
  258. package/dist/web/routes/stats.d.ts.map +1 -0
  259. package/dist/web/routes/stats.js +44 -0
  260. package/dist/web/routes/stats.js.map +1 -0
  261. package/dist/web/routes/status.js +1 -1
  262. package/dist/web/routes/status.js.map +1 -1
  263. package/dist/web/routes/tasks.d.ts +4 -0
  264. package/dist/web/routes/tasks.d.ts.map +1 -0
  265. package/dist/web/routes/tasks.js +181 -0
  266. package/dist/web/routes/tasks.js.map +1 -0
  267. package/dist/web/routes/trace.d.ts +10 -0
  268. package/dist/web/routes/trace.d.ts.map +1 -0
  269. package/dist/web/routes/trace.js +123 -0
  270. package/dist/web/routes/trace.js.map +1 -0
  271. package/dist/web/routes/types.d.ts +1 -14
  272. package/dist/web/routes/types.d.ts.map +1 -1
  273. package/dist/web/routes/types.js +8 -17
  274. package/dist/web/routes/types.js.map +1 -1
  275. package/dist/web/server.d.ts +1 -9
  276. package/dist/web/server.d.ts.map +1 -1
  277. package/dist/web/server.js +28 -28
  278. package/dist/web/server.js.map +1 -1
  279. package/dist/web/static/assets/AIConfig-BQCAQE9D.js +2 -0
  280. package/dist/web/static/assets/AIConfig-BQCAQE9D.js.map +1 -0
  281. package/dist/web/static/assets/Dashboard-D7Bo6Kan.js +2 -0
  282. package/dist/web/static/assets/Dashboard-D7Bo6Kan.js.map +1 -0
  283. package/dist/web/static/assets/{Drawer-DcU3ln98.js → Drawer-BeHRQxUS.js} +2 -2
  284. package/dist/web/static/assets/{Drawer-DcU3ln98.js.map → Drawer-BeHRQxUS.js.map} +1 -1
  285. package/dist/web/static/assets/Events-K_tCY2ti.js +2 -0
  286. package/dist/web/static/assets/Events-K_tCY2ti.js.map +1 -0
  287. package/dist/web/static/assets/Reports-BJCmBnc_.js +2 -0
  288. package/dist/web/static/assets/Reports-BJCmBnc_.js.map +1 -0
  289. package/dist/web/static/assets/SearchInput-BX2KhMkw.js +2 -0
  290. package/dist/web/static/assets/SearchInput-BX2KhMkw.js.map +1 -0
  291. package/dist/web/static/assets/SessionDetail-Bkr-kC7V.js +2 -0
  292. package/dist/web/static/assets/SessionDetail-Bkr-kC7V.js.map +1 -0
  293. package/dist/web/static/assets/Sessions-Chx9OCLH.js +2 -0
  294. package/dist/web/static/assets/Sessions-Chx9OCLH.js.map +1 -0
  295. package/dist/web/static/assets/Skills-O0GT1i7m.js +2 -0
  296. package/dist/web/static/assets/Skills-O0GT1i7m.js.map +1 -0
  297. package/dist/web/static/assets/TaskDetail-5SR8zGzv.js +2 -0
  298. package/dist/web/static/assets/TaskDetail-5SR8zGzv.js.map +1 -0
  299. package/dist/web/static/assets/Tasks-DCgDqvOZ.js +2 -0
  300. package/dist/web/static/assets/Tasks-DCgDqvOZ.js.map +1 -0
  301. package/dist/web/static/assets/export-L_VBD2p1.js +4 -0
  302. package/dist/web/static/assets/export-L_VBD2p1.js.map +1 -0
  303. package/dist/web/static/assets/index-D8AKj26b.css +1 -0
  304. package/dist/web/static/assets/index-DxIbmNmr.js +3 -0
  305. package/dist/web/static/assets/index-DxIbmNmr.js.map +1 -0
  306. package/dist/web/static/assets/{lucide-53bR2rki.js → lucide-fJlPI3H7.js} +68 -38
  307. package/dist/web/static/assets/lucide-fJlPI3H7.js.map +1 -0
  308. package/dist/web/static/assets/time-Bxuk0M-C.js +2 -0
  309. package/dist/web/static/assets/time-Bxuk0M-C.js.map +1 -0
  310. package/dist/web/static/index.html +3 -3
  311. package/docs/concurrent-agents.md +129 -0
  312. package/docs/design/architecture-review-20260516.md +232 -0
  313. package/docs/design/fix-skills-data-and-set-leak-spec-20260516-1300.md +219 -0
  314. package/docs/design/hook-failure-queue-spec-20260516-1530.md +204 -0
  315. package/docs/design/refactor-phase1-spec-20260515-1600.md +543 -0
  316. package/docs/design/refactor-phase2-spec-20260515-1700.md +424 -0
  317. package/docs/design/tasks-list-filter-pagination-spec-20260518-0930.md +208 -0
  318. package/docs/implementation/fix-skills-data-and-set-leak-changelog-20260516-1300.md +104 -0
  319. package/docs/implementation/hook-failure-queue-changelog-20260516-1530.md +196 -0
  320. package/docs/implementation/hotfix-daemon-event-reject-20260516-1430.md +56 -0
  321. package/docs/implementation/refactor-phase1-changelog-20260515-1630.md +354 -0
  322. package/docs/implementation/refactor-phase2-changelog-20260515-1705.md +421 -0
  323. package/docs/implementation/tasks-list-filter-pagination-changelog-20260518-0930.md +72 -0
  324. package/docs/reviews/claudemd-template-sync.md +54 -0
  325. package/docs/reviews/tasks-filter-pagination.md +80 -0
  326. package/docs/ruflo-learning-strategy.md +322 -0
  327. package/docs/skills-deduplication-analysis.md +83 -0
  328. package/docs/skills-multiformat-support.md +177 -0
  329. package/docs/skills-third-party.md +183 -0
  330. package/docs/testing/tasks-filter-pagination-test-report.md +86 -0
  331. package/forge +321 -0
  332. package/package.json +28 -62
  333. package/playwright.config.ts +40 -0
  334. package/scripts/demo-v2.ts +91 -0
  335. package/scripts/dev-daemon.sh +232 -0
  336. package/scripts/dev-web.ts +109 -0
  337. package/scripts/e2e-mcp-link.ts +423 -0
  338. package/scripts/e2e-methodology-quality.ts +253 -0
  339. package/scripts/e2e-routing.ts +456 -0
  340. package/scripts/e2e-user-methodology.ts +326 -0
  341. package/scripts/e2e-web-workflows.ts +299 -0
  342. package/scripts/migrate-legacy-to-dynamic.sql +108 -0
  343. package/scripts/regenerate-execution-docs.ts +116 -0
  344. package/scripts/sync-agent-skills.ts +193 -0
  345. package/scripts/test-hook.sh +71 -0
  346. package/scripts/verify-skill-loading.ts +62 -0
  347. package/src/claudemd/claudemd-generator.ts +777 -0
  348. package/src/claudemd/convention-extractor.ts +69 -0
  349. package/src/claudemd/index.ts +35 -0
  350. package/src/claudemd/persona-manager.ts +88 -0
  351. package/src/claudemd/resume-manager.ts +236 -0
  352. package/src/claudemd/tech-detector.ts +220 -0
  353. package/src/cli/commands/claudemd.ts +84 -0
  354. package/src/cli/commands/config.ts +46 -0
  355. package/src/cli/commands/daemon.ts +310 -0
  356. package/src/cli/commands/executions.ts +114 -0
  357. package/src/cli/commands/init.ts +204 -0
  358. package/src/cli/commands/logs.ts +181 -0
  359. package/src/cli/commands/mcp.ts +244 -0
  360. package/src/cli/commands/menu.ts +356 -0
  361. package/src/cli/commands/skills.ts +185 -0
  362. package/src/cli/commands/stats.ts +74 -0
  363. package/src/cli/commands/status.ts +69 -0
  364. package/src/cli/commands/template.ts +77 -0
  365. package/src/cli/commands/trace.ts +164 -0
  366. package/src/cli/index.ts +42 -0
  367. package/src/cli/init/hook-manager.ts +132 -0
  368. package/src/core/ai/provider.ts +308 -0
  369. package/src/core/ai/types.ts +51 -0
  370. package/src/core/config.ts +124 -0
  371. package/src/core/constants.ts +45 -0
  372. package/src/core/queue/index.ts +193 -0
  373. package/src/core/storage/base.ts +226 -0
  374. package/src/core/storage/events.ts +255 -0
  375. package/src/core/storage/injections.ts +78 -0
  376. package/src/core/storage/maintenance.ts +59 -0
  377. package/src/core/storage/migrations/002_add_skill_tracking.sql +6 -0
  378. package/src/core/storage/migrations/003_add_skill_invocations.sql +23 -0
  379. package/src/core/storage/performance-indexes.sql +23 -0
  380. package/src/core/storage/routing.ts +194 -0
  381. package/src/core/storage/rows.ts +112 -0
  382. package/src/core/storage/schema.sql +214 -0
  383. package/src/core/storage/sessions.ts +104 -0
  384. package/src/core/storage/skills.ts +164 -0
  385. package/src/core/storage/sqlite.ts +194 -0
  386. package/src/core/storage/tasks.ts +170 -0
  387. package/src/core/storage/token-usage.ts +93 -0
  388. package/src/core/types.ts +154 -0
  389. package/src/core/utils/error-handler.ts +256 -0
  390. package/src/core/utils/forge-resume-block.ts +74 -0
  391. package/src/core/utils/format.ts +69 -0
  392. package/src/core/utils/logger.ts +119 -0
  393. package/src/core/utils/lru-cache.ts +50 -0
  394. package/src/core/utils/path.ts +19 -0
  395. package/src/core/utils/session.ts +26 -0
  396. package/src/core/utils/time.ts +37 -0
  397. package/src/core/utils/token-tracker.ts +97 -0
  398. package/src/daemon/event-parser.ts +35 -0
  399. package/src/daemon/handlers/history-exporter.ts +117 -0
  400. package/src/daemon/handlers/post-tool-use.ts +50 -0
  401. package/src/daemon/handlers/stop.ts +215 -0
  402. package/src/daemon/handlers/user-prompt.ts +188 -0
  403. package/src/daemon/index.ts +278 -0
  404. package/src/daemon/launchd/com.claude-forge.daemon.plist.template +47 -0
  405. package/src/daemon/launchd-installer.ts +260 -0
  406. package/src/daemon/lifecycle.ts +128 -0
  407. package/src/daemon/router.ts +40 -0
  408. package/src/daemon/server.ts +209 -0
  409. package/src/daemon/services/anti-pattern-detector.ts +412 -0
  410. package/src/daemon/services/drift-detector.ts +232 -0
  411. package/src/daemon/services/task-segmenter.ts +112 -0
  412. package/src/daemon/services/weekly-report.ts +454 -0
  413. package/src/hooks/hook-lib.sh +81 -0
  414. package/src/hooks/notification.sh +35 -0
  415. package/src/hooks/post-tool-use.sh +61 -0
  416. package/src/hooks/pre-tool-use.sh +63 -0
  417. package/src/hooks/stop.sh +40 -0
  418. package/src/hooks/user-prompt-submit.sh +69 -0
  419. package/src/mcp/server.ts +322 -0
  420. package/src/skills/index.ts +2 -0
  421. package/src/skills/invocation-guard.ts +177 -0
  422. package/src/skills/matcher.ts +148 -0
  423. package/src/skills/official/code-simplifier.md +16 -0
  424. package/src/skills/official/find-skills.md +23 -0
  425. package/src/skills/official/official-api-design.md +17 -0
  426. package/src/skills/official/official-architecture-decision.md +20 -0
  427. package/src/skills/official/official-bmad.md +118 -0
  428. package/src/skills/official/official-db-schema-design.md +16 -0
  429. package/src/skills/official/official-debug.md +17 -0
  430. package/src/skills/official/official-doc-driven.md +31 -0
  431. package/src/skills/official/official-harness-engineering.md +108 -0
  432. package/src/skills/official/official-performance-optimization.md +30 -0
  433. package/src/skills/official/official-pr-review.md +35 -0
  434. package/src/skills/official/official-release-checklist.md +30 -0
  435. package/src/skills/official/official-security-hardening.md +26 -0
  436. package/src/skills/official/official-spec-driven-design.md +31 -0
  437. package/src/skills/official/planning-with-files.md +37 -0
  438. package/src/skills/official/ui-ux-pro-max.md +18 -0
  439. package/src/skills/official/webapp-testing.md +12 -0
  440. package/src/skills/official-skills.ts +89 -0
  441. package/src/skills/registry.ts +355 -0
  442. package/src/skills/semantic-matcher.ts +231 -0
  443. package/src/skills/tools/pipeline-suggest.ts +226 -0
  444. package/src/skills/tools/skill-invoke.ts +168 -0
  445. package/src/skills/tools/skill-list.ts +59 -0
  446. package/src/templates/go.yaml +53 -0
  447. package/src/templates/python.yaml +59 -0
  448. package/src/templates/react.yaml +55 -0
  449. package/src/templates/template-manager.ts +170 -0
  450. package/src/web/auth-middleware.ts +55 -0
  451. package/src/web/routes/ai.ts +204 -0
  452. package/src/web/routes/auth.ts +22 -0
  453. package/src/web/routes/drift.ts +25 -0
  454. package/src/web/routes/error-handler.ts +120 -0
  455. package/src/web/routes/events.ts +47 -0
  456. package/src/web/routes/insights.ts +43 -0
  457. package/src/web/routes/patch.ts +117 -0
  458. package/src/web/routes/reports.ts +34 -0
  459. package/src/web/routes/rules.ts +101 -0
  460. package/src/web/routes/sessions.ts +262 -0
  461. package/src/web/routes/skill-stats.ts +132 -0
  462. package/src/web/routes/skills.ts +349 -0
  463. package/src/web/routes/static.ts +67 -0
  464. package/src/web/routes/stats.ts +60 -0
  465. package/src/web/routes/status.ts +30 -0
  466. package/src/web/routes/tasks.ts +218 -0
  467. package/src/web/routes/token-usage.ts +20 -0
  468. package/src/web/routes/trace.ts +138 -0
  469. package/src/web/routes/types.ts +56 -0
  470. package/src/web/server.ts +134 -0
  471. package/src/web/ssrf-guard.ts +112 -0
  472. package/src/web/static/index.html +3251 -0
  473. package/src/web/static/vendor/chart.umd.min.js +20 -0
  474. package/tests/e2e/dashboard.spec.ts +205 -0
  475. package/tests/e2e/routing-skill-e2e.test.ts +39 -0
  476. package/tests/helpers/mock-ai.ts +92 -0
  477. package/tests/helpers/mock-storage.ts +159 -0
  478. package/tests/integration/queue-replay.integration.test.ts +193 -0
  479. package/tests/integration/tasks-filter.integration.test.ts +154 -0
  480. package/tests/performance/database.benchmark.ts +161 -0
  481. package/tests/semantic-matcher.test.ts +99 -0
  482. package/tests/skill-matcher.test.ts +110 -0
  483. package/tests/unit/ai-provider-retry.test.ts +194 -0
  484. package/tests/unit/ai-provider-vision.test.ts +224 -0
  485. package/tests/unit/claudemd-generator.test.ts +68 -0
  486. package/tests/unit/cli-mcp.test.ts +141 -0
  487. package/tests/unit/handlers.test.ts +171 -0
  488. package/tests/unit/invocation-guard.test.ts +125 -0
  489. package/tests/unit/queue.test.ts +272 -0
  490. package/tests/unit/router.test.ts +138 -0
  491. package/tests/unit/security.test.ts +128 -0
  492. package/tests/unit/skill-invocations-workflow.test.ts +495 -0
  493. package/tests/unit/skill-registry.test.ts +94 -0
  494. package/tests/unit/skills/invocation-guard-ttl.test.ts +211 -0
  495. package/tests/unit/skills/official-skills-loader.test.ts +126 -0
  496. package/tests/unit/skills/registry-multiformat.test.ts +92 -0
  497. package/tests/unit/storage/sessions-aggregate.test.ts +435 -0
  498. package/tests/unit/storage/sqlite-refactor-harness.test.ts +314 -0
  499. package/tests/unit/storage.test.ts +172 -0
  500. package/tests/unit/token-usage.test.ts +144 -0
  501. package/tests/unit/type-guards.test.ts +201 -0
  502. package/tests/unit/utils/format.test.ts +189 -0
  503. package/tests/unit/utils/session.test.ts +89 -0
  504. package/tests/unit/utils/time.test.ts +112 -0
  505. package/tests/unit/web/routes-auth.test.ts +93 -0
  506. package/tests/unit/web/routes-events.test.ts +101 -0
  507. package/tests/unit/web/routes-sessions.test.ts +181 -0
  508. package/tests/unit/web/routes-skill-stats.test.ts +179 -0
  509. package/tests/unit/web/routes-stats.test.ts +92 -0
  510. package/tests/unit/web/routes-tasks.test.ts +351 -0
  511. package/tsconfig.json +22 -0
  512. package/vitest.config.ts +21 -0
  513. package/vitest.integration.config.ts +16 -0
  514. package/web/CLAUDE.md +20 -0
  515. package/web/index.html +13 -0
  516. package/web/package-lock.json +4854 -0
  517. package/web/package.json +35 -0
  518. package/web/postcss.config.js +6 -0
  519. package/web/src/App.tsx +110 -0
  520. package/web/src/components/CodeBlock.tsx +31 -0
  521. package/web/src/components/Confirm.tsx +96 -0
  522. package/web/src/components/Drawer.tsx +60 -0
  523. package/web/src/components/Layout.tsx +145 -0
  524. package/web/src/components/MarkdownRenderer.tsx +77 -0
  525. package/web/src/components/SearchInput.tsx +31 -0
  526. package/web/src/components/SessionDetailContent.tsx +157 -0
  527. package/web/src/components/Toast.tsx +92 -0
  528. package/web/src/index.css +19 -0
  529. package/web/src/main.tsx +31 -0
  530. package/web/src/pages/AIConfig.tsx +233 -0
  531. package/web/src/pages/Dashboard.tsx +572 -0
  532. package/web/src/pages/Events.tsx +271 -0
  533. package/web/src/pages/Reports.tsx +428 -0
  534. package/web/src/pages/SessionDetail.tsx +162 -0
  535. package/web/src/pages/Sessions.tsx +205 -0
  536. package/web/src/pages/Skills.tsx +180 -0
  537. package/web/src/pages/TaskDetail.tsx +511 -0
  538. package/web/src/pages/Tasks.tsx +150 -0
  539. package/web/src/utils/auth.ts +59 -0
  540. package/web/src/utils/export.ts +54 -0
  541. package/web/src/utils/time.ts +13 -0
  542. package/web/tailwind.config.js +11 -0
  543. package/web/tsconfig.json +21 -0
  544. package/web/tsconfig.node.json +10 -0
  545. package/web/vite.config.ts +76 -0
  546. package/winspan-claude-forge-8.43.0.tgz +0 -0
  547. package/dist/agents/definition.d.ts +0 -62
  548. package/dist/agents/definition.d.ts.map +0 -1
  549. package/dist/agents/definition.js +0 -27
  550. package/dist/agents/definition.js.map +0 -1
  551. package/dist/agents/distributor.d.ts +0 -23
  552. package/dist/agents/distributor.d.ts.map +0 -1
  553. package/dist/agents/distributor.js +0 -85
  554. package/dist/agents/distributor.js.map +0 -1
  555. package/dist/agents/index.d.ts +0 -5
  556. package/dist/agents/index.d.ts.map +0 -1
  557. package/dist/agents/index.js +0 -5
  558. package/dist/agents/index.js.map +0 -1
  559. package/dist/agents/methodologies/agent-builder.d.ts +0 -21
  560. package/dist/agents/methodologies/agent-builder.d.ts.map +0 -1
  561. package/dist/agents/methodologies/agent-builder.js +0 -149
  562. package/dist/agents/methodologies/agent-builder.js.map +0 -1
  563. package/dist/agents/methodologies/phases/bmad/analyze.d.ts +0 -3
  564. package/dist/agents/methodologies/phases/bmad/analyze.d.ts.map +0 -1
  565. package/dist/agents/methodologies/phases/bmad/analyze.js +0 -19
  566. package/dist/agents/methodologies/phases/bmad/analyze.js.map +0 -1
  567. package/dist/agents/methodologies/phases/bmad/design.d.ts +0 -3
  568. package/dist/agents/methodologies/phases/bmad/design.d.ts.map +0 -1
  569. package/dist/agents/methodologies/phases/bmad/design.js +0 -18
  570. package/dist/agents/methodologies/phases/bmad/design.js.map +0 -1
  571. package/dist/agents/methodologies/phases/bmad/implement.d.ts +0 -3
  572. package/dist/agents/methodologies/phases/bmad/implement.d.ts.map +0 -1
  573. package/dist/agents/methodologies/phases/bmad/implement.js +0 -17
  574. package/dist/agents/methodologies/phases/bmad/implement.js.map +0 -1
  575. package/dist/agents/methodologies/phases/bmad/index.d.ts +0 -6
  576. package/dist/agents/methodologies/phases/bmad/index.d.ts.map +0 -1
  577. package/dist/agents/methodologies/phases/bmad/index.js +0 -6
  578. package/dist/agents/methodologies/phases/bmad/index.js.map +0 -1
  579. package/dist/agents/methodologies/phases/bmad/review.d.ts +0 -3
  580. package/dist/agents/methodologies/phases/bmad/review.d.ts.map +0 -1
  581. package/dist/agents/methodologies/phases/bmad/review.js +0 -17
  582. package/dist/agents/methodologies/phases/bmad/review.js.map +0 -1
  583. package/dist/agents/methodologies/phases/bmad/test.d.ts +0 -3
  584. package/dist/agents/methodologies/phases/bmad/test.d.ts.map +0 -1
  585. package/dist/agents/methodologies/phases/bmad/test.js +0 -21
  586. package/dist/agents/methodologies/phases/bmad/test.js.map +0 -1
  587. package/dist/agents/methodologies/phases/harness/fix.d.ts +0 -3
  588. package/dist/agents/methodologies/phases/harness/fix.d.ts.map +0 -1
  589. package/dist/agents/methodologies/phases/harness/fix.js +0 -17
  590. package/dist/agents/methodologies/phases/harness/fix.js.map +0 -1
  591. package/dist/agents/methodologies/phases/harness/index.d.ts +0 -6
  592. package/dist/agents/methodologies/phases/harness/index.d.ts.map +0 -1
  593. package/dist/agents/methodologies/phases/harness/index.js +0 -6
  594. package/dist/agents/methodologies/phases/harness/index.js.map +0 -1
  595. package/dist/agents/methodologies/phases/harness/reproduce.d.ts +0 -3
  596. package/dist/agents/methodologies/phases/harness/reproduce.d.ts.map +0 -1
  597. package/dist/agents/methodologies/phases/harness/reproduce.js +0 -20
  598. package/dist/agents/methodologies/phases/harness/reproduce.js.map +0 -1
  599. package/dist/agents/methodologies/phases/harness/root-cause.d.ts +0 -3
  600. package/dist/agents/methodologies/phases/harness/root-cause.d.ts.map +0 -1
  601. package/dist/agents/methodologies/phases/harness/root-cause.js +0 -21
  602. package/dist/agents/methodologies/phases/harness/root-cause.js.map +0 -1
  603. package/dist/agents/methodologies/phases/harness/safety-net.d.ts +0 -3
  604. package/dist/agents/methodologies/phases/harness/safety-net.d.ts.map +0 -1
  605. package/dist/agents/methodologies/phases/harness/safety-net.js +0 -17
  606. package/dist/agents/methodologies/phases/harness/safety-net.js.map +0 -1
  607. package/dist/agents/methodologies/phases/harness/verify.d.ts +0 -3
  608. package/dist/agents/methodologies/phases/harness/verify.d.ts.map +0 -1
  609. package/dist/agents/methodologies/phases/harness/verify.js +0 -22
  610. package/dist/agents/methodologies/phases/harness/verify.js.map +0 -1
  611. package/dist/agents/methodologies/presets.d.ts +0 -10
  612. package/dist/agents/methodologies/presets.d.ts.map +0 -1
  613. package/dist/agents/methodologies/presets.js +0 -79
  614. package/dist/agents/methodologies/presets.js.map +0 -1
  615. package/dist/agents/methodologies/types.d.ts +0 -45
  616. package/dist/agents/methodologies/types.d.ts.map +0 -1
  617. package/dist/agents/methodologies/types.js +0 -10
  618. package/dist/agents/methodologies/types.js.map +0 -1
  619. package/dist/agents/methodologies/user-config-loader.d.ts +0 -30
  620. package/dist/agents/methodologies/user-config-loader.d.ts.map +0 -1
  621. package/dist/agents/methodologies/user-config-loader.js +0 -159
  622. package/dist/agents/methodologies/user-config-loader.js.map +0 -1
  623. package/dist/agents/official-agents.d.ts +0 -4
  624. package/dist/agents/official-agents.d.ts.map +0 -1
  625. package/dist/agents/official-agents.js +0 -559
  626. package/dist/agents/official-agents.js.map +0 -1
  627. package/dist/agents/registry.d.ts +0 -57
  628. package/dist/agents/registry.d.ts.map +0 -1
  629. package/dist/agents/registry.js +0 -271
  630. package/dist/agents/registry.js.map +0 -1
  631. package/dist/capability/index.d.ts +0 -10
  632. package/dist/capability/index.d.ts.map +0 -1
  633. package/dist/capability/index.js +0 -10
  634. package/dist/capability/index.js.map +0 -1
  635. package/dist/capability/types.d.ts +0 -10
  636. package/dist/capability/types.d.ts.map +0 -1
  637. package/dist/capability/types.js +0 -10
  638. package/dist/capability/types.js.map +0 -1
  639. package/dist/cli/commands/agents.d.ts +0 -3
  640. package/dist/cli/commands/agents.d.ts.map +0 -1
  641. package/dist/cli/commands/agents.js +0 -62
  642. package/dist/cli/commands/agents.js.map +0 -1
  643. package/dist/cli/commands/rules.d.ts +0 -8
  644. package/dist/cli/commands/rules.d.ts.map +0 -1
  645. package/dist/cli/commands/rules.js +0 -89
  646. package/dist/cli/commands/rules.js.map +0 -1
  647. package/dist/daemon/auto-disable-scheduler.d.ts +0 -53
  648. package/dist/daemon/auto-disable-scheduler.d.ts.map +0 -1
  649. package/dist/daemon/auto-disable-scheduler.js +0 -114
  650. package/dist/daemon/auto-disable-scheduler.js.map +0 -1
  651. package/dist/daemon/handlers/pre-tool-use.d.ts +0 -39
  652. package/dist/daemon/handlers/pre-tool-use.d.ts.map +0 -1
  653. package/dist/daemon/handlers/pre-tool-use.js +0 -166
  654. package/dist/daemon/handlers/pre-tool-use.js.map +0 -1
  655. package/dist/daemon/routing-observer.d.ts +0 -42
  656. package/dist/daemon/routing-observer.d.ts.map +0 -1
  657. package/dist/daemon/routing-observer.js +0 -264
  658. package/dist/daemon/routing-observer.js.map +0 -1
  659. package/dist/daemon/routing-state.d.ts +0 -64
  660. package/dist/daemon/routing-state.d.ts.map +0 -1
  661. package/dist/daemon/routing-state.js +0 -240
  662. package/dist/daemon/routing-state.js.map +0 -1
  663. package/dist/engine/agent-router.d.ts +0 -142
  664. package/dist/engine/agent-router.d.ts.map +0 -1
  665. package/dist/engine/agent-router.js +0 -276
  666. package/dist/engine/agent-router.js.map +0 -1
  667. package/dist/engine/context-builder.d.ts +0 -23
  668. package/dist/engine/context-builder.d.ts.map +0 -1
  669. package/dist/engine/context-builder.js +0 -63
  670. package/dist/engine/context-builder.js.map +0 -1
  671. package/dist/engine/conventions/basic-security.yaml +0 -109
  672. package/dist/engine/conventions/code-quality.yaml +0 -123
  673. package/dist/engine/conventions/database-safety.yaml +0 -74
  674. package/dist/engine/conventions/dependency-safety.yaml +0 -132
  675. package/dist/engine/conventions/docker-safety.yaml +0 -69
  676. package/dist/engine/conventions/git-safety.yaml +0 -118
  677. package/dist/engine/conventions/go-best-practices.yaml +0 -84
  678. package/dist/engine/conventions/python-best-practices.yaml +0 -96
  679. package/dist/engine/conventions/react-best-practices.yaml +0 -96
  680. package/dist/engine/conventions/routing.yaml +0 -378
  681. package/dist/engine/conventions/strict-security.yaml +0 -30
  682. package/dist/engine/conventions/ts-quality.yaml +0 -49
  683. package/dist/engine/dsl/compiler.d.ts +0 -34
  684. package/dist/engine/dsl/compiler.d.ts.map +0 -1
  685. package/dist/engine/dsl/compiler.js +0 -702
  686. package/dist/engine/dsl/compiler.js.map +0 -1
  687. package/dist/engine/dsl/parser.d.ts +0 -25
  688. package/dist/engine/dsl/parser.d.ts.map +0 -1
  689. package/dist/engine/dsl/parser.js +0 -208
  690. package/dist/engine/dsl/parser.js.map +0 -1
  691. package/dist/engine/dsl/runtime.d.ts +0 -46
  692. package/dist/engine/dsl/runtime.d.ts.map +0 -1
  693. package/dist/engine/dsl/runtime.js +0 -173
  694. package/dist/engine/dsl/runtime.js.map +0 -1
  695. package/dist/engine/dsl/types.d.ts +0 -139
  696. package/dist/engine/dsl/types.d.ts.map +0 -1
  697. package/dist/engine/dsl/types.js +0 -11
  698. package/dist/engine/dsl/types.js.map +0 -1
  699. package/dist/engine/evidence-store.d.ts +0 -44
  700. package/dist/engine/evidence-store.d.ts.map +0 -1
  701. package/dist/engine/evidence-store.js +0 -109
  702. package/dist/engine/evidence-store.js.map +0 -1
  703. package/dist/engine/experiment-router.d.ts +0 -102
  704. package/dist/engine/experiment-router.d.ts.map +0 -1
  705. package/dist/engine/experiment-router.js +0 -289
  706. package/dist/engine/experiment-router.js.map +0 -1
  707. package/dist/engine/recommender.d.ts +0 -52
  708. package/dist/engine/recommender.d.ts.map +0 -1
  709. package/dist/engine/recommender.js +0 -162
  710. package/dist/engine/recommender.js.map +0 -1
  711. package/dist/engine/rule-engine.d.ts +0 -33
  712. package/dist/engine/rule-engine.d.ts.map +0 -1
  713. package/dist/engine/rule-engine.js +0 -250
  714. package/dist/engine/rule-engine.js.map +0 -1
  715. package/dist/engine/security-gates.d.ts +0 -42
  716. package/dist/engine/security-gates.d.ts.map +0 -1
  717. package/dist/engine/security-gates.js +0 -210
  718. package/dist/engine/security-gates.js.map +0 -1
  719. package/dist/intelligence/classifier.d.ts +0 -75
  720. package/dist/intelligence/classifier.d.ts.map +0 -1
  721. package/dist/intelligence/classifier.js +0 -352
  722. package/dist/intelligence/classifier.js.map +0 -1
  723. package/dist/intelligence/context-gatherer.d.ts +0 -101
  724. package/dist/intelligence/context-gatherer.d.ts.map +0 -1
  725. package/dist/intelligence/context-gatherer.js +0 -417
  726. package/dist/intelligence/context-gatherer.js.map +0 -1
  727. package/dist/intelligence/cot-classifier.d.ts +0 -95
  728. package/dist/intelligence/cot-classifier.d.ts.map +0 -1
  729. package/dist/intelligence/cot-classifier.js +0 -391
  730. package/dist/intelligence/cot-classifier.js.map +0 -1
  731. package/dist/intelligence/distiller.d.ts +0 -22
  732. package/dist/intelligence/distiller.d.ts.map +0 -1
  733. package/dist/intelligence/distiller.js +0 -108
  734. package/dist/intelligence/distiller.js.map +0 -1
  735. package/dist/intelligence/execution-doc-builder.d.ts +0 -151
  736. package/dist/intelligence/execution-doc-builder.d.ts.map +0 -1
  737. package/dist/intelligence/execution-doc-builder.js +0 -1018
  738. package/dist/intelligence/execution-doc-builder.js.map +0 -1
  739. package/dist/intelligence/intent-types.d.ts +0 -13
  740. package/dist/intelligence/intent-types.d.ts.map +0 -1
  741. package/dist/intelligence/intent-types.js +0 -19
  742. package/dist/intelligence/intent-types.js.map +0 -1
  743. package/dist/intelligence/multimodal-parser.d.ts +0 -105
  744. package/dist/intelligence/multimodal-parser.d.ts.map +0 -1
  745. package/dist/intelligence/multimodal-parser.js +0 -425
  746. package/dist/intelligence/multimodal-parser.js.map +0 -1
  747. package/dist/intelligence/quality-gate.d.ts +0 -45
  748. package/dist/intelligence/quality-gate.d.ts.map +0 -1
  749. package/dist/intelligence/quality-gate.js +0 -193
  750. package/dist/intelligence/quality-gate.js.map +0 -1
  751. package/dist/intelligence/task-segmenter.d.ts.map +0 -1
  752. package/dist/intelligence/task-segmenter.js.map +0 -1
  753. package/dist/web/routes/agents.d.ts +0 -7
  754. package/dist/web/routes/agents.d.ts.map +0 -1
  755. package/dist/web/routes/agents.js +0 -209
  756. package/dist/web/routes/agents.js.map +0 -1
  757. package/dist/web/routes/execution-trace.d.ts +0 -21
  758. package/dist/web/routes/execution-trace.d.ts.map +0 -1
  759. package/dist/web/routes/execution-trace.js +0 -353
  760. package/dist/web/routes/execution-trace.js.map +0 -1
  761. package/dist/web/routes/experiments.d.ts +0 -15
  762. package/dist/web/routes/experiments.d.ts.map +0 -1
  763. package/dist/web/routes/experiments.js +0 -187
  764. package/dist/web/routes/experiments.js.map +0 -1
  765. package/dist/web/routes/routing.d.ts +0 -17
  766. package/dist/web/routes/routing.d.ts.map +0 -1
  767. package/dist/web/routes/routing.js +0 -592
  768. package/dist/web/routes/routing.js.map +0 -1
  769. package/dist/web/routes/workflows.d.ts +0 -19
  770. package/dist/web/routes/workflows.d.ts.map +0 -1
  771. package/dist/web/routes/workflows.js +0 -86
  772. package/dist/web/routes/workflows.js.map +0 -1
  773. package/dist/web/static/assets/AIConfig-R5wZ3ZKT.js +0 -2
  774. package/dist/web/static/assets/AIConfig-R5wZ3ZKT.js.map +0 -1
  775. package/dist/web/static/assets/Agents-Beg34V1g.js +0 -2
  776. package/dist/web/static/assets/Agents-Beg34V1g.js.map +0 -1
  777. package/dist/web/static/assets/CodeBlock--H53gk46.js +0 -2
  778. package/dist/web/static/assets/CodeBlock--H53gk46.js.map +0 -1
  779. package/dist/web/static/assets/Dashboard-Cy1xsj1J.js +0 -2
  780. package/dist/web/static/assets/Dashboard-Cy1xsj1J.js.map +0 -1
  781. package/dist/web/static/assets/Events-mFhXl4zI.js +0 -2
  782. package/dist/web/static/assets/Events-mFhXl4zI.js.map +0 -1
  783. package/dist/web/static/assets/ExecutionTrace-DG901hLR.js +0 -3
  784. package/dist/web/static/assets/ExecutionTrace-DG901hLR.js.map +0 -1
  785. package/dist/web/static/assets/MarkdownRenderer-CCIz1MOz.js +0 -2
  786. package/dist/web/static/assets/MarkdownRenderer-CCIz1MOz.js.map +0 -1
  787. package/dist/web/static/assets/Routing-B7BFLfjh.js +0 -2
  788. package/dist/web/static/assets/Routing-B7BFLfjh.js.map +0 -1
  789. package/dist/web/static/assets/SessionDetail-BT0l4RrK.js +0 -2
  790. package/dist/web/static/assets/SessionDetail-BT0l4RrK.js.map +0 -1
  791. package/dist/web/static/assets/Sessions-C6J_HQ_u.js +0 -2
  792. package/dist/web/static/assets/Sessions-C6J_HQ_u.js.map +0 -1
  793. package/dist/web/static/assets/Skills-4DQWLaTv.js +0 -2
  794. package/dist/web/static/assets/Skills-4DQWLaTv.js.map +0 -1
  795. package/dist/web/static/assets/WorkflowDetail-zhNqUkBE.js +0 -2
  796. package/dist/web/static/assets/WorkflowDetail-zhNqUkBE.js.map +0 -1
  797. package/dist/web/static/assets/Workflows-Btvi-lGw.js +0 -2
  798. package/dist/web/static/assets/Workflows-Btvi-lGw.js.map +0 -1
  799. package/dist/web/static/assets/export-BQQZLaHV.js +0 -4
  800. package/dist/web/static/assets/export-BQQZLaHV.js.map +0 -1
  801. package/dist/web/static/assets/index-Cgr9qMtq.js +0 -3
  802. package/dist/web/static/assets/index-Cgr9qMtq.js.map +0 -1
  803. package/dist/web/static/assets/index-CngWb5gC.css +0 -1
  804. package/dist/web/static/assets/lucide-53bR2rki.js.map +0 -1
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Hook failure buffer queue — at-least-once event delivery
3
+ *
4
+ * When the daemon is not reachable (socket missing / nc fail), hook scripts
5
+ * write the raw event JSON to a per-file queue on disk. On the next daemon
6
+ * startup, `replayQueue` scans the directory and replays every file into
7
+ * SQLiteStorage.
8
+ *
9
+ * Design decisions:
10
+ * - One file per event: avoids concurrent-write collisions across 5 hook processes
11
+ * - File name: <iso-timestamp>-<event_id>.json (sortable + unique)
12
+ * - Dedup: events table PRIMARY KEY; UNIQUE constraint error → silently skip
13
+ * - Capacity: MAX_FILES=500, TTL=7d; overflow → drop oldest; expired → dead-letter
14
+ */
15
+
16
+ import { mkdirSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
17
+ import { readFile } from 'node:fs/promises';
18
+ import { join } from 'node:path';
19
+ import { homedir } from 'node:os';
20
+ import { randomUUID } from 'node:crypto';
21
+ import type { SQLiteStorage } from '../storage/sqlite.js';
22
+ import type { ForgeEvent } from '../types.js';
23
+ import { logger } from '../utils/logger.js';
24
+
25
+ // ── Constants ──────────────────────────────────────────────────────────────
26
+ const FORGE_DIR = join(homedir(), '.claude-forge');
27
+ export const QUEUE_DIR = join(FORGE_DIR, 'queue');
28
+ export const DEAD_DIR = join(FORGE_DIR, 'queue', 'dead');
29
+ export const MAX_FILES = 500;
30
+ export const TTL_DAYS = 7;
31
+
32
+ const TTL_MS = TTL_DAYS * 24 * 60 * 60 * 1000;
33
+
34
+ // ── Helpers ────────────────────────────────────────────────────────────────
35
+
36
+ function ensureDirs(): void {
37
+ mkdirSync(QUEUE_DIR, { recursive: true });
38
+ mkdirSync(DEAD_DIR, { recursive: true });
39
+ }
40
+
41
+ /** List all *.json queue files, sorted oldest-first (by filename). */
42
+ function listQueueFiles(): string[] {
43
+ try {
44
+ return readdirSync(QUEUE_DIR)
45
+ .filter((f) => f.endsWith('.json'))
46
+ .sort(); // ISO timestamp prefix → lexicographic = chronological
47
+ } catch {
48
+ return [];
49
+ }
50
+ }
51
+
52
+ /**
53
+ * pruneQueue — enforce MAX_FILES cap.
54
+ * Deletes the oldest files (smallest filename) until count < MAX_FILES.
55
+ * Called by enqueueEvent before writing a new file.
56
+ */
57
+ export function pruneQueue(): void {
58
+ const files = listQueueFiles();
59
+ if (files.length < MAX_FILES) return;
60
+
61
+ const toDelete = files.slice(0, files.length - MAX_FILES + 1);
62
+ for (const f of toDelete) {
63
+ try {
64
+ rmSync(join(QUEUE_DIR, f));
65
+ } catch (err) {
66
+ logger.warn(`[Queue] Failed to prune ${f}: ${err}`);
67
+ }
68
+ }
69
+ }
70
+
71
+ // ── Public API ─────────────────────────────────────────────────────────────
72
+
73
+ /**
74
+ * enqueueEvent — write a single event to the queue directory.
75
+ *
76
+ * Designed to be called synchronously from hook scripts via a bash child
77
+ * process. This TypeScript version is used by tests; the bash implementation
78
+ * mirrors the same file-naming convention.
79
+ */
80
+ export function enqueueEvent(event: ForgeEvent): void {
81
+ ensureDirs();
82
+ pruneQueue();
83
+
84
+ const eventId = event.event_id ?? randomUUID();
85
+ const ts = new Date().toISOString().replace(/[:.]/g, '-').replace('Z', 'Z');
86
+ const filename = `${ts}-${eventId}.json`;
87
+ const filePath = join(QUEUE_DIR, filename);
88
+
89
+ writeFileSync(filePath, JSON.stringify({ ...event, event_id: eventId }), 'utf8');
90
+ }
91
+
92
+ export interface ReplayResult {
93
+ replayed: number;
94
+ skipped: number;
95
+ dead: number;
96
+ }
97
+
98
+ /**
99
+ * replayQueue — called by daemon after socket starts listening.
100
+ *
101
+ * For each *.json in QUEUE_DIR:
102
+ * - If mtime > TTL → move to dead-letter
103
+ * - Parse JSON → storage.writeEvent()
104
+ * - Success → rm file, replayed++
105
+ * - UNIQUE constraint → rm file, skipped++ (already in DB)
106
+ * - Other error → move to dead-letter, dead++
107
+ * - Bad JSON → move to dead-letter, dead++
108
+ */
109
+ export async function replayQueue(storage: SQLiteStorage): Promise<ReplayResult> {
110
+ ensureDirs();
111
+
112
+ const files = listQueueFiles();
113
+ if (files.length === 0) {
114
+ return { replayed: 0, skipped: 0, dead: 0 };
115
+ }
116
+
117
+ logger.info(`[Queue] Replaying ${files.length} queued event(s)…`);
118
+
119
+ let replayed = 0;
120
+ let skipped = 0;
121
+ let dead = 0;
122
+ const now = Date.now();
123
+
124
+ for (const filename of files) {
125
+ const filePath = join(QUEUE_DIR, filename);
126
+
127
+ // ── TTL check ──────────────────────────────────────────────────────────
128
+ try {
129
+ const stat = statSync(filePath);
130
+ if (now - stat.mtimeMs > TTL_MS) {
131
+ moveToDeadLetter(filePath, filename);
132
+ dead++;
133
+ logger.warn(`[Queue] TTL expired, dead-lettered: ${filename}`);
134
+ continue;
135
+ }
136
+ } catch (err) {
137
+ logger.warn(`[Queue] Cannot stat ${filename}, skipping: ${err}`);
138
+ continue;
139
+ }
140
+
141
+ // ── Read + parse ───────────────────────────────────────────────────────
142
+ let event: ForgeEvent;
143
+ try {
144
+ const raw = await readFile(filePath, 'utf8');
145
+ event = JSON.parse(raw) as ForgeEvent;
146
+ } catch {
147
+ moveToDeadLetter(filePath, filename);
148
+ dead++;
149
+ logger.warn(`[Queue] Bad JSON, dead-lettered: ${filename}`);
150
+ continue;
151
+ }
152
+
153
+ // ── Write to storage ───────────────────────────────────────────────────
154
+ try {
155
+ storage.writeEvent(event);
156
+ rmSync(filePath);
157
+ replayed++;
158
+ } catch (err) {
159
+ const msg = err instanceof Error ? err.message : String(err);
160
+ if (isUniqueConstraintError(msg)) {
161
+ // Already in DB — remove the queue file silently
162
+ try { rmSync(filePath); } catch { /* ignore */ }
163
+ skipped++;
164
+ } else {
165
+ moveToDeadLetter(filePath, filename);
166
+ dead++;
167
+ logger.warn(`[Queue] writeEvent failed, dead-lettered ${filename}: ${msg}`);
168
+ }
169
+ }
170
+ }
171
+
172
+ return { replayed, skipped, dead };
173
+ }
174
+
175
+ // ── Internal helpers ───────────────────────────────────────────────────────
176
+
177
+ function moveToDeadLetter(filePath: string, filename: string): void {
178
+ try {
179
+ mkdirSync(DEAD_DIR, { recursive: true });
180
+ renameSync(filePath, join(DEAD_DIR, filename));
181
+ } catch (err) {
182
+ logger.warn(`[Queue] Failed to move ${filename} to dead-letter: ${err}`);
183
+ try { rmSync(filePath); } catch { /* ignore */ }
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Detect SQLite UNIQUE constraint violations.
189
+ * better-sqlite3 throws: "UNIQUE constraint failed: events.event_id"
190
+ */
191
+ function isUniqueConstraintError(message: string): boolean {
192
+ return message.includes('UNIQUE constraint failed');
193
+ }
@@ -0,0 +1,226 @@
1
+ /**
2
+ * SQLiteBase — 数据库初始化、schema、迁移、辅助
3
+ *
4
+ * 拆分自 sqlite.ts。仅负责:
5
+ * - 打开数据库连接 + 配置 PRAGMA
6
+ * - 加载 schema.sql 或 inline fallback
7
+ * - 增量迁移(hasColumn / addColumnIfMissing)
8
+ * - 一次性 sessions 回填
9
+ *
10
+ * 不涉及业务读写。所有业务读写由 *Operations 类组合 db 实例实现。
11
+ */
12
+
13
+ import Database from 'better-sqlite3';
14
+ import { readFileSync, existsSync, mkdirSync, statSync } from 'node:fs';
15
+ import { dirname, join } from 'node:path';
16
+ import { fileURLToPath } from 'node:url';
17
+ import { DATABASE } from '../constants.js';
18
+ import { logger } from '../utils/logger.js';
19
+
20
+ export class SQLiteBase {
21
+ protected db: Database.Database;
22
+ protected dbPath: string;
23
+
24
+ constructor(dbPath: string) {
25
+ this.dbPath = dbPath;
26
+
27
+ const dir = dirname(dbPath);
28
+ if (!existsSync(dir)) {
29
+ mkdirSync(dir, { recursive: true });
30
+ }
31
+
32
+ this.db = new Database(dbPath);
33
+
34
+ // WAL mode optimization for better concurrency
35
+ this.db.pragma('journal_mode = WAL');
36
+ this.db.pragma('synchronous = NORMAL');
37
+ this.db.pragma('wal_autocheckpoint = 1000'); // Increased from 100 for better performance
38
+ this.db.pragma(`cache_size = ${DATABASE.CACHE_SIZE}`);
39
+ this.db.pragma(`busy_timeout = ${DATABASE.BUSY_TIMEOUT}`);
40
+
41
+ // Additional performance optimizations
42
+ this.db.pragma('temp_store = MEMORY'); // Store temp tables in memory
43
+ this.db.pragma('mmap_size = 30000000000'); // 30GB memory-mapped I/O
44
+ this.db.pragma('page_size = 4096'); // Optimal page size for most systems
45
+
46
+ this.initSchema();
47
+ this.runMigrations();
48
+ }
49
+
50
+ private initSchema(): void {
51
+ const thisDir = dirname(fileURLToPath(import.meta.url));
52
+ const schemaPath = join(thisDir, 'schema.sql');
53
+
54
+ if (existsSync(schemaPath)) {
55
+ const schema = readFileSync(schemaPath, 'utf-8');
56
+ this.db.exec(schema);
57
+ } else {
58
+ // Inline fallback — events table only
59
+ this.db.exec(`
60
+ CREATE TABLE IF NOT EXISTS events (
61
+ event_id TEXT PRIMARY KEY, session_id TEXT NOT NULL,
62
+ project_path TEXT NOT NULL, timestamp TEXT NOT NULL,
63
+ hook_type TEXT NOT NULL, tool_name TEXT, tool_input TEXT,
64
+ tool_output TEXT, user_prompt TEXT, ai_response TEXT,
65
+ distilled INTEGER DEFAULT 0, created_at TEXT DEFAULT (datetime('now'))
66
+ );
67
+ CREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id);
68
+ CREATE INDEX IF NOT EXISTS idx_events_project ON events(project_path);
69
+ CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp DESC);
70
+ CREATE INDEX IF NOT EXISTS idx_events_distilled ON events(distilled);
71
+ `);
72
+ }
73
+ }
74
+
75
+ protected hasColumn(table: string, column: string): boolean {
76
+ try {
77
+ const rows = this.db.prepare(`PRAGMA table_info(${table})`).all() as Array<{ name: string }>;
78
+ return rows.some(r => r.name === column);
79
+ } catch {
80
+ return false;
81
+ }
82
+ }
83
+
84
+ protected addColumnIfMissing(table: string, column: string, definition: string): void {
85
+ if (!this.hasColumn(table, column)) {
86
+ try {
87
+ this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${definition}`);
88
+ logger.info(`[SQLiteStorage] migration: added ${table}.${column}`);
89
+ } catch (err) {
90
+ logger.warn(`[SQLiteStorage] migration: ALTER ${table} ADD ${column} failed: ${err}`);
91
+ }
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Incremental schema migrations. Kept idempotent so daemon restarts are safe.
97
+ */
98
+ private runMigrations(): void {
99
+ this.addColumnIfMissing('sessions', 'first_prompt', 'TEXT');
100
+
101
+ this.addColumnIfMissing('routing_events', 'skill_confidence', 'REAL');
102
+ this.addColumnIfMissing('routing_events', 'skill_source', 'TEXT');
103
+
104
+ this.addColumnIfMissing('skill_invocations', 'workflow', 'TEXT');
105
+ this.addColumnIfMissing('skill_invocations', 'phase', 'TEXT');
106
+ this.addColumnIfMissing('skill_invocations', 'feature_slug', 'TEXT');
107
+ this.addColumnIfMissing('skill_invocations', 'artifact_path', 'TEXT');
108
+
109
+ try {
110
+ this.db.exec(
111
+ `CREATE INDEX IF NOT EXISTS idx_skill_invocations_workflow ON skill_invocations(workflow, phase);
112
+ CREATE INDEX IF NOT EXISTS idx_skill_invocations_feature ON skill_invocations(feature_slug);`,
113
+ );
114
+ } catch (err) {
115
+ logger.warn(`[SQLiteStorage] migration: create skill_invocations workflow indexes failed: ${err}`);
116
+ }
117
+
118
+ try {
119
+ this.db.exec('CREATE INDEX IF NOT EXISTS idx_sessions_start_time ON sessions(start_time DESC)');
120
+ } catch (err) {
121
+ logger.warn(`[SQLiteStorage] migration: create idx_sessions_start_time failed: ${err}`);
122
+ }
123
+
124
+ // Composite indexes for high-frequency session-scoped queries (idempotent)
125
+ try {
126
+ this.db.exec(`
127
+ CREATE INDEX IF NOT EXISTS idx_events_session_ts ON events(session_id, timestamp DESC);
128
+ CREATE INDEX IF NOT EXISTS idx_routing_events_session_ts ON routing_events(session_id, ts DESC);
129
+ CREATE INDEX IF NOT EXISTS idx_skill_invocations_session_ts ON skill_invocations(session_id, timestamp DESC);
130
+ `);
131
+ } catch (err) {
132
+ logger.warn(`[SQLiteStorage] migration: create composite indexes failed: ${err}`);
133
+ }
134
+
135
+ // Phase 1 Refactor: Additional performance indexes (idempotent)
136
+ try {
137
+ this.db.exec(`
138
+ CREATE INDEX IF NOT EXISTS idx_routing_events_obeyed_ts ON routing_events(obeyed, ts DESC);
139
+ CREATE INDEX IF NOT EXISTS idx_events_session_hook ON events(session_id, hook_type, timestamp DESC);
140
+ CREATE INDEX IF NOT EXISTS idx_injections_session_handler ON injections(session_id, source_handler);
141
+ `);
142
+ logger.info('[SQLiteStorage] Phase 1 performance indexes created');
143
+ } catch (err) {
144
+ logger.warn(`[SQLiteStorage] migration: create Phase 1 performance indexes failed: ${err}`);
145
+ }
146
+
147
+ this.backfillSessionsIfNeeded();
148
+
149
+ const deprecatedTables = [
150
+ 'quality_issues', 'distill_results', 'v2_decisions',
151
+ 'v2_tool_events', 'experiment_assignments', 'routing_rule_states',
152
+ ];
153
+ for (const table of deprecatedTables) {
154
+ try { this.db.exec(`DROP TABLE IF EXISTS ${table}`); } catch { /* ignore */ }
155
+ }
156
+ }
157
+
158
+ /**
159
+ * 将 events 表按 session_id 聚合回填到 sessions 表,仅在 sessions 尚未对齐
160
+ * (没有任何一行带 first_prompt)时触发。
161
+ */
162
+ private backfillSessionsIfNeeded(): void {
163
+ const eventsExists = this.db
164
+ .prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='events'`)
165
+ .get();
166
+ if (!eventsExists) return;
167
+
168
+ const aggRow = this.db
169
+ .prepare(`SELECT COUNT(*) AS total, COUNT(first_prompt) AS with_prompt FROM sessions`)
170
+ .get() as { total: number; with_prompt: number };
171
+
172
+ if (aggRow.with_prompt > 0) return;
173
+
174
+ logger.info('[SQLiteStorage] backfilling sessions aggregate from events...');
175
+ try {
176
+ const backfill = this.db.transaction(() => {
177
+ this.db.exec(`
178
+ INSERT INTO sessions (
179
+ session_id, project_path, status, first_prompt,
180
+ start_time, end_time, last_event_time, event_count
181
+ )
182
+ SELECT
183
+ e.session_id,
184
+ e.project_path,
185
+ 'active' AS status,
186
+ (SELECT substr(COALESCE(e2.user_prompt, json_extract(e2.tool_input, '$.user_prompt')), 1, 200)
187
+ FROM events e2
188
+ WHERE e2.session_id = e.session_id
189
+ AND e2.hook_type = 'UserPromptSubmit'
190
+ AND (e2.user_prompt IS NOT NULL OR json_extract(e2.tool_input, '$.user_prompt') IS NOT NULL)
191
+ ORDER BY e2.timestamp ASC LIMIT 1) AS first_prompt,
192
+ MIN(e.timestamp) AS start_time,
193
+ MAX(e.timestamp) AS end_time,
194
+ MAX(e.timestamp) AS last_event_time,
195
+ COUNT(*) AS event_count
196
+ FROM events e
197
+ GROUP BY e.session_id, e.project_path
198
+ ON CONFLICT(session_id) DO UPDATE SET
199
+ first_prompt = COALESCE(sessions.first_prompt, excluded.first_prompt),
200
+ end_time = MAX(COALESCE(sessions.end_time, ''), excluded.end_time),
201
+ last_event_time = MAX(COALESCE(sessions.last_event_time, ''), excluded.last_event_time),
202
+ event_count = excluded.event_count,
203
+ updated_at = datetime('now')
204
+ `);
205
+ });
206
+ backfill();
207
+ const migrated = this.db.prepare('SELECT COUNT(*) AS c FROM sessions').get() as { c: number };
208
+ logger.info(`[SQLiteStorage] backfilled ${migrated.c} sessions from events`);
209
+ } catch (err) {
210
+ logger.warn(`[SQLiteStorage] backfillSessionsIfNeeded failed: ${err}`);
211
+ }
212
+ }
213
+
214
+ getDatabase(): Database.Database {
215
+ return this.db;
216
+ }
217
+
218
+ getDbPath(): string {
219
+ return this.dbPath;
220
+ }
221
+
222
+ protected getDbSizeMb(): number {
223
+ if (!existsSync(this.dbPath)) return 0;
224
+ return statSync(this.dbPath).size / (1024 * 1024);
225
+ }
226
+ }
@@ -0,0 +1,255 @@
1
+ /**
2
+ * EventOperations — 事件读写
3
+ *
4
+ * 拆分自 sqlite.ts。负责 events 表 + 触发 sessions 聚合 upsert。
5
+ *
6
+ * 注意:
7
+ * - writeEvent 会 emit 'event' 事件,因此构造时需要传入 EventEmitter 实例
8
+ * - upsertSession 通过 emitter(即 SQLiteStorage facade)调用,以保持原版本
9
+ * "monkey-patch storage.upsertSession 能拦截 writeEvent 内部调用"的行为
10
+ */
11
+
12
+ import type Database from 'better-sqlite3';
13
+ import { randomUUID } from 'node:crypto';
14
+ import type { EventEmitter } from 'node:events';
15
+ import { z } from 'zod';
16
+ import type { ForgeEvent } from '../types.js';
17
+ import { logger } from '../utils/logger.js';
18
+
19
+ interface UpsertCapableEmitter extends EventEmitter {
20
+ upsertSession(event: ForgeEvent): void;
21
+ }
22
+
23
+ // Zod schema for runtime validation
24
+ const ForgeEventSchema = z.object({
25
+ event_id: z.string().uuid().optional(),
26
+ session_id: z.string().min(1),
27
+ project_path: z.string().min(1),
28
+ timestamp: z.string().min(1),
29
+ hook_type: z.enum([
30
+ 'UserPromptSubmit',
31
+ 'PreToolUse',
32
+ 'PostToolUse',
33
+ 'Stop',
34
+ 'Notification',
35
+ 'HistoryExport',
36
+ ]),
37
+ tool_name: z.string().optional(),
38
+ tool_input: z.unknown().optional(),
39
+ tool_output: z.unknown().optional(),
40
+ user_prompt: z.string().optional(),
41
+ ai_response: z.string().optional(),
42
+ });
43
+
44
+ const DatabaseRowSchema = z.object({
45
+ event_id: z.string(),
46
+ session_id: z.string(),
47
+ project_path: z.string(),
48
+ timestamp: z.string(),
49
+ hook_type: z.string(),
50
+ tool_name: z.union([z.string(), z.null()]),
51
+ tool_input: z.union([z.string(), z.null()]),
52
+ tool_output: z.union([z.string(), z.null()]),
53
+ user_prompt: z.union([z.string(), z.null()]),
54
+ ai_response: z.union([z.string(), z.null()]),
55
+ });
56
+
57
+ interface UpsertCapableEmitter extends EventEmitter {
58
+ upsertSession(event: ForgeEvent): void;
59
+ }
60
+
61
+ export class EventOperations {
62
+ constructor(
63
+ private db: Database.Database,
64
+ private emitter: UpsertCapableEmitter,
65
+ ) {}
66
+
67
+ writeEvent(event: ForgeEvent): void {
68
+ // Runtime validation
69
+ try {
70
+ ForgeEventSchema.parse(event);
71
+ } catch (err) {
72
+ logger.error(`[EventOperations] Invalid event data: ${err}`);
73
+ throw new Error(`Invalid event data: ${err instanceof Error ? err.message : String(err)}`);
74
+ }
75
+
76
+ const eventId = event.event_id || randomUUID();
77
+ const stmt = this.db.prepare(`
78
+ INSERT INTO events (event_id, session_id, project_path, timestamp, hook_type,
79
+ tool_name, tool_input, tool_output, user_prompt, ai_response, distilled)
80
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0)
81
+ `);
82
+
83
+ try {
84
+ stmt.run(
85
+ eventId,
86
+ event.session_id,
87
+ event.project_path,
88
+ event.timestamp,
89
+ event.hook_type,
90
+ event.tool_name ?? null,
91
+ event.tool_input ? JSON.stringify(event.tool_input) : null,
92
+ event.tool_output ? JSON.stringify(event.tool_output) : null,
93
+ event.user_prompt ?? null,
94
+ event.ai_response ?? null,
95
+ );
96
+ } catch (err) {
97
+ const msg = err instanceof Error ? err.message : String(err);
98
+ if (msg.includes('UNIQUE constraint failed')) {
99
+ // Duplicate event_id — expected during queue replay. Caller decides how to handle.
100
+ logger.debug(`[EventOperations] Duplicate event_id ${eventId} (UNIQUE constraint)`);
101
+ } else {
102
+ logger.error(`[EventOperations] Failed to write event: ${err}`);
103
+ }
104
+ throw new Error(`Failed to write event: ${msg}`);
105
+ }
106
+
107
+ // 聚合到 sessions 表。单独 try/catch:一旦聚合失败,不能影响原事件持久化。
108
+ // 通过 facade 调用以保持 monkey-patch 兼容性。
109
+ try {
110
+ this.emitter.upsertSession(event);
111
+ } catch (err) {
112
+ logger.warn(`[SQLiteStorage] upsertSession failed (event still written): ${err}`);
113
+ }
114
+
115
+ this.emitter.emit('event', { ...event, event_id: eventId });
116
+ }
117
+
118
+ queryEvents(filter: {
119
+ session_id?: string;
120
+ project_path?: string;
121
+ hook_type?: string;
122
+ limit?: number;
123
+ offset?: number;
124
+ }): ForgeEvent[] {
125
+ const conditions: string[] = [];
126
+ const params: unknown[] = [];
127
+
128
+ if (filter.session_id) {
129
+ conditions.push('session_id = ?');
130
+ params.push(filter.session_id);
131
+ }
132
+ if (filter.project_path) {
133
+ conditions.push('project_path = ?');
134
+ params.push(filter.project_path);
135
+ }
136
+ if (filter.hook_type) {
137
+ conditions.push('hook_type = ?');
138
+ params.push(filter.hook_type);
139
+ }
140
+
141
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
142
+ const limit = filter.limit ?? 1000;
143
+ const offset = filter.offset ?? 0;
144
+ const sql = `SELECT * FROM events ${where} ORDER BY timestamp DESC LIMIT ? OFFSET ?`;
145
+
146
+ params.push(limit, offset);
147
+ const rows = this.db.prepare(sql).all(...params) as Array<Record<string, unknown>>;
148
+ return rows.map((row) => this.rowToEvent(row));
149
+ }
150
+
151
+ countEvents(filter?: {
152
+ session_id?: string;
153
+ project_path?: string;
154
+ hook_type?: string;
155
+ }): number {
156
+ if (!filter || (!filter.session_id && !filter.project_path && !filter.hook_type)) {
157
+ const result = this.db.prepare('SELECT COUNT(*) as count FROM events').get() as { count: number };
158
+ return result.count;
159
+ }
160
+
161
+ const conditions: string[] = [];
162
+ const params: unknown[] = [];
163
+
164
+ if (filter.session_id) {
165
+ conditions.push('session_id = ?');
166
+ params.push(filter.session_id);
167
+ }
168
+ if (filter.project_path) {
169
+ conditions.push('project_path = ?');
170
+ params.push(filter.project_path);
171
+ }
172
+ if (filter.hook_type) {
173
+ conditions.push('hook_type = ?');
174
+ params.push(filter.hook_type);
175
+ }
176
+
177
+ const where = `WHERE ${conditions.join(' AND ')}`;
178
+ const sql = `SELECT COUNT(*) as count FROM events ${where}`;
179
+ const result = this.db.prepare(sql).all(...params)[0] as { count: number };
180
+ return result.count;
181
+ }
182
+
183
+ /**
184
+ * Server-side event search with filtering.
185
+ * Reduces data transfer by filtering events before sending to client.
186
+ */
187
+ searchEvents(filter: {
188
+ session_id: string;
189
+ query?: string;
190
+ tool_name?: string;
191
+ hook_type?: string;
192
+ limit?: number;
193
+ offset?: number;
194
+ }): ForgeEvent[] {
195
+ const conditions: string[] = ['session_id = ?'];
196
+ const params: unknown[] = [filter.session_id];
197
+
198
+ if (filter.query) {
199
+ conditions.push('(user_prompt LIKE ? OR ai_response LIKE ? OR tool_input LIKE ?)');
200
+ const pattern = `%${filter.query}%`;
201
+ params.push(pattern, pattern, pattern);
202
+ }
203
+ if (filter.tool_name) {
204
+ conditions.push('tool_name = ?');
205
+ params.push(filter.tool_name);
206
+ }
207
+ if (filter.hook_type) {
208
+ conditions.push('hook_type = ?');
209
+ params.push(filter.hook_type);
210
+ }
211
+
212
+ const limit = filter.limit ?? 100;
213
+ const offset = filter.offset ?? 0;
214
+
215
+ const sql = `SELECT * FROM events WHERE ${conditions.join(' AND ')} ORDER BY timestamp DESC LIMIT ? OFFSET ?`;
216
+ params.push(limit, offset);
217
+ const rows = this.db.prepare(sql).all(...params) as Array<Record<string, unknown>>;
218
+ return rows.map((row) => this.rowToEvent(row));
219
+ }
220
+
221
+ rowToEvent(row: Record<string, unknown>): ForgeEvent {
222
+ // Runtime validation of database row
223
+ try {
224
+ const validatedRow = DatabaseRowSchema.parse(row);
225
+
226
+ return {
227
+ event_id: validatedRow.event_id,
228
+ session_id: validatedRow.session_id,
229
+ project_path: validatedRow.project_path,
230
+ timestamp: validatedRow.timestamp,
231
+ hook_type: validatedRow.hook_type as ForgeEvent['hook_type'],
232
+ tool_name: validatedRow.tool_name || undefined,
233
+ tool_input: validatedRow.tool_input ? JSON.parse(validatedRow.tool_input) : undefined,
234
+ tool_output: validatedRow.tool_output ? JSON.parse(validatedRow.tool_output) : undefined,
235
+ user_prompt: validatedRow.user_prompt || undefined,
236
+ ai_response: validatedRow.ai_response || undefined,
237
+ };
238
+ } catch (err) {
239
+ logger.error(`[EventOperations] Invalid database row: ${err}`);
240
+ // Return a safe fallback instead of throwing to prevent query failures
241
+ return {
242
+ event_id: String(row.event_id || 'unknown'),
243
+ session_id: String(row.session_id || 'unknown'),
244
+ project_path: String(row.project_path || ''),
245
+ timestamp: String(row.timestamp || new Date().toISOString()),
246
+ hook_type: 'UserPromptSubmit', // Safe default
247
+ tool_name: undefined,
248
+ tool_input: undefined,
249
+ tool_output: undefined,
250
+ user_prompt: undefined,
251
+ ai_response: undefined,
252
+ };
253
+ }
254
+ }
255
+ }