@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,204 +0,0 @@
1
- import type { Application } from 'express';
2
- import { ConfigManager } from '../../core/config.js';
3
- import { logger } from '../../core/utils/logger.js';
4
- import {
5
- validateBaseUrl,
6
- extractHostFromBaseUrl,
7
- UPSTREAM_TIMEOUT_MS,
8
- } from '../ssrf-guard.js';
9
- import type { RouteContext } from './types.js';
10
-
11
- /**
12
- * /api/ai/* and /api/config/ai — AI provider config + upstream connectivity.
13
- *
14
- * All outbound calls route through validateBaseUrl + redirect:'manual' so the
15
- * daemon cannot be tricked into reaching internal hosts (SSRF guard).
16
- */
17
- export function registerAIRoutes(app: Application, _ctx: RouteContext): void {
18
- // GET /api/config/ai — read current AI config
19
- app.get('/api/config/ai', (_req, res) => {
20
- const configManager = new ConfigManager();
21
- const config = configManager.get();
22
-
23
- res.json({
24
- api_key: config.ai.api_key || '',
25
- base_url: config.ai.base_url || '',
26
- model: config.ai.model,
27
- provider: config.ai.provider,
28
- });
29
- });
30
-
31
- // PUT /api/config/ai — update AI config
32
- app.put('/api/config/ai', (req, res) => {
33
- const { api_key, base_url, model, provider } = req.body ?? {};
34
-
35
- try {
36
- const configManager = new ConfigManager();
37
- const config = configManager.get();
38
-
39
- if (typeof api_key === 'string') config.ai.api_key = api_key;
40
- if (typeof base_url === 'string') {
41
- // Normalize http → https for upstream gateways that force HTTPS redirect
42
- // (e.g. iflytek one.iflytek.com returns 302 and drops Authorization header)
43
- let url = base_url.trim();
44
- if (url.startsWith('http://')) url = 'https://' + url.slice('http://'.length);
45
- config.ai.base_url = url;
46
- }
47
- if (typeof model === 'string') config.ai.model = model;
48
- if (typeof provider === 'string') config.ai.provider = provider;
49
-
50
- configManager.save();
51
- logger.info('[Web] AI config updated');
52
- res.json({ success: true });
53
- } catch (err) {
54
- logger.warn(`[Web] Failed to update AI config: ${err}`);
55
- res.status(500).json({ error: String(err) });
56
- }
57
- });
58
-
59
- // GET /api/ai/models — proxy to upstream /v1/models
60
- app.get('/api/ai/models', async (req, res) => {
61
- try {
62
- const configManager = new ConfigManager();
63
- const config = configManager.get();
64
-
65
- const queryKey = typeof req.query.api_key === 'string' ? req.query.api_key : '';
66
- const queryUrl = typeof req.query.base_url === 'string' ? req.query.base_url : '';
67
- const apiKey = queryKey || config.ai.api_key;
68
- let baseUrl = queryUrl || config.ai.base_url || 'https://api.anthropic.com';
69
- if (baseUrl.startsWith('http://')) baseUrl = 'https://' + baseUrl.slice('http://'.length);
70
-
71
- if (!apiKey) {
72
- res.status(400).json({ error: 'API key not configured' });
73
- return;
74
- }
75
-
76
- const configuredHost = extractHostFromBaseUrl(config.ai.base_url);
77
- const validation = validateBaseUrl(baseUrl, configuredHost ? [configuredHost] : []);
78
- if (!validation.ok) {
79
- res.status(400).json({ error: validation.error });
80
- return;
81
- }
82
-
83
- const modelsUrl = baseUrl.endsWith('/v1')
84
- ? `${baseUrl}/models`
85
- : `${baseUrl}/v1/models`;
86
-
87
- const controller = new AbortController();
88
- const timer = setTimeout(() => controller.abort(), UPSTREAM_TIMEOUT_MS);
89
- let response: Response;
90
- try {
91
- response = await fetch(modelsUrl, {
92
- redirect: 'manual',
93
- signal: controller.signal,
94
- headers: {
95
- 'Authorization': `Bearer ${apiKey}`,
96
- 'x-api-key': apiKey,
97
- },
98
- });
99
- } finally {
100
- clearTimeout(timer);
101
- }
102
-
103
- if (response.status >= 300 && response.status < 400) {
104
- res.status(400).json({
105
- error: `Upstream tried to redirect (status ${response.status}); refusing to follow`,
106
- });
107
- return;
108
- }
109
-
110
- if (!response.ok) {
111
- const text = await response.text();
112
- logger.warn(`[Web] Upstream /v1/models failed: ${response.status} ${text}`);
113
- res.status(response.status).json({ error: text });
114
- return;
115
- }
116
-
117
- const data = await response.json();
118
- res.json(data);
119
- } catch (err) {
120
- if (err instanceof Error && err.name === 'AbortError') {
121
- res.status(504).json({ error: `Upstream timeout (${UPSTREAM_TIMEOUT_MS}ms)` });
122
- return;
123
- }
124
- logger.warn(`[Web] Failed to fetch models: ${err}`);
125
- res.status(500).json({ error: String(err) });
126
- }
127
- });
128
-
129
- // POST /api/ai/test — test AI connection using provided or saved config
130
- app.post('/api/ai/test', async (req, res) => {
131
- try {
132
- const configManager = new ConfigManager();
133
- const config = configManager.get();
134
-
135
- const { api_key, base_url, model } = req.body ?? {};
136
- const apiKey = (typeof api_key === 'string' && api_key) || config.ai.api_key;
137
- let baseUrl = (typeof base_url === 'string' && base_url) || config.ai.base_url || 'https://api.anthropic.com';
138
- const useModel = (typeof model === 'string' && model) || config.ai.model;
139
- if (baseUrl.startsWith('http://')) baseUrl = 'https://' + baseUrl.slice('http://'.length);
140
-
141
- if (!apiKey) {
142
- res.status(400).json({ error: 'API key not configured' });
143
- return;
144
- }
145
-
146
- const configuredHost = extractHostFromBaseUrl(config.ai.base_url);
147
- const validation = validateBaseUrl(baseUrl, configuredHost ? [configuredHost] : []);
148
- if (!validation.ok) {
149
- res.status(400).json({ error: validation.error });
150
- return;
151
- }
152
-
153
- const messagesUrl = baseUrl.endsWith('/v1')
154
- ? `${baseUrl}/messages`
155
- : `${baseUrl}/v1/messages`;
156
-
157
- const controller = new AbortController();
158
- const timer = setTimeout(() => controller.abort(), UPSTREAM_TIMEOUT_MS);
159
- let response: Response;
160
- try {
161
- response = await fetch(messagesUrl, {
162
- method: 'POST',
163
- redirect: 'manual',
164
- signal: controller.signal,
165
- headers: {
166
- 'Content-Type': 'application/json',
167
- 'x-api-key': apiKey,
168
- 'anthropic-version': '2023-06-01',
169
- },
170
- body: JSON.stringify({
171
- model: useModel,
172
- max_tokens: 10,
173
- messages: [{ role: 'user', content: 'ping' }],
174
- }),
175
- });
176
- } finally {
177
- clearTimeout(timer);
178
- }
179
-
180
- if (response.status >= 300 && response.status < 400) {
181
- res.status(400).json({
182
- error: `Upstream tried to redirect (status ${response.status}); refusing to follow`,
183
- });
184
- return;
185
- }
186
-
187
- if (!response.ok) {
188
- const text = await response.text();
189
- res.status(response.status).json({ error: text });
190
- return;
191
- }
192
-
193
- const data = await response.json() as { model?: string };
194
- res.json({ success: true, model: data.model || useModel });
195
- } catch (err) {
196
- if (err instanceof Error && err.name === 'AbortError') {
197
- res.status(504).json({ error: `Upstream timeout (${UPSTREAM_TIMEOUT_MS}ms)` });
198
- return;
199
- }
200
- logger.warn(`[Web] AI connection test failed: ${err}`);
201
- res.status(500).json({ error: String(err) });
202
- }
203
- });
204
- }
@@ -1,22 +0,0 @@
1
- import type { Application } from 'express';
2
- import { readAuthToken } from '../auth-middleware.js';
3
- import type { RouteContext } from './types.js';
4
-
5
- /**
6
- * Auth routes — localhost bootstrap endpoint that returns the daemon token.
7
- *
8
- * This endpoint is intentionally NOT protected by requireAuth: the token file
9
- * on disk is chmod 600 (current user only), so anyone who can reach this
10
- * endpoint already has access to the file. Protecting it would create a
11
- * chicken-and-egg problem for the UI.
12
- */
13
- export function registerAuthRoutes(app: Application, _ctx: RouteContext): void {
14
- app.get('/api/auth/token', (_req, res) => {
15
- const token = readAuthToken();
16
- if (!token) {
17
- res.status(404).json({ error: 'TOKEN_NOT_FOUND' });
18
- return;
19
- }
20
- res.json({ token });
21
- });
22
- }
@@ -1,25 +0,0 @@
1
- import type { Application } from 'express';
2
- import type { RouteContext } from './types.js';
3
- import { DriftDetector } from '../analytics/drift-detector.js';
4
-
5
- /**
6
- * /api/drift/* — CLAUDE.md 漂移检测 API
7
- *
8
- * Provides compliance drift report comparing declared behavior
9
- * in CLAUDE.md against actual event stream data.
10
- */
11
- export function registerDriftRoutes(app: Application, ctx: RouteContext): void {
12
- const { storage } = ctx;
13
-
14
- app.get('/api/drift/report', (req, res) => {
15
- const days = parseInt((req.query.days as string) || '7');
16
- if (isNaN(days) || days < 1 || days > 90) {
17
- res.status(400).json({ error: 'days must be between 1 and 90' });
18
- return;
19
- }
20
-
21
- const detector = new DriftDetector(storage);
22
- const report = detector.run(days);
23
- res.json(report);
24
- });
25
- }
@@ -1,120 +0,0 @@
1
- import type { Request, Response, NextFunction } from 'express';
2
- import { logger } from '../../core/utils/logger.js';
3
-
4
- /**
5
- * Error types for classification
6
- */
7
- export enum ErrorType {
8
- VALIDATION = 'validation',
9
- NOT_FOUND = 'not_found',
10
- UNAUTHORIZED = 'unauthorized',
11
- FORBIDDEN = 'forbidden',
12
- INTERNAL = 'internal',
13
- BAD_REQUEST = 'bad_request',
14
- }
15
-
16
- /**
17
- * Structured error response
18
- */
19
- export interface ErrorResponse {
20
- error: string;
21
- type: ErrorType;
22
- details?: unknown;
23
- timestamp: string;
24
- }
25
-
26
- /**
27
- * Custom error class with type information
28
- */
29
- export class AppError extends Error {
30
- constructor(
31
- message: string,
32
- public type: ErrorType = ErrorType.INTERNAL,
33
- public statusCode: number = 500,
34
- public details?: unknown,
35
- ) {
36
- super(message);
37
- this.name = 'AppError';
38
- }
39
- }
40
-
41
- /**
42
- * Global error handler middleware
43
- */
44
- export function errorHandler(
45
- err: Error | AppError,
46
- req: Request,
47
- res: Response,
48
- _next: NextFunction,
49
- ): void {
50
- // Log error
51
- const errorMessage = err.message;
52
- const errorStack = err.stack;
53
-
54
- logger.error(`[Web] ${req.method} ${req.path} - ${errorMessage}`);
55
- if (errorStack) {
56
- logger.error(`[Web] Stack trace: ${errorStack}`);
57
- }
58
-
59
- // Determine error type and status code
60
- let statusCode = 500;
61
- let errorType = ErrorType.INTERNAL;
62
- let details: unknown = undefined;
63
-
64
- if (err instanceof AppError) {
65
- statusCode = err.statusCode;
66
- errorType = err.type;
67
- details = err.details;
68
- } else if (err.name === 'ValidationError') {
69
- statusCode = 400;
70
- errorType = ErrorType.VALIDATION;
71
- } else if (err.message.includes('not found')) {
72
- statusCode = 404;
73
- errorType = ErrorType.NOT_FOUND;
74
- } else if (err.message.includes('unauthorized') || err.message.includes('authentication')) {
75
- statusCode = 401;
76
- errorType = ErrorType.UNAUTHORIZED;
77
- } else if (err.message.includes('forbidden') || err.message.includes('permission')) {
78
- statusCode = 403;
79
- errorType = ErrorType.FORBIDDEN;
80
- } else if (err.message.includes('Invalid') || err.message.includes('invalid')) {
81
- statusCode = 400;
82
- errorType = ErrorType.BAD_REQUEST;
83
- }
84
-
85
- // Send error response
86
- const response: ErrorResponse = {
87
- error: err.message,
88
- type: errorType,
89
- timestamp: new Date().toISOString(),
90
- };
91
-
92
- if (details) {
93
- response.details = details;
94
- }
95
-
96
- res.status(statusCode).json(response);
97
- }
98
-
99
- /**
100
- * 404 handler for undefined routes
101
- */
102
- export function notFoundHandler(req: Request, res: Response): void {
103
- logger.warn(`[Web] 404 - ${req.method} ${req.path}`);
104
- res.status(404).json({
105
- error: `Route not found: ${req.method} ${req.path}`,
106
- type: ErrorType.NOT_FOUND,
107
- timestamp: new Date().toISOString(),
108
- });
109
- }
110
-
111
- /**
112
- * Async route handler wrapper to catch errors
113
- */
114
- export function asyncHandler(
115
- fn: (req: Request, res: Response, next: NextFunction) => Promise<void>,
116
- ) {
117
- return (req: Request, res: Response, next: NextFunction): void => {
118
- Promise.resolve(fn(req, res, next)).catch(next);
119
- };
120
- }
@@ -1,47 +0,0 @@
1
- import type { Application } from 'express';
2
- import type { RouteContext } from './types.js';
3
-
4
- /**
5
- * /api/events — recent events list + SSE live stream.
6
- *
7
- * Note: /api/events/stream must be registered BEFORE `/api/events` handlers
8
- * that might swallow it, but Express matches exact paths so order here is
9
- * not actually load-bearing for correctness.
10
- */
11
- export function registerEventsRoutes(app: Application, ctx: RouteContext): void {
12
- const { storage } = ctx;
13
-
14
- app.get('/api/events', (req, res) => {
15
- const limit = parseInt(req.query.limit as string) || 50;
16
- const projectPath = req.query.project as string | undefined;
17
- const sessionId = req.query.session as string | undefined;
18
- const events = storage.queryEvents({ project_path: projectPath, session_id: sessionId, limit });
19
- res.json(events);
20
- });
21
-
22
- // SSE: real-time event stream
23
- app.get('/api/events/stream', (req, res) => {
24
- res.writeHead(200, {
25
- 'Content-Type': 'text/event-stream',
26
- 'Cache-Control': 'no-cache',
27
- Connection: 'keep-alive',
28
- });
29
- res.write('data: {"type":"connected"}\n\n');
30
-
31
- const filterSession = req.query.session as string | undefined;
32
- const filterProject = req.query.project as string | undefined;
33
- const filterHook = req.query.hook as string | undefined;
34
-
35
- const onEvent = (event: Record<string, unknown>) => {
36
- if (filterSession && event.session_id !== filterSession) return;
37
- if (filterProject && event.project_path !== filterProject) return;
38
- if (filterHook && event.hook_type !== filterHook) return;
39
- res.write(`data: ${JSON.stringify(event)}\n\n`);
40
- };
41
-
42
- storage.on('event', onEvent);
43
- req.on('close', () => {
44
- storage.removeListener('event', onEvent);
45
- });
46
- });
47
- }
@@ -1,43 +0,0 @@
1
- import type { Application } from 'express';
2
- import type { RouteContext } from './types.js';
3
- import {
4
- AntiPatternDetector,
5
- type AntiPattern,
6
- type AntiPatternSeverity,
7
- } from '../analytics/anti-pattern-detector.js';
8
-
9
- /**
10
- * /api/insights — 工作流反模式检测 API
11
- *
12
- * 扫描事件流,识别可操作的反模式(编辑循环 / 工具失败 / 无 commit / 事件爆量)。
13
- */
14
- export function registerInsightsRoutes(app: Application, ctx: RouteContext): void {
15
- const { storage } = ctx;
16
-
17
- app.get('/api/insights', (req, res) => {
18
- const raw = req.query.days;
19
- const days = parseInt(typeof raw === 'string' ? raw : '7', 10);
20
- if (Number.isNaN(days) || days < 1 || days > 90) {
21
- res.status(400).json({ error: 'days must be between 1 and 90' });
22
- return;
23
- }
24
-
25
- const detector = new AntiPatternDetector(storage);
26
- const patterns = detector.detectAll(days);
27
-
28
- const summary: Record<AntiPatternSeverity, number> & { total: number } = {
29
- total: patterns.length,
30
- critical: 0,
31
- warn: 0,
32
- info: 0,
33
- };
34
- for (const p of patterns) summary[p.severity] += 1;
35
-
36
- res.json({
37
- generatedAt: new Date().toISOString(),
38
- windowDays: days,
39
- summary,
40
- patterns: patterns satisfies AntiPattern[],
41
- });
42
- });
43
- }
@@ -1,117 +0,0 @@
1
- import type { Application } from 'express';
2
- import fs from 'fs';
3
- import path from 'path';
4
- import { ConfigManager } from '../../core/config.js';
5
- import { ClaudeProvider } from '../../core/ai/provider.js';
6
- import { logger } from '../../core/utils/logger.js';
7
- import type { RouteContext } from './types.js';
8
- import { resolvePatchTarget } from './types.js';
9
-
10
- /**
11
- * /api/patch/* — AI-assisted patch preview + apply (with backup).
12
- */
13
- export function registerPatchRoutes(app: Application, _ctx: RouteContext): void {
14
- // POST /api/patch/preview — generate structured patch preview
15
- app.post('/api/patch/preview', async (req, res) => {
16
- const { targetType, targetName, recommendation, rationale } = req.body ?? {};
17
- if (!targetType || !targetName || !recommendation) {
18
- res.status(400).json({ error: 'targetType, targetName, and recommendation are required' });
19
- return;
20
- }
21
-
22
- try {
23
- const config = new ConfigManager().get();
24
- const apiKey = config.ai.api_key || process.env.ANTHROPIC_API_KEY || '';
25
- if (!apiKey) {
26
- res.status(400).json({ error: 'AI API key not configured' });
27
- return;
28
- }
29
-
30
- const { filePath } = resolvePatchTarget(targetType, targetName);
31
- if (!fs.existsSync(filePath)) {
32
- res.status(404).json({ error: `Target file not found: ${filePath}` });
33
- return;
34
- }
35
-
36
- const currentContent = fs.readFileSync(filePath, 'utf-8');
37
- const ai = new ClaudeProvider(apiKey, config.ai.model, config.ai.base_url);
38
-
39
- const prompt = `You are a code/config optimization assistant. Given the current file content and a recommended change, generate the updated content.
40
-
41
- Current file (${targetType}/${targetName}):
42
- \`\`\`
43
- ${currentContent}
44
- \`\`\`
45
-
46
- Recommendation: ${recommendation}
47
- ${rationale ? `Rationale: ${rationale}` : ''}
48
-
49
- Return ONLY a JSON object with this exact structure (no markdown, no explanation):
50
- {
51
- "summary": "brief description of what changed",
52
- "afterContent": "the complete updated file content",
53
- "risk": "low|medium|high"
54
- }`;
55
-
56
- const response = await ai.complete(prompt, { maxTokens: 4096 });
57
- const parsed = JSON.parse(response.trim());
58
-
59
- res.json({
60
- targetType,
61
- targetName,
62
- filePath,
63
- before: currentContent,
64
- after: parsed.afterContent,
65
- summary: parsed.summary,
66
- risk: parsed.risk || 'medium',
67
- recommendation,
68
- rationale: rationale || null,
69
- });
70
- } catch (err) {
71
- logger.warn(`[Web] Patch preview failed: ${err}`);
72
- res.status(500).json({ error: String(err) });
73
- }
74
- });
75
-
76
- // POST /api/patch/apply — apply patch with backup
77
- app.post('/api/patch/apply', (req, res) => {
78
- const { targetType, targetName, afterContent } = req.body ?? {};
79
- if (!targetType || !targetName || typeof afterContent !== 'string') {
80
- res.status(400).json({ error: 'targetType, targetName, and afterContent are required' });
81
- return;
82
- }
83
-
84
- try {
85
- const { filePath, backupDir } = resolvePatchTarget(targetType, targetName);
86
- if (!fs.existsSync(filePath)) {
87
- res.status(404).json({ error: `Target file not found: ${filePath}` });
88
- return;
89
- }
90
-
91
- // Create backup
92
- if (!fs.existsSync(backupDir)) {
93
- fs.mkdirSync(backupDir, { recursive: true });
94
- }
95
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
96
- const backupName = `${targetName}_${timestamp}${path.extname(filePath)}`;
97
- const backupPath = path.join(backupDir, backupName);
98
- fs.copyFileSync(filePath, backupPath);
99
-
100
- // Apply patch
101
- fs.writeFileSync(filePath, afterContent, 'utf-8');
102
-
103
- logger.info(`[Web] Patch applied to ${targetType}/${targetName}, backup: ${backupPath}`);
104
- res.json({
105
- success: true,
106
- targetType,
107
- targetName,
108
- filePath,
109
- backupPath,
110
- timestamp,
111
- });
112
- } catch (err) {
113
- logger.warn(`[Web] Patch apply failed: ${err}`);
114
- res.status(500).json({ error: String(err) });
115
- }
116
- });
117
- }
@@ -1,34 +0,0 @@
1
- /**
2
- * /api/reports/weekly — Weekly work report
3
- *
4
- * Aggregates the last week's activity into a structured report. Supports
5
- * both JSON (default) and Markdown output formats.
6
- */
7
-
8
- import type { Application } from 'express';
9
- import type { RouteContext } from './types.js';
10
- import { WeeklyReportGenerator } from '../analytics/weekly-report.js';
11
-
12
- export function registerReportsRoutes(app: Application, ctx: RouteContext): void {
13
- const { storage, skillRegistry } = ctx;
14
-
15
- app.get('/api/reports/weekly', (req, res) => {
16
- const weekOffsetRaw = (req.query.weekOffset as string) ?? '0';
17
- const weekOffset = Number.isFinite(Number(weekOffsetRaw)) ? Number(weekOffsetRaw) : 0;
18
- const format = (req.query.format as string) === 'markdown' ? 'markdown' : 'json';
19
-
20
- const generator = new WeeklyReportGenerator(storage, skillRegistry);
21
- const report = generator.generate(weekOffset);
22
-
23
- if (format === 'markdown') {
24
- const md = generator.toMarkdown(report);
25
- const filename = `weekly-report-w${report.week.isoWeekNumber}.md`;
26
- res.setHeader('Content-Type', 'text/markdown; charset=utf-8');
27
- res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
28
- res.send(md);
29
- return;
30
- }
31
-
32
- res.json(report);
33
- });
34
- }
@@ -1,76 +0,0 @@
1
- /**
2
- * /api/rules/hit-rate — Rule hit-rate statistics
3
- *
4
- * Aggregates routing_events and skill_invocations to show:
5
- * - Agent delegation rate
6
- * - Skill invocation rate
7
- * - Per-skill breakdown
8
- * - Per-agent breakdown
9
- * - Never-triggered skills
10
- *
11
- * H1 refactor: pushed GROUP BY into SQL via storage.aggregateRoutingStats /
12
- * aggregateSkillInvocationsBySkill — no more 100k-row JS scans.
13
- */
14
-
15
- import type { Application } from 'express';
16
- import type { RouteContext } from './types.js';
17
-
18
- function pct(num: number, denom: number): string {
19
- if (denom <= 0) return '0%';
20
- return `${(num / denom * 100).toFixed(1)}%`;
21
- }
22
-
23
- export function registerRulesRoutes(app: Application, ctx: RouteContext): void {
24
- const { storage, skillRegistry } = ctx;
25
-
26
- app.get('/api/rules/hit-rate', (req, res) => {
27
- const days = Math.max(1, parseInt((req.query.days as string) || '7'));
28
- const since = Date.now() - days * 24 * 3600 * 1000;
29
- const sinceISO = new Date(since).toISOString();
30
-
31
- // SQL-side aggregates (replaces 2 × 100k-row scans + JS reduce)
32
- const routing = storage.aggregateRoutingStats({ since_ts: since });
33
- const skillsAgg = storage.aggregateSkillInvocationsBySkill({ since });
34
-
35
- const totalPrompts = routing.total;
36
- const agentDelegations = routing.obeyed;
37
-
38
- // Skills frequency (sorted by total desc — SQL ORDER BY total DESC)
39
- const skillTotal = skillsAgg.reduce((acc, s) => acc + s.total, 0);
40
- const skills = skillsAgg.map(s => ({
41
- skill_id: s.skill_id,
42
- total: s.total,
43
- success: s.success,
44
- failed: s.failed,
45
- rate: pct(s.total, totalPrompts),
46
- }));
47
-
48
- // Agents breakdown (already obeyed=1, sorted by count desc in SQL)
49
- const agents = routing.by_agent.map(a => ({
50
- agent: a.agent,
51
- count: a.count,
52
- rate: pct(a.count, totalPrompts),
53
- }));
54
-
55
- // Never-triggered skills
56
- const triggeredSkillIds = new Set(skillsAgg.map(s => s.skill_id));
57
- const allSkillIds = skillRegistry
58
- ? skillRegistry.getAll().map(s => s.id)
59
- : [];
60
- const neverTriggered = allSkillIds.filter(id => !triggeredSkillIds.has(id));
61
-
62
- res.json({
63
- period: { days, since: sinceISO },
64
- summary: {
65
- totalPrompts,
66
- agentDelegations,
67
- agentRate: pct(agentDelegations, totalPrompts),
68
- skillInvocations: skillTotal,
69
- skillRate: pct(skillTotal, totalPrompts),
70
- },
71
- skills,
72
- agents,
73
- neverTriggered,
74
- });
75
- });
76
- }