@winspan/claude-forge 8.53.2 → 8.54.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (390) hide show
  1. package/DEVELOPMENT.md +290 -221
  2. package/README.md +50 -8
  3. package/dist/cli/commands/skills.d.ts.map +1 -1
  4. package/dist/cli/commands/skills.js +7 -3
  5. package/dist/cli/commands/skills.js.map +1 -1
  6. package/dist/cli/init/hook-manager.d.ts +1 -1
  7. package/dist/cli/init/hook-manager.d.ts.map +1 -1
  8. package/dist/cli/init/hook-manager.js +1 -0
  9. package/dist/cli/init/hook-manager.js.map +1 -1
  10. package/dist/core/storage/events.d.ts.map +1 -1
  11. package/dist/core/storage/events.js +0 -1
  12. package/dist/core/storage/events.js.map +1 -1
  13. package/dist/core/storage/maintenance.d.ts +25 -3
  14. package/dist/core/storage/maintenance.d.ts.map +1 -1
  15. package/dist/core/storage/maintenance.js +33 -4
  16. package/dist/core/storage/maintenance.js.map +1 -1
  17. package/dist/core/storage/routing.d.ts +4 -0
  18. package/dist/core/storage/routing.d.ts.map +1 -1
  19. package/dist/core/storage/routing.js +10 -4
  20. package/dist/core/storage/routing.js.map +1 -1
  21. package/dist/core/storage/sessions.d.ts +17 -0
  22. package/dist/core/storage/sessions.d.ts.map +1 -1
  23. package/dist/core/storage/sessions.js +64 -0
  24. package/dist/core/storage/sessions.js.map +1 -1
  25. package/dist/core/storage/skills.d.ts +4 -0
  26. package/dist/core/storage/skills.d.ts.map +1 -1
  27. package/dist/core/storage/skills.js +10 -2
  28. package/dist/core/storage/skills.js.map +1 -1
  29. package/dist/core/storage/sqlite.d.ts +5 -0
  30. package/dist/core/storage/sqlite.d.ts.map +1 -1
  31. package/dist/core/storage/sqlite.js +6 -0
  32. package/dist/core/storage/sqlite.js.map +1 -1
  33. package/dist/core/storage/tasks.d.ts.map +1 -1
  34. package/dist/core/storage/tasks.js +2 -0
  35. package/dist/core/storage/tasks.js.map +1 -1
  36. package/dist/core/types.d.ts +7 -0
  37. package/dist/core/types.d.ts.map +1 -1
  38. package/dist/daemon/index.d.ts.map +1 -1
  39. package/dist/daemon/index.js +19 -4
  40. package/dist/daemon/index.js.map +1 -1
  41. package/dist/skills/registry.d.ts.map +1 -1
  42. package/dist/skills/registry.js +13 -2
  43. package/dist/skills/registry.js.map +1 -1
  44. package/dist/skills/semantic-matcher.d.ts +2 -2
  45. package/dist/skills/semantic-matcher.d.ts.map +1 -1
  46. package/dist/skills/semantic-matcher.js +14 -19
  47. package/dist/skills/semantic-matcher.js.map +1 -1
  48. package/dist/skills/upgrade-engine.d.ts +3 -1
  49. package/dist/skills/upgrade-engine.d.ts.map +1 -1
  50. package/dist/skills/upgrade-engine.js +25 -14
  51. package/dist/skills/upgrade-engine.js.map +1 -1
  52. package/dist/web/analytics/weekly-report.d.ts.map +1 -1
  53. package/dist/web/analytics/weekly-report.js +21 -29
  54. package/dist/web/analytics/weekly-report.js.map +1 -1
  55. package/dist/web/routes/patch.d.ts.map +1 -1
  56. package/dist/web/routes/patch.js +32 -2
  57. package/dist/web/routes/patch.js.map +1 -1
  58. package/dist/web/routes/sessions.d.ts.map +1 -1
  59. package/dist/web/routes/sessions.js +9 -7
  60. package/dist/web/routes/sessions.js.map +1 -1
  61. package/dist/web/routes/trace.d.ts.map +1 -1
  62. package/dist/web/routes/trace.js +2 -3
  63. package/dist/web/routes/trace.js.map +1 -1
  64. package/dist/web/server.d.ts.map +1 -1
  65. package/dist/web/server.js +3 -2
  66. package/dist/web/server.js.map +1 -1
  67. package/package.json +12 -2
  68. package/scripts/postinstall.cjs +21 -0
  69. package/.claude/CLAUDE.md +0 -17
  70. package/.eslintrc.js +0 -23
  71. package/.prettierrc +0 -8
  72. package/ARCHITECTURE_ISSUES.md +0 -249
  73. package/CLAUDE.md +0 -265
  74. package/CLAUDE.md.backup +0 -488
  75. package/docs/concurrent-agents.md +0 -129
  76. package/docs/design/architecture-review-20260516.md +0 -232
  77. package/docs/design/fix-skills-data-and-set-leak-spec-20260516-1300.md +0 -219
  78. package/docs/design/h1-storage-aggregation-spec-20260518-1121.md +0 -299
  79. package/docs/design/h2-getdatabase-encapsulation-spec-20260518-1450.md +0 -191
  80. package/docs/design/h3-fallback-removal-spec-20260518-1245.md +0 -76
  81. package/docs/design/h4-index-dedup-spec-20260518-1230.md +0 -109
  82. package/docs/design/h6-services-migration-spec-20260518-1355.md +0 -82
  83. package/docs/design/hook-failure-queue-spec-20260516-1530.md +0 -204
  84. package/docs/design/l1-swarm-protocol-extract-spec-20260518-1605.md +0 -106
  85. package/docs/design/m10-forge-paths-spec-20260518-1320.md +0 -121
  86. package/docs/design/m2-m3-tool-input-spec-20260518-1425.md +0 -131
  87. package/docs/design/m7-routing-event-association-spec-20260518-1545.md +0 -103
  88. package/docs/design/project-path-gitroot-spec-20260518-1715.md +0 -134
  89. package/docs/design/refactor-phase1-spec-20260515-1600.md +0 -543
  90. package/docs/design/refactor-phase2-spec-20260515-1700.md +0 -424
  91. package/docs/design/skill-ai-upgrade-spec-20260518-1930.md +0 -297
  92. package/docs/design/task-active-gc-spec-20260518-1745.md +0 -146
  93. package/docs/design/tasks-list-filter-pagination-spec-20260518-0930.md +0 -208
  94. package/docs/implementation/daemon-skill-sync-changelog-20260518-2000.md +0 -22
  95. package/docs/implementation/fix-skills-data-and-set-leak-changelog-20260516-1300.md +0 -104
  96. package/docs/implementation/h1-storage-aggregation-changelog-20260518-1121.md +0 -82
  97. package/docs/implementation/h2-final-changelog-20260518-1530.md +0 -61
  98. package/docs/implementation/h2-phase1-safety-net-changelog-20260518-1450.md +0 -70
  99. package/docs/implementation/h2-phase2-operations-changelog-20260518-1450.md +0 -120
  100. package/docs/implementation/h2-phase3-callsites-changelog-20260518-1450.md +0 -71
  101. package/docs/implementation/h3-fallback-removal-changelog-20260518-1245.md +0 -71
  102. package/docs/implementation/h4-index-dedup-changelog-20260518-1230.md +0 -60
  103. package/docs/implementation/h6-services-migration-changelog-20260518-1355.md +0 -46
  104. package/docs/implementation/h7-m9-defaults-changelog-20260518-1300.md +0 -46
  105. package/docs/implementation/hook-failure-queue-changelog-20260516-1530.md +0 -196
  106. package/docs/implementation/hotfix-daemon-event-reject-20260516-1430.md +0 -56
  107. package/docs/implementation/l1-swarm-protocol-extract-changelog-20260518-1605.md +0 -45
  108. package/docs/implementation/l3-l4-daemon-perf-changelog-20260518-1410.md +0 -63
  109. package/docs/implementation/l6-l8-final-cleanup-changelog-20260518-1640.md +0 -38
  110. package/docs/implementation/m1-m4-m5-l7-cleanup-changelog-20260518-1310.md +0 -58
  111. package/docs/implementation/m10-forge-paths-changelog-20260518-1320.md +0 -60
  112. package/docs/implementation/m2-m3-tool-input-changelog-20260518-1425.md +0 -43
  113. package/docs/implementation/m6-m8-naming-shutdown-changelog-20260518-1340.md +0 -56
  114. package/docs/implementation/m7-routing-association-changelog-20260518-1545.md +0 -69
  115. package/docs/implementation/project-path-gitroot-changelog-20260518-1715.md +0 -63
  116. package/docs/implementation/refactor-phase1-changelog-20260515-1630.md +0 -354
  117. package/docs/implementation/refactor-phase2-changelog-20260515-1705.md +0 -421
  118. package/docs/implementation/skill-ai-upgrade-changelog-20260518-1930.md +0 -49
  119. package/docs/implementation/task-active-gc-changelog-20260518-1745.md +0 -35
  120. package/docs/implementation/task-title-summary-changelog-20260518-1130.md +0 -39
  121. package/docs/implementation/tasks-detail-back-loses-filters-changelog-20260518-1100.md +0 -22
  122. package/docs/implementation/tasks-list-filter-pagination-changelog-20260518-0930.md +0 -72
  123. package/docs/implementation/tasks-page-white-screen-hotfix-changelog-20260518-1015.md +0 -56
  124. package/docs/reviews/claudemd-template-sync.md +0 -54
  125. package/docs/reviews/task-title-summary.md +0 -92
  126. package/docs/reviews/tasks-detail-back-loses-filters.md +0 -58
  127. package/docs/reviews/tasks-filter-pagination.md +0 -80
  128. package/docs/reviews/tasks-page-white-screen-hotfix.md +0 -126
  129. package/docs/ruflo-learning-strategy.md +0 -322
  130. package/docs/skills-deduplication-analysis.md +0 -83
  131. package/docs/skills-multiformat-support.md +0 -177
  132. package/docs/skills-third-party.md +0 -183
  133. package/docs/testing/tasks-filter-pagination-test-report.md +0 -86
  134. package/forge +0 -321
  135. package/playwright.config.ts +0 -40
  136. package/scripts/demo-v2.ts +0 -91
  137. package/scripts/dev-daemon.sh +0 -232
  138. package/scripts/dev-web.ts +0 -109
  139. package/scripts/e2e-mcp-link.ts +0 -423
  140. package/scripts/e2e-methodology-quality.ts +0 -253
  141. package/scripts/e2e-routing.ts +0 -456
  142. package/scripts/e2e-user-methodology.ts +0 -326
  143. package/scripts/e2e-web-workflows.ts +0 -299
  144. package/scripts/migrate-legacy-to-dynamic.sql +0 -108
  145. package/scripts/regenerate-execution-docs.ts +0 -116
  146. package/scripts/sync-agent-skills.ts +0 -193
  147. package/scripts/test-hook.sh +0 -71
  148. package/scripts/verify-skill-loading.ts +0 -62
  149. package/src/claudemd/claudemd-generator.ts +0 -568
  150. package/src/claudemd/convention-extractor.ts +0 -69
  151. package/src/claudemd/index.ts +0 -35
  152. package/src/claudemd/persona-manager.ts +0 -88
  153. package/src/claudemd/resume-manager.ts +0 -236
  154. package/src/claudemd/tech-detector.ts +0 -220
  155. package/src/claudemd/templates/swarm-protocol.md +0 -222
  156. package/src/cli/commands/claudemd.ts +0 -84
  157. package/src/cli/commands/config.ts +0 -46
  158. package/src/cli/commands/daemon.ts +0 -310
  159. package/src/cli/commands/executions.ts +0 -115
  160. package/src/cli/commands/init.ts +0 -204
  161. package/src/cli/commands/logs.ts +0 -181
  162. package/src/cli/commands/mcp.ts +0 -242
  163. package/src/cli/commands/menu.ts +0 -357
  164. package/src/cli/commands/skills.ts +0 -328
  165. package/src/cli/commands/stats.ts +0 -73
  166. package/src/cli/commands/status.ts +0 -69
  167. package/src/cli/commands/template.ts +0 -77
  168. package/src/cli/commands/trace.ts +0 -148
  169. package/src/cli/index.ts +0 -42
  170. package/src/cli/init/hook-manager.ts +0 -132
  171. package/src/core/ai/provider.ts +0 -308
  172. package/src/core/ai/types.ts +0 -51
  173. package/src/core/config.ts +0 -124
  174. package/src/core/constants.ts +0 -67
  175. package/src/core/event-fields.ts +0 -32
  176. package/src/core/queue/index.ts +0 -192
  177. package/src/core/storage/base.ts +0 -302
  178. package/src/core/storage/events.ts +0 -434
  179. package/src/core/storage/injections.ts +0 -78
  180. package/src/core/storage/maintenance.ts +0 -59
  181. package/src/core/storage/migrations/002_add_skill_tracking.sql +0 -6
  182. package/src/core/storage/migrations/003_add_skill_invocations.sql +0 -23
  183. package/src/core/storage/performance-indexes.sql +0 -23
  184. package/src/core/storage/routing.ts +0 -322
  185. package/src/core/storage/rows.ts +0 -112
  186. package/src/core/storage/schema.sql +0 -224
  187. package/src/core/storage/sessions.ts +0 -168
  188. package/src/core/storage/skills.ts +0 -233
  189. package/src/core/storage/sqlite.ts +0 -293
  190. package/src/core/storage/tasks.ts +0 -318
  191. package/src/core/storage/token-usage.ts +0 -93
  192. package/src/core/types.ts +0 -181
  193. package/src/core/utils/error-handler.ts +0 -257
  194. package/src/core/utils/forge-resume-block.ts +0 -74
  195. package/src/core/utils/format.ts +0 -69
  196. package/src/core/utils/git.ts +0 -23
  197. package/src/core/utils/logger.ts +0 -134
  198. package/src/core/utils/lru-cache.ts +0 -54
  199. package/src/core/utils/path.ts +0 -19
  200. package/src/core/utils/session.ts +0 -26
  201. package/src/core/utils/time.ts +0 -37
  202. package/src/core/utils/token-tracker.ts +0 -97
  203. package/src/daemon/event-parser.ts +0 -36
  204. package/src/daemon/handlers/history-exporter.ts +0 -117
  205. package/src/daemon/handlers/post-tool-use.ts +0 -54
  206. package/src/daemon/handlers/stop.ts +0 -208
  207. package/src/daemon/handlers/user-prompt.ts +0 -178
  208. package/src/daemon/hook-sync.ts +0 -91
  209. package/src/daemon/index.ts +0 -312
  210. package/src/daemon/launchd/com.claude-forge.daemon.plist.template +0 -47
  211. package/src/daemon/launchd-installer.ts +0 -260
  212. package/src/daemon/lifecycle.ts +0 -128
  213. package/src/daemon/router.ts +0 -40
  214. package/src/daemon/server.ts +0 -196
  215. package/src/daemon/services/task-segmenter.ts +0 -112
  216. package/src/daemon/skill-sync.ts +0 -88
  217. package/src/hooks/hook-lib.sh +0 -118
  218. package/src/hooks/notification.sh +0 -35
  219. package/src/hooks/post-tool-use.sh +0 -61
  220. package/src/hooks/pre-tool-use.sh +0 -63
  221. package/src/hooks/stop.sh +0 -43
  222. package/src/hooks/user-prompt-submit.sh +0 -69
  223. package/src/mcp/server.ts +0 -322
  224. package/src/skills/index.ts +0 -2
  225. package/src/skills/invocation-guard.ts +0 -177
  226. package/src/skills/matcher.ts +0 -148
  227. package/src/skills/official/code-simplifier.md +0 -52
  228. package/src/skills/official/find-skills.md +0 -142
  229. package/src/skills/official/official-api-design.md +0 -30
  230. package/src/skills/official/official-architecture-decision.md +0 -41
  231. package/src/skills/official/official-bmad.md +0 -118
  232. package/src/skills/official/official-db-schema-design.md +0 -34
  233. package/src/skills/official/official-debug.md +0 -25
  234. package/src/skills/official/official-doc-driven.md +0 -31
  235. package/src/skills/official/official-harness-engineering.md +0 -108
  236. package/src/skills/official/official-performance-optimization.md +0 -30
  237. package/src/skills/official/official-pr-review.md +0 -35
  238. package/src/skills/official/official-release-checklist.md +0 -30
  239. package/src/skills/official/official-security-hardening.md +0 -32
  240. package/src/skills/official/official-spec-driven-design.md +0 -31
  241. package/src/skills/official/planning-with-files.md +0 -241
  242. package/src/skills/official/ui-ux-pro-max.md +0 -105
  243. package/src/skills/official/webapp-testing.md +0 -96
  244. package/src/skills/official-skills.ts +0 -89
  245. package/src/skills/registry.ts +0 -355
  246. package/src/skills/semantic-matcher.ts +0 -234
  247. package/src/skills/tools/pipeline-suggest.ts +0 -226
  248. package/src/skills/tools/skill-invoke.ts +0 -168
  249. package/src/skills/tools/skill-list.ts +0 -59
  250. package/src/skills/upgrade-engine.ts +0 -541
  251. package/src/skills/upgrade-prompt.ts +0 -84
  252. package/src/templates/go.yaml +0 -53
  253. package/src/templates/python.yaml +0 -59
  254. package/src/templates/react.yaml +0 -55
  255. package/src/templates/template-manager.ts +0 -170
  256. package/src/web/analytics/anti-pattern-detector.ts +0 -367
  257. package/src/web/analytics/drift-detector.ts +0 -219
  258. package/src/web/analytics/weekly-report.ts +0 -431
  259. package/src/web/auth-middleware.ts +0 -54
  260. package/src/web/routes/_helpers.ts +0 -34
  261. package/src/web/routes/ai.ts +0 -204
  262. package/src/web/routes/auth.ts +0 -22
  263. package/src/web/routes/drift.ts +0 -25
  264. package/src/web/routes/error-handler.ts +0 -120
  265. package/src/web/routes/events.ts +0 -47
  266. package/src/web/routes/insights.ts +0 -43
  267. package/src/web/routes/patch.ts +0 -117
  268. package/src/web/routes/reports.ts +0 -34
  269. package/src/web/routes/rules.ts +0 -76
  270. package/src/web/routes/sessions.ts +0 -250
  271. package/src/web/routes/skill-stats.ts +0 -92
  272. package/src/web/routes/skills.ts +0 -350
  273. package/src/web/routes/static.ts +0 -67
  274. package/src/web/routes/stats.ts +0 -50
  275. package/src/web/routes/status.ts +0 -30
  276. package/src/web/routes/tasks.ts +0 -193
  277. package/src/web/routes/token-usage.ts +0 -20
  278. package/src/web/routes/trace.ts +0 -126
  279. package/src/web/routes/types.ts +0 -57
  280. package/src/web/server.ts +0 -134
  281. package/src/web/ssrf-guard.ts +0 -112
  282. package/src/web/static/index.html +0 -3251
  283. package/src/web/static/vendor/chart.umd.min.js +0 -20
  284. package/tests/e2e/dashboard.spec.ts +0 -205
  285. package/tests/e2e/routing-skill-e2e.test.ts +0 -39
  286. package/tests/helpers/mock-ai.ts +0 -92
  287. package/tests/helpers/mock-storage.ts +0 -159
  288. package/tests/integration/claudemd-generator.test.ts +0 -90
  289. package/tests/integration/queue-replay.integration.test.ts +0 -193
  290. package/tests/integration/tasks-filter.integration.test.ts +0 -154
  291. package/tests/integration/web-analytics.integration.test.ts +0 -133
  292. package/tests/integration/web-stats.integration.test.ts +0 -135
  293. package/tests/integration/web-trace.integration.test.ts +0 -175
  294. package/tests/performance/database.benchmark.ts +0 -161
  295. package/tests/semantic-matcher.test.ts +0 -99
  296. package/tests/skill-matcher.test.ts +0 -110
  297. package/tests/unit/ai-provider-retry.test.ts +0 -194
  298. package/tests/unit/ai-provider-vision.test.ts +0 -224
  299. package/tests/unit/claudemd-generator.test.ts +0 -68
  300. package/tests/unit/cli-mcp.test.ts +0 -141
  301. package/tests/unit/core/forge-paths.test.ts +0 -99
  302. package/tests/unit/daemon/hook-sync.test.ts +0 -71
  303. package/tests/unit/daemon/post-tool-use.test.ts +0 -121
  304. package/tests/unit/daemon/skill-sync.test.ts +0 -75
  305. package/tests/unit/daemon/stop-handler-behavior-summary.test.ts +0 -202
  306. package/tests/unit/daemon/task-segmenter-recover.test.ts +0 -84
  307. package/tests/unit/event-fields.test.ts +0 -88
  308. package/tests/unit/event-parser.test.ts +0 -55
  309. package/tests/unit/handlers.test.ts +0 -171
  310. package/tests/unit/hooks/resolve-project-path.test.ts +0 -122
  311. package/tests/unit/invocation-guard.test.ts +0 -125
  312. package/tests/unit/queue.test.ts +0 -272
  313. package/tests/unit/router.test.ts +0 -138
  314. package/tests/unit/security.test.ts +0 -128
  315. package/tests/unit/skill-invocations-workflow.test.ts +0 -495
  316. package/tests/unit/skill-registry.test.ts +0 -94
  317. package/tests/unit/skills/invocation-guard-ttl.test.ts +0 -211
  318. package/tests/unit/skills/official-skills-loader.test.ts +0 -126
  319. package/tests/unit/skills/registry-multiformat.test.ts +0 -92
  320. package/tests/unit/skills/upgrade-engine-parse.test.ts +0 -138
  321. package/tests/unit/skills/upgrade-engine.test.ts +0 -401
  322. package/tests/unit/skills/upgrade-prompt.test.ts +0 -89
  323. package/tests/unit/socket-server.test.ts +0 -183
  324. package/tests/unit/storage/event-operations-aggregates.test.ts +0 -342
  325. package/tests/unit/storage/migration-idempotent.test.ts +0 -304
  326. package/tests/unit/storage/routing-aggregates.test.ts +0 -276
  327. package/tests/unit/storage/routing.test.ts +0 -117
  328. package/tests/unit/storage/schema-missing.test.ts +0 -81
  329. package/tests/unit/storage/session-operations-aggregates.test.ts +0 -120
  330. package/tests/unit/storage/sessions-aggregate.test.ts +0 -435
  331. package/tests/unit/storage/skill-operations-counts.test.ts +0 -106
  332. package/tests/unit/storage/skills-aggregates.test.ts +0 -104
  333. package/tests/unit/storage/sqlite-refactor-harness.test.ts +0 -314
  334. package/tests/unit/storage/task-operations-counts.test.ts +0 -46
  335. package/tests/unit/storage/tasks-getById.test.ts +0 -343
  336. package/tests/unit/storage/tasks-stale-gc.test.ts +0 -86
  337. package/tests/unit/storage.test.ts +0 -172
  338. package/tests/unit/token-usage.test.ts +0 -144
  339. package/tests/unit/type-guards.test.ts +0 -201
  340. package/tests/unit/utils/format.test.ts +0 -189
  341. package/tests/unit/utils/session.test.ts +0 -89
  342. package/tests/unit/utils/time.test.ts +0 -112
  343. package/tests/unit/web/navigation-back-contract.test.ts +0 -134
  344. package/tests/unit/web/routes-auth.test.ts +0 -93
  345. package/tests/unit/web/routes-events.test.ts +0 -101
  346. package/tests/unit/web/routes-rules.test.ts +0 -182
  347. package/tests/unit/web/routes-sessions.test.ts +0 -181
  348. package/tests/unit/web/routes-skill-stats.test.ts +0 -179
  349. package/tests/unit/web/routes-stats.test.ts +0 -92
  350. package/tests/unit/web/routes-tasks.test.ts +0 -385
  351. package/tests/unit/web/task-title-contract.test.ts +0 -210
  352. package/tests/unit/web/tasks-component-contract.test.ts +0 -179
  353. package/tsconfig.json +0 -22
  354. package/vitest.config.ts +0 -21
  355. package/vitest.integration.config.ts +0 -16
  356. package/web/CLAUDE.md +0 -20
  357. package/web/index.html +0 -13
  358. package/web/package-lock.json +0 -4854
  359. package/web/package.json +0 -35
  360. package/web/postcss.config.js +0 -6
  361. package/web/src/App.tsx +0 -110
  362. package/web/src/components/CodeBlock.tsx +0 -31
  363. package/web/src/components/Confirm.tsx +0 -96
  364. package/web/src/components/Drawer.tsx +0 -60
  365. package/web/src/components/Layout.tsx +0 -145
  366. package/web/src/components/MarkdownRenderer.tsx +0 -77
  367. package/web/src/components/SearchInput.tsx +0 -31
  368. package/web/src/components/SessionDetailContent.tsx +0 -157
  369. package/web/src/components/Toast.tsx +0 -92
  370. package/web/src/index.css +0 -19
  371. package/web/src/main.tsx +0 -31
  372. package/web/src/pages/AIConfig.tsx +0 -233
  373. package/web/src/pages/Dashboard.tsx +0 -572
  374. package/web/src/pages/Events.tsx +0 -271
  375. package/web/src/pages/Reports.tsx +0 -428
  376. package/web/src/pages/SessionDetail.tsx +0 -162
  377. package/web/src/pages/Sessions.tsx +0 -205
  378. package/web/src/pages/Skills.tsx +0 -180
  379. package/web/src/pages/TaskDetail.tsx +0 -515
  380. package/web/src/pages/Tasks.tsx +0 -415
  381. package/web/src/utils/auth.ts +0 -59
  382. package/web/src/utils/export.ts +0 -54
  383. package/web/src/utils/navigation.ts +0 -25
  384. package/web/src/utils/task-title.ts +0 -49
  385. package/web/src/utils/time.ts +0 -13
  386. package/web/tailwind.config.js +0 -11
  387. package/web/tsconfig.json +0 -21
  388. package/web/tsconfig.node.json +0 -10
  389. package/web/vite.config.ts +0 -76
  390. package/winspan-claude-forge-8.43.0.tgz +0 -0
