@winspan/claude-forge 8.53.2 → 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 (390) 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/registry.d.ts.map +1 -1
  42. package/dist/skills/registry.js +13 -2
  43. package/dist/skills/registry.js.map +1 -1
  44. package/dist/skills/semantic-matcher.d.ts +2 -2
  45. package/dist/skills/semantic-matcher.d.ts.map +1 -1
  46. package/dist/skills/semantic-matcher.js +14 -19
  47. package/dist/skills/semantic-matcher.js.map +1 -1
  48. package/dist/skills/upgrade-engine.d.ts +3 -1
  49. package/dist/skills/upgrade-engine.d.ts.map +1 -1
  50. package/dist/skills/upgrade-engine.js +25 -14
  51. package/dist/skills/upgrade-engine.js.map +1 -1
  52. package/dist/web/analytics/weekly-report.d.ts.map +1 -1
  53. package/dist/web/analytics/weekly-report.js +21 -29
  54. package/dist/web/analytics/weekly-report.js.map +1 -1
  55. package/dist/web/routes/patch.d.ts.map +1 -1
  56. package/dist/web/routes/patch.js +32 -2
  57. package/dist/web/routes/patch.js.map +1 -1
  58. package/dist/web/routes/sessions.d.ts.map +1 -1
  59. package/dist/web/routes/sessions.js +9 -7
  60. package/dist/web/routes/sessions.js.map +1 -1
  61. package/dist/web/routes/trace.d.ts.map +1 -1
  62. package/dist/web/routes/trace.js +2 -3
  63. package/dist/web/routes/trace.js.map +1 -1
  64. package/dist/web/server.d.ts.map +1 -1
  65. package/dist/web/server.js +3 -2
  66. package/dist/web/server.js.map +1 -1
  67. package/package.json +12 -2
  68. package/scripts/postinstall.cjs +21 -0
  69. package/.claude/CLAUDE.md +0 -17
  70. package/.eslintrc.js +0 -23
  71. package/.prettierrc +0 -8
  72. package/ARCHITECTURE_ISSUES.md +0 -249
  73. package/CLAUDE.md +0 -265
  74. package/CLAUDE.md.backup +0 -488
  75. package/docs/concurrent-agents.md +0 -129
  76. package/docs/design/architecture-review-20260516.md +0 -232
  77. package/docs/design/fix-skills-data-and-set-leak-spec-20260516-1300.md +0 -219
  78. package/docs/design/h1-storage-aggregation-spec-20260518-1121.md +0 -299
  79. package/docs/design/h2-getdatabase-encapsulation-spec-20260518-1450.md +0 -191
  80. package/docs/design/h3-fallback-removal-spec-20260518-1245.md +0 -76
  81. package/docs/design/h4-index-dedup-spec-20260518-1230.md +0 -109
  82. package/docs/design/h6-services-migration-spec-20260518-1355.md +0 -82
  83. package/docs/design/hook-failure-queue-spec-20260516-1530.md +0 -204
  84. package/docs/design/l1-swarm-protocol-extract-spec-20260518-1605.md +0 -106
  85. package/docs/design/m10-forge-paths-spec-20260518-1320.md +0 -121
  86. package/docs/design/m2-m3-tool-input-spec-20260518-1425.md +0 -131
  87. package/docs/design/m7-routing-event-association-spec-20260518-1545.md +0 -103
  88. package/docs/design/project-path-gitroot-spec-20260518-1715.md +0 -134
  89. package/docs/design/refactor-phase1-spec-20260515-1600.md +0 -543
  90. package/docs/design/refactor-phase2-spec-20260515-1700.md +0 -424
  91. package/docs/design/skill-ai-upgrade-spec-20260518-1930.md +0 -297
  92. package/docs/design/task-active-gc-spec-20260518-1745.md +0 -146
  93. package/docs/design/tasks-list-filter-pagination-spec-20260518-0930.md +0 -208
  94. package/docs/implementation/daemon-skill-sync-changelog-20260518-2000.md +0 -22
  95. package/docs/implementation/fix-skills-data-and-set-leak-changelog-20260516-1300.md +0 -104
  96. package/docs/implementation/h1-storage-aggregation-changelog-20260518-1121.md +0 -82
  97. package/docs/implementation/h2-final-changelog-20260518-1530.md +0 -61
  98. package/docs/implementation/h2-phase1-safety-net-changelog-20260518-1450.md +0 -70
  99. package/docs/implementation/h2-phase2-operations-changelog-20260518-1450.md +0 -120
  100. package/docs/implementation/h2-phase3-callsites-changelog-20260518-1450.md +0 -71
  101. package/docs/implementation/h3-fallback-removal-changelog-20260518-1245.md +0 -71
  102. package/docs/implementation/h4-index-dedup-changelog-20260518-1230.md +0 -60
  103. package/docs/implementation/h6-services-migration-changelog-20260518-1355.md +0 -46
  104. package/docs/implementation/h7-m9-defaults-changelog-20260518-1300.md +0 -46
  105. package/docs/implementation/hook-failure-queue-changelog-20260516-1530.md +0 -196
  106. package/docs/implementation/hotfix-daemon-event-reject-20260516-1430.md +0 -56
  107. package/docs/implementation/l1-swarm-protocol-extract-changelog-20260518-1605.md +0 -45
  108. package/docs/implementation/l3-l4-daemon-perf-changelog-20260518-1410.md +0 -63
  109. package/docs/implementation/l6-l8-final-cleanup-changelog-20260518-1640.md +0 -38
  110. package/docs/implementation/m1-m4-m5-l7-cleanup-changelog-20260518-1310.md +0 -58
  111. package/docs/implementation/m10-forge-paths-changelog-20260518-1320.md +0 -60
  112. package/docs/implementation/m2-m3-tool-input-changelog-20260518-1425.md +0 -43
  113. package/docs/implementation/m6-m8-naming-shutdown-changelog-20260518-1340.md +0 -56
  114. package/docs/implementation/m7-routing-association-changelog-20260518-1545.md +0 -69
  115. package/docs/implementation/project-path-gitroot-changelog-20260518-1715.md +0 -63
  116. package/docs/implementation/refactor-phase1-changelog-20260515-1630.md +0 -354
  117. package/docs/implementation/refactor-phase2-changelog-20260515-1705.md +0 -421
  118. package/docs/implementation/skill-ai-upgrade-changelog-20260518-1930.md +0 -49
  119. package/docs/implementation/task-active-gc-changelog-20260518-1745.md +0 -35
  120. package/docs/implementation/task-title-summary-changelog-20260518-1130.md +0 -39
  121. package/docs/implementation/tasks-detail-back-loses-filters-changelog-20260518-1100.md +0 -22
  122. package/docs/implementation/tasks-list-filter-pagination-changelog-20260518-0930.md +0 -72
  123. package/docs/implementation/tasks-page-white-screen-hotfix-changelog-20260518-1015.md +0 -56
  124. package/docs/reviews/claudemd-template-sync.md +0 -54
  125. package/docs/reviews/task-title-summary.md +0 -92
  126. package/docs/reviews/tasks-detail-back-loses-filters.md +0 -58
  127. package/docs/reviews/tasks-filter-pagination.md +0 -80
  128. package/docs/reviews/tasks-page-white-screen-hotfix.md +0 -126
  129. package/docs/ruflo-learning-strategy.md +0 -322
  130. package/docs/skills-deduplication-analysis.md +0 -83
  131. package/docs/skills-multiformat-support.md +0 -177
  132. package/docs/skills-third-party.md +0 -183
  133. package/docs/testing/tasks-filter-pagination-test-report.md +0 -86
  134. package/forge +0 -321
  135. package/playwright.config.ts +0 -40
  136. package/scripts/demo-v2.ts +0 -91
  137. package/scripts/dev-daemon.sh +0 -232
  138. package/scripts/dev-web.ts +0 -109
  139. package/scripts/e2e-mcp-link.ts +0 -423
  140. package/scripts/e2e-methodology-quality.ts +0 -253
  141. package/scripts/e2e-routing.ts +0 -456
  142. package/scripts/e2e-user-methodology.ts +0 -326
  143. package/scripts/e2e-web-workflows.ts +0 -299
  144. package/scripts/migrate-legacy-to-dynamic.sql +0 -108
  145. package/scripts/regenerate-execution-docs.ts +0 -116
  146. package/scripts/sync-agent-skills.ts +0 -193
  147. package/scripts/test-hook.sh +0 -71
  148. package/scripts/verify-skill-loading.ts +0 -62
  149. package/src/claudemd/claudemd-generator.ts +0 -568
  150. package/src/claudemd/convention-extractor.ts +0 -69
  151. package/src/claudemd/index.ts +0 -35
  152. package/src/claudemd/persona-manager.ts +0 -88
  153. package/src/claudemd/resume-manager.ts +0 -236
  154. package/src/claudemd/tech-detector.ts +0 -220
  155. package/src/claudemd/templates/swarm-protocol.md +0 -222
  156. package/src/cli/commands/claudemd.ts +0 -84
  157. package/src/cli/commands/config.ts +0 -46
  158. package/src/cli/commands/daemon.ts +0 -310
  159. package/src/cli/commands/executions.ts +0 -115
  160. package/src/cli/commands/init.ts +0 -204
  161. package/src/cli/commands/logs.ts +0 -181
  162. package/src/cli/commands/mcp.ts +0 -242
  163. package/src/cli/commands/menu.ts +0 -357
  164. package/src/cli/commands/skills.ts +0 -328
  165. package/src/cli/commands/stats.ts +0 -73
  166. package/src/cli/commands/status.ts +0 -69
  167. package/src/cli/commands/template.ts +0 -77
  168. package/src/cli/commands/trace.ts +0 -148
  169. package/src/cli/index.ts +0 -42
  170. package/src/cli/init/hook-manager.ts +0 -132
  171. package/src/core/ai/provider.ts +0 -308
  172. package/src/core/ai/types.ts +0 -51
  173. package/src/core/config.ts +0 -124
  174. package/src/core/constants.ts +0 -67
  175. package/src/core/event-fields.ts +0 -32
  176. package/src/core/queue/index.ts +0 -192
  177. package/src/core/storage/base.ts +0 -302
  178. package/src/core/storage/events.ts +0 -434
  179. package/src/core/storage/injections.ts +0 -78
  180. package/src/core/storage/maintenance.ts +0 -59
  181. package/src/core/storage/migrations/002_add_skill_tracking.sql +0 -6
  182. package/src/core/storage/migrations/003_add_skill_invocations.sql +0 -23
  183. package/src/core/storage/performance-indexes.sql +0 -23
  184. package/src/core/storage/routing.ts +0 -322
  185. package/src/core/storage/rows.ts +0 -112
  186. package/src/core/storage/schema.sql +0 -224
  187. package/src/core/storage/sessions.ts +0 -168
  188. package/src/core/storage/skills.ts +0 -233
  189. package/src/core/storage/sqlite.ts +0 -293
  190. package/src/core/storage/tasks.ts +0 -318
  191. package/src/core/storage/token-usage.ts +0 -93
  192. package/src/core/types.ts +0 -181
  193. package/src/core/utils/error-handler.ts +0 -257
  194. package/src/core/utils/forge-resume-block.ts +0 -74
  195. package/src/core/utils/format.ts +0 -69
  196. package/src/core/utils/git.ts +0 -23
  197. package/src/core/utils/logger.ts +0 -134
  198. package/src/core/utils/lru-cache.ts +0 -54
  199. package/src/core/utils/path.ts +0 -19
  200. package/src/core/utils/session.ts +0 -26
  201. package/src/core/utils/time.ts +0 -37
  202. package/src/core/utils/token-tracker.ts +0 -97
  203. package/src/daemon/event-parser.ts +0 -36
  204. package/src/daemon/handlers/history-exporter.ts +0 -117
  205. package/src/daemon/handlers/post-tool-use.ts +0 -54
  206. package/src/daemon/handlers/stop.ts +0 -208
  207. package/src/daemon/handlers/user-prompt.ts +0 -178
  208. package/src/daemon/hook-sync.ts +0 -91
  209. package/src/daemon/index.ts +0 -312
  210. package/src/daemon/launchd/com.claude-forge.daemon.plist.template +0 -47
  211. package/src/daemon/launchd-installer.ts +0 -260
  212. package/src/daemon/lifecycle.ts +0 -128
  213. package/src/daemon/router.ts +0 -40
  214. package/src/daemon/server.ts +0 -196
  215. package/src/daemon/services/task-segmenter.ts +0 -112
  216. package/src/daemon/skill-sync.ts +0 -88
  217. package/src/hooks/hook-lib.sh +0 -118
  218. package/src/hooks/notification.sh +0 -35
  219. package/src/hooks/post-tool-use.sh +0 -61
  220. package/src/hooks/pre-tool-use.sh +0 -63
  221. package/src/hooks/stop.sh +0 -43
  222. package/src/hooks/user-prompt-submit.sh +0 -69
  223. package/src/mcp/server.ts +0 -322
  224. package/src/skills/index.ts +0 -2
  225. package/src/skills/invocation-guard.ts +0 -177
  226. package/src/skills/matcher.ts +0 -148
  227. package/src/skills/official/code-simplifier.md +0 -52
  228. package/src/skills/official/find-skills.md +0 -142
  229. package/src/skills/official/official-api-design.md +0 -30
  230. package/src/skills/official/official-architecture-decision.md +0 -41
  231. package/src/skills/official/official-bmad.md +0 -118
  232. package/src/skills/official/official-db-schema-design.md +0 -34
  233. package/src/skills/official/official-debug.md +0 -25
  234. package/src/skills/official/official-doc-driven.md +0 -31
  235. package/src/skills/official/official-harness-engineering.md +0 -108
  236. package/src/skills/official/official-performance-optimization.md +0 -30
  237. package/src/skills/official/official-pr-review.md +0 -35
  238. package/src/skills/official/official-release-checklist.md +0 -30
  239. package/src/skills/official/official-security-hardening.md +0 -32
  240. package/src/skills/official/official-spec-driven-design.md +0 -31
  241. package/src/skills/official/planning-with-files.md +0 -241
  242. package/src/skills/official/ui-ux-pro-max.md +0 -105
  243. package/src/skills/official/webapp-testing.md +0 -96
  244. package/src/skills/official-skills.ts +0 -89
  245. package/src/skills/registry.ts +0 -355
  246. package/src/skills/semantic-matcher.ts +0 -234
  247. package/src/skills/tools/pipeline-suggest.ts +0 -226
  248. package/src/skills/tools/skill-invoke.ts +0 -168
  249. package/src/skills/tools/skill-list.ts +0 -59
  250. package/src/skills/upgrade-engine.ts +0 -541
  251. package/src/skills/upgrade-prompt.ts +0 -84
  252. package/src/templates/go.yaml +0 -53
  253. package/src/templates/python.yaml +0 -59
  254. package/src/templates/react.yaml +0 -55
  255. package/src/templates/template-manager.ts +0 -170
  256. package/src/web/analytics/anti-pattern-detector.ts +0 -367
  257. package/src/web/analytics/drift-detector.ts +0 -219
  258. package/src/web/analytics/weekly-report.ts +0 -431
  259. package/src/web/auth-middleware.ts +0 -54
  260. package/src/web/routes/_helpers.ts +0 -34
  261. package/src/web/routes/ai.ts +0 -204
  262. package/src/web/routes/auth.ts +0 -22
  263. package/src/web/routes/drift.ts +0 -25
  264. package/src/web/routes/error-handler.ts +0 -120
  265. package/src/web/routes/events.ts +0 -47
  266. package/src/web/routes/insights.ts +0 -43
  267. package/src/web/routes/patch.ts +0 -117
  268. package/src/web/routes/reports.ts +0 -34
  269. package/src/web/routes/rules.ts +0 -76
  270. package/src/web/routes/sessions.ts +0 -250
  271. package/src/web/routes/skill-stats.ts +0 -92
  272. package/src/web/routes/skills.ts +0 -350
  273. package/src/web/routes/static.ts +0 -67
  274. package/src/web/routes/stats.ts +0 -50
  275. package/src/web/routes/status.ts +0 -30
  276. package/src/web/routes/tasks.ts +0 -193
  277. package/src/web/routes/token-usage.ts +0 -20
  278. package/src/web/routes/trace.ts +0 -126
  279. package/src/web/routes/types.ts +0 -57
  280. package/src/web/server.ts +0 -134
  281. package/src/web/ssrf-guard.ts +0 -112
  282. package/src/web/static/index.html +0 -3251
  283. package/src/web/static/vendor/chart.umd.min.js +0 -20
  284. package/tests/e2e/dashboard.spec.ts +0 -205
  285. package/tests/e2e/routing-skill-e2e.test.ts +0 -39
  286. package/tests/helpers/mock-ai.ts +0 -92
  287. package/tests/helpers/mock-storage.ts +0 -159
  288. package/tests/integration/claudemd-generator.test.ts +0 -90
  289. package/tests/integration/queue-replay.integration.test.ts +0 -193
  290. package/tests/integration/tasks-filter.integration.test.ts +0 -154
  291. package/tests/integration/web-analytics.integration.test.ts +0 -133
  292. package/tests/integration/web-stats.integration.test.ts +0 -135
  293. package/tests/integration/web-trace.integration.test.ts +0 -175
  294. package/tests/performance/database.benchmark.ts +0 -161
  295. package/tests/semantic-matcher.test.ts +0 -99
  296. package/tests/skill-matcher.test.ts +0 -110
  297. package/tests/unit/ai-provider-retry.test.ts +0 -194
  298. package/tests/unit/ai-provider-vision.test.ts +0 -224
  299. package/tests/unit/claudemd-generator.test.ts +0 -68
  300. package/tests/unit/cli-mcp.test.ts +0 -141
  301. package/tests/unit/core/forge-paths.test.ts +0 -99
  302. package/tests/unit/daemon/hook-sync.test.ts +0 -71
  303. package/tests/unit/daemon/post-tool-use.test.ts +0 -121
  304. package/tests/unit/daemon/skill-sync.test.ts +0 -75
  305. package/tests/unit/daemon/stop-handler-behavior-summary.test.ts +0 -202
  306. package/tests/unit/daemon/task-segmenter-recover.test.ts +0 -84
  307. package/tests/unit/event-fields.test.ts +0 -88
  308. package/tests/unit/event-parser.test.ts +0 -55
  309. package/tests/unit/handlers.test.ts +0 -171
  310. package/tests/unit/hooks/resolve-project-path.test.ts +0 -122
  311. package/tests/unit/invocation-guard.test.ts +0 -125
  312. package/tests/unit/queue.test.ts +0 -272
  313. package/tests/unit/router.test.ts +0 -138
  314. package/tests/unit/security.test.ts +0 -128
  315. package/tests/unit/skill-invocations-workflow.test.ts +0 -495
  316. package/tests/unit/skill-registry.test.ts +0 -94
  317. package/tests/unit/skills/invocation-guard-ttl.test.ts +0 -211
  318. package/tests/unit/skills/official-skills-loader.test.ts +0 -126
  319. package/tests/unit/skills/registry-multiformat.test.ts +0 -92
  320. package/tests/unit/skills/upgrade-engine-parse.test.ts +0 -138
  321. package/tests/unit/skills/upgrade-engine.test.ts +0 -401
  322. package/tests/unit/skills/upgrade-prompt.test.ts +0 -89
  323. package/tests/unit/socket-server.test.ts +0 -183
  324. package/tests/unit/storage/event-operations-aggregates.test.ts +0 -342
  325. package/tests/unit/storage/migration-idempotent.test.ts +0 -304
  326. package/tests/unit/storage/routing-aggregates.test.ts +0 -276
  327. package/tests/unit/storage/routing.test.ts +0 -117
  328. package/tests/unit/storage/schema-missing.test.ts +0 -81
  329. package/tests/unit/storage/session-operations-aggregates.test.ts +0 -120
  330. package/tests/unit/storage/sessions-aggregate.test.ts +0 -435
  331. package/tests/unit/storage/skill-operations-counts.test.ts +0 -106
  332. package/tests/unit/storage/skills-aggregates.test.ts +0 -104
  333. package/tests/unit/storage/sqlite-refactor-harness.test.ts +0 -314
  334. package/tests/unit/storage/task-operations-counts.test.ts +0 -46
  335. package/tests/unit/storage/tasks-getById.test.ts +0 -343
  336. package/tests/unit/storage/tasks-stale-gc.test.ts +0 -86
  337. package/tests/unit/storage.test.ts +0 -172
  338. package/tests/unit/token-usage.test.ts +0 -144
  339. package/tests/unit/type-guards.test.ts +0 -201
  340. package/tests/unit/utils/format.test.ts +0 -189
  341. package/tests/unit/utils/session.test.ts +0 -89
  342. package/tests/unit/utils/time.test.ts +0 -112
  343. package/tests/unit/web/navigation-back-contract.test.ts +0 -134
  344. package/tests/unit/web/routes-auth.test.ts +0 -93
  345. package/tests/unit/web/routes-events.test.ts +0 -101
  346. package/tests/unit/web/routes-rules.test.ts +0 -182
  347. package/tests/unit/web/routes-sessions.test.ts +0 -181
  348. package/tests/unit/web/routes-skill-stats.test.ts +0 -179
  349. package/tests/unit/web/routes-stats.test.ts +0 -92
  350. package/tests/unit/web/routes-tasks.test.ts +0 -385
  351. package/tests/unit/web/task-title-contract.test.ts +0 -210
  352. package/tests/unit/web/tasks-component-contract.test.ts +0 -179
  353. package/tsconfig.json +0 -22
  354. package/vitest.config.ts +0 -21
  355. package/vitest.integration.config.ts +0 -16
  356. package/web/CLAUDE.md +0 -20
  357. package/web/index.html +0 -13
  358. package/web/package-lock.json +0 -4854
  359. package/web/package.json +0 -35
  360. package/web/postcss.config.js +0 -6
  361. package/web/src/App.tsx +0 -110
  362. package/web/src/components/CodeBlock.tsx +0 -31
  363. package/web/src/components/Confirm.tsx +0 -96
  364. package/web/src/components/Drawer.tsx +0 -60
  365. package/web/src/components/Layout.tsx +0 -145
  366. package/web/src/components/MarkdownRenderer.tsx +0 -77
  367. package/web/src/components/SearchInput.tsx +0 -31
  368. package/web/src/components/SessionDetailContent.tsx +0 -157
  369. package/web/src/components/Toast.tsx +0 -92
  370. package/web/src/index.css +0 -19
  371. package/web/src/main.tsx +0 -31
  372. package/web/src/pages/AIConfig.tsx +0 -233
  373. package/web/src/pages/Dashboard.tsx +0 -572
  374. package/web/src/pages/Events.tsx +0 -271
  375. package/web/src/pages/Reports.tsx +0 -428
  376. package/web/src/pages/SessionDetail.tsx +0 -162
  377. package/web/src/pages/Sessions.tsx +0 -205
  378. package/web/src/pages/Skills.tsx +0 -180
  379. package/web/src/pages/TaskDetail.tsx +0 -515
  380. package/web/src/pages/Tasks.tsx +0 -415
  381. package/web/src/utils/auth.ts +0 -59
  382. package/web/src/utils/export.ts +0 -54
  383. package/web/src/utils/navigation.ts +0 -25
  384. package/web/src/utils/task-title.ts +0 -49
  385. package/web/src/utils/time.ts +0 -13
  386. package/web/tailwind.config.js +0 -11
  387. package/web/tsconfig.json +0 -21
  388. package/web/tsconfig.node.json +0 -10
  389. package/web/vite.config.ts +0 -76
  390. package/winspan-claude-forge-8.43.0.tgz +0 -0
