@winspan/claude-forge 8.50.6 → 8.51.0

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 (361) hide show
  1. package/CLAUDE.md +7 -7
  2. package/dist/claudemd/claudemd-generator.d.ts.map +1 -1
  3. package/dist/claudemd/claudemd-generator.js +27 -237
  4. package/dist/claudemd/claudemd-generator.js.map +1 -1
  5. package/dist/claudemd/resume-manager.js +1 -1
  6. package/dist/claudemd/resume-manager.js.map +1 -1
  7. package/dist/claudemd/templates/swarm-protocol.md +222 -0
  8. package/dist/cli/commands/daemon.js +6 -6
  9. package/dist/cli/commands/daemon.js.map +1 -1
  10. package/dist/cli/commands/executions.d.ts.map +1 -1
  11. package/dist/cli/commands/executions.js +4 -3
  12. package/dist/cli/commands/executions.js.map +1 -1
  13. package/dist/cli/commands/init.js +2 -2
  14. package/dist/cli/commands/init.js.map +1 -1
  15. package/dist/cli/commands/logs.js.map +1 -1
  16. package/dist/cli/commands/mcp.d.ts.map +1 -1
  17. package/dist/cli/commands/mcp.js +3 -5
  18. package/dist/cli/commands/mcp.js.map +1 -1
  19. package/dist/cli/commands/menu.d.ts.map +1 -1
  20. package/dist/cli/commands/menu.js +4 -3
  21. package/dist/cli/commands/menu.js.map +1 -1
  22. package/dist/cli/commands/stats.d.ts.map +1 -1
  23. package/dist/cli/commands/stats.js +2 -3
  24. package/dist/cli/commands/stats.js.map +1 -1
  25. package/dist/cli/commands/status.js +2 -2
  26. package/dist/cli/commands/status.js.map +1 -1
  27. package/dist/cli/commands/trace.d.ts.map +1 -1
  28. package/dist/cli/commands/trace.js +11 -23
  29. package/dist/cli/commands/trace.js.map +1 -1
  30. package/dist/cli/init/hook-manager.d.ts.map +1 -1
  31. package/dist/cli/init/hook-manager.js +2 -2
  32. package/dist/cli/init/hook-manager.js.map +1 -1
  33. package/dist/core/ai/provider.js +2 -2
  34. package/dist/core/ai/provider.js.map +1 -1
  35. package/dist/core/constants.d.ts +12 -1
  36. package/dist/core/constants.d.ts.map +1 -1
  37. package/dist/core/constants.js +15 -1
  38. package/dist/core/constants.js.map +1 -1
  39. package/dist/core/event-fields.d.ts +16 -0
  40. package/dist/core/event-fields.d.ts.map +1 -0
  41. package/dist/core/event-fields.js +19 -0
  42. package/dist/core/event-fields.js.map +1 -0
  43. package/dist/core/queue/index.d.ts.map +1 -1
  44. package/dist/core/queue/index.js +3 -4
  45. package/dist/core/queue/index.js.map +1 -1
  46. package/dist/core/storage/base.d.ts +36 -3
  47. package/dist/core/storage/base.d.ts.map +1 -1
  48. package/dist/core/storage/base.js +101 -58
  49. package/dist/core/storage/base.js.map +1 -1
  50. package/dist/core/storage/events.d.ts +92 -3
  51. package/dist/core/storage/events.d.ts.map +1 -1
  52. package/dist/core/storage/events.js +147 -0
  53. package/dist/core/storage/events.js.map +1 -1
  54. package/dist/core/storage/routing.d.ts +54 -1
  55. package/dist/core/storage/routing.d.ts.map +1 -1
  56. package/dist/core/storage/routing.js +99 -1
  57. package/dist/core/storage/routing.js.map +1 -1
  58. package/dist/core/storage/schema.sql +12 -2
  59. package/dist/core/storage/sessions.d.ts +20 -0
  60. package/dist/core/storage/sessions.d.ts.map +1 -1
  61. package/dist/core/storage/sessions.js +59 -0
  62. package/dist/core/storage/sessions.js.map +1 -1
  63. package/dist/core/storage/skills.d.ts +23 -0
  64. package/dist/core/storage/skills.d.ts.map +1 -1
  65. package/dist/core/storage/skills.js +47 -0
  66. package/dist/core/storage/skills.js.map +1 -1
  67. package/dist/core/storage/sqlite.d.ts +35 -2
  68. package/dist/core/storage/sqlite.d.ts.map +1 -1
  69. package/dist/core/storage/sqlite.js +93 -4
  70. package/dist/core/storage/sqlite.js.map +1 -1
  71. package/dist/core/storage/tasks.d.ts +49 -0
  72. package/dist/core/storage/tasks.d.ts.map +1 -1
  73. package/dist/core/storage/tasks.js +143 -1
  74. package/dist/core/storage/tasks.js.map +1 -1
  75. package/dist/core/storage/token-usage.d.ts +1 -1
  76. package/dist/core/storage/token-usage.d.ts.map +1 -1
  77. package/dist/core/storage/token-usage.js +1 -1
  78. package/dist/core/storage/token-usage.js.map +1 -1
  79. package/dist/core/types.d.ts +24 -3
  80. package/dist/core/types.d.ts.map +1 -1
  81. package/dist/core/types.js.map +1 -1
  82. package/dist/core/utils/error-handler.d.ts.map +1 -1
  83. package/dist/core/utils/error-handler.js +3 -2
  84. package/dist/core/utils/error-handler.js.map +1 -1
  85. package/dist/core/utils/git.d.ts +10 -0
  86. package/dist/core/utils/git.d.ts.map +1 -0
  87. package/dist/core/utils/git.js +24 -0
  88. package/dist/core/utils/git.js.map +1 -0
  89. package/dist/core/utils/logger.d.ts.map +1 -1
  90. package/dist/core/utils/logger.js +15 -1
  91. package/dist/core/utils/logger.js.map +1 -1
  92. package/dist/core/utils/lru-cache.d.ts +1 -0
  93. package/dist/core/utils/lru-cache.d.ts.map +1 -1
  94. package/dist/core/utils/lru-cache.js +3 -0
  95. package/dist/core/utils/lru-cache.js.map +1 -1
  96. package/dist/core/utils/token-tracker.js +1 -1
  97. package/dist/core/utils/token-tracker.js.map +1 -1
  98. package/dist/daemon/event-parser.d.ts.map +1 -1
  99. package/dist/daemon/event-parser.js +2 -1
  100. package/dist/daemon/event-parser.js.map +1 -1
  101. package/dist/daemon/handlers/history-exporter.js.map +1 -1
  102. package/dist/daemon/handlers/post-tool-use.d.ts.map +1 -1
  103. package/dist/daemon/handlers/post-tool-use.js +7 -3
  104. package/dist/daemon/handlers/post-tool-use.js.map +1 -1
  105. package/dist/daemon/handlers/stop.d.ts +4 -0
  106. package/dist/daemon/handlers/stop.d.ts.map +1 -1
  107. package/dist/daemon/handlers/stop.js +23 -35
  108. package/dist/daemon/handlers/stop.js.map +1 -1
  109. package/dist/daemon/handlers/user-prompt.d.ts +3 -3
  110. package/dist/daemon/handlers/user-prompt.d.ts.map +1 -1
  111. package/dist/daemon/handlers/user-prompt.js +12 -22
  112. package/dist/daemon/handlers/user-prompt.js.map +1 -1
  113. package/dist/daemon/index.d.ts.map +1 -1
  114. package/dist/daemon/index.js +23 -9
  115. package/dist/daemon/index.js.map +1 -1
  116. package/dist/daemon/lifecycle.js +3 -4
  117. package/dist/daemon/lifecycle.js.map +1 -1
  118. package/dist/daemon/server.d.ts +6 -4
  119. package/dist/daemon/server.d.ts.map +1 -1
  120. package/dist/daemon/server.js +76 -85
  121. package/dist/daemon/server.js.map +1 -1
  122. package/dist/daemon/services/task-segmenter.js +1 -1
  123. package/dist/daemon/services/task-segmenter.js.map +1 -1
  124. package/dist/hooks/hook-lib.sh +37 -0
  125. package/dist/hooks/notification.sh +2 -2
  126. package/dist/hooks/post-tool-use.sh +2 -2
  127. package/dist/hooks/pre-tool-use.sh +2 -2
  128. package/dist/hooks/stop.sh +9 -6
  129. package/dist/hooks/user-prompt-submit.sh +2 -2
  130. package/dist/{daemon/services → web/analytics}/anti-pattern-detector.d.ts +3 -4
  131. package/dist/web/analytics/anti-pattern-detector.d.ts.map +1 -0
  132. package/dist/{daemon/services → web/analytics}/anti-pattern-detector.js +7 -46
  133. package/dist/{daemon/services → web/analytics}/anti-pattern-detector.js.map +1 -1
  134. package/dist/web/analytics/drift-detector.d.ts.map +1 -0
  135. package/dist/{daemon/services → web/analytics}/drift-detector.js +10 -13
  136. package/dist/web/analytics/drift-detector.js.map +1 -0
  137. package/dist/web/analytics/weekly-report.d.ts.map +1 -0
  138. package/dist/{daemon/services → web/analytics}/weekly-report.js +51 -50
  139. package/dist/web/analytics/weekly-report.js.map +1 -0
  140. package/dist/web/auth-middleware.d.ts.map +1 -1
  141. package/dist/web/auth-middleware.js +1 -2
  142. package/dist/web/auth-middleware.js.map +1 -1
  143. package/dist/web/routes/_helpers.d.ts +16 -0
  144. package/dist/web/routes/_helpers.d.ts.map +1 -0
  145. package/dist/web/routes/_helpers.js +32 -0
  146. package/dist/web/routes/_helpers.js.map +1 -0
  147. package/dist/web/routes/drift.js +1 -1
  148. package/dist/web/routes/drift.js.map +1 -1
  149. package/dist/web/routes/insights.js +1 -1
  150. package/dist/web/routes/insights.js.map +1 -1
  151. package/dist/web/routes/reports.js +1 -1
  152. package/dist/web/routes/reports.js.map +1 -1
  153. package/dist/web/routes/rules.d.ts +3 -0
  154. package/dist/web/routes/rules.d.ts.map +1 -1
  155. package/dist/web/routes/rules.js +28 -52
  156. package/dist/web/routes/rules.js.map +1 -1
  157. package/dist/web/routes/sessions.d.ts.map +1 -1
  158. package/dist/web/routes/sessions.js +16 -30
  159. package/dist/web/routes/sessions.js.map +1 -1
  160. package/dist/web/routes/skill-stats.d.ts +2 -0
  161. package/dist/web/routes/skill-stats.d.ts.map +1 -1
  162. package/dist/web/routes/skill-stats.js +28 -64
  163. package/dist/web/routes/skill-stats.js.map +1 -1
  164. package/dist/web/routes/skills.d.ts.map +1 -1
  165. package/dist/web/routes/skills.js +5 -4
  166. package/dist/web/routes/skills.js.map +1 -1
  167. package/dist/web/routes/stats.d.ts +4 -0
  168. package/dist/web/routes/stats.d.ts.map +1 -1
  169. package/dist/web/routes/stats.js +19 -21
  170. package/dist/web/routes/stats.js.map +1 -1
  171. package/dist/web/routes/tasks.d.ts.map +1 -1
  172. package/dist/web/routes/tasks.js +17 -42
  173. package/dist/web/routes/tasks.js.map +1 -1
  174. package/dist/web/routes/trace.d.ts.map +1 -1
  175. package/dist/web/routes/trace.js +7 -17
  176. package/dist/web/routes/trace.js.map +1 -1
  177. package/dist/web/routes/types.d.ts.map +1 -1
  178. package/dist/web/routes/types.js +4 -3
  179. package/dist/web/routes/types.js.map +1 -1
  180. package/dist/web/static/assets/{AIConfig-BQCAQE9D.js → AIConfig-CdDWzJyO.js} +2 -2
  181. package/dist/web/static/assets/{AIConfig-BQCAQE9D.js.map → AIConfig-CdDWzJyO.js.map} +1 -1
  182. package/dist/web/static/assets/{Dashboard-D7Bo6Kan.js → Dashboard-CoEmmIDt.js} +2 -2
  183. package/dist/web/static/assets/{Dashboard-D7Bo6Kan.js.map → Dashboard-CoEmmIDt.js.map} +1 -1
  184. package/dist/web/static/assets/{Drawer-BeHRQxUS.js → Drawer-DdRTzlLB.js} +2 -2
  185. package/dist/web/static/assets/{Drawer-BeHRQxUS.js.map → Drawer-DdRTzlLB.js.map} +1 -1
  186. package/dist/web/static/assets/{Events-K_tCY2ti.js → Events-DrIq1SUS.js} +2 -2
  187. package/dist/web/static/assets/{Events-K_tCY2ti.js.map → Events-DrIq1SUS.js.map} +1 -1
  188. package/dist/web/static/assets/{Reports-BJCmBnc_.js → Reports-DFBM3MDK.js} +2 -2
  189. package/dist/web/static/assets/{Reports-BJCmBnc_.js.map → Reports-DFBM3MDK.js.map} +1 -1
  190. package/dist/web/static/assets/{SearchInput-BX2KhMkw.js → SearchInput-qCj_jAcf.js} +2 -2
  191. package/dist/web/static/assets/{SearchInput-BX2KhMkw.js.map → SearchInput-qCj_jAcf.js.map} +1 -1
  192. package/dist/web/static/assets/{SessionDetail-Bkr-kC7V.js → SessionDetail-CCzwdoT7.js} +2 -2
  193. package/dist/web/static/assets/{SessionDetail-Bkr-kC7V.js.map → SessionDetail-CCzwdoT7.js.map} +1 -1
  194. package/dist/web/static/assets/{Sessions-Chx9OCLH.js → Sessions-FfLYkAw9.js} +2 -2
  195. package/dist/web/static/assets/{Sessions-Chx9OCLH.js.map → Sessions-FfLYkAw9.js.map} +1 -1
  196. package/dist/web/static/assets/{Skills-O0GT1i7m.js → Skills-C8Gvs3Qa.js} +2 -2
  197. package/dist/web/static/assets/{Skills-O0GT1i7m.js.map → Skills-C8Gvs3Qa.js.map} +1 -1
  198. package/dist/web/static/assets/TaskDetail-BS8pYhaR.js +2 -0
  199. package/dist/web/static/assets/TaskDetail-BS8pYhaR.js.map +1 -0
  200. package/dist/web/static/assets/Tasks-CyuhizG8.js +2 -0
  201. package/dist/web/static/assets/Tasks-CyuhizG8.js.map +1 -0
  202. package/dist/web/static/assets/index-CBX47X8l.js +3 -0
  203. package/dist/web/static/assets/{index-DxIbmNmr.js.map → index-CBX47X8l.js.map} +1 -1
  204. package/dist/web/static/assets/index-DjIoMdoR.css +1 -0
  205. package/dist/web/static/assets/{lucide-fJlPI3H7.js → lucide-Bs_edTLa.js} +44 -39
  206. package/dist/web/static/assets/lucide-Bs_edTLa.js.map +1 -0
  207. package/dist/web/static/assets/react-router-r79dBVy4.js +20 -0
  208. package/dist/web/static/assets/{react-router-I-HqunH7.js.map → react-router-r79dBVy4.js.map} +1 -1
  209. package/dist/web/static/assets/task-title-BhOcemuR.js +2 -0
  210. package/dist/web/static/assets/task-title-BhOcemuR.js.map +1 -0
  211. package/dist/web/static/index.html +4 -4
  212. package/docs/design/h1-storage-aggregation-spec-20260518-1121.md +299 -0
  213. package/docs/design/h2-getdatabase-encapsulation-spec-20260518-1450.md +191 -0
  214. package/docs/design/h3-fallback-removal-spec-20260518-1245.md +76 -0
  215. package/docs/design/h4-index-dedup-spec-20260518-1230.md +109 -0
  216. package/docs/design/h6-services-migration-spec-20260518-1355.md +82 -0
  217. package/docs/design/l1-swarm-protocol-extract-spec-20260518-1605.md +106 -0
  218. package/docs/design/m10-forge-paths-spec-20260518-1320.md +121 -0
  219. package/docs/design/m2-m3-tool-input-spec-20260518-1425.md +131 -0
  220. package/docs/design/m7-routing-event-association-spec-20260518-1545.md +103 -0
  221. package/docs/design/project-path-gitroot-spec-20260518-1715.md +134 -0
  222. package/docs/design/task-active-gc-spec-20260518-1745.md +146 -0
  223. package/docs/implementation/h1-storage-aggregation-changelog-20260518-1121.md +82 -0
  224. package/docs/implementation/h2-final-changelog-20260518-1530.md +61 -0
  225. package/docs/implementation/h2-phase1-safety-net-changelog-20260518-1450.md +70 -0
  226. package/docs/implementation/h2-phase2-operations-changelog-20260518-1450.md +120 -0
  227. package/docs/implementation/h2-phase3-callsites-changelog-20260518-1450.md +71 -0
  228. package/docs/implementation/h3-fallback-removal-changelog-20260518-1245.md +71 -0
  229. package/docs/implementation/h4-index-dedup-changelog-20260518-1230.md +60 -0
  230. package/docs/implementation/h6-services-migration-changelog-20260518-1355.md +46 -0
  231. package/docs/implementation/h7-m9-defaults-changelog-20260518-1300.md +46 -0
  232. package/docs/implementation/l1-swarm-protocol-extract-changelog-20260518-1605.md +45 -0
  233. package/docs/implementation/l3-l4-daemon-perf-changelog-20260518-1410.md +63 -0
  234. package/docs/implementation/l6-l8-final-cleanup-changelog-20260518-1640.md +38 -0
  235. package/docs/implementation/m1-m4-m5-l7-cleanup-changelog-20260518-1310.md +58 -0
  236. package/docs/implementation/m10-forge-paths-changelog-20260518-1320.md +60 -0
  237. package/docs/implementation/m2-m3-tool-input-changelog-20260518-1425.md +43 -0
  238. package/docs/implementation/m6-m8-naming-shutdown-changelog-20260518-1340.md +56 -0
  239. package/docs/implementation/m7-routing-association-changelog-20260518-1545.md +69 -0
  240. package/docs/implementation/project-path-gitroot-changelog-20260518-1715.md +63 -0
  241. package/docs/implementation/task-active-gc-changelog-20260518-1745.md +35 -0
  242. package/docs/implementation/task-title-summary-changelog-20260518-1130.md +39 -0
  243. package/docs/implementation/tasks-detail-back-loses-filters-changelog-20260518-1100.md +22 -0
  244. package/docs/implementation/tasks-page-white-screen-hotfix-changelog-20260518-1015.md +56 -0
  245. package/docs/reviews/task-title-summary.md +92 -0
  246. package/docs/reviews/tasks-detail-back-loses-filters.md +58 -0
  247. package/docs/reviews/tasks-page-white-screen-hotfix.md +126 -0
  248. package/package.json +2 -2
  249. package/src/claudemd/claudemd-generator.ts +29 -238
  250. package/src/claudemd/resume-manager.ts +1 -1
  251. package/src/claudemd/templates/swarm-protocol.md +222 -0
  252. package/src/cli/commands/daemon.ts +6 -6
  253. package/src/cli/commands/executions.ts +4 -3
  254. package/src/cli/commands/init.ts +2 -2
  255. package/src/cli/commands/logs.ts +1 -1
  256. package/src/cli/commands/mcp.ts +3 -5
  257. package/src/cli/commands/menu.ts +4 -3
  258. package/src/cli/commands/stats.ts +2 -3
  259. package/src/cli/commands/status.ts +2 -2
  260. package/src/cli/commands/trace.ts +10 -26
  261. package/src/cli/init/hook-manager.ts +2 -2
  262. package/src/core/ai/provider.ts +2 -2
  263. package/src/core/constants.ts +18 -1
  264. package/src/core/event-fields.ts +32 -0
  265. package/src/core/queue/index.ts +3 -4
  266. package/src/core/storage/base.ts +132 -56
  267. package/src/core/storage/events.ts +183 -4
  268. package/src/core/storage/routing.ts +129 -1
  269. package/src/core/storage/schema.sql +12 -2
  270. package/src/core/storage/sessions.ts +64 -0
  271. package/src/core/storage/skills.ts +69 -0
  272. package/src/core/storage/sqlite.ts +103 -4
  273. package/src/core/storage/tasks.ts +149 -1
  274. package/src/core/storage/token-usage.ts +1 -1
  275. package/src/core/types.ts +30 -3
  276. package/src/core/utils/error-handler.ts +3 -2
  277. package/src/core/utils/git.ts +23 -0
  278. package/src/core/utils/logger.ts +16 -1
  279. package/src/core/utils/lru-cache.ts +4 -0
  280. package/src/core/utils/token-tracker.ts +1 -1
  281. package/src/daemon/event-parser.ts +4 -3
  282. package/src/daemon/handlers/history-exporter.ts +1 -1
  283. package/src/daemon/handlers/post-tool-use.ts +7 -3
  284. package/src/daemon/handlers/stop.ts +32 -39
  285. package/src/daemon/handlers/user-prompt.ts +12 -22
  286. package/src/daemon/index.ts +24 -10
  287. package/src/daemon/lifecycle.ts +3 -3
  288. package/src/daemon/server.ts +76 -89
  289. package/src/daemon/services/task-segmenter.ts +1 -1
  290. package/src/hooks/hook-lib.sh +37 -0
  291. package/src/hooks/notification.sh +2 -2
  292. package/src/hooks/post-tool-use.sh +2 -2
  293. package/src/hooks/pre-tool-use.sh +2 -2
  294. package/src/hooks/stop.sh +9 -6
  295. package/src/hooks/user-prompt-submit.sh +2 -2
  296. package/src/{daemon/services → web/analytics}/anti-pattern-detector.ts +9 -54
  297. package/src/{daemon/services → web/analytics}/drift-detector.ts +10 -23
  298. package/src/{daemon/services → web/analytics}/weekly-report.ts +52 -75
  299. package/src/web/auth-middleware.ts +1 -2
  300. package/src/web/routes/_helpers.ts +34 -0
  301. package/src/web/routes/drift.ts +1 -1
  302. package/src/web/routes/insights.ts +1 -1
  303. package/src/web/routes/reports.ts +1 -1
  304. package/src/web/routes/rules.ts +31 -56
  305. package/src/web/routes/sessions.ts +18 -30
  306. package/src/web/routes/skill-stats.ts +29 -69
  307. package/src/web/routes/skills.ts +5 -4
  308. package/src/web/routes/stats.ts +19 -29
  309. package/src/web/routes/tasks.ts +17 -42
  310. package/src/web/routes/trace.ts +7 -19
  311. package/src/web/routes/types.ts +4 -3
  312. package/tests/integration/claudemd-generator.test.ts +90 -0
  313. package/tests/integration/web-analytics.integration.test.ts +133 -0
  314. package/tests/integration/web-stats.integration.test.ts +135 -0
  315. package/tests/integration/web-trace.integration.test.ts +175 -0
  316. package/tests/unit/core/forge-paths.test.ts +99 -0
  317. package/tests/unit/daemon/post-tool-use.test.ts +121 -0
  318. package/tests/unit/daemon/stop-handler-behavior-summary.test.ts +202 -0
  319. package/tests/unit/daemon/task-segmenter-recover.test.ts +84 -0
  320. package/tests/unit/event-fields.test.ts +88 -0
  321. package/tests/unit/event-parser.test.ts +55 -0
  322. package/tests/unit/hooks/resolve-project-path.test.ts +122 -0
  323. package/tests/unit/socket-server.test.ts +183 -0
  324. package/tests/unit/storage/event-operations-aggregates.test.ts +342 -0
  325. package/tests/unit/storage/migration-idempotent.test.ts +304 -0
  326. package/tests/unit/storage/routing-aggregates.test.ts +276 -0
  327. package/tests/unit/storage/routing.test.ts +117 -0
  328. package/tests/unit/storage/schema-missing.test.ts +81 -0
  329. package/tests/unit/storage/session-operations-aggregates.test.ts +120 -0
  330. package/tests/unit/storage/skill-operations-counts.test.ts +106 -0
  331. package/tests/unit/storage/skills-aggregates.test.ts +104 -0
  332. package/tests/unit/storage/sqlite-refactor-harness.test.ts +3 -3
  333. package/tests/unit/storage/task-operations-counts.test.ts +46 -0
  334. package/tests/unit/storage/tasks-getById.test.ts +343 -0
  335. package/tests/unit/storage/tasks-stale-gc.test.ts +86 -0
  336. package/tests/unit/token-usage.test.ts +6 -6
  337. package/tests/unit/web/navigation-back-contract.test.ts +134 -0
  338. package/tests/unit/web/routes-rules.test.ts +182 -0
  339. package/tests/unit/web/routes-tasks.test.ts +34 -0
  340. package/tests/unit/web/task-title-contract.test.ts +210 -0
  341. package/tests/unit/web/tasks-component-contract.test.ts +179 -0
  342. package/vitest.config.ts +1 -1
  343. package/web/src/pages/TaskDetail.tsx +9 -5
  344. package/web/src/pages/Tasks.tsx +315 -50
  345. package/web/src/utils/navigation.ts +25 -0
  346. package/web/src/utils/task-title.ts +49 -0
  347. package/dist/daemon/services/anti-pattern-detector.d.ts.map +0 -1
  348. package/dist/daemon/services/drift-detector.d.ts.map +0 -1
  349. package/dist/daemon/services/drift-detector.js.map +0 -1
  350. package/dist/daemon/services/weekly-report.d.ts.map +0 -1
  351. package/dist/daemon/services/weekly-report.js.map +0 -1
  352. package/dist/web/static/assets/TaskDetail-5SR8zGzv.js +0 -2
  353. package/dist/web/static/assets/TaskDetail-5SR8zGzv.js.map +0 -1
  354. package/dist/web/static/assets/Tasks-DCgDqvOZ.js +0 -2
  355. package/dist/web/static/assets/Tasks-DCgDqvOZ.js.map +0 -1
  356. package/dist/web/static/assets/index-D8AKj26b.css +0 -1
  357. package/dist/web/static/assets/index-DxIbmNmr.js +0 -3
  358. package/dist/web/static/assets/lucide-fJlPI3H7.js.map +0 -1
  359. package/dist/web/static/assets/react-router-I-HqunH7.js +0 -20
  360. /package/dist/{daemon/services → web/analytics}/drift-detector.d.ts +0 -0
  361. /package/dist/{daemon/services → web/analytics}/weekly-report.d.ts +0 -0
