@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,211 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
- import { InvocationGuard } from '../../../src/skills/invocation-guard.js';
3
-
4
- describe('InvocationGuard - TTL Mechanism', () => {
5
- let guard: InvocationGuard;
6
-
7
- beforeEach(() => {
8
- guard = new InvocationGuard();
9
- });
10
-
11
- afterEach(() => {
12
- guard.stopCleanupTimer();
13
- });
14
-
15
- describe('TTL expiration', () => {
16
- it('should allow invocation after session expires', () => {
17
- const sessionId = 'test-session-1';
18
- const skillId = 'test-skill';
19
-
20
- // Record initial invocation
21
- guard.record(sessionId, skillId);
22
-
23
- // Verify skill is blocked (idempotent guard)
24
- let result = guard.check(sessionId, skillId);
25
- expect(result.allowed).toBe(false);
26
- expect(result.reason).toContain('already invoked');
27
-
28
- // Fast-forward time by 31 minutes (beyond TTL)
29
- const stats = guard.getStats(sessionId);
30
- if (stats) {
31
- stats.lastAccessTime = Date.now() - 31 * 60 * 1000;
32
- }
33
-
34
- // Should allow invocation after expiration
35
- result = guard.check(sessionId, skillId);
36
- expect(result.allowed).toBe(true);
37
- });
38
-
39
- it('should update lastAccessTime on record', () => {
40
- const sessionId = 'test-session-2';
41
- const skillId = 'test-skill';
42
-
43
- const before = Date.now();
44
- guard.record(sessionId, skillId);
45
- const after = Date.now();
46
-
47
- const stats = guard.getStats(sessionId);
48
- expect(stats).not.toBeNull();
49
- expect(stats!.lastAccessTime).toBeGreaterThanOrEqual(before);
50
- expect(stats!.lastAccessTime).toBeLessThanOrEqual(after);
51
- });
52
-
53
- it('should update lastAccessTime on complete', () => {
54
- const sessionId = 'test-session-3';
55
- const skillId = 'test-skill';
56
-
57
- guard.record(sessionId, skillId);
58
- const initialTime = guard.getStats(sessionId)!.lastAccessTime;
59
-
60
- // Wait a bit
61
- vi.useFakeTimers();
62
- vi.advanceTimersByTime(1000);
63
-
64
- guard.complete(sessionId);
65
- const updatedTime = guard.getStats(sessionId)!.lastAccessTime;
66
-
67
- expect(updatedTime).toBeGreaterThan(initialTime);
68
-
69
- vi.useRealTimers();
70
- });
71
- });
72
-
73
- describe('automatic cleanup', () => {
74
- it('should clean up expired sessions', () => {
75
- const sessionId1 = 'expired-session-1';
76
- const sessionId2 = 'active-session-2';
77
-
78
- // Record two sessions
79
- guard.record(sessionId1, 'skill-1');
80
- guard.record(sessionId2, 'skill-2');
81
-
82
- expect(guard.getSessionCount()).toBe(2);
83
-
84
- // Expire first session
85
- const stats1 = guard.getStats(sessionId1);
86
- if (stats1) {
87
- stats1.lastAccessTime = Date.now() - 31 * 60 * 1000;
88
- }
89
-
90
- // Trigger cleanup manually
91
- (guard as any).cleanupExpiredSessions();
92
-
93
- // Only active session should remain
94
- expect(guard.getSessionCount()).toBe(1);
95
- expect(guard.getStats(sessionId1)).toBeNull();
96
- expect(guard.getStats(sessionId2)).not.toBeNull();
97
- });
98
-
99
- it('should not clean up active sessions', () => {
100
- const sessionId = 'active-session';
101
-
102
- guard.record(sessionId, 'skill-1');
103
- expect(guard.getSessionCount()).toBe(1);
104
-
105
- // Trigger cleanup
106
- (guard as any).cleanupExpiredSessions();
107
-
108
- // Session should still exist
109
- expect(guard.getSessionCount()).toBe(1);
110
- expect(guard.getStats(sessionId)).not.toBeNull();
111
- });
112
-
113
- it('should handle empty sessions map', () => {
114
- expect(guard.getSessionCount()).toBe(0);
115
-
116
- // Should not throw
117
- expect(() => {
118
- (guard as any).cleanupExpiredSessions();
119
- }).not.toThrow();
120
-
121
- expect(guard.getSessionCount()).toBe(0);
122
- });
123
- });
124
-
125
- describe('cleanup timer', () => {
126
- it('should start cleanup timer on construction', () => {
127
- const newGuard = new InvocationGuard();
128
- expect((newGuard as any).cleanupTimer).not.toBeNull();
129
- newGuard.stopCleanupTimer();
130
- });
131
-
132
- it('should stop cleanup timer', () => {
133
- guard.stopCleanupTimer();
134
- expect((guard as any).cleanupTimer).toBeNull();
135
- });
136
-
137
- it('should not start multiple timers', () => {
138
- const timer1 = (guard as any).cleanupTimer;
139
- (guard as any).startCleanupTimer();
140
- const timer2 = (guard as any).cleanupTimer;
141
-
142
- expect(timer1).toBe(timer2);
143
- });
144
- });
145
-
146
- describe('getSessionCount', () => {
147
- it('should return 0 for empty guard', () => {
148
- expect(guard.getSessionCount()).toBe(0);
149
- });
150
-
151
- it('should return correct count after recording', () => {
152
- guard.record('session-1', 'skill-1');
153
- expect(guard.getSessionCount()).toBe(1);
154
-
155
- guard.record('session-2', 'skill-2');
156
- expect(guard.getSessionCount()).toBe(2);
157
- });
158
-
159
- it('should decrease count after clear', () => {
160
- guard.record('session-1', 'skill-1');
161
- guard.record('session-2', 'skill-2');
162
- expect(guard.getSessionCount()).toBe(2);
163
-
164
- guard.clear('session-1');
165
- expect(guard.getSessionCount()).toBe(1);
166
- });
167
- });
168
-
169
- describe('integration with existing functionality', () => {
170
- it('should maintain depth tracking with TTL', () => {
171
- const sessionId = 'test-session';
172
-
173
- guard.record(sessionId, 'skill-1');
174
- expect(guard.getStats(sessionId)!.depth).toBe(1);
175
-
176
- guard.record(sessionId, 'skill-2');
177
- expect(guard.getStats(sessionId)!.depth).toBe(2);
178
-
179
- guard.complete(sessionId);
180
- expect(guard.getStats(sessionId)!.depth).toBe(1);
181
-
182
- // lastAccessTime should be updated
183
- expect(guard.getStats(sessionId)!.lastAccessTime).toBeLessThanOrEqual(Date.now());
184
- });
185
-
186
- it('should maintain total count with TTL', () => {
187
- const sessionId = 'test-session';
188
-
189
- guard.record(sessionId, 'skill-1');
190
- expect(guard.getStats(sessionId)!.total).toBe(1);
191
-
192
- guard.record(sessionId, 'skill-2');
193
- expect(guard.getStats(sessionId)!.total).toBe(2);
194
-
195
- // lastAccessTime should be updated
196
- expect(guard.getStats(sessionId)!.lastAccessTime).toBeLessThanOrEqual(Date.now());
197
- });
198
-
199
- it('should maintain calledSkills set with TTL', () => {
200
- const sessionId = 'test-session';
201
-
202
- guard.record(sessionId, 'skill-1');
203
- guard.record(sessionId, 'skill-2');
204
-
205
- const stats = guard.getStats(sessionId);
206
- expect(stats!.calledSkills.has('skill-1')).toBe(true);
207
- expect(stats!.calledSkills.has('skill-2')).toBe(true);
208
- expect(stats!.lastAccessTime).toBeLessThanOrEqual(Date.now());
209
- });
210
- });
211
- });
@@ -1,126 +0,0 @@
1
- import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
- import { mkdtempSync, rmSync, writeFileSync, mkdirSync } from 'node:fs';
3
- import { tmpdir } from 'node:os';
4
- import { join } from 'node:path';
5
- import { loadOfficialSkills } from '../../../src/skills/official-skills.js';
6
-
7
- describe('loadOfficialSkills', () => {
8
- let tempDir: string;
9
-
10
- beforeAll(() => {
11
- tempDir = mkdtempSync(join(tmpdir(), 'forge-official-skills-test-'));
12
- });
13
-
14
- afterAll(() => {
15
- rmSync(tempDir, { recursive: true, force: true });
16
- });
17
-
18
- it('loads all .md files from the given directory', () => {
19
- writeFileSync(
20
- join(tempDir, 'test-skill-a.md'),
21
- `---\nname: test-skill-a\nversion: 1.0.0\ndescription: "Test skill A"\ntags: [tag1, tag2]\n---\n\n# Test Skill A\n\nBody content here.`
22
- );
23
- writeFileSync(
24
- join(tempDir, 'test-skill-b.md'),
25
- `---\nname: test-skill-b\nversion: 2.0.0\ndescription: "Test skill B"\nkeywords: [kw1, kw2]\n---\n\n# Test Skill B\n\nMore content.`
26
- );
27
-
28
- const skills = loadOfficialSkills(tempDir);
29
- const names = skills.map(s => s.name);
30
- expect(names).toContain('test-skill-a');
31
- expect(names).toContain('test-skill-b');
32
- expect(skills.length).toBeGreaterThanOrEqual(2);
33
- });
34
-
35
- it('parses frontmatter correctly (name, version, description, keywords from tags)', () => {
36
- const skillDir = mkdtempSync(join(tmpdir(), 'forge-frontmatter-test-'));
37
- try {
38
- writeFileSync(
39
- join(skillDir, 'my-skill.md'),
40
- `---\nname: my-skill\nversion: 3.1.0\ndescription: "My test skill"\ntags: [alpha, beta, gamma]\n---\n\n# My Skill\n\nContent.`
41
- );
42
-
43
- const skills = loadOfficialSkills(skillDir);
44
- expect(skills).toHaveLength(1);
45
- const skill = skills[0];
46
- expect(skill.name).toBe('my-skill');
47
- expect(skill.version).toBe('3.1.0');
48
- expect(skill.description).toBe('My test skill');
49
- expect(skill.keywords).toEqual(['alpha', 'beta', 'gamma']);
50
- } finally {
51
- rmSync(skillDir, { recursive: true, force: true });
52
- }
53
- });
54
-
55
- it('parses frontmatter correctly (keywords field takes precedence over tags)', () => {
56
- const skillDir = mkdtempSync(join(tmpdir(), 'forge-kw-test-'));
57
- try {
58
- writeFileSync(
59
- join(skillDir, 'kw-skill.md'),
60
- `---\nname: kw-skill\nversion: 1.0.0\ndescription: "KW skill"\nkeywords: [kw-a, kw-b]\ntags: [tag-a]\n---\n\n# KW Skill`
61
- );
62
-
63
- const skills = loadOfficialSkills(skillDir);
64
- expect(skills).toHaveLength(1);
65
- // keywords takes precedence (data.keywords ?? data.tags => keywords wins)
66
- expect(skills[0].keywords).toEqual(['kw-a', 'kw-b']);
67
- } finally {
68
- rmSync(skillDir, { recursive: true, force: true });
69
- }
70
- });
71
-
72
- it('falls back to filename as name when frontmatter name is missing', () => {
73
- const skillDir = mkdtempSync(join(tmpdir(), 'forge-fallback-test-'));
74
- try {
75
- writeFileSync(
76
- join(skillDir, 'unnamed-skill.md'),
77
- `---\nversion: 1.0.0\n---\n\n# Unnamed Skill`
78
- );
79
-
80
- const skills = loadOfficialSkills(skillDir);
81
- expect(skills).toHaveLength(1);
82
- expect(skills[0].name).toBe('unnamed-skill');
83
- } finally {
84
- rmSync(skillDir, { recursive: true, force: true });
85
- }
86
- });
87
-
88
- it('ignores non-.md files in the directory', () => {
89
- const skillDir = mkdtempSync(join(tmpdir(), 'forge-nonmd-test-'));
90
- try {
91
- writeFileSync(join(skillDir, 'skill.md'), `---\nname: skill\n---\n# Skill`);
92
- writeFileSync(join(skillDir, 'README.txt'), 'not a skill');
93
- writeFileSync(join(skillDir, 'index.json'), '{}');
94
-
95
- const skills = loadOfficialSkills(skillDir);
96
- expect(skills).toHaveLength(1);
97
- expect(skills[0].name).toBe('skill');
98
- } finally {
99
- rmSync(skillDir, { recursive: true, force: true });
100
- }
101
- });
102
-
103
- it('throws a meaningful error when directory does not exist', () => {
104
- const nonExistentDir = join(tmpdir(), 'forge-does-not-exist-' + Date.now());
105
- expect(() => loadOfficialSkills(nonExistentDir)).toThrow(/Official skills directory not found/);
106
- });
107
-
108
- it('loads all official skills from the actual built-in directory', async () => {
109
- // Verify the real built-in directory works end-to-end
110
- const { fileURLToPath } = await import('node:url');
111
- const { dirname, join: pathJoin } = await import('node:path');
112
- const thisDir = dirname(fileURLToPath(import.meta.url));
113
- const builtinDir = pathJoin(thisDir, '../../../src/skills/official');
114
-
115
- const skills = loadOfficialSkills(builtinDir);
116
- // Should have 17 official skills
117
- expect(skills.length).toBeGreaterThanOrEqual(17);
118
- // Spot-check a known skill
119
- const harness = skills.find(s => s.name === 'official-harness-engineering');
120
- expect(harness).toBeDefined();
121
- expect(harness!.version).toBe('2.0.1');
122
- expect(harness!.description).toContain('Harness Engineering');
123
- // path should NOT be '<built-in>'
124
- expect(harness!.name).toBe('official-harness-engineering');
125
- });
126
- });
@@ -1,92 +0,0 @@
1
- /**
2
- * Unit tests for SkillRegistry multi-format support
3
- * Tests both flat .md and directory-based SKILL.md formats
4
- */
5
-
6
- import { describe, it, expect } from 'vitest';
7
- import matter from 'gray-matter';
8
-
9
- describe('SkillRegistry - Multi-format Support', () => {
10
- it('should parse flat .md format with description', () => {
11
- const flatSkillContent = `---
12
- name: flat-skill
13
- description: A flat format skill for testing
14
- keywords: [test, flat, format]
15
- ---
16
-
17
- # Flat Skill
18
-
19
- This is a flat format skill.`;
20
-
21
- const parsed = matter(flatSkillContent);
22
- expect(parsed.data.name).toBe('flat-skill');
23
- expect(parsed.data.description).toBe('A flat format skill for testing');
24
- expect(parsed.data.keywords).toEqual(['test', 'flat', 'format']);
25
- expect(parsed.content.trim()).toContain('# Flat Skill');
26
- });
27
-
28
- it('should parse directory format SKILL.md with description', () => {
29
- const dirSkillContent = `---
30
- name: dir-skill
31
- description: A directory format skill from agent-skills
32
- keywords: [agent, directory, skill]
33
- ---
34
-
35
- # Directory Skill
36
-
37
- This is a directory format skill (agent-skills format).`;
38
-
39
- const parsed = matter(dirSkillContent);
40
- expect(parsed.data.name).toBe('dir-skill');
41
- expect(parsed.data.description).toBe('A directory format skill from agent-skills');
42
- expect(parsed.data.keywords).toEqual(['agent', 'directory', 'skill']);
43
- expect(parsed.content.trim()).toContain('# Directory Skill');
44
- });
45
-
46
- it('should handle skills without keywords', () => {
47
- const noKeywordsContent = `---
48
- name: no-keywords-skill
49
- description: A skill without keywords for fallback testing
50
- ---
51
-
52
- # No Keywords Skill
53
-
54
- This skill has no keywords, should fallback to description matching.`;
55
-
56
- const parsed = matter(noKeywordsContent);
57
- expect(parsed.data.name).toBe('no-keywords-skill');
58
- expect(parsed.data.description).toBe('A skill without keywords for fallback testing');
59
- expect(parsed.data.keywords).toBeUndefined();
60
- expect(parsed.content.trim()).toContain('# No Keywords Skill');
61
- });
62
-
63
- it('should handle skills without description', () => {
64
- const noDescContent = `---
65
- name: no-desc-skill
66
- keywords: [test, nodesc]
67
- ---
68
-
69
- # No Description Skill
70
-
71
- This skill has no description field.`;
72
-
73
- const parsed = matter(noDescContent);
74
- expect(parsed.data.name).toBe('no-desc-skill');
75
- expect(parsed.data.description).toBeUndefined();
76
- expect(parsed.data.keywords).toEqual(['test', 'nodesc']);
77
- });
78
-
79
- it('should handle empty frontmatter gracefully', () => {
80
- const emptyFrontmatterContent = `---
81
- ---
82
-
83
- # Minimal Skill
84
-
85
- Just content, no metadata.`;
86
-
87
- const parsed = matter(emptyFrontmatterContent);
88
- expect(parsed.data).toEqual({});
89
- expect(parsed.content.trim()).toContain('# Minimal Skill');
90
- });
91
- });
92
-
@@ -1,183 +0,0 @@
1
- /**
2
- * SocketServer behavior tests
3
- *
4
- * 重点验证 L3 性能修复:buffer 分片到达时,仅在遇到换行符后才解析,
5
- * 避免大事件每个 chunk 都跑一次完整 JSON.parse 的 N² 行为。
6
- */
7
- import { describe, it, expect, afterEach, vi } from 'vitest';
8
- import net from 'node:net';
9
- import os from 'node:os';
10
- import path from 'node:path';
11
- import fs from 'node:fs';
12
- import { SocketServer } from '../../src/daemon/server.js';
13
- import { EventParser } from '../../src/daemon/event-parser.js';
14
-
15
- function makeSockPath(): string {
16
- const name = `forge-test-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.sock`;
17
- return path.join(os.tmpdir(), name);
18
- }
19
-
20
- function makeEvent(overrides: Record<string, unknown> = {}): Record<string, unknown> {
21
- return {
22
- hook_type: 'PostToolUse',
23
- timestamp: new Date().toISOString(),
24
- session_id: 'test-session',
25
- project_path: '/tmp/test',
26
- tool_name: 'Read',
27
- ...overrides,
28
- };
29
- }
30
-
31
- function sendOnSocket(sockPath: string, chunks: string[], delayMs = 5): Promise<string> {
32
- return new Promise((resolve, reject) => {
33
- const client = net.createConnection(sockPath);
34
- let response = '';
35
- client.on('connect', async () => {
36
- for (const chunk of chunks) {
37
- client.write(chunk);
38
- if (delayMs > 0) await new Promise((r) => setTimeout(r, delayMs));
39
- }
40
- });
41
- client.on('data', (data) => {
42
- response += data.toString();
43
- });
44
- client.on('end', () => resolve(response));
45
- client.on('close', () => resolve(response));
46
- client.on('error', reject);
47
- });
48
- }
49
-
50
- describe('SocketServer — L3 chunked-parse behavior', () => {
51
- const servers: SocketServer[] = [];
52
- const sockPaths: string[] = [];
53
-
54
- afterEach(async () => {
55
- for (const s of servers) await s.close();
56
- servers.length = 0;
57
- for (const p of sockPaths) {
58
- try { fs.unlinkSync(p); } catch { /* ignore */ }
59
- }
60
- sockPaths.length = 0;
61
- });
62
-
63
- it('parses a complete single-chunk message (with trailing newline)', async () => {
64
- const sockPath = makeSockPath();
65
- sockPaths.push(sockPath);
66
-
67
- const handler = vi.fn().mockReturnValue({ allow: true });
68
- const server = new SocketServer(sockPath, handler);
69
- servers.push(server);
70
- await new Promise((r) => setTimeout(r, 30));
71
-
72
- const event = makeEvent();
73
- const payload = JSON.stringify(event) + '\n';
74
- await sendOnSocket(sockPath, [payload]);
75
-
76
- expect(handler).toHaveBeenCalledTimes(1);
77
- const passed = handler.mock.calls[0][0];
78
- expect(passed.hook_type).toBe('PostToolUse');
79
- expect(passed.session_id).toBe('test-session');
80
- });
81
-
82
- it('parses a large message split across many chunks WITHOUT per-chunk JSON.parse', async () => {
83
- const sockPath = makeSockPath();
84
- sockPaths.push(sockPath);
85
-
86
- // 监视 JSON.parse —— 用于断言不再 N²(不能为每个 chunk 都 parse 一次)
87
- const parseSpy = vi.spyOn(JSON, 'parse');
88
-
89
- const handler = vi.fn().mockReturnValue({ allow: true });
90
- const server = new SocketServer(sockPath, handler);
91
- servers.push(server);
92
- await new Promise((r) => setTimeout(r, 30));
93
-
94
- // 构造大事件(约 50KB tool_output),分成 ~20 个 chunk 喂入
95
- const bigOutput = 'x'.repeat(50_000);
96
- const event = makeEvent({ tool_output: { data: bigOutput } });
97
- const payload = JSON.stringify(event) + '\n';
98
- const chunkSize = Math.ceil(payload.length / 20);
99
- const chunks: string[] = [];
100
- for (let i = 0; i < payload.length; i += chunkSize) {
101
- chunks.push(payload.slice(i, i + chunkSize));
102
- }
103
- expect(chunks.length).toBeGreaterThan(10);
104
-
105
- const callsBefore = parseSpy.mock.calls.length;
106
- await sendOnSocket(sockPath, chunks, 2);
107
- const callsAfter = parseSpy.mock.calls.length;
108
-
109
- // 关键断言:handler 必须收到完整事件
110
- expect(handler).toHaveBeenCalledTimes(1);
111
-
112
- // 关键断言:JSON.parse 调用次数远少于 chunk 数(旧代码会 ≈ chunks.length 次)
113
- // 现在应该只有:可能 1 次(无 auth 时)或 2 次(有 auth 时——auth 检查 + EventParser)
114
- const parseDelta = callsAfter - callsBefore;
115
- expect(parseDelta).toBeLessThanOrEqual(3);
116
- expect(parseDelta).toBeLessThan(chunks.length); // 强对比旧 N² 行为
117
-
118
- parseSpy.mockRestore();
119
- });
120
-
121
- it('rejects oversized buffer that never sees a newline', async () => {
122
- const sockPath = makeSockPath();
123
- sockPaths.push(sockPath);
124
-
125
- const handler = vi.fn();
126
- const server = new SocketServer(sockPath, handler);
127
- servers.push(server);
128
- await new Promise((r) => setTimeout(r, 30));
129
-
130
- // 600KB 无换行 —— 必须超过 MAX_BUFFER_SIZE (512KB) 并被丢弃
131
- // 注意:服务端 destroy 后客户端继续 write 会触发 EPIPE,要容错
132
- const oversized = 'a'.repeat(600 * 1024);
133
- await new Promise<void>((resolve) => {
134
- const client = net.createConnection(sockPath);
135
- client.on('error', () => { /* EPIPE 预期 */ });
136
- client.on('close', () => resolve());
137
- client.on('connect', () => {
138
- client.write(oversized, () => {
139
- // 给服务端处理时间
140
- setTimeout(() => client.destroy(), 80);
141
- });
142
- });
143
- });
144
-
145
- expect(handler).not.toHaveBeenCalled();
146
- });
147
-
148
- it('uses fallback parse on socket end() for messages without trailing newline', async () => {
149
- const sockPath = makeSockPath();
150
- sockPaths.push(sockPath);
151
-
152
- const handler = vi.fn().mockReturnValue({ allow: true });
153
- const server = new SocketServer(sockPath, handler);
154
- servers.push(server);
155
- await new Promise((r) => setTimeout(r, 30));
156
-
157
- // 无换行结尾 —— 走 socket.end() 兜底
158
- const event = makeEvent();
159
- const payload = JSON.stringify(event);
160
- await new Promise<void>((resolve, reject) => {
161
- const client = net.createConnection(sockPath);
162
- client.on('connect', () => {
163
- client.end(payload); // 写完即关闭:触发 server 的 'end' 事件
164
- });
165
- client.on('close', () => resolve());
166
- client.on('error', reject);
167
- });
168
- // 等 server 端处理完
169
- await new Promise((r) => setTimeout(r, 50));
170
-
171
- expect(handler).toHaveBeenCalledTimes(1);
172
- });
173
- });
174
-
175
- describe('EventParser — sanity', () => {
176
- it('parses a valid event JSON', () => {
177
- const parser = new EventParser();
178
- const raw = JSON.stringify(makeEvent());
179
- const out = parser.parse(raw);
180
- expect(out.session_id).toBe('test-session');
181
- expect(out.hook_type).toBe('PostToolUse');
182
- });
183
- });