@winspan/claude-forge 8.53.2 → 8.54.4

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 (394) 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 +7 -3
  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/storage/events.d.ts.map +1 -1
  11. package/dist/core/storage/events.js +0 -1
  12. package/dist/core/storage/events.js.map +1 -1
  13. package/dist/core/storage/maintenance.d.ts +25 -3
  14. package/dist/core/storage/maintenance.d.ts.map +1 -1
  15. package/dist/core/storage/maintenance.js +33 -4
  16. package/dist/core/storage/maintenance.js.map +1 -1
  17. package/dist/core/storage/routing.d.ts +4 -0
  18. package/dist/core/storage/routing.d.ts.map +1 -1
  19. package/dist/core/storage/routing.js +10 -4
  20. package/dist/core/storage/routing.js.map +1 -1
  21. package/dist/core/storage/sessions.d.ts +17 -0
  22. package/dist/core/storage/sessions.d.ts.map +1 -1
  23. package/dist/core/storage/sessions.js +64 -0
  24. package/dist/core/storage/sessions.js.map +1 -1
  25. package/dist/core/storage/skills.d.ts +4 -0
  26. package/dist/core/storage/skills.d.ts.map +1 -1
  27. package/dist/core/storage/skills.js +10 -2
  28. package/dist/core/storage/skills.js.map +1 -1
  29. package/dist/core/storage/sqlite.d.ts +5 -0
  30. package/dist/core/storage/sqlite.d.ts.map +1 -1
  31. package/dist/core/storage/sqlite.js +6 -0
  32. package/dist/core/storage/sqlite.js.map +1 -1
  33. package/dist/core/storage/tasks.d.ts.map +1 -1
  34. package/dist/core/storage/tasks.js +2 -0
  35. package/dist/core/storage/tasks.js.map +1 -1
  36. package/dist/core/types.d.ts +7 -0
  37. package/dist/core/types.d.ts.map +1 -1
  38. package/dist/daemon/index.d.ts.map +1 -1
  39. package/dist/daemon/index.js +19 -4
  40. package/dist/daemon/index.js.map +1 -1
  41. package/dist/skills/official/official-openspec.md +89 -0
  42. package/dist/skills/official-skills.d.ts.map +1 -1
  43. package/dist/skills/official-skills.js +1 -0
  44. package/dist/skills/official-skills.js.map +1 -1
  45. package/dist/skills/registry.d.ts.map +1 -1
  46. package/dist/skills/registry.js +13 -2
  47. package/dist/skills/registry.js.map +1 -1
  48. package/dist/skills/semantic-matcher.d.ts +2 -2
  49. package/dist/skills/semantic-matcher.d.ts.map +1 -1
  50. package/dist/skills/semantic-matcher.js +14 -19
  51. package/dist/skills/semantic-matcher.js.map +1 -1
  52. package/dist/skills/upgrade-engine.d.ts +3 -1
  53. package/dist/skills/upgrade-engine.d.ts.map +1 -1
  54. package/dist/skills/upgrade-engine.js +25 -14
  55. package/dist/skills/upgrade-engine.js.map +1 -1
  56. package/dist/web/analytics/weekly-report.d.ts.map +1 -1
  57. package/dist/web/analytics/weekly-report.js +21 -29
  58. package/dist/web/analytics/weekly-report.js.map +1 -1
  59. package/dist/web/routes/patch.d.ts.map +1 -1
  60. package/dist/web/routes/patch.js +32 -2
  61. package/dist/web/routes/patch.js.map +1 -1
  62. package/dist/web/routes/sessions.d.ts.map +1 -1
  63. package/dist/web/routes/sessions.js +9 -7
  64. package/dist/web/routes/sessions.js.map +1 -1
  65. package/dist/web/routes/trace.d.ts.map +1 -1
  66. package/dist/web/routes/trace.js +2 -3
  67. package/dist/web/routes/trace.js.map +1 -1
  68. package/dist/web/server.d.ts.map +1 -1
  69. package/dist/web/server.js +3 -2
  70. package/dist/web/server.js.map +1 -1
  71. package/package.json +12 -2
  72. package/scripts/postinstall.cjs +21 -0
  73. package/.claude/CLAUDE.md +0 -17
  74. package/.eslintrc.js +0 -23
  75. package/.prettierrc +0 -8
  76. package/ARCHITECTURE_ISSUES.md +0 -249
  77. package/CLAUDE.md +0 -265
  78. package/CLAUDE.md.backup +0 -488
  79. package/docs/concurrent-agents.md +0 -129
  80. package/docs/design/architecture-review-20260516.md +0 -232
  81. package/docs/design/fix-skills-data-and-set-leak-spec-20260516-1300.md +0 -219
  82. package/docs/design/h1-storage-aggregation-spec-20260518-1121.md +0 -299
  83. package/docs/design/h2-getdatabase-encapsulation-spec-20260518-1450.md +0 -191
  84. package/docs/design/h3-fallback-removal-spec-20260518-1245.md +0 -76
  85. package/docs/design/h4-index-dedup-spec-20260518-1230.md +0 -109
  86. package/docs/design/h6-services-migration-spec-20260518-1355.md +0 -82
  87. package/docs/design/hook-failure-queue-spec-20260516-1530.md +0 -204
  88. package/docs/design/l1-swarm-protocol-extract-spec-20260518-1605.md +0 -106
  89. package/docs/design/m10-forge-paths-spec-20260518-1320.md +0 -121
  90. package/docs/design/m2-m3-tool-input-spec-20260518-1425.md +0 -131
  91. package/docs/design/m7-routing-event-association-spec-20260518-1545.md +0 -103
  92. package/docs/design/project-path-gitroot-spec-20260518-1715.md +0 -134
  93. package/docs/design/refactor-phase1-spec-20260515-1600.md +0 -543
  94. package/docs/design/refactor-phase2-spec-20260515-1700.md +0 -424
  95. package/docs/design/skill-ai-upgrade-spec-20260518-1930.md +0 -297
  96. package/docs/design/task-active-gc-spec-20260518-1745.md +0 -146
  97. package/docs/design/tasks-list-filter-pagination-spec-20260518-0930.md +0 -208
  98. package/docs/implementation/daemon-skill-sync-changelog-20260518-2000.md +0 -22
  99. package/docs/implementation/fix-skills-data-and-set-leak-changelog-20260516-1300.md +0 -104
  100. package/docs/implementation/h1-storage-aggregation-changelog-20260518-1121.md +0 -82
  101. package/docs/implementation/h2-final-changelog-20260518-1530.md +0 -61
  102. package/docs/implementation/h2-phase1-safety-net-changelog-20260518-1450.md +0 -70
  103. package/docs/implementation/h2-phase2-operations-changelog-20260518-1450.md +0 -120
  104. package/docs/implementation/h2-phase3-callsites-changelog-20260518-1450.md +0 -71
  105. package/docs/implementation/h3-fallback-removal-changelog-20260518-1245.md +0 -71
  106. package/docs/implementation/h4-index-dedup-changelog-20260518-1230.md +0 -60
  107. package/docs/implementation/h6-services-migration-changelog-20260518-1355.md +0 -46
  108. package/docs/implementation/h7-m9-defaults-changelog-20260518-1300.md +0 -46
  109. package/docs/implementation/hook-failure-queue-changelog-20260516-1530.md +0 -196
  110. package/docs/implementation/hotfix-daemon-event-reject-20260516-1430.md +0 -56
  111. package/docs/implementation/l1-swarm-protocol-extract-changelog-20260518-1605.md +0 -45
  112. package/docs/implementation/l3-l4-daemon-perf-changelog-20260518-1410.md +0 -63
  113. package/docs/implementation/l6-l8-final-cleanup-changelog-20260518-1640.md +0 -38
  114. package/docs/implementation/m1-m4-m5-l7-cleanup-changelog-20260518-1310.md +0 -58
  115. package/docs/implementation/m10-forge-paths-changelog-20260518-1320.md +0 -60
  116. package/docs/implementation/m2-m3-tool-input-changelog-20260518-1425.md +0 -43
  117. package/docs/implementation/m6-m8-naming-shutdown-changelog-20260518-1340.md +0 -56
  118. package/docs/implementation/m7-routing-association-changelog-20260518-1545.md +0 -69
  119. package/docs/implementation/project-path-gitroot-changelog-20260518-1715.md +0 -63
  120. package/docs/implementation/refactor-phase1-changelog-20260515-1630.md +0 -354
  121. package/docs/implementation/refactor-phase2-changelog-20260515-1705.md +0 -421
  122. package/docs/implementation/skill-ai-upgrade-changelog-20260518-1930.md +0 -49
  123. package/docs/implementation/task-active-gc-changelog-20260518-1745.md +0 -35
  124. package/docs/implementation/task-title-summary-changelog-20260518-1130.md +0 -39
  125. package/docs/implementation/tasks-detail-back-loses-filters-changelog-20260518-1100.md +0 -22
  126. package/docs/implementation/tasks-list-filter-pagination-changelog-20260518-0930.md +0 -72
  127. package/docs/implementation/tasks-page-white-screen-hotfix-changelog-20260518-1015.md +0 -56
  128. package/docs/reviews/claudemd-template-sync.md +0 -54
  129. package/docs/reviews/task-title-summary.md +0 -92
  130. package/docs/reviews/tasks-detail-back-loses-filters.md +0 -58
  131. package/docs/reviews/tasks-filter-pagination.md +0 -80
  132. package/docs/reviews/tasks-page-white-screen-hotfix.md +0 -126
  133. package/docs/ruflo-learning-strategy.md +0 -322
  134. package/docs/skills-deduplication-analysis.md +0 -83
  135. package/docs/skills-multiformat-support.md +0 -177
  136. package/docs/skills-third-party.md +0 -183
  137. package/docs/testing/tasks-filter-pagination-test-report.md +0 -86
  138. package/forge +0 -321
  139. package/playwright.config.ts +0 -40
  140. package/scripts/demo-v2.ts +0 -91
  141. package/scripts/dev-daemon.sh +0 -232
  142. package/scripts/dev-web.ts +0 -109
  143. package/scripts/e2e-mcp-link.ts +0 -423
  144. package/scripts/e2e-methodology-quality.ts +0 -253
  145. package/scripts/e2e-routing.ts +0 -456
  146. package/scripts/e2e-user-methodology.ts +0 -326
  147. package/scripts/e2e-web-workflows.ts +0 -299
  148. package/scripts/migrate-legacy-to-dynamic.sql +0 -108
  149. package/scripts/regenerate-execution-docs.ts +0 -116
  150. package/scripts/sync-agent-skills.ts +0 -193
  151. package/scripts/test-hook.sh +0 -71
  152. package/scripts/verify-skill-loading.ts +0 -62
  153. package/src/claudemd/claudemd-generator.ts +0 -568
  154. package/src/claudemd/convention-extractor.ts +0 -69
  155. package/src/claudemd/index.ts +0 -35
  156. package/src/claudemd/persona-manager.ts +0 -88
  157. package/src/claudemd/resume-manager.ts +0 -236
  158. package/src/claudemd/tech-detector.ts +0 -220
  159. package/src/claudemd/templates/swarm-protocol.md +0 -222
  160. package/src/cli/commands/claudemd.ts +0 -84
  161. package/src/cli/commands/config.ts +0 -46
  162. package/src/cli/commands/daemon.ts +0 -310
  163. package/src/cli/commands/executions.ts +0 -115
  164. package/src/cli/commands/init.ts +0 -204
  165. package/src/cli/commands/logs.ts +0 -181
  166. package/src/cli/commands/mcp.ts +0 -242
  167. package/src/cli/commands/menu.ts +0 -357
  168. package/src/cli/commands/skills.ts +0 -328
  169. package/src/cli/commands/stats.ts +0 -73
  170. package/src/cli/commands/status.ts +0 -69
  171. package/src/cli/commands/template.ts +0 -77
  172. package/src/cli/commands/trace.ts +0 -148
  173. package/src/cli/index.ts +0 -42
  174. package/src/cli/init/hook-manager.ts +0 -132
  175. package/src/core/ai/provider.ts +0 -308
  176. package/src/core/ai/types.ts +0 -51
  177. package/src/core/config.ts +0 -124
  178. package/src/core/constants.ts +0 -67
  179. package/src/core/event-fields.ts +0 -32
  180. package/src/core/queue/index.ts +0 -192
  181. package/src/core/storage/base.ts +0 -302
  182. package/src/core/storage/events.ts +0 -434
  183. package/src/core/storage/injections.ts +0 -78
  184. package/src/core/storage/maintenance.ts +0 -59
  185. package/src/core/storage/migrations/002_add_skill_tracking.sql +0 -6
  186. package/src/core/storage/migrations/003_add_skill_invocations.sql +0 -23
  187. package/src/core/storage/performance-indexes.sql +0 -23
  188. package/src/core/storage/routing.ts +0 -322
  189. package/src/core/storage/rows.ts +0 -112
  190. package/src/core/storage/schema.sql +0 -224
  191. package/src/core/storage/sessions.ts +0 -168
  192. package/src/core/storage/skills.ts +0 -233
  193. package/src/core/storage/sqlite.ts +0 -293
  194. package/src/core/storage/tasks.ts +0 -318
  195. package/src/core/storage/token-usage.ts +0 -93
  196. package/src/core/types.ts +0 -181
  197. package/src/core/utils/error-handler.ts +0 -257
  198. package/src/core/utils/forge-resume-block.ts +0 -74
  199. package/src/core/utils/format.ts +0 -69
  200. package/src/core/utils/git.ts +0 -23
  201. package/src/core/utils/logger.ts +0 -134
  202. package/src/core/utils/lru-cache.ts +0 -54
  203. package/src/core/utils/path.ts +0 -19
  204. package/src/core/utils/session.ts +0 -26
  205. package/src/core/utils/time.ts +0 -37
  206. package/src/core/utils/token-tracker.ts +0 -97
  207. package/src/daemon/event-parser.ts +0 -36
  208. package/src/daemon/handlers/history-exporter.ts +0 -117
  209. package/src/daemon/handlers/post-tool-use.ts +0 -54
  210. package/src/daemon/handlers/stop.ts +0 -208
  211. package/src/daemon/handlers/user-prompt.ts +0 -178
  212. package/src/daemon/hook-sync.ts +0 -91
  213. package/src/daemon/index.ts +0 -312
  214. package/src/daemon/launchd/com.claude-forge.daemon.plist.template +0 -47
  215. package/src/daemon/launchd-installer.ts +0 -260
  216. package/src/daemon/lifecycle.ts +0 -128
  217. package/src/daemon/router.ts +0 -40
  218. package/src/daemon/server.ts +0 -196
  219. package/src/daemon/services/task-segmenter.ts +0 -112
  220. package/src/daemon/skill-sync.ts +0 -88
  221. package/src/hooks/hook-lib.sh +0 -118
  222. package/src/hooks/notification.sh +0 -35
  223. package/src/hooks/post-tool-use.sh +0 -61
  224. package/src/hooks/pre-tool-use.sh +0 -63
  225. package/src/hooks/stop.sh +0 -43
  226. package/src/hooks/user-prompt-submit.sh +0 -69
  227. package/src/mcp/server.ts +0 -322
  228. package/src/skills/index.ts +0 -2
  229. package/src/skills/invocation-guard.ts +0 -177
  230. package/src/skills/matcher.ts +0 -148
  231. package/src/skills/official/code-simplifier.md +0 -52
  232. package/src/skills/official/find-skills.md +0 -142
  233. package/src/skills/official/official-api-design.md +0 -30
  234. package/src/skills/official/official-architecture-decision.md +0 -41
  235. package/src/skills/official/official-bmad.md +0 -118
  236. package/src/skills/official/official-db-schema-design.md +0 -34
  237. package/src/skills/official/official-debug.md +0 -25
  238. package/src/skills/official/official-doc-driven.md +0 -31
  239. package/src/skills/official/official-harness-engineering.md +0 -108
  240. package/src/skills/official/official-performance-optimization.md +0 -30
  241. package/src/skills/official/official-pr-review.md +0 -35
  242. package/src/skills/official/official-release-checklist.md +0 -30
  243. package/src/skills/official/official-security-hardening.md +0 -32
  244. package/src/skills/official/official-spec-driven-design.md +0 -31
  245. package/src/skills/official/planning-with-files.md +0 -241
  246. package/src/skills/official/ui-ux-pro-max.md +0 -105
  247. package/src/skills/official/webapp-testing.md +0 -96
  248. package/src/skills/official-skills.ts +0 -89
  249. package/src/skills/registry.ts +0 -355
  250. package/src/skills/semantic-matcher.ts +0 -234
  251. package/src/skills/tools/pipeline-suggest.ts +0 -226
  252. package/src/skills/tools/skill-invoke.ts +0 -168
  253. package/src/skills/tools/skill-list.ts +0 -59
  254. package/src/skills/upgrade-engine.ts +0 -541
  255. package/src/skills/upgrade-prompt.ts +0 -84
  256. package/src/templates/go.yaml +0 -53
  257. package/src/templates/python.yaml +0 -59
  258. package/src/templates/react.yaml +0 -55
  259. package/src/templates/template-manager.ts +0 -170
  260. package/src/web/analytics/anti-pattern-detector.ts +0 -367
  261. package/src/web/analytics/drift-detector.ts +0 -219
  262. package/src/web/analytics/weekly-report.ts +0 -431
  263. package/src/web/auth-middleware.ts +0 -54
  264. package/src/web/routes/_helpers.ts +0 -34
  265. package/src/web/routes/ai.ts +0 -204
  266. package/src/web/routes/auth.ts +0 -22
  267. package/src/web/routes/drift.ts +0 -25
  268. package/src/web/routes/error-handler.ts +0 -120
  269. package/src/web/routes/events.ts +0 -47
  270. package/src/web/routes/insights.ts +0 -43
  271. package/src/web/routes/patch.ts +0 -117
  272. package/src/web/routes/reports.ts +0 -34
  273. package/src/web/routes/rules.ts +0 -76
  274. package/src/web/routes/sessions.ts +0 -250
  275. package/src/web/routes/skill-stats.ts +0 -92
  276. package/src/web/routes/skills.ts +0 -350
  277. package/src/web/routes/static.ts +0 -67
  278. package/src/web/routes/stats.ts +0 -50
  279. package/src/web/routes/status.ts +0 -30
  280. package/src/web/routes/tasks.ts +0 -193
  281. package/src/web/routes/token-usage.ts +0 -20
  282. package/src/web/routes/trace.ts +0 -126
  283. package/src/web/routes/types.ts +0 -57
  284. package/src/web/server.ts +0 -134
  285. package/src/web/ssrf-guard.ts +0 -112
  286. package/src/web/static/index.html +0 -3251
  287. package/src/web/static/vendor/chart.umd.min.js +0 -20
  288. package/tests/e2e/dashboard.spec.ts +0 -205
  289. package/tests/e2e/routing-skill-e2e.test.ts +0 -39
  290. package/tests/helpers/mock-ai.ts +0 -92
  291. package/tests/helpers/mock-storage.ts +0 -159
  292. package/tests/integration/claudemd-generator.test.ts +0 -90
  293. package/tests/integration/queue-replay.integration.test.ts +0 -193
  294. package/tests/integration/tasks-filter.integration.test.ts +0 -154
  295. package/tests/integration/web-analytics.integration.test.ts +0 -133
  296. package/tests/integration/web-stats.integration.test.ts +0 -135
  297. package/tests/integration/web-trace.integration.test.ts +0 -175
  298. package/tests/performance/database.benchmark.ts +0 -161
  299. package/tests/semantic-matcher.test.ts +0 -99
  300. package/tests/skill-matcher.test.ts +0 -110
  301. package/tests/unit/ai-provider-retry.test.ts +0 -194
  302. package/tests/unit/ai-provider-vision.test.ts +0 -224
  303. package/tests/unit/claudemd-generator.test.ts +0 -68
  304. package/tests/unit/cli-mcp.test.ts +0 -141
  305. package/tests/unit/core/forge-paths.test.ts +0 -99
  306. package/tests/unit/daemon/hook-sync.test.ts +0 -71
  307. package/tests/unit/daemon/post-tool-use.test.ts +0 -121
  308. package/tests/unit/daemon/skill-sync.test.ts +0 -75
  309. package/tests/unit/daemon/stop-handler-behavior-summary.test.ts +0 -202
  310. package/tests/unit/daemon/task-segmenter-recover.test.ts +0 -84
  311. package/tests/unit/event-fields.test.ts +0 -88
  312. package/tests/unit/event-parser.test.ts +0 -55
  313. package/tests/unit/handlers.test.ts +0 -171
  314. package/tests/unit/hooks/resolve-project-path.test.ts +0 -122
  315. package/tests/unit/invocation-guard.test.ts +0 -125
  316. package/tests/unit/queue.test.ts +0 -272
  317. package/tests/unit/router.test.ts +0 -138
  318. package/tests/unit/security.test.ts +0 -128
  319. package/tests/unit/skill-invocations-workflow.test.ts +0 -495
  320. package/tests/unit/skill-registry.test.ts +0 -94
  321. package/tests/unit/skills/invocation-guard-ttl.test.ts +0 -211
  322. package/tests/unit/skills/official-skills-loader.test.ts +0 -126
  323. package/tests/unit/skills/registry-multiformat.test.ts +0 -92
  324. package/tests/unit/skills/upgrade-engine-parse.test.ts +0 -138
  325. package/tests/unit/skills/upgrade-engine.test.ts +0 -401
  326. package/tests/unit/skills/upgrade-prompt.test.ts +0 -89
  327. package/tests/unit/socket-server.test.ts +0 -183
  328. package/tests/unit/storage/event-operations-aggregates.test.ts +0 -342
  329. package/tests/unit/storage/migration-idempotent.test.ts +0 -304
  330. package/tests/unit/storage/routing-aggregates.test.ts +0 -276
  331. package/tests/unit/storage/routing.test.ts +0 -117
  332. package/tests/unit/storage/schema-missing.test.ts +0 -81
  333. package/tests/unit/storage/session-operations-aggregates.test.ts +0 -120
  334. package/tests/unit/storage/sessions-aggregate.test.ts +0 -435
  335. package/tests/unit/storage/skill-operations-counts.test.ts +0 -106
  336. package/tests/unit/storage/skills-aggregates.test.ts +0 -104
  337. package/tests/unit/storage/sqlite-refactor-harness.test.ts +0 -314
  338. package/tests/unit/storage/task-operations-counts.test.ts +0 -46
  339. package/tests/unit/storage/tasks-getById.test.ts +0 -343
  340. package/tests/unit/storage/tasks-stale-gc.test.ts +0 -86
  341. package/tests/unit/storage.test.ts +0 -172
  342. package/tests/unit/token-usage.test.ts +0 -144
  343. package/tests/unit/type-guards.test.ts +0 -201
  344. package/tests/unit/utils/format.test.ts +0 -189
  345. package/tests/unit/utils/session.test.ts +0 -89
  346. package/tests/unit/utils/time.test.ts +0 -112
  347. package/tests/unit/web/navigation-back-contract.test.ts +0 -134
  348. package/tests/unit/web/routes-auth.test.ts +0 -93
  349. package/tests/unit/web/routes-events.test.ts +0 -101
  350. package/tests/unit/web/routes-rules.test.ts +0 -182
  351. package/tests/unit/web/routes-sessions.test.ts +0 -181
  352. package/tests/unit/web/routes-skill-stats.test.ts +0 -179
  353. package/tests/unit/web/routes-stats.test.ts +0 -92
  354. package/tests/unit/web/routes-tasks.test.ts +0 -385
  355. package/tests/unit/web/task-title-contract.test.ts +0 -210
  356. package/tests/unit/web/tasks-component-contract.test.ts +0 -179
  357. package/tsconfig.json +0 -22
  358. package/vitest.config.ts +0 -21
  359. package/vitest.integration.config.ts +0 -16
  360. package/web/CLAUDE.md +0 -20
  361. package/web/index.html +0 -13
  362. package/web/package-lock.json +0 -4854
  363. package/web/package.json +0 -35
  364. package/web/postcss.config.js +0 -6
  365. package/web/src/App.tsx +0 -110
  366. package/web/src/components/CodeBlock.tsx +0 -31
  367. package/web/src/components/Confirm.tsx +0 -96
  368. package/web/src/components/Drawer.tsx +0 -60
  369. package/web/src/components/Layout.tsx +0 -145
  370. package/web/src/components/MarkdownRenderer.tsx +0 -77
  371. package/web/src/components/SearchInput.tsx +0 -31
  372. package/web/src/components/SessionDetailContent.tsx +0 -157
  373. package/web/src/components/Toast.tsx +0 -92
  374. package/web/src/index.css +0 -19
  375. package/web/src/main.tsx +0 -31
  376. package/web/src/pages/AIConfig.tsx +0 -233
  377. package/web/src/pages/Dashboard.tsx +0 -572
  378. package/web/src/pages/Events.tsx +0 -271
  379. package/web/src/pages/Reports.tsx +0 -428
  380. package/web/src/pages/SessionDetail.tsx +0 -162
  381. package/web/src/pages/Sessions.tsx +0 -205
  382. package/web/src/pages/Skills.tsx +0 -180
  383. package/web/src/pages/TaskDetail.tsx +0 -515
  384. package/web/src/pages/Tasks.tsx +0 -415
  385. package/web/src/utils/auth.ts +0 -59
  386. package/web/src/utils/export.ts +0 -54
  387. package/web/src/utils/navigation.ts +0 -25
  388. package/web/src/utils/task-title.ts +0 -49
  389. package/web/src/utils/time.ts +0 -13
  390. package/web/tailwind.config.js +0 -11
  391. package/web/tsconfig.json +0 -21
  392. package/web/tsconfig.node.json +0 -10
  393. package/web/vite.config.ts +0 -76
  394. 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
- });