@@ -3,13 +3,40 @@
3
3
  */
4
4
 
5
5
  import { readFileSync, writeFileSync, existsSync, readdirSync, statSync } from 'node:fs';
6
- import { join } from 'node:path';
6
+ import { dirname, join } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
7
8
  import type { ClaudeProvider } from '../core/ai/provider.js';
8
9
  import { TechDetector, type TechStack } from './tech-detector.js';
9
10
 
10
11
  const AUTO_START = '<!-- forge:claudemd-auto -->';
11
12
  const AUTO_END = '<!-- forge:end-claudemd-auto -->';
12
13
 
14
+ /**
15
+ * Swarm Protocol — fixed CLAUDE.md preface injected into every project.
16
+ *
17
+ * Loaded from `templates/swarm-protocol.md` (same directory as this module's
18
+ * compiled output). `fileURLToPath(import.meta.url)` resolves to either
19
+ * `src/claudemd/` (dev via tsx) or `dist/claudemd/` (npm install) — both
20
+ * have a sibling `templates/` directory after build (see package.json `build`
21
+ * script which copies *.md into dist/claudemd/templates/).
22
+ *
23
+ * Cached at module level: the file is read at most once per process.
24
+ */
25
+ let _swarmProtocolCache: string | null = null;
26
+ function getSwarmProtocol(): string {
27
+ if (_swarmProtocolCache !== null) return _swarmProtocolCache;
28
+ const thisDir = dirname(fileURLToPath(import.meta.url));
29
+ const p = join(thisDir, 'templates', 'swarm-protocol.md');
30
+ if (!existsSync(p)) {
31
+ throw new Error(
32
+ `[ClaudeMdGenerator] swarm-protocol.md not found at ${p}. ` +
33
+ `This indicates a broken build/install. Run \`npm run build\` or reinstall the package.`,
34
+ );
35
+ }
36
+ _swarmProtocolCache = readFileSync(p, 'utf-8');
37
+ return _swarmProtocolCache;
38
+ }
39
+
13
40
  export class ClaudeMdGenerator {
14
41
  readonly detector: TechDetector;
15
42
 
@@ -56,7 +83,7 @@ export class ClaudeMdGenerator {
56
83
  }
57
84
 
58
85
  // 3. Swarm 协议 + 项目内容
59
- const full = SWARM_PROTOCOL + '\n\n' + projectContent;
86
+ const full = getSwarmProtocol() + '\n\n' + projectContent;
60
87
  return this.merge(full, userContent);
61
88
  }