@@ -1,194 +0,0 @@
1
- /**
2
- * Tests for ClaudeProvider retry + timeout logic.
3
- *
4
- * The SDK client is replaced with a mock object exposing only
5
- * `messages.create`; the provider never hits the network.
6
- */
7
-
8
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
9
- import { ClaudeProvider, timing } from '../../src/core/ai/provider.js';
10
-
11
- /** Shape of what `messages.create` must return for the provider to succeed. */
12
- function makeOkResponse(text = 'ok') {
13
- return {
14
- content: [{ type: 'text', text }],
15
- usage: { input_tokens: 3, output_tokens: 5 },
16
- model: 'claude-sonnet-4-20250514',
17
- };
18
- }
19
-
20
- /** Build an error object that mimics an Anthropic APIError. */
21
- function apiError(status: number, message = `HTTP ${status}`): Error & { status: number } {
22
- const err = new Error(message) as Error & { status: number };
23
- err.status = status;
24
- return err;
25
- }
26
-
27
- function abortError(message = 'Request aborted'): Error {
28
- const err = new Error(message);
29
- err.name = 'AbortError';
30
- return err;
31
- }
32
-
33
- function installMockClient(provider: ClaudeProvider, create: ReturnType<typeof vi.fn>) {
34
- // @ts-expect-error — overriding internal client for test isolation
35
- provider.client = { messages: { create } };
36
- }
37
-
38
- describe('ClaudeProvider retry + timeout', () => {
39
- let provider: ClaudeProvider;
40
- let delaySpy: ReturnType<typeof vi.spyOn>;
41
-
42
- beforeEach(() => {
43
- provider = new ClaudeProvider('test-key', 'claude-sonnet-4-20250514');
44
- vi.useRealTimers();
45
- // Skip the actual backoff wait so tests finish quickly while still being
46
- // able to assert the backoff durations via spy call args.
47
- delaySpy = vi.spyOn(timing, 'delay').mockResolvedValue();
48
- });
49
-
50
- afterEach(() => {
51
- vi.restoreAllMocks();
52
- vi.useRealTimers();
53
- });
54
-
55
- it('retries on 429 rate limit then succeeds', async () => {
56
- const create = vi
57
- .fn()
58
- .mockRejectedValueOnce(apiError(429, 'rate limited'))
59
- .mockRejectedValueOnce(apiError(429, 'rate limited'))
60
- .mockResolvedValueOnce(makeOkResponse('done'));
61
- installMockClient(provider, create);
62
-
63
- const res = await provider.complete('hi', { maxRetries: 2, timeoutMs: 1000 });
64
-
65
- expect(res).toBe('done');
66
- expect(create).toHaveBeenCalledTimes(3);
67
- });
68
-
69
- it('retries on 5xx errors then succeeds', async () => {
70
- const create = vi
71
- .fn()
72
- .mockRejectedValueOnce(apiError(503, 'unavailable'))
73
- .mockRejectedValueOnce(apiError(500, 'boom'))
74
- .mockResolvedValueOnce(makeOkResponse('recovered'));
75
- installMockClient(provider, create);
76
-
77
- const res = await provider.complete('hi', { maxRetries: 2, timeoutMs: 1000 });
78
-
79
- expect(res).toBe('recovered');
80
- expect(create).toHaveBeenCalledTimes(3);
81
- });
82
-
83
- it('gives up after maxRetries when errors keep coming', async () => {
84
- const create = vi.fn().mockRejectedValue(apiError(500, 'persistent'));
85
- installMockClient(provider, create);
86
-
87
- await expect(
88
- provider.complete('hi', { maxRetries: 2, timeoutMs: 1000 }),
89
- ).rejects.toThrow('persistent');
90
-
91
- // attempt 0 + 2 retries = 3 calls
92
- expect(create).toHaveBeenCalledTimes(3);
93
- });
94
-
95
- it('does not retry on 4xx other than 429', async () => {
96
- const create = vi.fn().mockRejectedValue(apiError(400, 'bad request'));
97
- installMockClient(provider, create);
98
-
99
- await expect(
100
- provider.complete('hi', { maxRetries: 3, timeoutMs: 1000 }),
101
- ).rejects.toThrow('bad request');
102
-
103
- expect(create).toHaveBeenCalledTimes(1);
104
- });
105
-
106
- it('does not retry on 401 auth errors', async () => {
107
- const create = vi.fn().mockRejectedValue(apiError(401, 'unauthorized'));
108
- installMockClient(provider, create);
109
-
110
- await expect(
111
- provider.complete('hi', { maxRetries: 3, timeoutMs: 1000 }),
112
- ).rejects.toThrow('unauthorized');
113
-
114
- expect(create).toHaveBeenCalledTimes(1);
115
- });
116
-
117
- it('timeout triggers retry and eventually surfaces', async () => {
118
- // Each call hangs longer than timeoutMs; we reject with AbortError to mirror
119
- // what the SDK throws when its signal fires, so we don't have to wire a
120
- // real long-running pending promise into the test.
121
- const create = vi.fn().mockImplementation(
122
- () => new Promise((_resolve, reject) => {
123
- setTimeout(() => reject(abortError()), 20);
124
- }),
125
- );
126
- installMockClient(provider, create);
127
-
128
- await expect(
129
- provider.complete('hi', { maxRetries: 2, timeoutMs: 10 }),
130
- ).rejects.toMatchObject({ name: 'AbortError' });
131
-
132
- expect(create).toHaveBeenCalledTimes(3);
133
- });
134
-
135
- it('uses exponential backoff between retries (1000ms, 2000ms, ...)', async () => {
136
- const create = vi
137
- .fn()
138
- .mockRejectedValueOnce(apiError(500))
139
- .mockRejectedValueOnce(apiError(500))
140
- .mockResolvedValueOnce(makeOkResponse());
141
- installMockClient(provider, create);
142
-
143
- const res = await provider.complete('hi', { maxRetries: 2, timeoutMs: 5000 });
144
-
145
- expect(res).toBe('ok');
146
- expect(create).toHaveBeenCalledTimes(3);
147
- // delay() is called once per retry with exponential durations.
148
- const delayCalls = delaySpy.mock.calls.map((args) => args[0]);
149
- expect(delayCalls).toEqual([1000, 2000]);
150
- });
151
-
152
- it('propagates external signal aborts without retry', async () => {
153
- const create = vi.fn().mockImplementation(() => {
154
- return new Promise((_resolve, reject) => {
155
- // Simulate SDK surfacing AbortError once external signal fires.
156
- setTimeout(() => reject(abortError('user abort')), 5);
157
- });
158
- });
159
- installMockClient(provider, create);
160
-
161
- const controller = new AbortController();
162
- setTimeout(() => controller.abort(), 1);
163
-
164
- await expect(
165
- provider.complete('hi', { maxRetries: 3, timeoutMs: 1000, signal: controller.signal }),
166
- ).rejects.toThrow('user abort');
167
-
168
- // Only one attempt: external aborts short-circuit retry.
169
- expect(create).toHaveBeenCalledTimes(1);
170
- });
171
-
172
- it('completeWithUsage returns usage and model on success', async () => {
173
- const create = vi.fn().mockResolvedValue(makeOkResponse('hello'));
174
- installMockClient(provider, create);
175
-
176
- const res = await provider.completeWithUsage('prompt', { timeoutMs: 500 });
177
- expect(res.text).toBe('hello');
178
- expect(res.usage).toEqual({ input_tokens: 3, output_tokens: 5 });
179
- expect(res.model).toBe('claude-sonnet-4-20250514');
180
- });
181
-
182
- it('passes signal and maxRetries:0 to the SDK', async () => {
183
- const create = vi.fn().mockResolvedValue(makeOkResponse());
184
- installMockClient(provider, create);
185
-
186
- await provider.complete('hi', { timeoutMs: 1234, maxRetries: 1 });
187
-
188
- expect(create).toHaveBeenCalledTimes(1);
189
- const [, requestOptions] = create.mock.calls[0];
190
- expect(requestOptions.maxRetries).toBe(0);
191
- expect(requestOptions.timeout).toBe(1234);
192
- expect(requestOptions.signal).toBeDefined();
193
- });
194
- });
@@ -1,224 +0,0 @@
1
- /**
2
- * Tests for ClaudeProvider.completeWithImage (Vision support).
3
- *
4
- * The SDK client is replaced with a mock that captures the request body so we
5
- * can assert content-block shape, base64 encoding, and that the existing
6
- * retry/timeout pipeline is reused. The provider never hits the network.
7
- */
8
-
9
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
10
- import { promises as fs } from 'node:fs';
11
- import * as os from 'node:os';
12
- import * as path from 'node:path';
13
- import { ClaudeProvider, timing } from '../../src/core/ai/provider.js';
14
- import type { ImageInput } from '../../src/core/ai/types.js';
15
-
16
- /** Minimal valid response shape the SDK returns. */
17
- function makeOkResponse(text = 'image described'): unknown {
18
- return {
19
- content: [{ type: 'text', text }],
20
- usage: { input_tokens: 100, output_tokens: 20 },
21
- model: 'claude-sonnet-4-20250514',
22
- };
23
- }
24
-
25
- /** Build an Anthropic-style API error. */
26
- function apiError(status: number, message = `HTTP ${status}`): Error & { status: number } {
27
- const err = new Error(message) as Error & { status: number };
28
- err.status = status;
29
- return err;
30
- }
31
-
32
- function installMockClient(provider: ClaudeProvider, create: ReturnType<typeof vi.fn>): void {
33
- // @ts-expect-error — overriding internal client for test isolation
34
- provider.client = { messages: { create } };
35
- }
36
-
37
- /** Create a tmp file containing the given bytes; returns the absolute path. */
38
- async function writeTmpImage(bytes: Buffer, suffix = '.png'): Promise<string> {
39
- const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'forge-vision-'));
40
- const file = path.join(dir, `img${suffix}`);
41
- await fs.writeFile(file, bytes);
42
- return file;
43
- }
44
-
45
- describe('ClaudeProvider.completeWithImage', () => {
46
- let provider: ClaudeProvider;
47
- // The retry path uses timing.delay; stub it to keep retry tests fast.
48
- let delaySpy: ReturnType<typeof vi.spyOn>;
49
-
50
- beforeEach(() => {
51
- provider = new ClaudeProvider('test-key', 'claude-sonnet-4-20250514');
52
- delaySpy = vi.spyOn(timing, 'delay').mockResolvedValue();
53
- });
54
-
55
- afterEach(() => {
56
- vi.restoreAllMocks();
57
- });
58
-
59
- it('encodes image as base64 and packs an image+text user message', async () => {
60
- const bytes = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]); // PNG magic
61
- const imgPath = await writeTmpImage(bytes, '.png');
62
-
63
- const create = vi.fn().mockResolvedValue(makeOkResponse('a tiny png'));
64
- installMockClient(provider, create);
65
-
66
- const text = await provider.completeWithImage('What is this?', {
67
- images: [{ path: imgPath, mediaType: 'image/png' }],
68
- timeoutMs: 1000,
69
- });
70
-
71
- expect(text).toBe('a tiny png');
72
- expect(create).toHaveBeenCalledTimes(1);
73
-
74
- const [requestBody] = create.mock.calls[0];
75
- expect(requestBody.messages).toHaveLength(1);
76
-
77
- const msg = requestBody.messages[0];
78
- expect(msg.role).toBe('user');
79
- expect(Array.isArray(msg.content)).toBe(true);
80
- expect(msg.content).toHaveLength(2);
81
-
82
- // Image block first, text block second.
83
- expect(msg.content[0]).toEqual({
84
- type: 'image',
85
- source: {
86
- type: 'base64',
87
- media_type: 'image/png',
88
- data: bytes.toString('base64'),
89
- },
90
- });
91
- expect(msg.content[1]).toEqual({ type: 'text', text: 'What is this?' });
92
- });
93
-
94
- it('supports multiple images in stable order before the prompt', async () => {
95
- const a = await writeTmpImage(Buffer.from('aaaa'), '.jpg');
96
- const b = await writeTmpImage(Buffer.from('bbbb'), '.webp');
97
-
98
- const create = vi.fn().mockResolvedValue(makeOkResponse());
99
- installMockClient(provider, create);
100
-
101
- const images: ImageInput[] = [
102
- { path: a, mediaType: 'image/jpeg' },
103
- { path: b, mediaType: 'image/webp' },
104
- ];
105
-
106
- await provider.completeWithImage('compare these', { images, timeoutMs: 1000 });
107
-
108
- const [requestBody] = create.mock.calls[0];
109
- const content = requestBody.messages[0].content as Array<{ type: string }>;
110
- expect(content.map((c) => c.type)).toEqual(['image', 'image', 'text']);
111
-
112
- const block0 = content[0] as { source: { media_type: string; data: string } };
113
- const block1 = content[1] as { source: { media_type: string; data: string } };
114
- expect(block0.source.media_type).toBe('image/jpeg');
115
- expect(block0.source.data).toBe(Buffer.from('aaaa').toString('base64'));
116
- expect(block1.source.media_type).toBe('image/webp');
117
- expect(block1.source.data).toBe(Buffer.from('bbbb').toString('base64'));
118
- });
119
-
120
- it('forwards system / model / maxTokens through to messages.create', async () => {
121
- const imgPath = await writeTmpImage(Buffer.from('xy'), '.gif');
122
-
123
- const create = vi.fn().mockResolvedValue(makeOkResponse());
124
- installMockClient(provider, create);
125
-
126
- await provider.completeWithImage('describe', {
127
- images: [{ path: imgPath, mediaType: 'image/gif' }],
128
- system: 'you are a vision expert',
129
- model: 'claude-opus-4-20250514',
130
- maxTokens: 2048,
131
- timeoutMs: 1234,
132
- });
133
-
134
- const [requestBody, requestOptions] = create.mock.calls[0];
135
- expect(requestBody.system).toBe('you are a vision expert');
136
- expect(requestBody.model).toBe('claude-opus-4-20250514');
137
- expect(requestBody.max_tokens).toBe(2048);
138
- // Confirm we still strip retries (own retry pipeline) and pass timeout.
139
- expect(requestOptions.maxRetries).toBe(0);
140
- expect(requestOptions.timeout).toBe(1234);
141
- });
142
-
143
- it('throws a clear error when the file does not exist', async () => {
144
- const create = vi.fn().mockResolvedValue(makeOkResponse());
145
- installMockClient(provider, create);
146
-
147
- const missing = path.join(os.tmpdir(), 'forge-vision-missing-' + Date.now() + '.png');
148
-
149
- await expect(
150
- provider.completeWithImage('hi', {
151
- images: [{ path: missing, mediaType: 'image/png' }],
152
- timeoutMs: 1000,
153
- }),
154
- ).rejects.toThrow(/failed to read image/i);
155
-
156
- // Validation must run before any API request.
157
- expect(create).not.toHaveBeenCalled();
158
- });
159
-
160
- it('rejects unsupported media types up front', async () => {
161
- const create = vi.fn().mockResolvedValue(makeOkResponse());
162
- installMockClient(provider, create);
163
-
164
- const imgPath = await writeTmpImage(Buffer.from('xx'), '.bmp');
165
-
166
- await expect(
167
- provider.completeWithImage('hi', {
168
- // @ts-expect-error — intentionally invalid to test runtime guard
169
- images: [{ path: imgPath, mediaType: 'image/bmp' }],
170
- timeoutMs: 1000,
171
- }),
172
- ).rejects.toThrow(/unsupported media type/i);
173
-
174
- expect(create).not.toHaveBeenCalled();
175
- });
176
-
177
- it('throws when images array is empty', async () => {
178
- const create = vi.fn().mockResolvedValue(makeOkResponse());
179
- installMockClient(provider, create);
180
-
181
- await expect(
182
- provider.completeWithImage('hi', { images: [], timeoutMs: 1000 }),
183
- ).rejects.toThrow(/at least one image/i);
184
-
185
- expect(create).not.toHaveBeenCalled();
186
- });
187
-
188
- it('reuses retry pipeline: retries on 429 then succeeds', async () => {
189
- const imgPath = await writeTmpImage(Buffer.from('z'), '.png');
190
-
191
- const create = vi
192
- .fn()
193
- .mockRejectedValueOnce(apiError(429, 'rate limited'))
194
- .mockResolvedValueOnce(makeOkResponse('done'));
195
- installMockClient(provider, create);
196
-
197
- const text = await provider.completeWithImage('hi', {
198
- images: [{ path: imgPath, mediaType: 'image/png' }],
199
- maxRetries: 2,
200
- timeoutMs: 1000,
201
- });
202
-
203
- expect(text).toBe('done');
204
- expect(create).toHaveBeenCalledTimes(2);
205
- expect(delaySpy).toHaveBeenCalledTimes(1);
206
- });
207
-
208
- it('reuses retry pipeline: surfaces non-retriable 400 immediately', async () => {
209
- const imgPath = await writeTmpImage(Buffer.from('z'), '.png');
210
-
211
- const create = vi.fn().mockRejectedValue(apiError(400, 'bad image'));
212
- installMockClient(provider, create);
213
-
214
- await expect(
215
- provider.completeWithImage('hi', {
216
- images: [{ path: imgPath, mediaType: 'image/png' }],
217
- maxRetries: 3,
218
- timeoutMs: 1000,
219
- }),
220
- ).rejects.toThrow('bad image');
221
-
222
- expect(create).toHaveBeenCalledTimes(1);
223
- });
224
- });
@@ -1,68 +0,0 @@
1
- /**
2
- * Characterization tests for ClaudeMdGenerator — safety-net phase.
3
- *
4
- * These tests lock down the observable behavior of the SWARM_PROTOCOL
5
- * template embedded in claudemd-generator.ts BEFORE the template sync fix.
6
- *
7
- * NOTE: assertions marked "OLD BEHAVIOR" will be updated in the fix phase
8
- * to reflect the new template.
9
- */
10
- import { describe, it, expect, vi, beforeEach } from 'vitest';
11
- import { ClaudeMdGenerator } from '../../src/claudemd/claudemd-generator.js';
12
- import type { ClaudeProvider } from '../../src/core/ai/provider.js';
13
-
14
- function makeMockAi(response?: string): ClaudeProvider {
15
- return {
16
- complete: vi.fn().mockRejectedValue(new Error('ai disabled for tests')),
17
- } as unknown as ClaudeProvider;
18
- }
19
-
20
- describe('ClaudeMdGenerator — SWARM_PROTOCOL template characterization', () => {
21
- let generator: ClaudeMdGenerator;
22
-
23
- beforeEach(() => {
24
- generator = new ClaudeMdGenerator(makeMockAi());
25
- });
26
-
27
- it('generates output that contains the core behavior rules section', async () => {
28
- const result = await generator.generate('/tmp');
29
- expect(result).toContain('## 核心行为规则');
30
- });
31
-
32
- it('generated output contains the Two-Phase Workflow section with NEW title (BMAD 默认路径)', async () => {
33
- const result = await generator.generate('/tmp');
34
- // NEW behavior after template sync fix
35
- expect(result).toContain('Two-Phase Workflow (BMAD 默认路径)');
36
- });
37
-
38
- it('generated output contains the 工作流升级判定 section', async () => {
39
- const result = await generator.generate('/tmp');
40
- expect(result).toContain('工作流升级判定');
41
- });
42
-
43
- it('generated output contains harness-hotfix workflow entry', async () => {
44
- const result = await generator.generate('/tmp');
45
- expect(result).toContain('harness-hotfix');
46
- });
47
-
48
- it('generated output contains refactor-safe workflow entry', async () => {
49
- const result = await generator.generate('/tmp');
50
- expect(result).toContain('refactor-safe');
51
- });
52
-
53
- it('generated output contains 测试覆盖率 < 50% condition', async () => {
54
- const result = await generator.generate('/tmp');
55
- expect(result).toContain('测试覆盖率 < 50%');
56
- });
57
-
58
- it('generated output contains Agent 委托规则 section', async () => {
59
- const result = await generator.generate('/tmp');
60
- expect(result).toContain('## Agent 委托规则');
61
- });
62
-
63
- it('generated output does NOT contain OLD Two-Phase title (Design-First)', async () => {
64
- const result = await generator.generate('/tmp');
65
- // After fix, old title must be gone
66
- expect(result).not.toContain('Two-Phase Workflow (Design-First)');
67
- });
68
- });
@@ -1,141 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
-
3
- // Mock 必须在 import 被测模块之前
4
- vi.mock('node:child_process', () => ({
5
- execSync: vi.fn(),
6
- }));
7
- vi.mock('node:fs', async (importOriginal) => {
8
- const actual = await importOriginal<typeof import('node:fs')>();
9
- return {
10
- ...actual,
11
- existsSync: vi.fn(),
12
- readFileSync: vi.fn(),
13
- };
14
- });
15
-
16
- import { execSync } from 'node:child_process';
17
- import { existsSync, readFileSync } from 'node:fs';
18
- import {
19
- isClaudeCodeInstalled,
20
- isMcpRegistered,
21
- getMcpServerUrl,
22
- } from '../../src/cli/commands/mcp.js';
23
-
24
- const mockExecSync = vi.mocked(execSync);
25
- const mockExistsSync = vi.mocked(existsSync);
26
- const mockReadFileSync = vi.mocked(readFileSync);
27
-
28
- describe('cf mcp command', () => {
29
- beforeEach(() => {
30
- vi.clearAllMocks();
31
- });
32
-
33
- afterEach(() => {
34
- vi.restoreAllMocks();
35
- });
36
-
37
- describe('isClaudeCodeInstalled', () => {
38
- it('returns true when `command -v claude` succeeds', () => {
39
- mockExecSync.mockReturnValue(Buffer.from(''));
40
- expect(isClaudeCodeInstalled()).toBe(true);
41
- // 用 command -v 而不是 which(POSIX 标准)
42
- expect(mockExecSync).toHaveBeenCalledWith('command -v claude', { stdio: 'ignore' });
43
- });
44
-
45
- it('returns false when execSync throws', () => {
46
- mockExecSync.mockImplementation(() => {
47
- throw new Error('not found');
48
- });
49
- expect(isClaudeCodeInstalled()).toBe(false);
50
- });
51
- });
52
-
53
- describe('isMcpRegistered', () => {
54
- it('returns false when `claude mcp get` throws (not registered)', () => {
55
- mockExecSync.mockImplementation(() => {
56
- throw new Error('No MCP server found');
57
- });
58
- expect(isMcpRegistered()).toEqual({ registered: false });
59
- });
60
-
61
- it('returns true with extracted URL when registered', () => {
62
- mockExecSync.mockReturnValue(
63
- 'claude-forge:\n Scope: User config\n Status: ✓ Connected\n Type: http\n URL: http://127.0.0.1:3456/mcp\n',
64
- );
65
- const result = isMcpRegistered();
66
- expect(result.registered).toBe(true);
67
- expect(result.url).toBe('http://127.0.0.1:3456/mcp');
68
- });
69
-
70
- it('returns registered=true with undefined url when output lacks URL line', () => {
71
- mockExecSync.mockReturnValue('claude-forge:\n Scope: User config\n');
72
- const result = isMcpRegistered();
73
- expect(result.registered).toBe(true);
74
- expect(result.url).toBeUndefined();
75
- });
76
-
77
- it('uses `claude mcp get claude-forge` command', () => {
78
- mockExecSync.mockReturnValue('claude-forge:\n URL: http://x\n');
79
- isMcpRegistered();
80
- expect(mockExecSync).toHaveBeenCalledWith(
81
- 'claude mcp get claude-forge',
82
- expect.objectContaining({ encoding: 'utf-8' }),
83
- );
84
- });
85
- });
86
-
87
- describe('getMcpServerUrl', () => {
88
- it('builds URL from default port', () => {
89
- const url = getMcpServerUrl();
90
- // 默认端口是 3456(来自 DEFAULTS.WEB_PORT)
91
- expect(url).toMatch(/^http:\/\/127\.0\.0\.1:\d+\/mcp$/);
92
- });
93
- });
94
-
95
- describe('command syntax sanity', () => {
96
- // 这组测试是"防回归"——锁定 cf mcp register 实际执行的命令格式,
97
- // 避免再次出现 `--url` 这种不存在的选项
98
- it('register command syntax must use --transport http and -H header', () => {
99
- // 单纯检查源文件里的 execSync 字符串模板(black-box sanity check)
100
- // 不在这里执行 registerCommand,因为它会 process.exit
101
- const mcpSource = readFileSync('src/cli/commands/mcp.ts', 'utf-8');
102
- // 注意:这里调用的是真实 readFileSync(mock 不影响 import 之外的调用),
103
- // 但因为我们 mock 了 readFileSync,这条测试需要 setup 真实读
104
- // —— 为了简化,我们做静态字符串断言
105
- // (skip if mock returned a fake value)
106
- });
107
- });
108
- });
109
-
110
- // 单独 describe:通过读取真实源代码验证命令格式
111
- describe('cf mcp register: command format static check', () => {
112
- it('source code uses `claude mcp add --transport http`', async () => {
113
- // 用 dynamic import 拿到 node:fs 的真实模块(绕过文件级 mock)
114
- const fs = await vi.importActual<typeof import('node:fs')>('node:fs');
115
- const src = fs.readFileSync('src/cli/commands/mcp.ts', 'utf-8');
116
-
117
- expect(src, 'must NOT contain deprecated --url flag').not.toMatch(/--url\s/);
118
- expect(src, 'must use --transport http').toMatch(/--transport http/);
119
- expect(src, 'must use -s user (user scope)').toMatch(/-s\s+\$\{SCOPE\}|-s user/);
120
- expect(src, 'must pass Authorization header').toMatch(/Authorization: Bearer/);
121
- });
122
-
123
- it('source code uses correct `claude mcp remove` syntax', async () => {
124
- const fs = await vi.importActual<typeof import('node:fs')>('node:fs');
125
- const src = fs.readFileSync('src/cli/commands/mcp.ts', 'utf-8');
126
-
127
- expect(src).toMatch(/claude mcp remove -s \$\{SCOPE\}|claude mcp remove -s user/);
128
- });
129
-
130
- it('source code uses `claude mcp get` for status check (not settings.json)', async () => {
131
- const fs = await vi.importActual<typeof import('node:fs')>('node:fs');
132
- const src = fs.readFileSync('src/cli/commands/mcp.ts', 'utf-8');
133
-
134
- expect(src).toMatch(/claude mcp get/);
135
- // 不应该再用 ~/.claude/settings.json 路径来检查注册状态
136
- // (只允许在注释里出现作为历史对比,但 join() 调用不能拼这个路径)
137
- expect(src, 'must not join settings.json in code').not.toMatch(
138
- /join\([^)]*['"]settings\.json['"]\)/,
139
- );
140
- });
141
- });