@winspan/claude-forge 8.51.1 → 8.54.3

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