62
89
 
@@ -539,239 +566,3 @@ export class ClaudeMdGenerator {
539
566
  return [generated.trim(), '', '---', '', userContent.trim()].join('\n');
540
567
  }
541
568
  }
542
-
543
- /**
544
- * Ruflo-style Swarm Protocol — injected into every project's CLAUDE.md
545
- * so Claude autonomously invokes multi-agent pipelines for complex tasks.
546
- */
547
- /**
548
- * Core behavioral rules — injected into every project's CLAUDE.md.
549
- * Simplified to ~140 lines for clarity and reduced instruction drift.
550
- */
551
- /**
552
- * Core behavioral rules — injected into every project's CLAUDE.md.
553
- * Simplified to ~140 lines for clarity and reduced instruction drift.
554
- */
555
- const SWARM_PROTOCOL = `# claude-forge 工作区规范
556
-
557
- ## 自检(每次响应前必读)
558
-
559
- **在开始任何操作前,先问自己**:
560
-
561
- 1. 我是否要修改 ≥2 个文件?
562
- 2. 我是否要修改 ≥10 行代码?
563
- 3. 这是否是新功能/重构/Bug 修复?
564
- 4. 我修改的代码所在模块**测试覆盖率 < 50%** 吗?
565
-
566
- **如果 1-3 任一答案是"是",且我没有 spawn Agent**:
567
- - ❌ 停下来 → 重新评估例外 → 否则立即 spawn Agent
568
-
569
- **如果 4 答案是"是"**(改老代码 + 测试薄弱):
570
- - ❌ 不要 spawn 普通 \`coder\` / \`planner\`
571
- - ✅ 必须走 **Harness 或 Hybrid 工作流**(详见下方「工作流升级判定」)
572
-
573
- **违规示例**:
574
- - "修复 daemon handler bug(改老代码 + 0 测试)" → 应走 \`harness-hotfix\`,不是 \`coder\`
575
- - "重构 738 行字面量为 .md 文件" → 应走 \`refactor-safe\`,不是 \`coder\`
576
- - "新功能加在 hook 脚本" → 应走 \`hybrid-feature-with-safety\`,不是 \`planner+coder\`
577
-
578
- ---
579
-
580
- ## 核心行为规则
581
-
582
- - Do what has been asked; nothing more, nothing less
583
- - NEVER create files unless absolutely necessary
584
- - ALWAYS prefer editing existing files to creating new ones
585
- - ALWAYS read a file before editing it
586
- - Never continuously check status after spawning agents — wait for results
587
-
588
- ---
589
-
590
- ## Two-Phase Workflow (BMAD 默认路径)
591
-
592
- **适用范围**:≥3 文件改动 / 新功能 / 重构 / 架构调整
593
- **前提**:通过「工作流升级判定」确认为 BMAD 路径(测试覆盖充分 / 全新模块)
594
- **如果改老代码 + 测试薄弱** → 改走 Harness/Hybrid 三阶段(见下方)
595
-
596
- ### Phase 1: Generate Spec
597
-
598
- 1. Spawn planner/researcher agent with \`run_in_background: true\`
599
- 2. Agent 输出 \`docs/design/[task]-spec-YYYYMMDD-HHMM.md\`,包含:
600
- - 目标、改动文件清单、关键设计决策、风险点、测试策略
601
- - **禁止写任何代码**
602
- 3. 主线程告诉用户"已 spawn planner,等待 spec 生成",**然后结束响应**
603
-
604
- ### Phase 1.5: User Review (Critical Gate)
605
-
606
- Agent 完成后,**主线程必须停止并等待用户确认**:
607
-
608
- \`\`\`
609
- 设计方案已生成:docs/design/xxx-spec.md
610
-
611
- 请 review 后回复:
612
- - "批准" / "approve" → 我会 spawn coder 实现
613
- - "修改 [意见]" → 我会调整 spec
614
- - "取消" → 停止任务
615
- \`\`\`
616
-
617
- **绝对禁止**:
618
- - ❌ 主线程自行判断 spec 质量并进入 Phase 2
619
- - ❌ 主线程直接 spawn coder 而不等用户确认
620
-
621
- ### Phase 2: Implement per Spec
622
-
623
- 收到用户"批准"后:
624
- 1. Spawn coder agent with \`run_in_background: true\`
625
- 2. 严格按 spec 执行,完成后写 \`docs/implementation/[task]-changelog-YYYYMMDD-HHMM.md\`
626
-
627
- ### 例外
628
-
629
- 可跳过两阶段:
630
- - 单文件 1-2 行修改
631
- - 用户明确说"不需要 spec"
632
- - 纯查询/调研任务
633
-
634
- ---
635
-
636
- ## Agent 委托规则
637
-
638
- ### 默认行为:spawn Agent
639
-
640
- 接到任务时,**第一反应是 spawn Agent**。主线程只做协调。
641
-
642
- ### 不 spawn 的例外(仅 4 条)
643
-
644
- 1. **单行 typo**:1 文件 1 行拼写错误
645
- 2. **协调与汇总**:整合 Agent 结果
646
- 3. **元任务(极小改动)**:git commit/push、daemon 启停、单行规则调整
647
- 4. **即时问答**:不涉及代码/文件的纯对话
648
-
649
- **注意**:
650
- - 例外 #3 不包括"重构 CLAUDE.md"、"修改多个配置文件"等大改动
651
- - 如果改动 ≥2 文件或 ≥10 行,必须 spawn Agent
652
-
653
- ### 强制 spawn 的场景
654
-
655
- 以下必须 spawn Agent:
656
- - 查/找/看/列出 → Explore agent
657
- - 函数分析/代码阅读 → doc-reviewer
658
- - 单文件 2+ 行修改 → coder
659
- - 实现/修复/重构/优化 → 对应 agent
660
-
661
- ### 强制理由声明
662
-
663
- 每次不 spawn 时,必须在响应开头说明:
664
-
665
- > 不 spawn 理由:例外 #N [具体原因]
666
-
667
- ---
668
-
669
- ## 工作流升级判定(spawn agent 前必查)
670
-
671
- **默认是 BMAD(planner+coder)**,但满足下表条件**必须升级到 Harness/Hybrid**。
672
-
673
- ### 判定表
674
-
675
- | 任务特征 | 推荐 agent | 工作流序列 |
676
- |---------|-----------|----------|
677
- | **改老代码 + 模块测试覆盖 < 50%** | \`refactor-safe\` | safety-net → design → implement → test → review |
678
- | **已知原因的 Bug 修复**(紧急/明确)| \`harness-hotfix\` | safety-net → fix → verify |
679
- | **未知/偶发 Bug**(需先定位)| \`harness-debug-full\` | reproduce → safety-net → root-cause → fix → verify |
680
- | **新功能改老代码 + 测试薄弱** | \`hybrid-feature-with-safety\` | safety-net → analyze → design → implement → test → review |
681
- | **先修 bug 再加功能** | \`hybrid-fix-then-feature\` | reproduce → fix → verify → design → implement → review |
682
- | **全新模块 / 测试充分**(覆盖 ≥50%)| \`planner\` + \`coder\` | BMAD analyze → design → implement |
683
- | **纯调研 / 单行 typo / 元任务** | 不 spawn 或 \`Explore\` | — |
684
-
685
- ### 测试覆盖快速判定
686
-
687
- 修改 \`src/<module>/\` 前必查:
688
-
689
- \`\`\`bash
690
- SRC=$(find src/<module> -name "*.ts" -not -name "*.test.ts" 2>/dev/null | wc -l)
691
- TST=$(find tests/unit/<module> src/tests -name "*.test.ts" 2>/dev/null | wc -l)
692
- echo "ratio: $TST / $SRC"
693
- \`\`\`
694
-
695
- \`TST/SRC < 50%\` → **触发 Harness safety-net 阶段**(不要直接改代码)
696
-
697
- ### 项目模块覆盖现状(提醒)
698
-
699
- | 模块 | 覆盖判定 | 默认走 |
700
- |------|---------|--------|
701
- | \`src/daemon/handlers/\` | 部分(≥50%)| BMAD,但 hotfix 类 bug 走 \`harness-hotfix\` |
702
- | \`src/daemon/services/\` | **零测试** | **Harness/Hybrid 强制** |
703
- | \`src/core/storage/\` | 高(≥80%)| BMAD |
704
- | \`src/core/queue/\` | 高(新写时已加)| BMAD |
705
- | \`src/skills/\` | 中(~40%)| 大改走 \`refactor-safe\` |
706
- | \`src/web/routes/\` | 中 | BMAD |
707
- | \`src/hooks/*.sh\` | 0(无 sh 测试)| 改 hook 必走 Hybrid |
708
- | \`src/claudemd/\` | 低 | \`refactor-safe\` |
709
-
710
- ### 违规自查
711
-
712
- 过去 5 次任务全踩坑,请引以为戒:
713
-
714
- - ❌ "修复 daemon schema 收紧拒收 bug"(改 events.ts + 5 个 hook)→ 走了 \`coder\`,应走 \`harness-hotfix\`
715
- - ❌ "Official Skills 数据分离"(重构 738 行老代码)→ 走了 \`planner+coder\`,应走 \`refactor-safe\`
716
- - ❌ "Hook 失败缓冲队列"(新功能 + 改 4 个老 hook)→ 走了 \`planner+coder\`,应走 \`hybrid-feature-with-safety\`
717
-
718
- **判定时机**:在「自检」之后、「Two-Phase Workflow」之前。判定结果若是 Harness/Hybrid,跳过 Two-Phase,直接 spawn 对应 agent。
719
-
720
- ---
721
-
722
- ## 并发规则
723
-
724
- - All operations MUST be concurrent/parallel in ONE message
725
- - ALWAYS batch ALL agents in ONE message with \`run_in_background: true\`
726
- - ALWAYS batch ALL file reads/writes/edits in ONE message
727
- - ALWAYS batch ALL terminal operations in ONE Bash message
728
-
729
- ---
730
-
731
- ## 项目特定信息
732
-
733
- ### 技术栈
734
-
735
- - **语言**: TypeScript (Node.js 24+)
736
- - **数据库**: SQLite (better-sqlite3)
737
- - **前端**: 原生 HTML + Vanilla JS(单文件 SPA),无打包
738
- - **测试**: Vitest
739
-
740
- ### 架构要点
741
-
742
- **分层**:
743
- - \`src/cli/\` — CLI 命令
744
- - \`src/daemon/\` — 后台守护进程(事件处理)
745
- - \`src/web/\` — Web 仪表盘
746
- - \`src/core/\` — 共享核心(storage/config/types)
747
- - \`src/claudemd/\` — CLAUDE.md 生成器
748
- - \`src/skills/\` — Skill 匹配与注入
749
-
750
- **存储**:
751
- - 主库:\`~/.claude-forge/data.db\`
752
- - 表:sessions, events, tasks, routing_events, skill_invocations, token_usage
753
-
754
- ### 开发规范
755
-
756
- **构建与测试**:
757
- \`\`\`bash
758
- npm run build # 构建前后端
759
- npm test # 运行测试
760
- ./scripts/dev-daemon.sh restart # 重启 daemon
761
- \`\`\`
762
-
763
- **守护进程事件推送**:
764
- - PreToolUse / PostToolUse / UserPromptSubmit / Stop / Notification
765
- - 通过 Unix socket 推送到 daemon
766
-
767
- **注意**:
768
- - daemon 和 CLI 都用系统 node(v26)
769
- - better-sqlite3 12.10.0 支持 v26
770
- - 修改 schema 需要手动迁移(无自动 migration)
771
-
772
- ---
773
-
774
- ## 上次工作续接
775
-
776
- (由 daemon 自动更新,记录最近任务上下文)
777
- `;
@@ -115,7 +115,7 @@ export class ResumeManager {
115
115
  }
