@winspan/claude-forge 8.51.1 → 8.54.3

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 (409) hide show
  1. package/DEVELOPMENT.md +290 -221
  2. package/README.md +50 -8
  3. package/dist/cli/commands/skills.d.ts.map +1 -1
  4. package/dist/cli/commands/skills.js +121 -2
  5. package/dist/cli/commands/skills.js.map +1 -1
  6. package/dist/cli/init/hook-manager.d.ts +1 -1
  7. package/dist/cli/init/hook-manager.d.ts.map +1 -1
  8. package/dist/cli/init/hook-manager.js +1 -0
  9. package/dist/cli/init/hook-manager.js.map +1 -1
  10. package/dist/core/constants.d.ts +2 -0
  11. package/dist/core/constants.d.ts.map +1 -1
  12. package/dist/core/constants.js +4 -0
  13. package/dist/core/constants.js.map +1 -1
  14. package/dist/core/storage/events.d.ts.map +1 -1
  15. package/dist/core/storage/events.js +0 -1
  16. package/dist/core/storage/events.js.map +1 -1
  17. package/dist/core/storage/maintenance.d.ts +25 -3
  18. package/dist/core/storage/maintenance.d.ts.map +1 -1
  19. package/dist/core/storage/maintenance.js +33 -4
  20. package/dist/core/storage/maintenance.js.map +1 -1
  21. package/dist/core/storage/routing.d.ts +4 -0
  22. package/dist/core/storage/routing.d.ts.map +1 -1
  23. package/dist/core/storage/routing.js +10 -4
  24. package/dist/core/storage/routing.js.map +1 -1
  25. package/dist/core/storage/sessions.d.ts +17 -0
  26. package/dist/core/storage/sessions.d.ts.map +1 -1
  27. package/dist/core/storage/sessions.js +64 -0
  28. package/dist/core/storage/sessions.js.map +1 -1
  29. package/dist/core/storage/skills.d.ts +4 -0
  30. package/dist/core/storage/skills.d.ts.map +1 -1
  31. package/dist/core/storage/skills.js +10 -2
  32. package/dist/core/storage/skills.js.map +1 -1
  33. package/dist/core/storage/sqlite.d.ts +5 -0
  34. package/dist/core/storage/sqlite.d.ts.map +1 -1
  35. package/dist/core/storage/sqlite.js +6 -0
  36. package/dist/core/storage/sqlite.js.map +1 -1
  37. package/dist/core/storage/tasks.d.ts.map +1 -1
  38. package/dist/core/storage/tasks.js +2 -0
  39. package/dist/core/storage/tasks.js.map +1 -1
  40. package/dist/core/types.d.ts +7 -0
  41. package/dist/core/types.d.ts.map +1 -1
  42. package/dist/daemon/index.d.ts.map +1 -1
  43. package/dist/daemon/index.js +30 -5
  44. package/dist/daemon/index.js.map +1 -1
  45. package/dist/daemon/skill-sync.d.ts +21 -0
  46. package/dist/daemon/skill-sync.d.ts.map +1 -0
  47. package/dist/daemon/skill-sync.js +75 -0
  48. package/dist/daemon/skill-sync.js.map +1 -0
  49. package/dist/hooks/notification.sh +1 -1
  50. package/dist/hooks/post-tool-use.sh +1 -1
  51. package/dist/hooks/pre-tool-use.sh +1 -1
  52. package/dist/hooks/stop.sh +1 -1
  53. package/dist/hooks/user-prompt-submit.sh +1 -1
  54. package/dist/skills/official/code-simplifier.md +37 -1
  55. package/dist/skills/official/find-skills.md +120 -1
  56. package/dist/skills/official/official-api-design.md +14 -1
  57. package/dist/skills/official/official-architecture-decision.md +22 -1
  58. package/dist/skills/official/official-db-schema-design.md +19 -1
  59. package/dist/skills/official/official-debug.md +9 -1
  60. package/dist/skills/official/official-pr-review.md +1 -1
  61. package/dist/skills/official/official-security-hardening.md +7 -1
  62. package/dist/skills/official/planning-with-files.md +206 -2
  63. package/dist/skills/official/ui-ux-pro-max.md +88 -1
  64. package/dist/skills/official/webapp-testing.md +85 -1
  65. package/dist/skills/registry.d.ts +1 -1
  66. package/dist/skills/registry.d.ts.map +1 -1
  67. package/dist/skills/registry.js +15 -4
  68. package/dist/skills/registry.js.map +1 -1
  69. package/dist/skills/semantic-matcher.d.ts +4 -3
  70. package/dist/skills/semantic-matcher.d.ts.map +1 -1
  71. package/dist/skills/semantic-matcher.js +20 -22
  72. package/dist/skills/semantic-matcher.js.map +1 -1
  73. package/dist/skills/upgrade-engine.d.ts +93 -0
  74. package/dist/skills/upgrade-engine.d.ts.map +1 -0
  75. package/dist/skills/upgrade-engine.js +447 -0
  76. package/dist/skills/upgrade-engine.js.map +1 -0
  77. package/dist/skills/upgrade-prompt.d.ts +20 -0
  78. package/dist/skills/upgrade-prompt.d.ts.map +1 -0
  79. package/dist/skills/upgrade-prompt.js +75 -0
  80. package/dist/skills/upgrade-prompt.js.map +1 -0
  81. package/dist/web/analytics/weekly-report.d.ts.map +1 -1
  82. package/dist/web/analytics/weekly-report.js +21 -29
  83. package/dist/web/analytics/weekly-report.js.map +1 -1
  84. package/dist/web/routes/patch.d.ts.map +1 -1
  85. package/dist/web/routes/patch.js +32 -2
  86. package/dist/web/routes/patch.js.map +1 -1
  87. package/dist/web/routes/sessions.d.ts.map +1 -1
  88. package/dist/web/routes/sessions.js +9 -7
  89. package/dist/web/routes/sessions.js.map +1 -1
  90. package/dist/web/routes/trace.d.ts.map +1 -1
  91. package/dist/web/routes/trace.js +2 -3
  92. package/dist/web/routes/trace.js.map +1 -1
  93. package/dist/web/server.d.ts.map +1 -1
  94. package/dist/web/server.js +3 -2
  95. package/dist/web/server.js.map +1 -1
  96. package/package.json +12 -2
  97. package/scripts/postinstall.cjs +21 -0
  98. package/.claude/CLAUDE.md +0 -17
  99. package/.eslintrc.js +0 -23
  100. package/.prettierrc +0 -8
  101. package/ARCHITECTURE_ISSUES.md +0 -249
  102. package/CLAUDE.md +0 -265
  103. package/CLAUDE.md.backup +0 -488
  104. package/docs/concurrent-agents.md +0 -129
  105. package/docs/design/architecture-review-20260516.md +0 -232
  106. package/docs/design/fix-skills-data-and-set-leak-spec-20260516-1300.md +0 -219
  107. package/docs/design/h1-storage-aggregation-spec-20260518-1121.md +0 -299
  108. package/docs/design/h2-getdatabase-encapsulation-spec-20260518-1450.md +0 -191
  109. package/docs/design/h3-fallback-removal-spec-20260518-1245.md +0 -76
  110. package/docs/design/h4-index-dedup-spec-20260518-1230.md +0 -109
  111. package/docs/design/h6-services-migration-spec-20260518-1355.md +0 -82
  112. package/docs/design/hook-failure-queue-spec-20260516-1530.md +0 -204
  113. package/docs/design/l1-swarm-protocol-extract-spec-20260518-1605.md +0 -106
  114. package/docs/design/m10-forge-paths-spec-20260518-1320.md +0 -121
  115. package/docs/design/m2-m3-tool-input-spec-20260518-1425.md +0 -131
  116. package/docs/design/m7-routing-event-association-spec-20260518-1545.md +0 -103
  117. package/docs/design/project-path-gitroot-spec-20260518-1715.md +0 -134
  118. package/docs/design/refactor-phase1-spec-20260515-1600.md +0 -543
  119. package/docs/design/refactor-phase2-spec-20260515-1700.md +0 -424
  120. package/docs/design/task-active-gc-spec-20260518-1745.md +0 -146
  121. package/docs/design/tasks-list-filter-pagination-spec-20260518-0930.md +0 -208
  122. package/docs/implementation/fix-skills-data-and-set-leak-changelog-20260516-1300.md +0 -104
  123. package/docs/implementation/h1-storage-aggregation-changelog-20260518-1121.md +0 -82
  124. package/docs/implementation/h2-final-changelog-20260518-1530.md +0 -61
  125. package/docs/implementation/h2-phase1-safety-net-changelog-20260518-1450.md +0 -70
  126. package/docs/implementation/h2-phase2-operations-changelog-20260518-1450.md +0 -120
  127. package/docs/implementation/h2-phase3-callsites-changelog-20260518-1450.md +0 -71
  128. package/docs/implementation/h3-fallback-removal-changelog-20260518-1245.md +0 -71
  129. package/docs/implementation/h4-index-dedup-changelog-20260518-1230.md +0 -60
  130. package/docs/implementation/h6-services-migration-changelog-20260518-1355.md +0 -46
  131. package/docs/implementation/h7-m9-defaults-changelog-20260518-1300.md +0 -46
  132. package/docs/implementation/hook-failure-queue-changelog-20260516-1530.md +0 -196
  133. package/docs/implementation/hotfix-daemon-event-reject-20260516-1430.md +0 -56
  134. package/docs/implementation/l1-swarm-protocol-extract-changelog-20260518-1605.md +0 -45
  135. package/docs/implementation/l3-l4-daemon-perf-changelog-20260518-1410.md +0 -63
  136. package/docs/implementation/l6-l8-final-cleanup-changelog-20260518-1640.md +0 -38
  137. package/docs/implementation/m1-m4-m5-l7-cleanup-changelog-20260518-1310.md +0 -58
  138. package/docs/implementation/m10-forge-paths-changelog-20260518-1320.md +0 -60
  139. package/docs/implementation/m2-m3-tool-input-changelog-20260518-1425.md +0 -43
  140. package/docs/implementation/m6-m8-naming-shutdown-changelog-20260518-1340.md +0 -56
  141. package/docs/implementation/m7-routing-association-changelog-20260518-1545.md +0 -69
  142. package/docs/implementation/project-path-gitroot-changelog-20260518-1715.md +0 -63
  143. package/docs/implementation/refactor-phase1-changelog-20260515-1630.md +0 -354
  144. package/docs/implementation/refactor-phase2-changelog-20260515-1705.md +0 -421
  145. package/docs/implementation/task-active-gc-changelog-20260518-1745.md +0 -35
  146. package/docs/implementation/task-title-summary-changelog-20260518-1130.md +0 -39
  147. package/docs/implementation/tasks-detail-back-loses-filters-changelog-20260518-1100.md +0 -22
  148. package/docs/implementation/tasks-list-filter-pagination-changelog-20260518-0930.md +0 -72
  149. package/docs/implementation/tasks-page-white-screen-hotfix-changelog-20260518-1015.md +0 -56
  150. package/docs/reviews/claudemd-template-sync.md +0 -54
  151. package/docs/reviews/task-title-summary.md +0 -92
  152. package/docs/reviews/tasks-detail-back-loses-filters.md +0 -58
  153. package/docs/reviews/tasks-filter-pagination.md +0 -80
  154. package/docs/reviews/tasks-page-white-screen-hotfix.md +0 -126
  155. package/docs/ruflo-learning-strategy.md +0 -322
  156. package/docs/skills-deduplication-analysis.md +0 -83
  157. package/docs/skills-multiformat-support.md +0 -177
  158. package/docs/skills-third-party.md +0 -183
  159. package/docs/testing/tasks-filter-pagination-test-report.md +0 -86
  160. package/forge +0 -321
  161. package/playwright.config.ts +0 -40
  162. package/scripts/demo-v2.ts +0 -91
  163. package/scripts/dev-daemon.sh +0 -232
  164. package/scripts/dev-web.ts +0 -109
  165. package/scripts/e2e-mcp-link.ts +0 -423
  166. package/scripts/e2e-methodology-quality.ts +0 -253
  167. package/scripts/e2e-routing.ts +0 -456
  168. package/scripts/e2e-user-methodology.ts +0 -326
  169. package/scripts/e2e-web-workflows.ts +0 -299
  170. package/scripts/migrate-legacy-to-dynamic.sql +0 -108
  171. package/scripts/regenerate-execution-docs.ts +0 -116
  172. package/scripts/sync-agent-skills.ts +0 -193
  173. package/scripts/test-hook.sh +0 -71
  174. package/scripts/verify-skill-loading.ts +0 -62
  175. package/src/claudemd/claudemd-generator.ts +0 -568
  176. package/src/claudemd/convention-extractor.ts +0 -69
  177. package/src/claudemd/index.ts +0 -35
  178. package/src/claudemd/persona-manager.ts +0 -88
  179. package/src/claudemd/resume-manager.ts +0 -236
  180. package/src/claudemd/tech-detector.ts +0 -220
  181. package/src/claudemd/templates/swarm-protocol.md +0 -222
  182. package/src/cli/commands/claudemd.ts +0 -84
  183. package/src/cli/commands/config.ts +0 -46
  184. package/src/cli/commands/daemon.ts +0 -310
  185. package/src/cli/commands/executions.ts +0 -115
  186. package/src/cli/commands/init.ts +0 -204
  187. package/src/cli/commands/logs.ts +0 -181
  188. package/src/cli/commands/mcp.ts +0 -242
  189. package/src/cli/commands/menu.ts +0 -357
  190. package/src/cli/commands/skills.ts +0 -185
  191. package/src/cli/commands/stats.ts +0 -73
  192. package/src/cli/commands/status.ts +0 -69
  193. package/src/cli/commands/template.ts +0 -77
  194. package/src/cli/commands/trace.ts +0 -148
  195. package/src/cli/index.ts +0 -42
  196. package/src/cli/init/hook-manager.ts +0 -132
  197. package/src/core/ai/provider.ts +0 -308
  198. package/src/core/ai/types.ts +0 -51
  199. package/src/core/config.ts +0 -124
  200. package/src/core/constants.ts +0 -62
  201. package/src/core/event-fields.ts +0 -32
  202. package/src/core/queue/index.ts +0 -192
  203. package/src/core/storage/base.ts +0 -302
  204. package/src/core/storage/events.ts +0 -434
  205. package/src/core/storage/injections.ts +0 -78
  206. package/src/core/storage/maintenance.ts +0 -59
  207. package/src/core/storage/migrations/002_add_skill_tracking.sql +0 -6
  208. package/src/core/storage/migrations/003_add_skill_invocations.sql +0 -23
  209. package/src/core/storage/performance-indexes.sql +0 -23
  210. package/src/core/storage/routing.ts +0 -322
  211. package/src/core/storage/rows.ts +0 -112
  212. package/src/core/storage/schema.sql +0 -224
  213. package/src/core/storage/sessions.ts +0 -168
  214. package/src/core/storage/skills.ts +0 -233
  215. package/src/core/storage/sqlite.ts +0 -293
  216. package/src/core/storage/tasks.ts +0 -318
  217. package/src/core/storage/token-usage.ts +0 -93
  218. package/src/core/types.ts +0 -181
  219. package/src/core/utils/error-handler.ts +0 -257
  220. package/src/core/utils/forge-resume-block.ts +0 -74
  221. package/src/core/utils/format.ts +0 -69
  222. package/src/core/utils/git.ts +0 -23
  223. package/src/core/utils/logger.ts +0 -134
  224. package/src/core/utils/lru-cache.ts +0 -54
  225. package/src/core/utils/path.ts +0 -19
  226. package/src/core/utils/session.ts +0 -26
  227. package/src/core/utils/time.ts +0 -37
  228. package/src/core/utils/token-tracker.ts +0 -97
  229. package/src/daemon/event-parser.ts +0 -36
  230. package/src/daemon/handlers/history-exporter.ts +0 -117
  231. package/src/daemon/handlers/post-tool-use.ts +0 -54
  232. package/src/daemon/handlers/stop.ts +0 -208
  233. package/src/daemon/handlers/user-prompt.ts +0 -178
  234. package/src/daemon/hook-sync.ts +0 -91
  235. package/src/daemon/index.ts +0 -302
  236. package/src/daemon/launchd/com.claude-forge.daemon.plist.template +0 -47
  237. package/src/daemon/launchd-installer.ts +0 -260
  238. package/src/daemon/lifecycle.ts +0 -128
  239. package/src/daemon/router.ts +0 -40
  240. package/src/daemon/server.ts +0 -196
  241. package/src/daemon/services/task-segmenter.ts +0 -112
  242. package/src/hooks/hook-lib.sh +0 -118
  243. package/src/hooks/notification.sh +0 -35
  244. package/src/hooks/post-tool-use.sh +0 -61
  245. package/src/hooks/pre-tool-use.sh +0 -63
  246. package/src/hooks/stop.sh +0 -43
  247. package/src/hooks/user-prompt-submit.sh +0 -69
  248. package/src/mcp/server.ts +0 -322
  249. package/src/skills/index.ts +0 -2
  250. package/src/skills/invocation-guard.ts +0 -177
  251. package/src/skills/matcher.ts +0 -148
  252. package/src/skills/official/code-simplifier.md +0 -16
  253. package/src/skills/official/find-skills.md +0 -23
  254. package/src/skills/official/official-api-design.md +0 -17
  255. package/src/skills/official/official-architecture-decision.md +0 -20
  256. package/src/skills/official/official-bmad.md +0 -118
  257. package/src/skills/official/official-db-schema-design.md +0 -16
  258. package/src/skills/official/official-debug.md +0 -17
  259. package/src/skills/official/official-doc-driven.md +0 -31
  260. package/src/skills/official/official-harness-engineering.md +0 -108
  261. package/src/skills/official/official-performance-optimization.md +0 -30
  262. package/src/skills/official/official-pr-review.md +0 -35
  263. package/src/skills/official/official-release-checklist.md +0 -30
  264. package/src/skills/official/official-security-hardening.md +0 -26
  265. package/src/skills/official/official-spec-driven-design.md +0 -31
  266. package/src/skills/official/planning-with-files.md +0 -37
  267. package/src/skills/official/ui-ux-pro-max.md +0 -18
  268. package/src/skills/official/webapp-testing.md +0 -12
  269. package/src/skills/official-skills.ts +0 -89
  270. package/src/skills/registry.ts +0 -355
  271. package/src/skills/semantic-matcher.ts +0 -231
  272. package/src/skills/tools/pipeline-suggest.ts +0 -226
  273. package/src/skills/tools/skill-invoke.ts +0 -168
  274. package/src/skills/tools/skill-list.ts +0 -59
  275. package/src/templates/go.yaml +0 -53
  276. package/src/templates/python.yaml +0 -59
  277. package/src/templates/react.yaml +0 -55
  278. package/src/templates/template-manager.ts +0 -170
  279. package/src/web/analytics/anti-pattern-detector.ts +0 -367
  280. package/src/web/analytics/drift-detector.ts +0 -219
  281. package/src/web/analytics/weekly-report.ts +0 -431
  282. package/src/web/auth-middleware.ts +0 -54
  283. package/src/web/routes/_helpers.ts +0 -34
  284. package/src/web/routes/ai.ts +0 -204
  285. package/src/web/routes/auth.ts +0 -22
  286. package/src/web/routes/drift.ts +0 -25
  287. package/src/web/routes/error-handler.ts +0 -120
  288. package/src/web/routes/events.ts +0 -47
  289. package/src/web/routes/insights.ts +0 -43
  290. package/src/web/routes/patch.ts +0 -117
  291. package/src/web/routes/reports.ts +0 -34
  292. package/src/web/routes/rules.ts +0 -76
  293. package/src/web/routes/sessions.ts +0 -250
  294. package/src/web/routes/skill-stats.ts +0 -92
  295. package/src/web/routes/skills.ts +0 -350
  296. package/src/web/routes/static.ts +0 -67
  297. package/src/web/routes/stats.ts +0 -50
  298. package/src/web/routes/status.ts +0 -30
  299. package/src/web/routes/tasks.ts +0 -193
  300. package/src/web/routes/token-usage.ts +0 -20
  301. package/src/web/routes/trace.ts +0 -126
  302. package/src/web/routes/types.ts +0 -57
  303. package/src/web/server.ts +0 -134
  304. package/src/web/ssrf-guard.ts +0 -112
  305. package/src/web/static/index.html +0 -3251
  306. package/src/web/static/vendor/chart.umd.min.js +0 -20
  307. package/tests/e2e/dashboard.spec.ts +0 -205
  308. package/tests/e2e/routing-skill-e2e.test.ts +0 -39
  309. package/tests/helpers/mock-ai.ts +0 -92
  310. package/tests/helpers/mock-storage.ts +0 -159
  311. package/tests/integration/claudemd-generator.test.ts +0 -90
  312. package/tests/integration/queue-replay.integration.test.ts +0 -193
  313. package/tests/integration/tasks-filter.integration.test.ts +0 -154
  314. package/tests/integration/web-analytics.integration.test.ts +0 -133
  315. package/tests/integration/web-stats.integration.test.ts +0 -135
  316. package/tests/integration/web-trace.integration.test.ts +0 -175
  317. package/tests/performance/database.benchmark.ts +0 -161
  318. package/tests/semantic-matcher.test.ts +0 -99
  319. package/tests/skill-matcher.test.ts +0 -110
  320. package/tests/unit/ai-provider-retry.test.ts +0 -194
  321. package/tests/unit/ai-provider-vision.test.ts +0 -224
  322. package/tests/unit/claudemd-generator.test.ts +0 -68
  323. package/tests/unit/cli-mcp.test.ts +0 -141
  324. package/tests/unit/core/forge-paths.test.ts +0 -99
  325. package/tests/unit/daemon/hook-sync.test.ts +0 -71
  326. package/tests/unit/daemon/post-tool-use.test.ts +0 -121
  327. package/tests/unit/daemon/stop-handler-behavior-summary.test.ts +0 -202
  328. package/tests/unit/daemon/task-segmenter-recover.test.ts +0 -84
  329. package/tests/unit/event-fields.test.ts +0 -88
  330. package/tests/unit/event-parser.test.ts +0 -55
  331. package/tests/unit/handlers.test.ts +0 -171
  332. package/tests/unit/hooks/resolve-project-path.test.ts +0 -122
  333. package/tests/unit/invocation-guard.test.ts +0 -125
  334. package/tests/unit/queue.test.ts +0 -272
  335. package/tests/unit/router.test.ts +0 -138
  336. package/tests/unit/security.test.ts +0 -128
  337. package/tests/unit/skill-invocations-workflow.test.ts +0 -495
  338. package/tests/unit/skill-registry.test.ts +0 -94
  339. package/tests/unit/skills/invocation-guard-ttl.test.ts +0 -211
  340. package/tests/unit/skills/official-skills-loader.test.ts +0 -126
  341. package/tests/unit/skills/registry-multiformat.test.ts +0 -92
  342. package/tests/unit/socket-server.test.ts +0 -183
  343. package/tests/unit/storage/event-operations-aggregates.test.ts +0 -342
  344. package/tests/unit/storage/migration-idempotent.test.ts +0 -304
  345. package/tests/unit/storage/routing-aggregates.test.ts +0 -276
  346. package/tests/unit/storage/routing.test.ts +0 -117
  347. package/tests/unit/storage/schema-missing.test.ts +0 -81
  348. package/tests/unit/storage/session-operations-aggregates.test.ts +0 -120
  349. package/tests/unit/storage/sessions-aggregate.test.ts +0 -435
  350. package/tests/unit/storage/skill-operations-counts.test.ts +0 -106
  351. package/tests/unit/storage/skills-aggregates.test.ts +0 -104
  352. package/tests/unit/storage/sqlite-refactor-harness.test.ts +0 -314
  353. package/tests/unit/storage/task-operations-counts.test.ts +0 -46
  354. package/tests/unit/storage/tasks-getById.test.ts +0 -343
  355. package/tests/unit/storage/tasks-stale-gc.test.ts +0 -86
  356. package/tests/unit/storage.test.ts +0 -172
  357. package/tests/unit/token-usage.test.ts +0 -144
  358. package/tests/unit/type-guards.test.ts +0 -201
  359. package/tests/unit/utils/format.test.ts +0 -189
  360. package/tests/unit/utils/session.test.ts +0 -89
  361. package/tests/unit/utils/time.test.ts +0 -112
  362. package/tests/unit/web/navigation-back-contract.test.ts +0 -134
  363. package/tests/unit/web/routes-auth.test.ts +0 -93
  364. package/tests/unit/web/routes-events.test.ts +0 -101
  365. package/tests/unit/web/routes-rules.test.ts +0 -182
  366. package/tests/unit/web/routes-sessions.test.ts +0 -181
  367. package/tests/unit/web/routes-skill-stats.test.ts +0 -179
  368. package/tests/unit/web/routes-stats.test.ts +0 -92
  369. package/tests/unit/web/routes-tasks.test.ts +0 -385
  370. package/tests/unit/web/task-title-contract.test.ts +0 -210
  371. package/tests/unit/web/tasks-component-contract.test.ts +0 -179
  372. package/tsconfig.json +0 -22
  373. package/vitest.config.ts +0 -21
  374. package/vitest.integration.config.ts +0 -16
  375. package/web/CLAUDE.md +0 -20
  376. package/web/index.html +0 -13
  377. package/web/package-lock.json +0 -4854
  378. package/web/package.json +0 -35
  379. package/web/postcss.config.js +0 -6
  380. package/web/src/App.tsx +0 -110
  381. package/web/src/components/CodeBlock.tsx +0 -31
  382. package/web/src/components/Confirm.tsx +0 -96
  383. package/web/src/components/Drawer.tsx +0 -60
  384. package/web/src/components/Layout.tsx +0 -145
  385. package/web/src/components/MarkdownRenderer.tsx +0 -77
  386. package/web/src/components/SearchInput.tsx +0 -31
  387. package/web/src/components/SessionDetailContent.tsx +0 -157
  388. package/web/src/components/Toast.tsx +0 -92
  389. package/web/src/index.css +0 -19
  390. package/web/src/main.tsx +0 -31
  391. package/web/src/pages/AIConfig.tsx +0 -233
  392. package/web/src/pages/Dashboard.tsx +0 -572
  393. package/web/src/pages/Events.tsx +0 -271
  394. package/web/src/pages/Reports.tsx +0 -428
  395. package/web/src/pages/SessionDetail.tsx +0 -162
  396. package/web/src/pages/Sessions.tsx +0 -205
  397. package/web/src/pages/Skills.tsx +0 -180
  398. package/web/src/pages/TaskDetail.tsx +0 -515
  399. package/web/src/pages/Tasks.tsx +0 -415
  400. package/web/src/utils/auth.ts +0 -59
  401. package/web/src/utils/export.ts +0 -54
  402. package/web/src/utils/navigation.ts +0 -25
  403. package/web/src/utils/task-title.ts +0 -49
  404. package/web/src/utils/time.ts +0 -13
  405. package/web/tailwind.config.js +0 -11
  406. package/web/tsconfig.json +0 -21
  407. package/web/tsconfig.node.json +0 -10
  408. package/web/vite.config.ts +0 -76
  409. package/winspan-claude-forge-8.43.0.tgz +0 -0