@@ -1,99 +0,0 @@
1
- /**
2
- * Unit tests for FORGE_PATHS (src/core/constants.ts)
3
- *
4
- * Verifies that every path method returns a value prefixed by FORGE_HOME
5
- * and matches the expected sub-path.
6
- */
7
-
8
- import { describe, it, expect } from 'vitest';
9
- import { join } from 'node:path';
10
- import { FORGE_HOME, FORGE_PATHS } from '../../../src/core/constants.js';
11
-
12
- describe('FORGE_PATHS', () => {
13
- describe('existing methods', () => {
14
- it('home() returns FORGE_HOME', () => {
15
- expect(FORGE_PATHS.home()).toBe(FORGE_HOME);
16
- });
17
-
18
- it('config() returns FORGE_HOME/config.yaml', () => {
19
- expect(FORGE_PATHS.config()).toBe(join(FORGE_HOME, 'config.yaml'));
20
- expect(FORGE_PATHS.config().startsWith(FORGE_HOME)).toBe(true);
21
- });
22
-
23
- it('database() returns FORGE_HOME/data.db', () => {
24
- expect(FORGE_PATHS.database()).toBe(join(FORGE_HOME, 'data.db'));
25
- expect(FORGE_PATHS.database().startsWith(FORGE_HOME)).toBe(true);
26
- });
27
-
28
- it('logs() returns FORGE_HOME/logs', () => {
29
- expect(FORGE_PATHS.logs()).toBe(join(FORGE_HOME, 'logs'));
30
- expect(FORGE_PATHS.logs().startsWith(FORGE_HOME)).toBe(true);
31
- });
32
- });
33
-
34
- describe('daemon runtime files', () => {
35
- it('daemonSocket() returns FORGE_HOME/daemon.sock', () => {
36
- expect(FORGE_PATHS.daemonSocket()).toBe(join(FORGE_HOME, 'daemon.sock'));
37
- expect(FORGE_PATHS.daemonSocket().startsWith(FORGE_HOME)).toBe(true);
38
- });
39
-
40
- it('daemonPid() returns FORGE_HOME/daemon.pid', () => {
41
- expect(FORGE_PATHS.daemonPid()).toBe(join(FORGE_HOME, 'daemon.pid'));
42
- expect(FORGE_PATHS.daemonPid().startsWith(FORGE_HOME)).toBe(true);
43
- });
44
-
45
- it('daemonToken() returns FORGE_HOME/daemon.token', () => {
46
- expect(FORGE_PATHS.daemonToken()).toBe(join(FORGE_HOME, 'daemon.token'));
47
- expect(FORGE_PATHS.daemonToken().startsWith(FORGE_HOME)).toBe(true);
48
- });
49
-
50
- it('daemonLog() returns FORGE_HOME/daemon.log', () => {
51
- expect(FORGE_PATHS.daemonLog()).toBe(join(FORGE_HOME, 'daemon.log'));
52
- expect(FORGE_PATHS.daemonLog().startsWith(FORGE_HOME)).toBe(true);
53
- });
54
-
55
- it('daemonStdout() returns FORGE_HOME/daemon-stdout.log', () => {
56
- expect(FORGE_PATHS.daemonStdout()).toBe(join(FORGE_HOME, 'daemon-stdout.log'));
57
- expect(FORGE_PATHS.daemonStdout().startsWith(FORGE_HOME)).toBe(true);
58
- });
59
-
60
- it('daemonStderr() returns FORGE_HOME/daemon-stderr.log', () => {
61
- expect(FORGE_PATHS.daemonStderr()).toBe(join(FORGE_HOME, 'daemon-stderr.log'));
62
- expect(FORGE_PATHS.daemonStderr().startsWith(FORGE_HOME)).toBe(true);
63
- });
64
- });
65
-
66
- describe('subdirs', () => {
67
- it('hooks() returns FORGE_HOME/hooks', () => {
68
- expect(FORGE_PATHS.hooks()).toBe(join(FORGE_HOME, 'hooks'));
69
- expect(FORGE_PATHS.hooks().startsWith(FORGE_HOME)).toBe(true);
70
- });
71
-
72
- it('queue() returns FORGE_HOME/queue', () => {
73
- expect(FORGE_PATHS.queue()).toBe(join(FORGE_HOME, 'queue'));
74
- expect(FORGE_PATHS.queue().startsWith(FORGE_HOME)).toBe(true);
75
- });
76
-
77
- it('queueDead() returns FORGE_HOME/queue/dead', () => {
78
- expect(FORGE_PATHS.queueDead()).toBe(join(FORGE_HOME, 'queue', 'dead'));
79
- expect(FORGE_PATHS.queueDead().startsWith(FORGE_HOME)).toBe(true);
80
- });
81
- });
82
-
83
- describe('patchable targets', () => {
84
- it('routingYaml() returns FORGE_HOME/routing.yaml', () => {
85
- expect(FORGE_PATHS.routingYaml()).toBe(join(FORGE_HOME, 'routing.yaml'));
86
- expect(FORGE_PATHS.routingYaml().startsWith(FORGE_HOME)).toBe(true);
87
- });
88
-
89
- it("backups('skills') returns FORGE_HOME/backups/skills", () => {
90
- expect(FORGE_PATHS.backups('skills')).toBe(join(FORGE_HOME, 'backups', 'skills'));
91
- expect(FORGE_PATHS.backups('skills').startsWith(FORGE_HOME)).toBe(true);
92
- });
93
-
94
- it("backups('routing') returns FORGE_HOME/backups/routing", () => {
95
- expect(FORGE_PATHS.backups('routing')).toBe(join(FORGE_HOME, 'backups', 'routing'));
96
- expect(FORGE_PATHS.backups('routing').startsWith(FORGE_HOME)).toBe(true);
97
- });
98
- });
99
- });
@@ -1,71 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, readFileSync } from 'node:fs';
3
- import { tmpdir } from 'node:os';
4
- import { join } from 'node:path';
5
- import { syncHooks } from '../../../src/daemon/hook-sync.js';
6
-
7
- describe('syncHooks', () => {
8
- let tmpRoot: string;
9
- let sourceDir: string;
10
- let targetDir: string;
11
-
12
- beforeEach(() => {
13
- tmpRoot = mkdtempSync(join(tmpdir(), 'forge-hook-sync-'));
14
- sourceDir = join(tmpRoot, 'src-hooks');
15
- targetDir = join(tmpRoot, 'target-hooks');
16
- mkdirSync(sourceDir, { recursive: true });
17
- });
18
-
19
- afterEach(() => {
20
- rmSync(tmpRoot, { recursive: true, force: true });
21
- });
22
-
23
- it('target dir not exist → returns zero counts (user did not init)', () => {
24
- writeFileSync(join(sourceDir, 'pre-tool-use.sh'), '#!/bin/bash\necho "new"\n');
25
- const result = syncHooks({ sourceDir, targetDir });
26
- expect(result.copied).toBe(0);
27
- expect(result.checked).toBe(0);
28
- });
29
-
30
- it('source and target identical → no copy', () => {
31
- mkdirSync(targetDir, { recursive: true });
32
- const content = '#!/bin/bash\necho "same"\n';
33
- writeFileSync(join(sourceDir, 'pre-tool-use.sh'), content);
34
- writeFileSync(join(targetDir, 'pre-tool-use.sh'), content);
35
-
36
- const result = syncHooks({ sourceDir, targetDir });
37
- expect(result.copied).toBe(0);
38
- expect(result.checked).toBe(1);
39
- });
40
-
41
- it('source and target differ → copies new content', () => {
42
- mkdirSync(targetDir, { recursive: true });
43
- writeFileSync(join(sourceDir, 'pre-tool-use.sh'), '#!/bin/bash\necho "NEW"\n');
44
- writeFileSync(join(targetDir, 'pre-tool-use.sh'), '#!/bin/bash\necho "old"\n');
45
-
46
- const result = syncHooks({ sourceDir, targetDir });
47
- expect(result.copied).toBe(1);
48
- expect(result.checked).toBe(1);
49
- expect(readFileSync(join(targetDir, 'pre-tool-use.sh'), 'utf-8')).toContain('NEW');
50
- });
51
-
52
- it('target missing file but exists in source → copies it', () => {
53
- mkdirSync(targetDir, { recursive: true });
54
- writeFileSync(join(sourceDir, 'hook-lib.sh'), '# lib\n');
55
- // target has no hook-lib.sh
56
-
57
- const result = syncHooks({ sourceDir, targetDir });
58
- expect(result.copied).toBe(1);
59
- expect(existsSync(join(targetDir, 'hook-lib.sh'))).toBe(true);
60
- });
61
-
62
- it('source missing some files → skips them silently', () => {
63
- mkdirSync(targetDir, { recursive: true });
64
- // Only one source file out of 6
65
- writeFileSync(join(sourceDir, 'pre-tool-use.sh'), '#!/bin/bash\necho "x"\n');
66
-
67
- const result = syncHooks({ sourceDir, targetDir });
68
- expect(result.checked).toBe(1);
69
- expect(result.skipped).toBe(5);
70
- });
71
- });
@@ -1,121 +0,0 @@
1
- /**
2
- * M7: PostToolUseHandler routing_event association behavior.
3
- *
4
- * Targets the time-ordering bug where 2+ Agent invocations within a single
5
- * user prompt could overwrite the same routing_event repeatedly. After M7,
6
- * the handler must only touch the *pending* (obeyed IS NULL) recent event;
7
- * later Agents within the same prompt are silently skipped (M7 limitation).
8
- */
9
-
10
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
11
- import { mkdtempSync, rmSync } from 'node:fs';
12
- import { tmpdir } from 'node:os';
13
- import { join } from 'node:path';
14
- import { SQLiteStorage } from '../../../src/core/storage/sqlite.js';
15
- import { PostToolUseHandler } from '../../../src/daemon/handlers/post-tool-use.js';
16
- import type { PostToolUseEvent } from '../../../src/core/types.js';
17
-
18
- const PROJECT = '/tmp/m7-handler-proj';
19
-
20
- function makeAgentEvent(session_id: string, agentType: string): PostToolUseEvent {
21
- return {
22
- session_id,
23
- project_path: PROJECT,
24
- timestamp: new Date().toISOString(),
25
- hook_type: 'PostToolUse',
26
- tool_name: 'Agent',
27
- tool_input: { subagent_type: agentType },
28
- tool_output: {},
29
- };
30
- }
31
-
32
- describe('PostToolUseHandler — routing_event association (M7)', () => {
33
- let tmp: string;
34
- let storage: SQLiteStorage;
35
- let handler: PostToolUseHandler;
36
-
37
- beforeEach(() => {
38
- tmp = mkdtempSync(join(tmpdir(), 'forge-m7-handler-'));
39
- storage = new SQLiteStorage(join(tmp, 'data.db'));
40
- handler = new PostToolUseHandler(storage);
41
- });
42
-
43
- afterEach(() => {
44
- try { storage.close(); } catch { /* ignore */ }
45
- rmSync(tmp, { recursive: true, force: true });
46
- });
47
-
48
- it('case 1: same prompt, 2 Agent calls — first updates pending row, second does NOT re-overwrite', async () => {
49
- const sess = 'sess-same-prompt';
50
-
51
- // UserPromptHandler writes a single pending routing_event.
52
- const eventId = storage.writeRoutingEvent({
53
- session_id: sess, project_path: PROJECT, ts: 1000,
54
- prompt: 'spawn two agents', intent_json: '{}',
55
- obeyed: null,
56
- });
57
-
58
- // Agent #1 fires → first PostToolUse fills the row.
59
- await handler.handle(makeAgentEvent(sess, 'researcher'));
60
-
61
- const rowsAfter1 = storage.queryRoutingEvents({ session_id: sess });
62
- expect(rowsAfter1).toHaveLength(1);
63
- expect(rowsAfter1[0].id).toBe(eventId);
64
- expect(rowsAfter1[0].obeyed).toBe(1);
65
- expect(rowsAfter1[0].routed_to_name).toBe('researcher');
66
- const firstToolTs = rowsAfter1[0].first_tool_ts;
67
- expect(firstToolTs).not.toBeNull();
68
-
69
- // Agent #2 fires → with the pending filter, no pending row remains;
70
- // the handler must NOT overwrite the existing obeyed=1 row.
71
- await handler.handle(makeAgentEvent(sess, 'coder'));
72
-
73
- const rowsAfter2 = storage.queryRoutingEvents({ session_id: sess });
74
- expect(rowsAfter2).toHaveLength(1);
75
- expect(rowsAfter2[0].id).toBe(eventId);
76
- expect(rowsAfter2[0].routed_to_name).toBe('researcher'); // NOT overwritten to 'coder'
77
- expect(rowsAfter2[0].first_tool_ts).toBe(firstToolTs); // ts not stomped
78
- expect(rowsAfter2[0].obeyed).toBe(1);
79
- });
80
-
81
- it('case 2: cross-prompt — each prompt’s Agent updates its own pending event', async () => {
82
- const sess = 'sess-cross-prompt';
83
-
84
- // Prompt 1 → routing_event #1 pending.
85
- const eid1 = storage.writeRoutingEvent({
86
- session_id: sess, project_path: PROJECT, ts: 1000,
87
- prompt: 'p1', intent_json: '{}',
88
- obeyed: null,
89
- });
90
- // Agent for P1.
91
- await handler.handle(makeAgentEvent(sess, 'researcher'));
92
-
93
- // Prompt 2 → routing_event #2 pending.
94
- const eid2 = storage.writeRoutingEvent({
95
- session_id: sess, project_path: PROJECT, ts: 2000,
96
- prompt: 'p2', intent_json: '{}',
97
- obeyed: null,
98
- });
99
- // Agent for P2.
100
- await handler.handle(makeAgentEvent(sess, 'coder'));
101
-
102
- const rows = storage.queryRoutingEvents({ session_id: sess });
103
- expect(rows).toHaveLength(2);
104
-
105
- const byId = new Map(rows.map(r => [r.id, r]));
106
- const r1 = byId.get(eid1)!;
107
- const r2 = byId.get(eid2)!;
108
- expect(r1.routed_to_name).toBe('researcher');
109
- expect(r1.obeyed).toBe(1);
110
- expect(r2.routed_to_name).toBe('coder');
111
- expect(r2.obeyed).toBe(1);
112
- });
113
-
114
- it('case 3: no routing_event in DB — handler does not throw', async () => {
115
- const sess = 'sess-none';
116
- await expect(handler.handle(makeAgentEvent(sess, 'researcher'))).resolves.toEqual({ allow: true });
117
-
118
- const rows = storage.queryRoutingEvents({ session_id: sess });
119
- expect(rows).toHaveLength(0);
120
- });
121
- });
@@ -1,75 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, readFileSync } from 'node:fs';
3
- import { tmpdir } from 'node:os';
4
- import { join } from 'node:path';
5
- import { syncSkills } from '../../../src/daemon/skill-sync.js';
6
-
7
- describe('syncSkills', () => {
8
- let tmpRoot: string;
9
- let sourceDir: string;
10
- let targetDir: string;
11
-
12
- beforeEach(() => {
13
- tmpRoot = mkdtempSync(join(tmpdir(), 'forge-skill-sync-'));
14
- sourceDir = join(tmpRoot, 'src-skills');
15
- targetDir = join(tmpRoot, 'target-skills');
16
- mkdirSync(sourceDir, { recursive: true });
17
- });
18
-
19
- afterEach(() => {
20
- rmSync(tmpRoot, { recursive: true, force: true });
21
- });
22
-
23
- it('source dir not found → returns zero counts, does not throw', () => {
24
- const result = syncSkills({ sourceDir: join(tmpRoot, 'nonexistent'), targetDir });
25
- expect(result.copied).toBe(0);
26
- expect(result.checked).toBe(0);
27
- expect(result.skipped_userOwned).toBe(0);
28
- });
29
-
30
- it('source and target identical → copied=0, checked=1', () => {
31
- const content = '# official-debug skill\nsome content\n';
32
- writeFileSync(join(sourceDir, 'official-debug.md'), content);
33
- mkdirSync(targetDir, { recursive: true });
34
- writeFileSync(join(targetDir, 'official-debug.md'), content);
35
-
36
- const result = syncSkills({ sourceDir, targetDir });
37
- expect(result.copied).toBe(0);
38
- expect(result.checked).toBe(1);
39
- });
40
-
41
- it('source and target differ → copied=1, target updated', () => {
42
- writeFileSync(join(sourceDir, 'official-debug.md'), '# new content\n');
43
- mkdirSync(targetDir, { recursive: true });
44
- writeFileSync(join(targetDir, 'official-debug.md'), '# old content\n');
45
-
46
- const result = syncSkills({ sourceDir, targetDir });
47
- expect(result.copied).toBe(1);
48
- expect(result.checked).toBe(1);
49
- expect(readFileSync(join(targetDir, 'official-debug.md'), 'utf-8')).toContain('new content');
50
- });
51
-
52
- it('target missing file → copies it from source', () => {
53
- writeFileSync(join(sourceDir, 'official-debug.md'), '# official-debug\n');
54
- mkdirSync(targetDir, { recursive: true });
55
- // target has no official-debug.md
56
-
57
- const result = syncSkills({ sourceDir, targetDir });
58
- expect(result.copied).toBe(1);
59
- expect(existsSync(join(targetDir, 'official-debug.md'))).toBe(true);
60
- });
61
-
62
- it('user-only skill in target (not in source) → untouched', () => {
63
- // source has one official skill
64
- writeFileSync(join(sourceDir, 'official-debug.md'), '# official-debug\n');
65
- mkdirSync(targetDir, { recursive: true });
66
- // target also has a user-custom skill not present in source
67
- writeFileSync(join(targetDir, 'my-custom-skill.md'), '# custom\n');
68
-
69
- syncSkills({ sourceDir, targetDir });
70
-
71
- // user skill must still exist and be unchanged
72
- expect(existsSync(join(targetDir, 'my-custom-skill.md'))).toBe(true);
73
- expect(readFileSync(join(targetDir, 'my-custom-skill.md'), 'utf-8')).toContain('custom');
74
- });
75
- });
@@ -1,202 +0,0 @@
1
- /**
2
- * H2 Phase 1 safety-net: StopHandler behavior summary.
3
- *
4
- * Locks the output format and aggregation logic of `generateBehaviorSummary`
5
- * before its inline SQL (events GROUP BY tool_name / Agent-Task subagent_type /
6
- * COUNT skill_invocations) is migrated into EventOperations / SkillOperations.
7
- *
8
- * Since `generateBehaviorSummary` is `private`, we exercise it indirectly via
9
- * `handle()` and capture the resume content passed to `resume.save`. That is
10
- * also the actually-exercised path in production, so this gives a higher-value
11
- * baseline than reflection-style private access.
12
- *
13
- * NOTE on test location: the spec listed `src/tests/...`, but vitest.config.ts
14
- * only discovers `tests/unit/**` and `tests/integration/**`, so the file is
15
- * placed under `tests/unit/daemon/` to ensure it actually runs. The semantic
16
- * intent (a unit test, not an integration test) is preserved.
17
- */
18
-
19
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
20
- import { StopHandler } from '../../../src/daemon/handlers/stop.js';
21
- import { SQLiteStorage } from '../../../src/core/storage/sqlite.js';
22
- import type { HistoryExporter } from '../../../src/daemon/handlers/history-exporter.js';
23
- import type { StopEvent } from '../../../src/core/types.js';
24
-
25
- const SESSION = 'sess-behavior-summary';
26
- const PROJECT = '/tmp/proj-behavior-summary';
27
-
28
- function nowIso(): string {
29
- return new Date().toISOString();
30
- }
31
-
32
- function makeEvent(): StopEvent {
33
- return {
34
- session_id: SESSION,
35
- project_path: PROJECT,
36
- timestamp: nowIso(),
37
- hook_type: 'Stop',
38
- };
39
- }
40
-
41
- describe('StopHandler.generateBehaviorSummary (safety-net)', () => {
42
- let storage: SQLiteStorage;
43
- let exporter: HistoryExporter;
44
- let saved: { content: string | null };
45
-
46
- // Build a mock ResumeManager that returns a known stub from generate() and
47
- // captures whatever the handler ends up passing to save().
48
- function makeResumeMock(): { generate: ReturnType<typeof vi.fn>; save: ReturnType<typeof vi.fn>; load: ReturnType<typeof vi.fn>; clear: ReturnType<typeof vi.fn> } {
49
- return {
50
- generate: vi.fn().mockReturnValue('STUB_RESUME\n_生成时间: 2026-01-01T00:00:00.000Z_\n'),
51
- save: vi.fn((_p: string, c: string) => { saved.content = c; }),
52
- load: vi.fn(),
53
- clear: vi.fn(),
54
- };
55
- }
56
-
57
- beforeEach(() => {
58
- storage = new SQLiteStorage(':memory:');
59
- saved = { content: null };
60
- exporter = { export: vi.fn().mockResolvedValue(undefined) } as unknown as HistoryExporter;
61
- });
62
-
63
- afterEach(() => {
64
- storage.close();
65
- });
66
-
67
- it('does not append summary when storage has no events for the session', async () => {
68
- const resume = makeResumeMock();
69
- const handler = new StopHandler(exporter, resume as any, null, storage, null, null);
70
-
71
- const res = await handler.handle(makeEvent());
72
- expect(res.allow).toBe(true);
73
- // resume.save should still be called (resume content exists)
74
- expect(resume.save).toHaveBeenCalledTimes(1);
75
- expect(saved.content).not.toBeNull();
76
- // Behavior summary line MUST NOT appear when there are no tool calls
77
- expect(saved.content!).not.toContain('会话行为');
78
- });
79
-
80
- it('appends a tool-only behavior summary when only tool events exist', async () => {
81
- // Seed events: 2 Bash, 1 Edit — no Agent/Task, no skill invocations
82
- storage.writeEvent({
83
- session_id: SESSION, project_path: PROJECT, timestamp: nowIso(),
84
- hook_type: 'PreToolUse', tool_name: 'Bash', tool_input: { command: 'ls' },
85
- });
86
- storage.writeEvent({
87
- session_id: SESSION, project_path: PROJECT, timestamp: nowIso(),
88
- hook_type: 'PreToolUse', tool_name: 'Bash', tool_input: { command: 'pwd' },
89
- });
90
- storage.writeEvent({
91
- session_id: SESSION, project_path: PROJECT, timestamp: nowIso(),
92
- hook_type: 'PreToolUse', tool_name: 'Edit', tool_input: { file_path: '/x' },
93
- });
94
-
95
- const resume = makeResumeMock();
96
- const handler = new StopHandler(exporter, resume as any, null, storage, null, null);
97
-
98
- const res = await handler.handle(makeEvent());
99
- expect(res.allow).toBe(true);
100
- expect(saved.content).not.toBeNull();
101
-
102
- // Must contain a behavior summary line with total tool calls
103
- expect(saved.content!).toContain('**会话行为**');
104
- expect(saved.content!).toContain('工具调用 3 次');
105
- expect(saved.content!).toContain('Agent 委托 0 次');
106
- expect(saved.content!).toContain('Skill 调用 0 次');
107
- });
108
-
109
- it('includes Agent delegation counts and agent type names', async () => {
110
- // 2 normal tools + 2 Task delegations to different subagents
111
- storage.writeEvent({
112
- session_id: SESSION, project_path: PROJECT, timestamp: nowIso(),
113
- hook_type: 'PreToolUse', tool_name: 'Bash', tool_input: { command: 'ls' },
114
- });
115
- storage.writeEvent({
116
- session_id: SESSION, project_path: PROJECT, timestamp: nowIso(),
117
- hook_type: 'PreToolUse', tool_name: 'Read', tool_input: { file_path: '/a' },
118
- });
119
- storage.writeEvent({
120
- session_id: SESSION, project_path: PROJECT, timestamp: nowIso(),
121
- hook_type: 'PreToolUse', tool_name: 'Task',
122
- tool_input: { subagent_type: 'explore', description: 'find x' },
123
- });
124
- storage.writeEvent({
125
- session_id: SESSION, project_path: PROJECT, timestamp: nowIso(),
126
- hook_type: 'PreToolUse', tool_name: 'Task',
127
- tool_input: { subagent_type: 'coder', description: 'fix y' },
128
- });
129
-
130
- const resume = makeResumeMock();
131
- const handler = new StopHandler(exporter, resume as any, null, storage, null, null);
132
-
133
- await handler.handle(makeEvent());
134
- expect(saved.content).not.toBeNull();
135
-
136
- const summary = saved.content!;
137
- expect(summary).toContain('工具调用 4 次');
138
- expect(summary).toContain('Agent 委托 2 次');
139
- // Agent names appear in parenthesised detail
140
- expect(summary).toMatch(/explore|coder/);
141
- });
142
-
143
- it('includes skill invocation count when skill_invocations rows exist', async () => {
144
- storage.writeEvent({
145
- session_id: SESSION, project_path: PROJECT, timestamp: nowIso(),
146
- hook_type: 'PreToolUse', tool_name: 'Bash', tool_input: { command: 'ls' },
147
- });
148
- storage.writeSkillInvocation({
149
- id: 'si-bs-1', route_request_id: null, session_id: SESSION,
150
- agent_id: null, skill_id: 'official-tdd',
151
- invocation_type: 'dynamic', reason: null,
152
- workflow: null, phase: null, feature_slug: null, artifact_path: null,
153
- depth: 0, success: 1, error: null, timestamp: Date.now(),
154
- });
155
- storage.writeSkillInvocation({
156
- id: 'si-bs-2', route_request_id: null, session_id: SESSION,
157
- agent_id: null, skill_id: 'official-debug',
158
- invocation_type: 'dynamic', reason: null,
159
- workflow: null, phase: null, feature_slug: null, artifact_path: null,
160
- depth: 0, success: 1, error: null, timestamp: Date.now(),
161
- });
162
-
163
- const resume = makeResumeMock();
164
- const handler = new StopHandler(exporter, resume as any, null, storage, null, null);
165
-
166
- await handler.handle(makeEvent());
167
- expect(saved.content).not.toBeNull();
168
- expect(saved.content!).toContain('Skill 调用 2 次');
169
- });
170
-
171
- it('does not include events from other sessions in the summary', async () => {
172
- // Same session — 1 tool call
173
- storage.writeEvent({
174
- session_id: SESSION, project_path: PROJECT, timestamp: nowIso(),
175
- hook_type: 'PreToolUse', tool_name: 'Bash', tool_input: { command: 'ls' },
176
- });
177
- // Different session — 5 tool calls + 3 skill invocations: MUST be excluded
178
- for (let i = 0; i < 5; i++) {
179
- storage.writeEvent({
180
- session_id: 'other-session', project_path: PROJECT, timestamp: nowIso(),
181
- hook_type: 'PreToolUse', tool_name: 'Bash', tool_input: { command: 'noise' },
182
- });
183
- }
184
- for (let i = 0; i < 3; i++) {
185
- storage.writeSkillInvocation({
186
- id: `si-noise-${i}`, route_request_id: null, session_id: 'other-session',
187
- agent_id: null, skill_id: 'official-noise',
188
- invocation_type: 'dynamic', reason: null,
189
- workflow: null, phase: null, feature_slug: null, artifact_path: null,
190
- depth: 0, success: 1, error: null, timestamp: Date.now(),
191
- });
192
- }
193
-
194
- const resume = makeResumeMock();
195
- const handler = new StopHandler(exporter, resume as any, null, storage, null, null);
196
-
197
- await handler.handle(makeEvent());
198
- expect(saved.content).not.toBeNull();
199
- expect(saved.content!).toContain('工具调用 1 次');
200
- expect(saved.content!).toContain('Skill 调用 0 次');
201
- });
202
- });
@@ -1,84 +0,0 @@
1
- /**
2
- * 单测:TaskSegmenter.completeCurrentTask recover fallback
3
- *
4
- * 覆盖:
5
- * - Map 为空但 DB 有 active task → completeCurrentTask 仍能将其转为 completed
6
- * - Map 不为空的正常路径不退化
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
- import { TaskSegmenter } from '../../../src/daemon/services/task-segmenter.js';
15
-
16
- const SESSION = 'sess-segmenter-recover';
17
-
18
- function openStorage(tmp: string): SQLiteStorage {
19
- return new SQLiteStorage(join(tmp, 'data.db'));
20
- }
21
-
22
- describe('TaskSegmenter.completeCurrentTask recover fallback', () => {
23
- let tmp: string;
24
- let storage: SQLiteStorage;
25
-
26
- beforeEach(() => {
27
- tmp = mkdtempSync(join(tmpdir(), 'forge-segmenter-recover-'));
28
- storage = openStorage(tmp);
29
- });
30
-
31
- afterEach(() => {
32
- try { storage.close(); } catch { /* ignore */ }
33
- rmSync(tmp, { recursive: true, force: true });
34
- });
35
-
36
- it('Map 为空但 DB 有 active task → completeCurrentTask 将其转 completed', () => {
37
- // 直接写 active task 到 DB(不经过 processPrompt,模拟 daemon 重启后内存丢失)
38
- storage.writeTask({
39
- id: 'task-in-db',
40
- session_id: SESSION,
41
- title: 'Persisted active task',
42
- start_time: '2026-01-01T10:00:00.000Z',
43
- });
44
-
45
- // 确认此刻 DB 中是 active
46
- expect(storage.getTask('task-in-db')?.status).toBe('active');
47
-
48
- // 创建新的 TaskSegmenter,Map 为空(模拟重启)
49
- const segmenter = new TaskSegmenter(storage);
50
-
51
- // Stop hook 触发 completeCurrentTask,Map 中没有该 session 的 entry
52
- segmenter.completeCurrentTask(SESSION, '2026-01-01T10:30:00.000Z');
53
-
54
- // task 应该被 recover 并标记为 completed
55
- const task = storage.getTask('task-in-db');
56
- expect(task?.status).toBe('completed');
57
- expect(task?.end_time).toBe('2026-01-01T10:30:00.000Z');
58
- });
59
-
60
- it('DB 中无 active task 时 completeCurrentTask 安全退出(不报错)', () => {
61
- // 完全空 DB,completeCurrentTask 不应抛出
62
- const segmenter = new TaskSegmenter(storage);
63
- expect(() => {
64
- segmenter.completeCurrentTask(SESSION, '2026-01-01T10:30:00.000Z');
65
- }).not.toThrow();
66
- });
67
-
68
- it('Map 有 entry 时(正常路径)completeCurrentTask 正常关闭 task', () => {
69
- const segmenter = new TaskSegmenter(storage);
70
-
71
- // 通过 processPrompt 走正常路径,将 task 写入 Map
72
- const taskId = segmenter.processPrompt(SESSION, 'implement feature', '2026-01-01T10:00:00.000Z');
73
-
74
- // 确认 DB 中是 active
75
- expect(storage.getTask(taskId)?.status).toBe('active');
76
-
77
- // completeCurrentTask 正常关闭
78
- segmenter.completeCurrentTask(SESSION, '2026-01-01T10:30:00.000Z');
79
-
80
- const task = storage.getTask(taskId);
81
- expect(task?.status).toBe('completed');
82
- expect(task?.end_time).toBe('2026-01-01T10:30:00.000Z');
83
- });
84
- });