116
116
 
117
117
  if (e.tool_name && ['Edit', 'Write'].includes(e.tool_name)) {
118
- const fp = e.tool_input?.file_path as string | undefined;
118
+ const fp = typeof e.tool_input?.file_path === 'string' ? e.tool_input.file_path : undefined;
119
119
  if (fp) editedFiles.add(fp);
120
120
  }
121
121
 
@@ -0,0 +1,222 @@
1
+ # claude-forge 工作区规范
2
+
3
+ ## 自检(每次响应前必读)
4
+
5
+ **在开始任何操作前,先问自己**:
6
+
7
+ 1. 我是否要修改 ≥2 个文件?
8
+ 2. 我是否要修改 ≥10 行代码?
9
+ 3. 这是否是新功能/重构/Bug 修复?
10
+ 4. 我修改的代码所在模块**测试覆盖率 < 50%** 吗?
11
+
12
+ **如果 1-3 任一答案是"是",且我没有 spawn Agent**:
13
+ - ❌ 停下来 → 重新评估例外 → 否则立即 spawn Agent
14
+
15
+ **如果 4 答案是"是"**(改老代码 + 测试薄弱):
16
+ - ❌ 不要 spawn 普通 `coder` / `planner`
17
+ - ✅ 必须走 **Harness 或 Hybrid 工作流**(详见下方「工作流升级判定」)
18
+
19
+ **违规示例**:
20
+ - "修复 daemon handler bug(改老代码 + 0 测试)" → 应走 `harness-hotfix`,不是 `coder`
21
+ - "重构 738 行字面量为 .md 文件" → 应走 `refactor-safe`,不是 `coder`
22
+ - "新功能加在 hook 脚本" → 应走 `hybrid-feature-with-safety`,不是 `planner+coder`
23
+
24
+ ---
25
+
26
+ ## 核心行为规则
27
+
28
+ - Do what has been asked; nothing more, nothing less
29
+ - NEVER create files unless absolutely necessary
30
+ - ALWAYS prefer editing existing files to creating new ones
31
+ - ALWAYS read a file before editing it
32
+ - Never continuously check status after spawning agents — wait for results
33
+
34
+ ---
35
+
36
+ ## Two-Phase Workflow (BMAD 默认路径)
37
+
38
+ **适用范围**:≥3 文件改动 / 新功能 / 重构 / 架构调整
39
+ **前提**:通过「工作流升级判定」确认为 BMAD 路径(测试覆盖充分 / 全新模块)
40
+ **如果改老代码 + 测试薄弱** → 改走 Harness/Hybrid 三阶段(见下方)
41
+
42
+ ### Phase 1: Generate Spec
43
+
44
+ 1. Spawn planner/researcher agent with `run_in_background: true`
45
+ 2. Agent 输出 `docs/design/[task]-spec-YYYYMMDD-HHMM.md`,包含:
46
+ - 目标、改动文件清单、关键设计决策、风险点、测试策略
47
+ - **禁止写任何代码**
48
+ 3. 主线程告诉用户"已 spawn planner,等待 spec 生成",**然后结束响应**
49
+
50
+ ### Phase 1.5: User Review (Critical Gate)
51
+
52
+ Agent 完成后,**主线程必须停止并等待用户确认**:
53
+
54
+ ```
55
+ 设计方案已生成:docs/design/xxx-spec.md
56
+
57
+ 请 review 后回复:
58
+ - "批准" / "approve" → 我会 spawn coder 实现
59
+ - "修改 [意见]" → 我会调整 spec
60
+ - "取消" → 停止任务
61
+ ```
62
+
63
+ **绝对禁止**:
64
+ - ❌ 主线程自行判断 spec 质量并进入 Phase 2
65
+ - ❌ 主线程直接 spawn coder 而不等用户确认
66
+
67
+ ### Phase 2: Implement per Spec
68
+
69
+ 收到用户"批准"后:
70
+ 1. Spawn coder agent with `run_in_background: true`
71
+ 2. 严格按 spec 执行,完成后写 `docs/implementation/[task]-changelog-YYYYMMDD-HHMM.md`
72
+
73
+ ### 例外
74
+
75
+ 可跳过两阶段:
76
+ - 单文件 1-2 行修改
77
+ - 用户明确说"不需要 spec"
78
+ - 纯查询/调研任务
79
+
80
+ ---
81
+
82
+ ## Agent 委托规则
83
+
84
+ ### 默认行为:spawn Agent
85
+
86
+ 接到任务时,**第一反应是 spawn Agent**。主线程只做协调。
87
+
88
+ ### 不 spawn 的例外(仅 4 条)
89
+
90
+ 1. **单行 typo**:1 文件 1 行拼写错误
91
+ 2. **协调与汇总**:整合 Agent 结果
92
+ 3. **元任务(极小改动)**:git commit/push、daemon 启停、单行规则调整
93
+ 4. **即时问答**:不涉及代码/文件的纯对话
94
+
95
+ **注意**:
96
+ - 例外 #3 不包括"重构 CLAUDE.md"、"修改多个配置文件"等大改动
97
+ - 如果改动 ≥2 文件或 ≥10 行,必须 spawn Agent
98
+
99
+ ### 强制 spawn 的场景
100
+
101
+ 以下必须 spawn Agent:
102
+ - 查/找/看/列出 → Explore agent
103
+ - 函数分析/代码阅读 → doc-reviewer
104
+ - 单文件 2+ 行修改 → coder
105
+ - 实现/修复/重构/优化 → 对应 agent
106
+
107
+ ### 强制理由声明
108
+
109
+ 每次不 spawn 时,必须在响应开头说明:
110
+
111
+ > 不 spawn 理由:例外 #N [具体原因]
112
+
113
+ ---
114
+
115
+ ## 工作流升级判定(spawn agent 前必查)
116
+
117
+ **默认是 BMAD(planner+coder)**,但满足下表条件**必须升级到 Harness/Hybrid**。
118
+
119
+ ### 判定表
120
+
121
+ | 任务特征 | 推荐 agent | 工作流序列 |
122
+ |---------|-----------|----------|
123
+ | **改老代码 + 模块测试覆盖 < 50%** | `refactor-safe` | safety-net → design → implement → test → review |
124
+ | **已知原因的 Bug 修复**(紧急/明确)| `harness-hotfix` | safety-net → fix → verify |
125
+ | **未知/偶发 Bug**(需先定位)| `harness-debug-full` | reproduce → safety-net → root-cause → fix → verify |
126
+ | **新功能改老代码 + 测试薄弱** | `hybrid-feature-with-safety` | safety-net → analyze → design → implement → test → review |
127
+ | **先修 bug 再加功能** | `hybrid-fix-then-feature` | reproduce → fix → verify → design → implement → review |
128
+ | **全新模块 / 测试充分**(覆盖 ≥50%)| `planner` + `coder` | BMAD analyze → design → implement |
129
+ | **纯调研 / 单行 typo / 元任务** | 不 spawn 或 `Explore` | — |
130
+
131
+ ### 测试覆盖快速判定
132
+
133
+ 修改 `src/<module>/` 前必查:
134
+
135
+ ```bash
136
+ SRC=$(find src/<module> -name "*.ts" -not -name "*.test.ts" 2>/dev/null | wc -l)
137
+ TST=$(find tests/unit/<module> src/tests -name "*.test.ts" 2>/dev/null | wc -l)
138
+ echo "ratio: $TST / $SRC"
139
+ ```
140
+
141
+ `TST/SRC < 50%` → **触发 Harness safety-net 阶段**(不要直接改代码)
142
+
143
+ ### 项目模块覆盖现状(提醒)
144
+
145
+ | 模块 | 覆盖判定 | 默认走 |
146
+ |------|---------|--------|
147
+ | `src/daemon/handlers/` | 部分(≥50%)| BMAD,但 hotfix 类 bug 走 `harness-hotfix` |
148
+ | `src/daemon/services/` | **零测试** | **Harness/Hybrid 强制** |
149
+ | `src/core/storage/` | 高(≥80%)| BMAD |
150
+ | `src/core/queue/` | 高(新写时已加)| BMAD |
151
+ | `src/skills/` | 中(~40%)| 大改走 `refactor-safe` |
152
+ | `src/web/routes/` | 中 | BMAD |
153
+ | `src/hooks/*.sh` | 0(无 sh 测试)| 改 hook 必走 Hybrid |
154
+ | `src/claudemd/` | 低 | `refactor-safe` |
155
+
156
+ ### 违规自查
157
+
158
+ 过去 5 次任务全踩坑,请引以为戒:
159
+
160
+ - ❌ "修复 daemon schema 收紧拒收 bug"(改 events.ts + 5 个 hook)→ 走了 `coder`,应走 `harness-hotfix`
161
+ - ❌ "Official Skills 数据分离"(重构 738 行老代码)→ 走了 `planner+coder`,应走 `refactor-safe`
162
+ - ❌ "Hook 失败缓冲队列"(新功能 + 改 4 个老 hook)→ 走了 `planner+coder`,应走 `hybrid-feature-with-safety`
163
+
164
+ **判定时机**:在「自检」之后、「Two-Phase Workflow」之前。判定结果若是 Harness/Hybrid,跳过 Two-Phase,直接 spawn 对应 agent。
165
+
166
+ ---
167
+
168
+ ## 并发规则
169
+
170
+ - All operations MUST be concurrent/parallel in ONE message
171
+ - ALWAYS batch ALL agents in ONE message with `run_in_background: true`
172
+ - ALWAYS batch ALL file reads/writes/edits in ONE message
173
+ - ALWAYS batch ALL terminal operations in ONE Bash message
174
+
175
+ ---
176
+
177
+ ## 项目特定信息
178
+
179
+ ### 技术栈
180
+
181
+ - **语言**: TypeScript (Node.js 24+)
182
+ - **数据库**: SQLite (better-sqlite3)
183
+ - **前端**: React 18 + Vite + Tailwind + react-router-dom + react-query + Recharts(`web/` 目录,build 后被 cp 到 dist/web/static/)
184
+ - **测试**: Vitest
185
+
186
+ ### 架构要点
187
+
188
+ **分层**:
189
+ - `src/cli/` — CLI 命令
190
+ - `src/daemon/` — 后台守护进程(事件处理)
191
+ - `src/web/` — Web 仪表盘
192
+ - `src/core/` — 共享核心(storage/config/types)
193
+ - `src/claudemd/` — CLAUDE.md 生成器
194
+ - `src/skills/` — Skill 匹配与注入
195
+
196
+ **存储**:
197
+ - 主库:`~/.claude-forge/data.db`
198
+ - 表:sessions, events, tasks, routing_events, skill_invocations, token_usage
199
+
200
+ ### 开发规范
201
+
202
+ **构建与测试**:
203
+ ```bash
204
+ npm run build # 构建前后端
205
+ npm test # 运行测试
206
+ ./scripts/dev-daemon.sh restart # 重启 daemon
207
+ ```
208
+
209
+ **守护进程事件推送**:
210
+ - PreToolUse / PostToolUse / UserPromptSubmit / Stop / Notification
211
+ - 通过 Unix socket 推送到 daemon
212
+
213
+ **注意**:
214
+ - daemon 和 CLI 都用系统 node(v26)
215
+ - better-sqlite3 12.10.0 支持 v26
216
+ - 修改 schema 需要手动迁移(无自动 migration)
217
+
218
+ ---
219
+
220
+ ## 上次工作续接
221
+
222
+ (由 daemon 自动更新,记录最近任务上下文)
@@ -31,7 +31,7 @@ import fs from 'fs';
31
31
  import path from 'path';