@@ -1,276 +0,0 @@
1
- /**
2
- * H1 storage aggregates: routing_events
3
- *
4
- * 覆盖:
5
- * - 空表返回 0
6
- * - 混合 obeyed=1/0/NULL 的计数
7
- * - since_ts 过滤边界
8
- * - by_agent 排除 obeyed != 1 / routed_to_name IS NULL
9
- * - by_skill_routed 只统计 routed_to_type='skill'
10
- * - aggregateRoutingTrendByDay 跨日聚合(UTC)
11
- * - ts <= 0 兜底(不应崩,不进入 trend 结果)
12
- * - project_path 过滤
13
- */
14
-
15
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
16
- import { mkdtempSync, rmSync } from 'node:fs';
17
- import { tmpdir } from 'node:os';
18
- import { join } from 'node:path';
19
- import { SQLiteStorage } from '../../../src/core/storage/sqlite.js';
20
-
21
- const SESSION = 'sess-r';
22
- const PROJECT = '/tmp/p';
23
-
24
- describe('storage.aggregateRoutingStats', () => {
25
- let tmp: string;
26
- let storage: SQLiteStorage;
27
-
28
- beforeEach(() => {
29
- tmp = mkdtempSync(join(tmpdir(), 'forge-h1-routing-'));
30
- storage = new SQLiteStorage(join(tmp, 'data.db'));
31
- });
32
-
33
- afterEach(() => {
34
- try { storage.close(); } catch { /* ignore */ }
35
- rmSync(tmp, { recursive: true, force: true });
36
- });
37
-
38
- it('空表返回全 0 / 空数组', () => {
39
- const r = storage.aggregateRoutingStats({ since_ts: 0 });
40
- expect(r.total).toBe(0);
41
- expect(r.obeyed).toBe(0);
42
- expect(r.refused).toBe(0);
43
- expect(r.unknown).toBe(0);
44
- expect(r.by_type).toEqual([]);
45
- expect(r.by_agent).toEqual([]);
46
- expect(r.by_skill_routed).toEqual([]);
47
- });
48
-
49
- it('混合 obeyed=1/0/NULL 的计数正确', () => {
50
- const now = Date.now();
51
- storage.writeRoutingEvent({
52
- session_id: SESSION, project_path: PROJECT, ts: now,
53
- prompt: 'p1', intent_json: '{}',
54
- routed_to_type: 'agent', routed_to_name: 'researcher', obeyed: 1,
55
- });
56
- storage.writeRoutingEvent({
57
- session_id: SESSION, project_path: PROJECT, ts: now,
58
- prompt: 'p2', intent_json: '{}',
59
- routed_to_type: 'agent', routed_to_name: 'coder', obeyed: 0,
60
- });
61
- storage.writeRoutingEvent({
62
- session_id: SESSION, project_path: PROJECT, ts: now,
63
- prompt: 'p3', intent_json: '{}',
64
- routed_to_type: 'skill', routed_to_name: 'tdd', obeyed: null,
65
- });
66
-
67
- const r = storage.aggregateRoutingStats({ since_ts: 0 });
68
- expect(r.total).toBe(3);
69
- expect(r.obeyed).toBe(1);
70
- expect(r.refused).toBe(1);
71
- expect(r.unknown).toBe(1);
72
- });
73
-
74
- it('by_type 分布(含 NULL → "none")', () => {
75
- const now = Date.now();
76
- storage.writeRoutingEvent({
77
- session_id: SESSION, project_path: PROJECT, ts: now,
78
- prompt: 'a', intent_json: '{}', routed_to_type: 'agent', routed_to_name: 'x',
79
- });
80
- storage.writeRoutingEvent({
81
- session_id: SESSION, project_path: PROJECT, ts: now,
82
- prompt: 'b', intent_json: '{}', routed_to_type: 'skill', routed_to_name: 'y',
83
- });
84
- storage.writeRoutingEvent({
85
- session_id: SESSION, project_path: PROJECT, ts: now,
86
- prompt: 'c', intent_json: '{}', routed_to_type: null, routed_to_name: null,
87
- });
88
-
89
- const r = storage.aggregateRoutingStats({ since_ts: 0 });
90
- const map = Object.fromEntries(r.by_type.map(e => [e.type, e.count]));
91
- expect(map.agent).toBe(1);
92
- expect(map.skill).toBe(1);
93
- expect(map.none).toBe(1);
94
- });
95
-
96
- it('by_agent 仅含 obeyed=1 且 routed_to_name 非 NULL', () => {
97
- const now = Date.now();
98
- storage.writeRoutingEvent({
99
- session_id: SESSION, project_path: PROJECT, ts: now,
100
- prompt: 'p1', intent_json: '{}',
101
- routed_to_type: 'agent', routed_to_name: 'researcher', obeyed: 1,
102
- });
103
- storage.writeRoutingEvent({
104
- session_id: SESSION, project_path: PROJECT, ts: now,
105
- prompt: 'p2', intent_json: '{}',
106
- routed_to_type: 'agent', routed_to_name: 'researcher', obeyed: 1,
107
- });
108
- // obeyed = 0 → 不计入 by_agent
109
- storage.writeRoutingEvent({
110
- session_id: SESSION, project_path: PROJECT, ts: now,
111
- prompt: 'p3', intent_json: '{}',
112
- routed_to_type: 'agent', routed_to_name: 'tester', obeyed: 0,
113
- });
114
- // obeyed = NULL → 不计入 by_agent
115
- storage.writeRoutingEvent({
116
- session_id: SESSION, project_path: PROJECT, ts: now,
117
- prompt: 'p4', intent_json: '{}',
118
- routed_to_type: 'agent', routed_to_name: 'planner', obeyed: null,
119
- });
120
- // routed_to_name NULL → 不计入
121
- storage.writeRoutingEvent({
122
- session_id: SESSION, project_path: PROJECT, ts: now,
123
- prompt: 'p5', intent_json: '{}',
124
- routed_to_type: null, routed_to_name: null, obeyed: 1,
125
- });
126
-
127
- const r = storage.aggregateRoutingStats({ since_ts: 0 });
128
- expect(r.by_agent).toEqual([{ agent: 'researcher', count: 2 }]);
129
- });
130
-
131
- it('by_skill_routed 仅含 routed_to_type=skill 且 routed_to_name 非 NULL', () => {
132
- const now = Date.now();
133
- storage.writeRoutingEvent({
134
- session_id: SESSION, project_path: PROJECT, ts: now,
135
- prompt: 'p1', intent_json: '{}',
136
- routed_to_type: 'skill', routed_to_name: 'official-tdd',
137
- });
138
- storage.writeRoutingEvent({
139
- session_id: SESSION, project_path: PROJECT, ts: now,
140
- prompt: 'p2', intent_json: '{}',
141
- routed_to_type: 'skill', routed_to_name: 'official-tdd',
142
- });
143
- storage.writeRoutingEvent({
144
- session_id: SESSION, project_path: PROJECT, ts: now,
145
- prompt: 'p3', intent_json: '{}',
146
- routed_to_type: 'skill', routed_to_name: 'debug',
147
- });
148
- // agent 不计入
149
- storage.writeRoutingEvent({
150
- session_id: SESSION, project_path: PROJECT, ts: now,
151
- prompt: 'p4', intent_json: '{}',
152
- routed_to_type: 'agent', routed_to_name: 'researcher',
153
- });
154
-
155
- const r = storage.aggregateRoutingStats({ since_ts: 0 });
156
- expect(r.by_skill_routed).toEqual([
157
- { skill: 'official-tdd', count: 2 },
158
- { skill: 'debug', count: 1 },
159
- ]);
160
- });
161
-
162
- it('since_ts 严格 >= 边界(等于通过 / 小于排除)', () => {
163
- storage.writeRoutingEvent({
164
- session_id: SESSION, project_path: PROJECT, ts: 1000,
165
- prompt: 'old', intent_json: '{}',
166
- routed_to_type: 'agent', routed_to_name: 'a', obeyed: 1,
167
- });
168
- storage.writeRoutingEvent({
169
- session_id: SESSION, project_path: PROJECT, ts: 2000,
170
- prompt: 'mid', intent_json: '{}',
171
- routed_to_type: 'agent', routed_to_name: 'a', obeyed: 1,
172
- });
173
-
174
- const r1 = storage.aggregateRoutingStats({ since_ts: 2000 });
175
- expect(r1.total).toBe(1); // 仅 ts=2000
176
-
177
- const r2 = storage.aggregateRoutingStats({ since_ts: 1500 });
178
- expect(r2.total).toBe(1); // 仅 ts=2000
179
-
180
- const r3 = storage.aggregateRoutingStats({ since_ts: 1000 });
181
- expect(r3.total).toBe(2);
182
- });
183
-
184
- it('project_path 过滤', () => {
185
- const now = Date.now();
186
- storage.writeRoutingEvent({
187
- session_id: SESSION, project_path: '/proj/a', ts: now,
188
- prompt: 'pa', intent_json: '{}', routed_to_type: 'agent', routed_to_name: 'x', obeyed: 1,
189
- });
190
- storage.writeRoutingEvent({
191
- session_id: SESSION, project_path: '/proj/b', ts: now,
192
- prompt: 'pb', intent_json: '{}', routed_to_type: 'agent', routed_to_name: 'y', obeyed: 1,
193
- });
194
-
195
- const r = storage.aggregateRoutingStats({ since_ts: 0, project_path: '/proj/a' });
196
- expect(r.total).toBe(1);
197
- expect(r.by_agent).toEqual([{ agent: 'x', count: 1 }]);
198
- });
199
-
200
- it('ts <= 0 被 WHERE ts > 0 兜底排除', () => {
201
- storage.writeRoutingEvent({
202
- session_id: SESSION, project_path: PROJECT, ts: 0,
203
- prompt: 'zero', intent_json: '{}',
204
- routed_to_type: 'agent', routed_to_name: 'a', obeyed: 1,
205
- });
206
- storage.writeRoutingEvent({
207
- session_id: SESSION, project_path: PROJECT, ts: 1000,
208
- prompt: 'ok', intent_json: '{}',
209
- routed_to_type: 'agent', routed_to_name: 'a', obeyed: 1,
210
- });
211
-
212
- const r = storage.aggregateRoutingStats({ since_ts: 0 });
213
- // ts=0 被排除
214
- expect(r.total).toBe(1);
215
- });
216
- });
217
-
218
- describe('storage.aggregateRoutingTrendByDay', () => {
219
- let tmp: string;
220
- let storage: SQLiteStorage;
221
-
222
- beforeEach(() => {
223
- tmp = mkdtempSync(join(tmpdir(), 'forge-h1-trend-'));
224
- storage = new SQLiteStorage(join(tmp, 'data.db'));
225
- });
226
-
227
- afterEach(() => {
228
- try { storage.close(); } catch { /* ignore */ }
229
- rmSync(tmp, { recursive: true, force: true });
230
- });
231
-
232
- it('空表返回 []', () => {
233
- expect(storage.aggregateRoutingTrendByDay({ since_ts: 0 })).toEqual([]);
234
- });
235
-
236
- it('跨日聚合(UTC),返回每日 total + skill 计数', () => {
237
- // 2026-01-01T00:00:01Z, 01-01T23:00:00Z, 01-02T00:00:01Z
238
- const day1 = Date.UTC(2026, 0, 1, 0, 0, 1); // 2026-01-01
239
- const day1b = Date.UTC(2026, 0, 1, 23, 0, 0); // 2026-01-01
240
- const day2 = Date.UTC(2026, 0, 2, 0, 0, 1); // 2026-01-02
241
-
242
- storage.writeRoutingEvent({
243
- session_id: SESSION, project_path: PROJECT, ts: day1,
244
- prompt: 'a', intent_json: '{}', routed_to_type: 'agent', routed_to_name: 'x',
245
- });
246
- storage.writeRoutingEvent({
247
- session_id: SESSION, project_path: PROJECT, ts: day1b,
248
- prompt: 'b', intent_json: '{}', routed_to_type: 'skill', routed_to_name: 'y',
249
- });
250
- storage.writeRoutingEvent({
251
- session_id: SESSION, project_path: PROJECT, ts: day2,
252
- prompt: 'c', intent_json: '{}', routed_to_type: 'skill', routed_to_name: 'y',
253
- });
254
-
255
- const trend = storage.aggregateRoutingTrendByDay({ since_ts: 0 });
256
- expect(trend).toHaveLength(2);
257
- expect(trend[0]).toEqual({ day: '2026-01-01', total: 2, skill: 1 });
258
- expect(trend[1]).toEqual({ day: '2026-01-02', total: 1, skill: 1 });
259
- });
260
-
261
- it('ts <= 0 不出现在结果中(strftime 兜底)', () => {
262
- storage.writeRoutingEvent({
263
- session_id: SESSION, project_path: PROJECT, ts: 0,
264
- prompt: 'zero', intent_json: '{}', routed_to_type: 'agent', routed_to_name: 'x',
265
- });
266
- storage.writeRoutingEvent({
267
- session_id: SESSION, project_path: PROJECT, ts: Date.UTC(2026, 0, 1, 0, 0, 0),
268
- prompt: 'ok', intent_json: '{}', routed_to_type: 'skill', routed_to_name: 'y',
269
- });
270
-
271
- const trend = storage.aggregateRoutingTrendByDay({ since_ts: 0 });
272
- expect(trend.every(t => t.day !== null && t.day.length > 0)).toBe(true);
273
- expect(trend).toHaveLength(1);
274
- expect(trend[0].day).toBe('2026-01-01');
275
- });
276
- });
@@ -1,117 +0,0 @@
1
- /**
2
- * M7: getRecentPendingRoutingEvent semantics.
3
- *
4
- * Verifies the "pending only" filter that the PostToolUseHandler relies on
5
- * to associate Agent invocations with the still-unfilled routing_event from
6
- * the current user prompt (rather than re-overwriting a completed one).
7
- */
8
-
9
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
10
- import { mkdtempSync, rmSync } from 'node:fs';
11
- import { tmpdir } from 'node:os';
12
- import { join } from 'node:path';
13
- import { SQLiteStorage } from '../../../src/core/storage/sqlite.js';
14
-
15
- const PROJECT = '/tmp/m7-proj';
16
-
17
- describe('storage.getRecentPendingRoutingEvent', () => {
18
- let tmp: string;
19
- let storage: SQLiteStorage;
20
-
21
- beforeEach(() => {
22
- tmp = mkdtempSync(join(tmpdir(), 'forge-m7-routing-'));
23
- storage = new SQLiteStorage(join(tmp, 'data.db'));
24
- });
25
-
26
- afterEach(() => {
27
- try { storage.close(); } catch { /* ignore */ }
28
- rmSync(tmp, { recursive: true, force: true });
29
- });
30
-
31
- it('case 1: older obeyed=1 + newer pending → returns newer pending', () => {
32
- const sess = 'sess-1';
33
- const oldId = storage.writeRoutingEvent({
34
- session_id: sess, project_path: PROJECT, ts: 1000,
35
- prompt: 'old', intent_json: '{}',
36
- routed_to_type: 'agent', routed_to_name: 'a', obeyed: 1,
37
- });
38
- const newId = storage.writeRoutingEvent({
39
- session_id: sess, project_path: PROJECT, ts: 2000,
40
- prompt: 'new', intent_json: '{}',
41
- obeyed: null,
42
- });
43
-
44
- const recent = storage.getRecentPendingRoutingEvent(sess);
45
- expect(recent).not.toBeNull();
46
- expect(recent!.id).toBe(newId);
47
- expect(recent!.id).not.toBe(oldId);
48
- });
49
-
50
- it('case 2: all events obeyed=1 → returns null', () => {
51
- const sess = 'sess-2';
52
- storage.writeRoutingEvent({
53
- session_id: sess, project_path: PROJECT, ts: 1000,
54
- prompt: 'p1', intent_json: '{}',
55
- routed_to_type: 'agent', routed_to_name: 'a', obeyed: 1,
56
- });
57
- storage.writeRoutingEvent({
58
- session_id: sess, project_path: PROJECT, ts: 2000,
59
- prompt: 'p2', intent_json: '{}',
60
- routed_to_type: 'agent', routed_to_name: 'b', obeyed: 1,
61
- });
62
-
63
- expect(storage.getRecentPendingRoutingEvent(sess)).toBeNull();
64
- });
65
-
66
- it('case 3: session isolation — pending in another session is ignored', () => {
67
- const sessA = 'sess-A';
68
- const sessB = 'sess-B';
69
- const aId = storage.writeRoutingEvent({
70
- session_id: sessA, project_path: PROJECT, ts: 1000,
71
- prompt: 'a-pending', intent_json: '{}',
72
- obeyed: null,
73
- });
74
- storage.writeRoutingEvent({
75
- session_id: sessB, project_path: PROJECT, ts: 2000,
76
- prompt: 'b-pending', intent_json: '{}',
77
- obeyed: null,
78
- });
79
-
80
- const rA = storage.getRecentPendingRoutingEvent(sessA);
81
- expect(rA).not.toBeNull();
82
- expect(rA!.id).toBe(aId);
83
-
84
- // Session with no rows at all returns null.
85
- expect(storage.getRecentPendingRoutingEvent('sess-none')).toBeNull();
86
- });
87
-
88
- it('case 4: single pending row → returns that row', () => {
89
- const sess = 'sess-4';
90
- const id = storage.writeRoutingEvent({
91
- session_id: sess, project_path: PROJECT, ts: 1234,
92
- prompt: 'only', intent_json: '{}',
93
- obeyed: null,
94
- });
95
-
96
- const recent = storage.getRecentPendingRoutingEvent(sess);
97
- expect(recent).not.toBeNull();
98
- expect(recent!.id).toBe(id);
99
- });
100
-
101
- it('legacy getRecentRoutingEvent still returns most-recent regardless of obeyed', () => {
102
- const sess = 'sess-legacy';
103
- storage.writeRoutingEvent({
104
- session_id: sess, project_path: PROJECT, ts: 1000,
105
- prompt: 'old-pending', intent_json: '{}', obeyed: null,
106
- });
107
- const newerCompletedId = storage.writeRoutingEvent({
108
- session_id: sess, project_path: PROJECT, ts: 2000,
109
- prompt: 'new-completed', intent_json: '{}',
110
- routed_to_type: 'agent', routed_to_name: 'x', obeyed: 1,
111
- });
112
-
113
- const recent = storage.getRecentRoutingEvent(sess);
114
- expect(recent).not.toBeNull();
115
- expect(recent!.id).toBe(newerCompletedId);
116
- });
117
- });
@@ -1,81 +0,0 @@
1
- /**
2
- * H3: schema.sql 缺失时 fail-fast
3
- *
4
- * 覆盖 base.ts::initSchema 的两个分支:
5
- * 1. schema.sql 存在 → 正常初始化 8 张表
6
- * 2. schema.sql 缺失 → 立即抛 Error,不再走旧 inline fallback(events 表不会被创建)
7
- *
8
- * case 2 的实现:用 vi.doMock + 动态 import,对 'node:fs' 的 existsSync 做选择性 mock —
9
- * 只让 schema.sql 路径返回 false,其他路径保留真实行为(避免破坏 mkdirSync 的目录检查)。
10
- */
11
-
12
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
13
- import { mkdtempSync, rmSync } from 'node:fs';
14
- import { tmpdir } from 'node:os';
15
- import { join } from 'node:path';
16
- import Database from 'better-sqlite3';
17
-
18
- describe('H3: schema.sql missing fail-fast', () => {
19
- let tmp: string;
20
- let dbPath: string;
21
-
22
- beforeEach(() => {
23
- tmp = mkdtempSync(join(tmpdir(), 'forge-h3-schema-missing-'));
24
- dbPath = join(tmp, 'data.db');
25
- });
26
-
27
- afterEach(() => {
28
- vi.resetModules();
29
- vi.restoreAllMocks();
30
- vi.doUnmock('node:fs');
31
- rmSync(tmp, { recursive: true, force: true });
32
- });
33
-
34
- it('case 1: schema.sql 存在 → 正常初始化,sessions 表已创建', async () => {
35
- const { SQLiteStorage } = await import('../../../src/core/storage/sqlite.js');
36
- const storage = new SQLiteStorage(dbPath);
37
- const db = storage.getDatabase();
38
-
39
- const row = db
40
- .prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='sessions'`)
41
- .get();
42
- expect(row).toBeTruthy();
43
-
44
- storage.close();
45
- });
46
-
47
- it('case 2: schema.sql 缺失 → 抛 Error,且未创建任何业务表(确认不走旧 fallback)', async () => {
48
- vi.resetModules();
49
-
50
- // 选择性 mock:只让 schema.sql 返回 false,其余路径保留真实 fs 行为
51
- vi.doMock('node:fs', async () => {
52
- const realFs = await vi.importActual<typeof import('node:fs')>('node:fs');
53
- return {
54
- ...realFs,
55
- existsSync: (p: import('node:fs').PathLike) => {
56
- const s = typeof p === 'string' ? p : p.toString();
57
- if (s.endsWith('schema.sql')) return false;
58
- return realFs.existsSync(p);
59
- },
60
- };
61
- });
62
-
63
- const { SQLiteStorage } = await import('../../../src/core/storage/sqlite.js');
64
- expect(() => new SQLiteStorage(dbPath)).toThrow(/schema\.sql not found/);
65
-
66
- // 验证未走旧 inline fallback:events 表不应存在
67
- const raw = new Database(dbPath);
68
- const eventsRow = raw
69
- .prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='events'`)
70
- .get();
71
- expect(eventsRow).toBeFalsy();
72
-
73
- // sessions 表也不存在(schema.sql 完全没跑)
74
- const sessionsRow = raw
75
- .prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='sessions'`)
76
- .get();
77
- expect(sessionsRow).toBeFalsy();
78
-
79
- raw.close();
80
- });
81
- });
@@ -1,120 +0,0 @@
1
- /**
2
- * H2: SessionOperations 新增 aggregate / count / query 方法测试
3
- *
4
- * 覆盖 4 个方法:
5
- * countAllSessions
6
- * aggregateDailySessionCounts
7
- * querySessionsByTimeRange
8
- * countSessionsByRange
9
- */
10
-
11
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
12
- import { mkdtempSync, rmSync } from 'node:fs';
13
- import { tmpdir } from 'node:os';
14
- import { join } from 'node:path';
15
- import { SQLiteStorage } from '../../../src/core/storage/sqlite.js';
16
- import type { ForgeEvent } from '../../../src/core/types.js';
17
-
18
- describe('SessionOperations H2 aggregates', () => {
19
- let tmp: string;
20
- let storage: SQLiteStorage;
21
-
22
- beforeEach(() => {
23
- tmp = mkdtempSync(join(tmpdir(), 'forge-h2-sessions-'));
24
- storage = new SQLiteStorage(join(tmp, 'data.db'));
25
- });
26
-
27
- afterEach(() => {
28
- try { storage.close(); } catch { /* ignore */ }
29
- rmSync(tmp, { recursive: true, force: true });
30
- });
31
-
32
- function makeEvent(overrides: Partial<ForgeEvent>): ForgeEvent {
33
- return {
34
- session_id: 's1',
35
- project_path: '/tmp/proj',
36
- timestamp: '2026-05-10T10:00:00.000Z',
37
- hook_type: 'UserPromptSubmit',
38
- user_prompt: 'hello',
39
- ...overrides,
40
- };
41
- }
42
-
43
- describe('countAllSessions', () => {
44
- it('空表返回 0', () => {
45
- expect(storage.countAllSessions()).toBe(0);
46
- });
47
- it('多 session 返回总数', () => {
48
- storage.writeEvent(makeEvent({ session_id: 'a' }));
49
- storage.writeEvent(makeEvent({ session_id: 'b' }));
50
- storage.writeEvent(makeEvent({ session_id: 'a' })); // same session, upsert
51
- expect(storage.countAllSessions()).toBe(2);
52
- });
53
- });
54
-
55
- describe('aggregateDailySessionCounts', () => {
56
- it('按 date(start_time) 分组', () => {
57
- storage.writeEvent(makeEvent({ session_id: 'a', timestamp: '2026-05-10T10:00:00.000Z' }));
58
- storage.writeEvent(makeEvent({ session_id: 'b', timestamp: '2026-05-10T15:00:00.000Z' }));
59
- storage.writeEvent(makeEvent({ session_id: 'c', timestamp: '2026-05-11T10:00:00.000Z' }));
60
- const r = storage.aggregateDailySessionCounts({ since: '2026-05-01T00:00:00.000Z' });
61
- expect(r).toEqual([
62
- { date: '2026-05-10', count: 2 },
63
- { date: '2026-05-11', count: 1 },
64
- ]);
65
- });
66
- it('until 过滤排除上界', () => {
67
- storage.writeEvent(makeEvent({ session_id: 'a', timestamp: '2026-05-10T10:00:00.000Z' }));
68
- storage.writeEvent(makeEvent({ session_id: 'b', timestamp: '2026-05-20T10:00:00.000Z' }));
69
- const r = storage.aggregateDailySessionCounts({
70
- since: '2026-05-01T00:00:00.000Z',
71
- until: '2026-05-15T00:00:00.000Z',
72
- });
73
- expect(r).toEqual([{ date: '2026-05-10', count: 1 }]);
74
- });
75
- it('空返回空数组', () => {
76
- expect(storage.aggregateDailySessionCounts({ since: '2026-05-01T00:00:00.000Z' })).toEqual([]);
77
- });
78
- });
79
-
80
- describe('querySessionsByTimeRange', () => {
81
- it('按 start_time DESC,区间过滤', () => {
82
- storage.writeEvent(makeEvent({ session_id: 'old', timestamp: '2026-05-01T10:00:00.000Z' }));
83
- storage.writeEvent(makeEvent({ session_id: 'mid', timestamp: '2026-05-10T10:00:00.000Z' }));
84
- storage.writeEvent(makeEvent({ session_id: 'new', timestamp: '2026-05-15T10:00:00.000Z' }));
85
- const r = storage.querySessionsByTimeRange({ since: '2026-05-05T00:00:00.000Z' });
86
- expect(r.length).toBe(2);
87
- expect(r[0].session_id).toBe('new');
88
- expect(r[1].session_id).toBe('mid');
89
- });
90
- it('暴露 first_prompt 字段', () => {
91
- storage.writeEvent(makeEvent({
92
- session_id: 'a',
93
- timestamp: '2026-05-10T10:00:00.000Z',
94
- user_prompt: 'hello world',
95
- }));
96
- const r = storage.querySessionsByTimeRange({ since: '2026-05-01T00:00:00.000Z' });
97
- expect(r[0].first_prompt).toBe('hello world');
98
- });
99
- });
100
-
101
- describe('countSessionsByRange', () => {
102
- it('空表返回 0', () => {
103
- expect(storage.countSessionsByRange({ since: '2026-05-01T00:00:00.000Z' })).toBe(0);
104
- });
105
- it('区间内 session 计数', () => {
106
- storage.writeEvent(makeEvent({ session_id: 'a', timestamp: '2026-05-01T10:00:00.000Z' }));
107
- storage.writeEvent(makeEvent({ session_id: 'b', timestamp: '2026-05-10T10:00:00.000Z' }));
108
- storage.writeEvent(makeEvent({ session_id: 'c', timestamp: '2026-05-20T10:00:00.000Z' }));
109
- expect(storage.countSessionsByRange({
110
- since: '2026-05-05T00:00:00.000Z',
111
- until: '2026-05-15T00:00:00.000Z',
112
- })).toBe(1);
113
- });
114
- it('无 until 不限上界', () => {
115
- storage.writeEvent(makeEvent({ session_id: 'a', timestamp: '2026-05-10T10:00:00.000Z' }));
116
- storage.writeEvent(makeEvent({ session_id: 'b', timestamp: '2026-05-20T10:00:00.000Z' }));
117
- expect(storage.countSessionsByRange({ since: '2026-05-01T00:00:00.000Z' })).toBe(2);
118
- });
119
- });
120
- });