@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,63 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Claude Forge - PreToolUse Hook(双向通信)
3
-
4
- # shellcheck source=./hook-lib.sh
5
- source "$(dirname "$0")/hook-lib.sh"
6
-
7
- AUTH_TOKEN=$(cat "$HOME/.claude-forge/daemon.token" 2>/dev/null || echo '')
8
-
9
- # 读取 stdin(Claude Code 传入的 JSON)
10
- INPUT=$(cat)
11
-
12
- # 提取字段(单次 jq 调用,替代 3 个 python3 进程)
13
- TOOL_NAME="${CLAUDE_TOOL_NAME:-$(echo "$INPUT" | jq -r '.tool_name // ""')}"
14
- TOOL_INPUT=$(echo "$INPUT" | jq -c '.tool_input // {}')
15
- RAW_CWD=$(echo "$INPUT" | jq -r '.cwd // ""')
16
- PROJECT_PATH=$(resolve_project_path "${RAW_CWD:-$PWD}")
17
- SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // .sessionId // ""')
18
- SESSION_ID="${SESSION_ID:-${CLAUDE_CODE_SESSION_ID:-cli}}"
19
-
20
- # 构造事件 JSON(jq -n 替代 python3 heredoc)
21
- TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
22
- EVENT=$(jq -n \
23
- --arg hook_type "PreToolUse" \
24
- --arg timestamp "$TIMESTAMP" \
25
- --arg session_id "$SESSION_ID" \
26
- --arg project_path "$PROJECT_PATH" \
27
- --arg tool_name "$TOOL_NAME" \
28
- --argjson tool_input "$TOOL_INPUT" \
29
- --arg auth "$AUTH_TOKEN" \
30
- '{hook_type: $hook_type, timestamp: $timestamp, session_id: $session_id,
31
- project_path: $project_path, tool_name: $tool_name, tool_input: $tool_input,
32
- _auth: $auth}')
33
-
34
- # 发送事件(5 秒超时;失败时入队)
35
- send_event_or_enqueue "$EVENT" 5
36
- RESPONSE="$HOOK_RESPONSE"
37
-
38
- if [ -n "$RESPONSE" ]; then
39
- ALLOW=$(echo "$RESPONSE" | jq -r '.allow // true')
40
-
41
- if [ "$ALLOW" = "false" ]; then
42
- # 优先使用 hookSpecificOutput(Claude Code 官方格式),否则降级到 stderr + exit 2
43
- HAS_HOOK_OUTPUT=$(echo "$RESPONSE" | jq -r 'if .hookSpecificOutput then "yes" else "no" end')
44
- if [ "$HAS_HOOK_OUTPUT" = "yes" ]; then
45
- # 输出 hookSpecificOutput 格式到 stdout,但必须 exit 非0 才能真正阻止工具执行
46
- echo "$RESPONSE" | jq '{hookSpecificOutput: .hookSpecificOutput}'
47
- exit 1
48
- else
49
- # 降级:输出原因到 stderr,exit 2 强制阻断
50
- REASON=$(echo "$RESPONSE" | jq -r '.reason // "操作被 Forge 阻断"')
51
- echo "$REASON" >&2
52
- exit 2
53
- fi
54
- fi
55
-
56
- # 有 additionalContext 时输出
57
- HAS_CONTEXT=$(echo "$RESPONSE" | jq -r 'if .additionalContext and (.additionalContext != "") then "yes" else "no" end')
58
- if [ "$HAS_CONTEXT" = "yes" ]; then
59
- echo "$RESPONSE"
60
- fi
61
- fi
62
-
63
- exit 0
package/src/hooks/stop.sh DELETED
@@ -1,43 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Claude Forge - Stop Hook
3
-
4
- # shellcheck source=./hook-lib.sh
5
- source "$(dirname "$0")/hook-lib.sh"
6
-
7
- AUTH_TOKEN=$(cat "$HOME/.claude-forge/daemon.token" 2>/dev/null || echo '')
8
-
9
- # 读取 stdin
10
- INPUT=$(cat 2>/dev/null || echo '{}')
11
-
12
- # 提取 cwd,上溯 git root
13
- RAW_CWD=$(echo "$INPUT" | jq -r '.cwd // ""')
14
- PROJECT_PATH=$(resolve_project_path "${RAW_CWD:-$PWD}")
15
- SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // .sessionId // ""')
16
- SESSION_ID="${SESSION_ID:-${CLAUDE_CODE_SESSION_ID:-cli}}"
17
-
18
- # 构造事件 JSON
19
- TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
20
- EVENT=$(jq -n \
21
- --arg hook_type "Stop" \
22
- --arg timestamp "$TIMESTAMP" \
23
- --arg session_id "$SESSION_ID" \
24
- --arg project_path "$PROJECT_PATH" \
25
- --arg auth "$AUTH_TOKEN" \
26
- '{hook_type: $hook_type, timestamp: $timestamp, session_id: $session_id,
27
- project_path: $project_path, tool_name: "stop", tool_input: {}, _auth: $auth}')
28
-
29
- # 发送事件并读取响应(失败时入队)
30
- send_event_or_enqueue "$EVENT" 5
31
- RESPONSE="$HOOK_RESPONSE"
32
-
33
- # 解析响应:allow=false 时输出 reason 并以 exit 2 阻断
34
- if [ -n "$RESPONSE" ]; then
35
- ALLOW=$(echo "$RESPONSE" | jq -r '.allow // true' 2>/dev/null)
36
- if [ "$ALLOW" = "false" ]; then
37
- REASON=$(echo "$RESPONSE" | jq -r '.reason // "质量门禁阻断"' 2>/dev/null)
38
- echo "$REASON"
39
- exit 2
40
- fi
41
- fi
42
-
43
- exit 0
@@ -1,69 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Claude Forge - UserPromptSubmit Hook
3
- # 拦截用户输入,触发意图分析和自动 Pipeline 编排
4
-
5
- # shellcheck source=./hook-lib.sh
6
- source "$(dirname "$0")/hook-lib.sh"
7
-
8
- AUTH_TOKEN=$(cat "$HOME/.claude-forge/daemon.token" 2>/dev/null || echo '')
9
-
10
- # 读取 stdin(Claude Code 传入的 JSON)
11
- INPUT=$(cat)
12
-
13
- # 提取字段(jq 替代 python3)
14
- USER_PROMPT=$(echo "$INPUT" | jq -r '.prompt // ""')
15
- RAW_CWD=$(echo "$INPUT" | jq -r '.cwd // ""')
16
- PROJECT_PATH=$(resolve_project_path "${RAW_CWD:-$PWD}")
17
- SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // .sessionId // ""')
18
- SESSION_ID="${SESSION_ID:-${CLAUDE_CODE_SESSION_ID:-cli}}"
19
-
20
- # 空 prompt 跳过
21
- if [ -z "$USER_PROMPT" ]; then
22
- exit 0
23
- fi
24
-
25
- # 构造事件 JSON(jq -n 替代 python3 heredoc)
26
- TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
27
- EVENT=$(jq -n \
28
- --arg hook_type "UserPromptSubmit" \
29
- --arg timestamp "$TIMESTAMP" \
30
- --arg session_id "$SESSION_ID" \
31
- --arg project_path "$PROJECT_PATH" \
32
- --arg user_prompt "$USER_PROMPT" \
33
- --arg auth "$AUTH_TOKEN" \
34
- '{hook_type: $hook_type, timestamp: $timestamp, session_id: $session_id,
35
- project_path: $project_path, tool_name: "UserPrompt",
36
- user_prompt: $user_prompt, tool_input: {user_prompt: $user_prompt}, _auth: $auth}')
37
-
38
- if [ -z "$EVENT" ]; then
39
- exit 0
40
- fi
41
-
42
- # 发送事件(10 秒超时,AI 分析可能需要更多时间)
43
- # 失败时自动入队(daemon 停机期间事件不丢失)
44
- send_event_or_enqueue "$EVENT" 10
45
- RESPONSE="$HOOK_RESPONSE"
46
-
47
- if [ -n "$RESPONSE" ]; then
48
- HAS_SYSTEM=$(echo "$RESPONSE" | jq -r 'if .systemMessage and (.systemMessage != "") then "yes" else "no" end')
49
- HAS_CONTEXT=$(echo "$RESPONSE" | jq -r 'if .additionalContext and (.additionalContext != "") then "yes" else "no" end')
50
-
51
- if [ "$HAS_SYSTEM" = "yes" ] || [ "$HAS_CONTEXT" = "yes" ]; then
52
- # stderr 输出摘要到终端
53
- if [ "$HAS_SYSTEM" = "yes" ]; then
54
- SM_LEN=$(echo "$RESPONSE" | jq -r '.systemMessage | length')
55
- printf '\033[36m💉 [Forge] 注入 systemMessage (%s chars)\033[0m\n' "$SM_LEN" >&2
56
- fi
57
- if [ "$HAS_CONTEXT" = "yes" ]; then
58
- CONTEXT=$(echo "$RESPONSE" | jq -r '.additionalContext // ""')
59
- DISPLAY_LINES=$(printf '%s' "$CONTEXT" | head -3)
60
- if [ -n "$DISPLAY_LINES" ]; then
61
- printf '\033[36m💉 [Forge] %s\033[0m\n' "$DISPLAY_LINES" >&2
62
- fi
63
- fi
64
- # stdout 返回给 Claude Code
65
- echo "$RESPONSE"
66
- fi
67
- fi
68
-
69
- exit 0
package/src/mcp/server.ts DELETED
@@ -1,322 +0,0 @@
1
- /**
2
- * MCP Server for claude-forge
3
- *
4
- * 嵌入在 daemon 进程内,通过 HTTP(Streamable HTTP transport)向 Claude Code
5
- * 暴露以下工具:
6
- * - skill_invoke:Agent 按需获取某个 Skill 的方法论内容
7
- * - skill_list:列出所有可用 Skill(id / name / description / keywords)
8
- *
9
- * 调用链路:
10
- * Claude Code → HTTP POST /mcp → MCP server → skillInvoke()/skillList()
11
- * → writeSkillInvocation() 记录
12
- *
13
- * 设计要点:
14
- * - 复用现有 Express app(不新增端口),路径前缀固定为 /mcp
15
- * - 沿用 daemon auth token(Bearer),与 /api 写操作同一鉴权链路
16
- * - Stateless 模式:每个请求开一对 server/transport,避免长连接污染
17
- * daemon 主循环;Skill 调用本身是短平快、无需流式推送的场景
18
- * - Session 追踪:优先 header `X-Forge-Session-Id`,缺失时生成匿名 session
19
- * (Ruflo 模式下 Claude 自主决策,不需要 routing state 回退)
20
- */
21
-
22
- import type { Application, Request, Response } from 'express';
23
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
24
- import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
25
- import { z } from 'zod';
26
-
27
- import type { SkillRegistry } from '../skills/registry.js';
28
- import type { SQLiteStorage } from '../core/storage/sqlite.js';
29
- import type { InvocationGuard } from '../skills/invocation-guard.js';
30
- import { skillInvoke } from '../skills/tools/skill-invoke.js';
31
- import { skillList } from '../skills/tools/skill-list.js';
32
- import { pipelineSuggest } from '../skills/tools/pipeline-suggest.js';
33
- import { logger } from '../core/utils/logger.js';
34
- import { requireAuth } from '../web/auth-middleware.js';
35
-
36
- /** Resolved session context for a single MCP tool call. */
37
- export interface SessionContext {
38
- sessionId: string | null;
39
- routeRequestId: string | null;
40
- agentId: string | null;
41
- }
42
-
43
- export interface McpServerOptions {
44
- skillRegistry: SkillRegistry;
45
- storage: SQLiteStorage;
46
- guard?: InvocationGuard;
47
- /**
48
- * Override the session-resolution strategy. Default behavior:
49
- * 1. Read header `X-Forge-Session-Id` (passed by the harness if available)
50
- * 2. Fallback: pick the freshest live routing entry from routingState
51
- * 3. If neither yields a sessionId → return all-null context (invocation
52
- * will be recorded with session_id derived from the synthetic
53
- * "mcp-anon-<ts>" so the writeSkillInvocation NOT NULL constraint still
54
- * passes; see resolveSessionContext below).
55
- */
56
- resolveSession?: (req: Request) => SessionContext;
57
- }
58
-
59
- const SERVER_INFO = {
60
- name: 'claude-forge',
61
- version: '1.0.0',
62
- } as const;
63
-
64
- /**
65
- * Resolve session context for a request.
66
- *
67
- * Priority order:
68
- * 1. Explicit header `X-Forge-Session-Id` (+ optional `X-Forge-Route-Request-Id`,
69
- * `X-Forge-Agent-Id`).
70
- * 2. routingState.getMostRecent() — heuristic fallback during the routing
71
- * window (≤30min TTL).
72
- * 3. Synthetic anonymous session "mcp-anon-<unix-ts>" so we never blow the
73
- * NOT NULL session_id constraint when persisting invocations.
74
- */
75
- function resolveSessionContext(req: Request): SessionContext {
76
- const headerSession = req.header('X-Forge-Session-Id');
77
- if (headerSession && headerSession.trim().length > 0) {
78
- const headerRoute = req.header('X-Forge-Route-Request-Id');
79
- const headerAgent = req.header('X-Forge-Agent-Id');
80
- return {
81
- sessionId: headerSession.trim(),
82
- routeRequestId: headerRoute?.trim() || null,
83
- agentId: headerAgent?.trim() || null,
84
- };
85
- }
86
-
87
- // In Ruflo mode, Claude makes autonomous decisions via CLAUDE.md.
88
- // MCP tools no longer need routing state fallback.
89
- return {
90
- sessionId: `mcp-anon-${Date.now()}`,
91
- routeRequestId: null,
92
- agentId: null,
93
- };
94
- }
95
-
96
- /** Build a fresh MCP server instance bound to the given options & context. */
97
- function buildServer(
98
- options: McpServerOptions,
99
- sessionCtx: SessionContext,
100
- ): McpServer {
101
- const server = new McpServer(SERVER_INFO, {
102
- capabilities: { tools: { listChanged: false } },
103
- });
104
-
105
- // ── Tool 1: skill_invoke ───────────────────────────────────────────────
106
- server.registerTool(
107
- 'skill_invoke',
108
- {
109
- title: 'Invoke a claude-forge skill',
110
- description:
111
- '调用指定的 Skill,获取该方法论的完整内容。使用场景:当你需要特定领域的方法论指导(如 TDD、重构、性能优化)时。',
112
- inputSchema: {
113
- skill_id: z
114
- .string()
115
- .min(1)
116
- .describe('Skill 的 ID(如 "official-tdd")。可通过 skill_list 工具查询。'),
117
- reason: z
118
- .string()
119
- .optional()
120
- .describe('调用理由(可选,用于审计 / skill_invocations.reason 列)'),
121
- },
122
- },
123
- async (args) => {
124
- try {
125
- const sessionId = sessionCtx.sessionId ?? `mcp-anon-${Date.now()}`;
126
- const result = await skillInvoke(
127
- { skill_id: args.skill_id, reason: args.reason },
128
- {
129
- skillRegistry: options.skillRegistry,
130
- guard: options.guard,
131
- storage: options.storage,
132
- sessionId,
133
- routeRequestId: sessionCtx.routeRequestId ?? undefined,
134
- agentId: sessionCtx.agentId ?? undefined,
135
- invocationType: 'dynamic',
136
- },
137
- );
138
- return {
139
- content: [
140
- {
141
- type: 'text' as const,
142
- text: `# ${result.skill_name}\n\n${result.content}`,
143
- },
144
- ],
145
- };
146
- } catch (err) {
147
- const message = err instanceof Error ? err.message : String(err);
148
- logger.warn(`[MCP] skill_invoke failed: ${message}`);
149
- return {
150
- isError: true,
151
- content: [{ type: 'text' as const, text: message }],
152
- };
153
- }
154
- },
155
- );
156
-
157
- // ── Tool 2: skill_list ─────────────────────────────────────────────────
158
- server.registerTool(
159
- 'skill_list',
160
- {
161
- title: 'List available claude-forge skills',
162
- description: '列出所有可用的 Skill。可以通过 keywords 过滤(OR 语义)。',
163
- inputSchema: {
164
- keywords: z
165
- .array(z.string())
166
- .optional()
167
- .describe('可选的关键词过滤(如 ["test", "tdd"])'),
168
- },
169
- },
170
- async (args) => {
171
- try {
172
- logger.info(`[MCP] skill_list called with args: ${JSON.stringify(args)}`);
173
- const result = await skillList(
174
- { keywords: args.keywords },
175
- { skillRegistry: options.skillRegistry },
176
- );
177
- return {
178
- content: [
179
- { type: 'text' as const, text: JSON.stringify(result, null, 2) },
180
- ],
181
- };
182
- } catch (err) {
183
- const message = err instanceof Error ? err.message : String(err);
184
- logger.warn(`[MCP] skill_list failed: ${message}`);
185
- return {
186
- isError: true,
187
- content: [{ type: 'text' as const, text: message }],
188
- };
189
- }
190
- },
191
- );
192
-
193
- // ── Tool 3: pipeline_suggest ───────────────────────────────────────────
194
- server.registerTool(
195
- 'pipeline_suggest',
196
- {
197
- title: 'Suggest a swarm pipeline for a task',
198
- description:
199
- '根据任务特征判断是否需要启动多 Agent 协作的 Pipeline,并生成可直接使用的 Task({...}) 调用代码。',
200
- inputSchema: {
201
- task_description: z
202
- .string()
203
- .min(1)
204
- .describe('任务描述(如 "修复用户登录时的 session 过期问题")'),
205
- file_count: z
206
- .number()
207
- .int()
208
- .optional()
209
- .describe('预估涉及的文件数量(>=2 时建议使用 Pipeline)'),
210
- is_new_feature: z
211
- .boolean()
212
- .optional()
213
- .describe('是否为新功能开发'),
214
- is_refactor: z
215
- .boolean()
216
- .optional()
217
- .describe('是否为重构任务'),
218
- is_bug_fix: z
219
- .boolean()
220
- .optional()
221
- .describe('是否为 Bug 修复'),
222
- is_performance: z
223
- .boolean()
224
- .optional()
225
- .describe('是否为性能优化'),
226
- is_migration: z
227
- .boolean()
228
- .optional()
229
- .describe('是否为技术迁移/升级'),
230
- is_security: z
231
- .boolean()
232
- .optional()
233
- .describe('是否为安全审计/漏洞修复'),
234
- },
235
- },
236
- async (args) => {
237
- try {
238
- const result = await pipelineSuggest({
239
- task_description: args.task_description,
240
- file_count: args.file_count,
241
- is_new_feature: args.is_new_feature,
242
- is_refactor: args.is_refactor,
243
- is_bug_fix: args.is_bug_fix,
244
- is_performance: args.is_performance,
245
- is_migration: args.is_migration,
246
- is_security: args.is_security,
247
- });
248
- return {
249
- content: [
250
- { type: 'text' as const, text: JSON.stringify(result, null, 2) },
251
- ],
252
- };
253
- } catch (err) {
254
- const message = err instanceof Error ? err.message : String(err);
255
- logger.warn(`[MCP] pipeline_suggest failed: ${message}`);
256
- return {
257
- isError: true,
258
- content: [{ type: 'text' as const, text: message }],
259
- };
260
- }
261
- },
262
- );
263
-
264
- return server;
265
- }
266
-
267
- /**
268
- * Mount the MCP server endpoints on an existing Express application.
269
- *
270
- * Routes:
271
- * POST /mcp — JSON-RPC over Streamable HTTP (initialize, tools/list, tools/call)
272
- * GET /mcp — Standalone SSE channel (server→client notifications)
273
- * DELETE /mcp — Session termination (stateful mode only; no-op in stateless)
274
- *
275
- * Authentication: every method requires the daemon Bearer token. Token-file
276
- * absence still falls through (early-startup safety, identical to /api).
277
- */
278
- export function mountMcpServer(app: Application, options: McpServerOptions): void {
279
- const resolveSession = options.resolveSession ?? resolveSessionContext;
280
-
281
- const handler = async (req: Request, res: Response): Promise<void> => {
282
- // Stateless: build a fresh server+transport per request. The transport
283
- // mutates res so it cannot be reused across requests anyway.
284
- const sessionCtx = resolveSession(req);
285
- const server = buildServer(options, sessionCtx);
286
- const transport = new StreamableHTTPServerTransport({
287
- // Stateless mode — no session ID generator.
288
- sessionIdGenerator: undefined,
289
- enableJsonResponse: true,
290
- });
291
-
292
- res.on('close', () => {
293
- // best-effort cleanup; ignore errors because transport may already be closed
294
- void transport.close().catch(() => undefined);
295
- void server.close().catch(() => undefined);
296
- });
297
-
298
- try {
299
- await server.connect(transport);
300
- // express.json() has already populated req.body; pass it through.
301
- await transport.handleRequest(req, res, req.body);
302
- } catch (err) {
303
- logger.warn(`[MCP] request failed: ${err instanceof Error ? err.message : String(err)}`);
304
- if (!res.headersSent) {
305
- res.status(500).json({
306
- jsonrpc: '2.0',
307
- error: { code: -32603, message: 'Internal error' },
308
- id: null,
309
- });
310
- }
311
- }
312
- };
313
-
314
- // All MCP requests are auth-gated. We deliberately gate GET too because MCP
315
- // tool-calls (sensitive ops) can come over GET via SSE; /api keeps GET open
316
- // only for read-only dashboard polling.
317
- app.post('/mcp', requireAuth, handler);
318
- app.get('/mcp', requireAuth, handler);
319
- app.delete('/mcp', requireAuth, handler);
320
-
321
- logger.info('[MCP] Server mounted on /mcp (Streamable HTTP, stateless)');
322
- }
@@ -1,2 +0,0 @@
1
- export { SkillRegistry, type Skill } from './registry.js';
2
- export { matchKeywords, formatSkillRecommendation } from './matcher.js';
@@ -1,177 +0,0 @@
1
- /**
2
- * InvocationGuard — Skill 调用防护机制
3
- *
4
- * 防止 Agent 陷入无限调用 Skill 的循环,限制:
5
- * - 单会话总调用次数(默认 10)
6
- * - 单会话调用深度(默认 3)
7
- * - 同 session 同 skill 幂等(避免重复调用)
8
- * - TTL 机制:会话状态 30 分钟后自动过期
9
- *
10
- * 状态为进程内存,进程重启会清空;调用链路通常集中在单个会话内,
11
- * 重启后重建状态是可接受的。
12
- */
13
-
14
- const MAX_INVOCATIONS_PER_SESSION = 10;
15
- const MAX_INVOCATION_DEPTH = 3;
16
- const SESSION_TTL_MS = 30 * 60 * 1000; // 30 minutes
17
- const CLEANUP_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
18
-
19
- export interface SessionInvocations {
20
- total: number;
21
- depth: number;
22
- calledSkills: Set<string>;
23
- lastAccessTime: number; // Timestamp of last access
24
- }
25
-
26
- export interface InvocationCheckResult {
27
- allowed: boolean;
28
- reason?: string;
29
- }
30
-
31
- export class InvocationGuard {
32
- private sessions = new Map<string, SessionInvocations>();
33
- private cleanupTimer: NodeJS.Timeout | null = null;
34
-
35
- constructor() {
36
- // Start automatic cleanup timer
37
- this.startCleanupTimer();
38
- }
39
-
40
- /**
41
- * 检查是否允许调用。
42
- * 不修改状态;调用方决定是否继续后调用 record()。
43
- */
44
- check(sessionId: string, skillId: string): InvocationCheckResult {
45
- const session = this.sessions.get(sessionId) ?? {
46
- total: 0,
47
- depth: 0,
48
- calledSkills: new Set<string>(),
49
- lastAccessTime: Date.now(),
50
- };
51
-
52
- // Check if session has expired
53
- if (Date.now() - session.lastAccessTime > SESSION_TTL_MS) {
54
- this.sessions.delete(sessionId);
55
- return { allowed: true }; // Expired session, allow new invocation
56
- }
57
-
58
- if (session.total >= MAX_INVOCATIONS_PER_SESSION) {
59
- return {
60
- allowed: false,
61
- reason: `Reached max invocations per session (${MAX_INVOCATIONS_PER_SESSION})`,
62
- };
63
- }
64
-
65
- if (session.depth >= MAX_INVOCATION_DEPTH) {
66
- return {
67
- allowed: false,
68
- reason: `Reached max invocation depth (${MAX_INVOCATION_DEPTH})`,
69
- };
70
- }
71
-
72
- if (session.calledSkills.has(skillId)) {
73
- return {
74
- allowed: false,
75
- reason: `Skill '${skillId}' already invoked in this session (idempotent guard)`,
76
- };
77
- }
78
-
79
- return { allowed: true };
80
- }
81
-
82
- /**
83
- * 记录一次调用开始(递增 total 和 depth,登记 skill)。
84
- */
85
- record(sessionId: string, skillId: string): void {
86
- const session = this.sessions.get(sessionId) ?? {
87
- total: 0,
88
- depth: 0,
89
- calledSkills: new Set<string>(),
90
- lastAccessTime: Date.now(),
91
- };
92
- session.total += 1;
93
- session.depth += 1;
94
- session.calledSkills.add(skillId);
95
- session.lastAccessTime = Date.now(); // Update access time
96
- this.sessions.set(sessionId, session);
97
- }
98
-
99
- /**
100
- * 调用完成,递减 depth(不清除已调用 skills)。
101
- */
102
- complete(sessionId: string): void {
103
- const session = this.sessions.get(sessionId);
104
- if (session) {
105
- session.depth = Math.max(0, session.depth - 1);
106
- session.lastAccessTime = Date.now(); // Update access time
107
- }
108
- }
109
-
110
- /**
111
- * 清理会话(Stop hook 或会话结束时调用)。
112
- */
113
- clear(sessionId: string): void {
114
- this.sessions.delete(sessionId);
115
- }
116
-
117
- /**
118
- * 获取会话统计(调试/观测用)。
119
- */
120
- getStats(sessionId: string): SessionInvocations | null {
121
- return this.sessions.get(sessionId) ?? null;
122
- }
123
-
124
- /**
125
- * 清理过期会话(TTL 机制)。
126
- */
127
- private cleanupExpiredSessions(): void {
128
- const now = Date.now();
129
- const expiredSessions: string[] = [];
130
-
131
- for (const [sessionId, session] of this.sessions.entries()) {
132
- if (now - session.lastAccessTime > SESSION_TTL_MS) {
133
- expiredSessions.push(sessionId);
134
- }
135
- }
136
-
137
- for (const sessionId of expiredSessions) {
138
- this.sessions.delete(sessionId);
139
- }
140
-
141
- if (expiredSessions.length > 0) {
142
- // Use console.log instead of logger to avoid circular dependency
143
- console.log(`[InvocationGuard] Cleaned up ${expiredSessions.length} expired sessions`);
144
- }
145
- }
146
-
147
- /**
148
- * 启动自动清理定时器。
149
- */
150
- private startCleanupTimer(): void {
151
- if (this.cleanupTimer) return;
152
-
153
- this.cleanupTimer = setInterval(() => {
154
- this.cleanupExpiredSessions();
155
- }, CLEANUP_INTERVAL_MS);
156
-
157
- // Prevent timer from keeping process alive
158
- this.cleanupTimer.unref();
159
- }
160
-
161
- /**
162
- * 停止自动清理定时器(用于测试或进程关闭)。
163
- */
164
- stopCleanupTimer(): void {
165
- if (this.cleanupTimer) {
166
- clearInterval(this.cleanupTimer);
167
- this.cleanupTimer = null;
168
- }
169
- }
170
-
171
- /**
172
- * 获取当前会话数量(用于监控)。
173
- */
174
- getSessionCount(): number {
175
- return this.sessions.size;
176
- }
177
- }