32
32
  import { spawn, execSync } from 'child_process';
33
33
  import type { Command } from 'commander';
34
- import { FORGE_HOME } from '../../core/constants.js';
34
+ import { FORGE_PATHS, DEFAULTS } from '../../core/constants.js';
35
35
  import { fileURLToPath } from 'url';
36
36
  import {
37
37
  install as installLaunchAgent,
@@ -42,10 +42,10 @@ import {
42
42
  } from '../../daemon/launchd-installer.js';
43
43
 
44
44
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
45
- const PID_FILE = path.join(FORGE_HOME, 'daemon.pid');
46
- const DEFAULT_PORT = 3721;
47
- const STDOUT_LOG = path.join(FORGE_HOME, 'daemon-stdout.log');
48
- const STDERR_LOG = path.join(FORGE_HOME, 'daemon-stderr.log');
45
+ const PID_FILE = FORGE_PATHS.daemonPid();
46
+ const DEFAULT_PORT = DEFAULTS.WEB_PORT;
47
+ const STDOUT_LOG = FORGE_PATHS.daemonStdout();
48
+ const STDERR_LOG = FORGE_PATHS.daemonStderr();
49
49
 
50
50
  function readPid(): number | null {
51
51
  if (!fs.existsSync(PID_FILE)) return null;
@@ -112,7 +112,7 @@ export function register(program: Command): void {
112
112
 
113
113
  // Background mode: spawn detached child process
114
114
  const daemonScript = path.resolve(__dirname, '../../daemon/index.js');
115
- const logFile = path.join(FORGE_HOME, 'daemon.log');
115
+ const logFile = FORGE_PATHS.daemonLog();
116
116
  const out = fs.openSync(logFile, 'a');
117
117
 
118
118
  const env: Record<string, string> = {
@@ -2,6 +2,7 @@ import type { Command } from 'commander';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { execSync } from 'node:child_process';
5
+ import { resolveGitRoot } from '../../core/utils/git.js';
5
6
 
6
7
  export function registerExecutionsCommand(program: Command): void {
7
8
  const cmd = program.command('executions').description('管理 Agent/Skill 执行文档');
@@ -13,7 +14,7 @@ export function registerExecutionsCommand(program: Command): void {
13
14
  .option('--limit <n>', '限制数量', '20')
14
15
  .option('--date <date>', '按日期筛选(YYYY-MM-DD)')
15
16
  .action(async (opts) => {
16
- const baseDir = path.join(process.cwd(), '.claude-forge', 'executions', 'by-route');
17
+ const baseDir = path.join(resolveGitRoot(), '.claude-forge', 'executions', 'by-route');
17
18
  if (!fs.existsSync(baseDir)) {
18
19
  console.log('暂无执行文档。请先触发 Agent/Skill 执行。');
19
20
  return;
@@ -50,7 +51,7 @@ export function registerExecutionsCommand(program: Command): void {
50
51
  .command('open <route_id>')
51
52
  .description('打开指定执行的 overview 文档')
52
53
  .action((routeId: string) => {
53
- const baseDir = path.join(process.cwd(), '.claude-forge', 'executions', 'by-route');
54
+ const baseDir = path.join(resolveGitRoot(), '.claude-forge', 'executions', 'by-route');
54
55
  if (!fs.existsSync(baseDir)) {
55
56
  console.error('执行文档目录不存在。');
56
57
  process.exit(1);
@@ -82,7 +83,7 @@ export function registerExecutionsCommand(program: Command): void {
82
83
  .option('--before <days>', '清理 N 天前的文档', '30')
83
84
  .option('--dry-run', '只显示将删除的文件,不实际删除')
84
85
  .action(async (opts) => {
85
- const baseDir = path.join(process.cwd(), '.claude-forge', 'executions', 'by-route');
86
+ const baseDir = path.join(resolveGitRoot(), '.claude-forge', 'executions', 'by-route');
86
87
  if (!fs.existsSync(baseDir)) {
87
88
  console.log('执行文档目录不存在,无需清理。');
88
89
  return;
@@ -3,7 +3,7 @@ import path from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import readline from 'readline';
5
5
  import type { Command } from 'commander';
6
- import { FORGE_HOME, FORGE_PATHS } from '../../core/constants.js';
6
+ import { FORGE_HOME, FORGE_PATHS, DEFAULTS } from '../../core/constants.js';
7
7
  import { copyHookScripts, backupSettings, injectHooks } from '../init/hook-manager.js';
8
8
  import chalk from 'chalk';
9
9
 
@@ -104,7 +104,7 @@ export function register(program: Command): void {
104
104
  // 2. Interactive configuration
105
105
  let apiKey = process.env.ANTHROPIC_API_KEY || '';
106
106
  let baseUrl = 'https://api.anthropic.com';
107
- let webPort = 3721;
107
+ let webPort: number = DEFAULTS.WEB_PORT;
108
108
 
109
109
  if (!skipPrompts) {
110
110
  console.log(chalk.yellow('\n📝 配置选项(直接回车使用默认值)\n'));
@@ -40,7 +40,7 @@ function formatEventGrouped(e: ForgeEvent): string {
40
40
  function extractDetail(e: ForgeEvent): string {
41
41
  if (e.user_prompt) return e.user_prompt.slice(0, 80);
42
42
  if (!e.tool_input) return '';
43
- const input = e.tool_input as Record<string, unknown>;
43
+ const input = e.tool_input;
44
44
  if (input.command) return String(input.command).slice(0, 80);
45
45
  if (input.file_path) return String(input.file_path);
46
46
  return JSON.stringify(input).slice(0, 80);
@@ -17,11 +17,9 @@
17
17
  import { Command } from 'commander';
18
18
  import { execSync } from 'node:child_process';
19
19
  import { existsSync, readFileSync } from 'node:fs';
20
- import { homedir } from 'node:os';
21
- import { join } from 'node:path';
22
20
  import chalk from 'chalk';
23
21
  import { ConfigManager } from '../../core/config.js';
24
- import { DEFAULTS } from '../../core/constants.js';
22
+ import { DEFAULTS, FORGE_PATHS } from '../../core/constants.js';
25
23
 
26
24
  const SERVER_NAME = 'claude-forge';
27
25
  const SCOPE = 'user'; // user scope: 全局可用
@@ -42,7 +40,7 @@ export function isClaudeCodeInstalled(): boolean {
42
40
  * 读取 daemon token(如果存在)
43
41
  */
44
42
  function readDaemonToken(): string | null {
45
- const tokenPath = join(homedir(), '.claude-forge', 'daemon.token');
43
+ const tokenPath = FORGE_PATHS.daemonToken();
46
44
  if (!existsSync(tokenPath)) return null;
47
45
  try {
48
46
  return readFileSync(tokenPath, 'utf-8').trim() || null;
@@ -83,7 +81,7 @@ export function getMcpServerUrl(): string {
83
81
  * 是否处于 daemon 运行中(通过 pid 文件存在性判断,松散探测)
84
82
  */
85
83
  function isDaemonRunning(): boolean {
86
- return existsSync(join(homedir(), '.claude-forge', 'daemon.pid'));
84
+ return existsSync(FORGE_PATHS.daemonPid());
87
85
  }
88
86
 
89
87
  /**
@@ -2,6 +2,7 @@ import readline from 'readline';
2
2
  import { spawn } from 'child_process';
3
3
  import { createRequire } from 'module';
4
4
  import type { Command } from 'commander';
5
+ import { DEFAULTS } from '../../core/constants.js';
5
6
 
6
7
  const require = createRequire(import.meta.url);
7
8
  const { version } = require('../../../package.json') as { version: string };
@@ -124,9 +125,9 @@ function runCommand(cmd: string, args: string[]): Promise<void> {
124
125
 
125
126
  function openDashboard(): void {
126
127
  console.log('\x1b[32m正在打开 Web Dashboard...\x1b[0m');
127
- console.log('访问: \x1b[36mhttp://localhost:3721\x1b[0m\n');
128
+ console.log(`访问: \x1b[36mhttp://localhost:${DEFAULTS.WEB_PORT}\x1b[0m\n`);
128
129
  const open = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
129
- spawn(open, ['http://localhost:3721'], { detached: true, stdio: 'ignore' }).unref();
130
+ spawn(open, [`http://localhost:${DEFAULTS.WEB_PORT}`], { detached: true, stdio: 'ignore' }).unref();
130
131
  }
131
132
 
132
133
  function prompt(question: string): Promise<string> {
@@ -241,7 +242,7 @@ async function selectModel(): Promise<void> {
241
242
  try {
242
243
  console.log('\n\x1b[36m正在获取模型列表...\x1b[0m');
243
244
 
244
- const response = await fetch('http://localhost:3721/api/ai/models');
245
+ const response = await fetch(`http://localhost:${DEFAULTS.WEB_PORT}/api/ai/models`);
245
246
  if (!response.ok) {
246
247
  const text = await response.text();
247
248
  console.log(`\x1b[31m获取模型列表失败: ${text}\x1b[0m`);