gsd-pi 2.18.0 → 2.20.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 (289) hide show
  1. package/README.md +5 -1
  2. package/dist/cli.js +3 -3
  3. package/dist/onboarding.d.ts +3 -1
  4. package/dist/onboarding.js +77 -3
  5. package/dist/remote-questions-config.d.ts +1 -1
  6. package/dist/resources/extensions/google-search/index.ts +164 -47
  7. package/dist/resources/extensions/gsd/auto-dashboard.ts +14 -2
  8. package/dist/resources/extensions/gsd/auto-prompts.ts +148 -39
  9. package/dist/resources/extensions/gsd/auto-worktree.ts +93 -9
  10. package/dist/resources/extensions/gsd/auto.ts +690 -39
  11. package/dist/resources/extensions/gsd/captures.ts +384 -0
  12. package/dist/resources/extensions/gsd/commands.ts +654 -36
  13. package/dist/resources/extensions/gsd/complexity-classifier.ts +322 -0
  14. package/dist/resources/extensions/gsd/context-budget.ts +243 -0
  15. package/dist/resources/extensions/gsd/context-store.ts +195 -0
  16. package/dist/resources/extensions/gsd/dashboard-overlay.ts +51 -3
  17. package/dist/resources/extensions/gsd/db-writer.ts +341 -0
  18. package/dist/resources/extensions/gsd/debug-logger.ts +178 -0
  19. package/dist/resources/extensions/gsd/dispatch-guard.ts +0 -1
  20. package/dist/resources/extensions/gsd/docs/preferences-reference.md +54 -0
  21. package/dist/resources/extensions/gsd/doctor-proactive.ts +286 -0
  22. package/dist/resources/extensions/gsd/doctor.ts +283 -2
  23. package/dist/resources/extensions/gsd/export.ts +81 -2
  24. package/dist/resources/extensions/gsd/files.ts +39 -9
  25. package/dist/resources/extensions/gsd/git-service.ts +6 -0
  26. package/dist/resources/extensions/gsd/gsd-db.ts +752 -0
  27. package/dist/resources/extensions/gsd/guided-flow.ts +26 -1
  28. package/dist/resources/extensions/gsd/history.ts +0 -1
  29. package/dist/resources/extensions/gsd/index.ts +277 -1
  30. package/dist/resources/extensions/gsd/md-importer.ts +526 -0
  31. package/dist/resources/extensions/gsd/metrics.ts +84 -0
  32. package/dist/resources/extensions/gsd/model-cost-table.ts +65 -0
  33. package/dist/resources/extensions/gsd/model-router.ts +256 -0
  34. package/dist/resources/extensions/gsd/notifications.ts +0 -1
  35. package/dist/resources/extensions/gsd/post-unit-hooks.ts +72 -2
  36. package/dist/resources/extensions/gsd/preferences.ts +198 -150
  37. package/dist/resources/extensions/gsd/prompt-loader.ts +45 -9
  38. package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -5
  39. package/dist/resources/extensions/gsd/prompts/heal-skill.md +45 -0
  40. package/dist/resources/extensions/gsd/prompts/plan-slice.md +5 -1
  41. package/dist/resources/extensions/gsd/prompts/quick-task.md +48 -0
  42. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -0
  43. package/dist/resources/extensions/gsd/prompts/replan-slice.md +8 -0
  44. package/dist/resources/extensions/gsd/prompts/system.md +2 -1
  45. package/dist/resources/extensions/gsd/prompts/triage-captures.md +62 -0
  46. package/dist/resources/extensions/gsd/quick.ts +156 -0
  47. package/dist/resources/extensions/gsd/skill-discovery.ts +5 -3
  48. package/dist/resources/extensions/gsd/skill-health.ts +417 -0
  49. package/dist/resources/extensions/gsd/skill-telemetry.ts +127 -0
  50. package/dist/resources/extensions/gsd/state.ts +30 -0
  51. package/dist/resources/extensions/gsd/templates/preferences.md +1 -0
  52. package/dist/resources/extensions/gsd/tests/captures.test.ts +438 -0
  53. package/dist/resources/extensions/gsd/tests/complexity-classifier.test.ts +181 -0
  54. package/dist/resources/extensions/gsd/tests/context-budget.test.ts +283 -0
  55. package/dist/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
  56. package/dist/resources/extensions/gsd/tests/context-store.test.ts +462 -0
  57. package/dist/resources/extensions/gsd/tests/continue-here.test.ts +204 -0
  58. package/dist/resources/extensions/gsd/tests/dashboard-budget.test.ts +346 -0
  59. package/dist/resources/extensions/gsd/tests/db-writer.test.ts +602 -0
  60. package/dist/resources/extensions/gsd/tests/debug-logger.test.ts +185 -0
  61. package/dist/resources/extensions/gsd/tests/derive-state-db.test.ts +406 -0
  62. package/dist/resources/extensions/gsd/tests/dispatch-guard.test.ts +0 -1
  63. package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +22 -0
  64. package/dist/resources/extensions/gsd/tests/doctor-proactive.test.ts +244 -0
  65. package/dist/resources/extensions/gsd/tests/doctor-runtime.test.ts +303 -0
  66. package/dist/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -0
  67. package/dist/resources/extensions/gsd/tests/gsd-db.test.ts +353 -0
  68. package/dist/resources/extensions/gsd/tests/gsd-inspect.test.ts +125 -0
  69. package/dist/resources/extensions/gsd/tests/gsd-tools.test.ts +326 -0
  70. package/dist/resources/extensions/gsd/tests/integration-edge.test.ts +228 -0
  71. package/dist/resources/extensions/gsd/tests/integration-lifecycle.test.ts +277 -0
  72. package/dist/resources/extensions/gsd/tests/md-importer.test.ts +411 -0
  73. package/dist/resources/extensions/gsd/tests/metrics.test.ts +197 -0
  74. package/dist/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +144 -0
  75. package/dist/resources/extensions/gsd/tests/model-cost-table.test.ts +69 -0
  76. package/dist/resources/extensions/gsd/tests/model-isolation.test.ts +99 -0
  77. package/dist/resources/extensions/gsd/tests/model-router.test.ts +167 -0
  78. package/dist/resources/extensions/gsd/tests/parsers.test.ts +40 -0
  79. package/dist/resources/extensions/gsd/tests/post-unit-hooks.test.ts +41 -1
  80. package/dist/resources/extensions/gsd/tests/preferences-git.test.ts +0 -1
  81. package/dist/resources/extensions/gsd/tests/preferences-hooks.test.ts +0 -1
  82. package/dist/resources/extensions/gsd/tests/preferences-mode.test.ts +110 -0
  83. package/dist/resources/extensions/gsd/tests/preferences-models.test.ts +0 -1
  84. package/dist/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +464 -0
  85. package/dist/resources/extensions/gsd/tests/prompt-db.test.ts +385 -0
  86. package/dist/resources/extensions/gsd/tests/remote-questions.test.ts +488 -1
  87. package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +17 -29
  88. package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +2 -8
  89. package/dist/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
  90. package/dist/resources/extensions/gsd/tests/skill-lifecycle.test.ts +126 -0
  91. package/dist/resources/extensions/gsd/tests/stop-auto-remote.test.ts +31 -8
  92. package/dist/resources/extensions/gsd/tests/token-savings.test.ts +366 -0
  93. package/dist/resources/extensions/gsd/tests/triage-dispatch.test.ts +224 -0
  94. package/dist/resources/extensions/gsd/tests/triage-resolution.test.ts +215 -0
  95. package/dist/resources/extensions/gsd/tests/unit-runtime.test.ts +25 -1
  96. package/dist/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +145 -0
  97. package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +290 -0
  98. package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +120 -0
  99. package/dist/resources/extensions/gsd/tests/visualizer-views.test.ts +478 -0
  100. package/dist/resources/extensions/gsd/tests/worktree-db-integration.test.ts +205 -0
  101. package/dist/resources/extensions/gsd/tests/worktree-db.test.ts +442 -0
  102. package/dist/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +165 -0
  103. package/dist/resources/extensions/gsd/triage-resolution.ts +200 -0
  104. package/dist/resources/extensions/gsd/triage-ui.ts +175 -0
  105. package/dist/resources/extensions/gsd/types.ts +29 -0
  106. package/dist/resources/extensions/gsd/undo.ts +0 -1
  107. package/dist/resources/extensions/gsd/unit-runtime.ts +5 -1
  108. package/dist/resources/extensions/gsd/visualizer-data.ts +505 -0
  109. package/dist/resources/extensions/gsd/visualizer-overlay.ts +337 -0
  110. package/dist/resources/extensions/gsd/visualizer-views.ts +755 -0
  111. package/dist/resources/extensions/gsd/worktree-command.ts +18 -0
  112. package/dist/resources/extensions/gsd/worktree-manager.ts +11 -4
  113. package/dist/resources/extensions/remote-questions/config.ts +4 -2
  114. package/dist/resources/extensions/remote-questions/discord-adapter.ts +35 -4
  115. package/dist/resources/extensions/remote-questions/format.ts +166 -14
  116. package/dist/resources/extensions/remote-questions/manager.ts +14 -4
  117. package/dist/resources/extensions/remote-questions/remote-command.ts +100 -4
  118. package/dist/resources/extensions/remote-questions/slack-adapter.ts +58 -2
  119. package/dist/resources/extensions/remote-questions/telegram-adapter.ts +161 -0
  120. package/dist/resources/extensions/remote-questions/types.ts +2 -1
  121. package/dist/resources/extensions/ttsr/ttsr-manager.ts +26 -0
  122. package/dist/resources/extensions/voice/index.ts +4 -3
  123. package/package.json +1 -1
  124. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  125. package/packages/pi-coding-agent/dist/core/agent-session.js +12 -1
  126. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  127. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  128. package/packages/pi-coding-agent/dist/core/extensions/loader.js +5 -0
  129. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  130. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts +6 -0
  131. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
  132. package/packages/pi-coding-agent/dist/core/lsp/client.js +25 -0
  133. package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
  134. package/packages/pi-coding-agent/dist/core/lsp/index.d.ts +2 -0
  135. package/packages/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -1
  136. package/packages/pi-coding-agent/dist/core/lsp/index.js +106 -3
  137. package/packages/pi-coding-agent/dist/core/lsp/index.js.map +1 -1
  138. package/packages/pi-coding-agent/dist/core/lsp/lsp.md +6 -0
  139. package/packages/pi-coding-agent/dist/core/lsp/types.d.ts +35 -0
  140. package/packages/pi-coding-agent/dist/core/lsp/types.d.ts.map +1 -1
  141. package/packages/pi-coding-agent/dist/core/lsp/types.js +6 -0
  142. package/packages/pi-coding-agent/dist/core/lsp/types.js.map +1 -1
  143. package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts +3 -1
  144. package/packages/pi-coding-agent/dist/core/lsp/utils.d.ts.map +1 -1
  145. package/packages/pi-coding-agent/dist/core/lsp/utils.js +45 -0
  146. package/packages/pi-coding-agent/dist/core/lsp/utils.js.map +1 -1
  147. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +6 -0
  148. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  149. package/packages/pi-coding-agent/dist/core/settings-manager.js +43 -11
  150. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  151. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  152. package/packages/pi-coding-agent/dist/core/system-prompt.js +7 -1
  153. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  154. package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
  155. package/packages/pi-coding-agent/dist/core/tools/edit.js +5 -0
  156. package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
  157. package/packages/pi-coding-agent/dist/core/tools/index.d.ts +2 -0
  158. package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
  159. package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
  160. package/packages/pi-coding-agent/dist/core/tools/write.js +5 -0
  161. package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
  162. package/packages/pi-coding-agent/src/core/agent-session.ts +13 -1
  163. package/packages/pi-coding-agent/src/core/extensions/loader.ts +6 -0
  164. package/packages/pi-coding-agent/src/core/lsp/client.ts +26 -0
  165. package/packages/pi-coding-agent/src/core/lsp/index.ts +157 -2
  166. package/packages/pi-coding-agent/src/core/lsp/lsp.md +6 -0
  167. package/packages/pi-coding-agent/src/core/lsp/types.ts +53 -0
  168. package/packages/pi-coding-agent/src/core/lsp/utils.ts +56 -0
  169. package/packages/pi-coding-agent/src/core/settings-manager.ts +41 -11
  170. package/packages/pi-coding-agent/src/core/system-prompt.ts +7 -1
  171. package/packages/pi-coding-agent/src/core/tools/edit.ts +3 -0
  172. package/packages/pi-coding-agent/src/core/tools/write.ts +3 -0
  173. package/src/resources/extensions/google-search/index.ts +164 -47
  174. package/src/resources/extensions/gsd/auto-dashboard.ts +14 -2
  175. package/src/resources/extensions/gsd/auto-prompts.ts +148 -39
  176. package/src/resources/extensions/gsd/auto-worktree.ts +93 -9
  177. package/src/resources/extensions/gsd/auto.ts +690 -39
  178. package/src/resources/extensions/gsd/captures.ts +384 -0
  179. package/src/resources/extensions/gsd/commands.ts +654 -36
  180. package/src/resources/extensions/gsd/complexity-classifier.ts +322 -0
  181. package/src/resources/extensions/gsd/context-budget.ts +243 -0
  182. package/src/resources/extensions/gsd/context-store.ts +195 -0
  183. package/src/resources/extensions/gsd/dashboard-overlay.ts +51 -3
  184. package/src/resources/extensions/gsd/db-writer.ts +341 -0
  185. package/src/resources/extensions/gsd/debug-logger.ts +178 -0
  186. package/src/resources/extensions/gsd/dispatch-guard.ts +0 -1
  187. package/src/resources/extensions/gsd/docs/preferences-reference.md +54 -0
  188. package/src/resources/extensions/gsd/doctor-proactive.ts +286 -0
  189. package/src/resources/extensions/gsd/doctor.ts +283 -2
  190. package/src/resources/extensions/gsd/export.ts +81 -2
  191. package/src/resources/extensions/gsd/files.ts +39 -9
  192. package/src/resources/extensions/gsd/git-service.ts +6 -0
  193. package/src/resources/extensions/gsd/gsd-db.ts +752 -0
  194. package/src/resources/extensions/gsd/guided-flow.ts +26 -1
  195. package/src/resources/extensions/gsd/history.ts +0 -1
  196. package/src/resources/extensions/gsd/index.ts +277 -1
  197. package/src/resources/extensions/gsd/md-importer.ts +526 -0
  198. package/src/resources/extensions/gsd/metrics.ts +84 -0
  199. package/src/resources/extensions/gsd/model-cost-table.ts +65 -0
  200. package/src/resources/extensions/gsd/model-router.ts +256 -0
  201. package/src/resources/extensions/gsd/notifications.ts +0 -1
  202. package/src/resources/extensions/gsd/post-unit-hooks.ts +72 -2
  203. package/src/resources/extensions/gsd/preferences.ts +198 -150
  204. package/src/resources/extensions/gsd/prompt-loader.ts +45 -9
  205. package/src/resources/extensions/gsd/prompts/execute-task.md +3 -5
  206. package/src/resources/extensions/gsd/prompts/heal-skill.md +45 -0
  207. package/src/resources/extensions/gsd/prompts/plan-slice.md +5 -1
  208. package/src/resources/extensions/gsd/prompts/quick-task.md +48 -0
  209. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -0
  210. package/src/resources/extensions/gsd/prompts/replan-slice.md +8 -0
  211. package/src/resources/extensions/gsd/prompts/system.md +2 -1
  212. package/src/resources/extensions/gsd/prompts/triage-captures.md +62 -0
  213. package/src/resources/extensions/gsd/quick.ts +156 -0
  214. package/src/resources/extensions/gsd/skill-discovery.ts +5 -3
  215. package/src/resources/extensions/gsd/skill-health.ts +417 -0
  216. package/src/resources/extensions/gsd/skill-telemetry.ts +127 -0
  217. package/src/resources/extensions/gsd/state.ts +30 -0
  218. package/src/resources/extensions/gsd/templates/preferences.md +1 -0
  219. package/src/resources/extensions/gsd/tests/captures.test.ts +438 -0
  220. package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +181 -0
  221. package/src/resources/extensions/gsd/tests/context-budget.test.ts +283 -0
  222. package/src/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
  223. package/src/resources/extensions/gsd/tests/context-store.test.ts +462 -0
  224. package/src/resources/extensions/gsd/tests/continue-here.test.ts +204 -0
  225. package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +346 -0
  226. package/src/resources/extensions/gsd/tests/db-writer.test.ts +602 -0
  227. package/src/resources/extensions/gsd/tests/debug-logger.test.ts +185 -0
  228. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +406 -0
  229. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +0 -1
  230. package/src/resources/extensions/gsd/tests/dist-redirect.mjs +22 -0
  231. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +244 -0
  232. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +303 -0
  233. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -0
  234. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +353 -0
  235. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +125 -0
  236. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +326 -0
  237. package/src/resources/extensions/gsd/tests/integration-edge.test.ts +228 -0
  238. package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +277 -0
  239. package/src/resources/extensions/gsd/tests/md-importer.test.ts +411 -0
  240. package/src/resources/extensions/gsd/tests/metrics.test.ts +197 -0
  241. package/src/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +144 -0
  242. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +69 -0
  243. package/src/resources/extensions/gsd/tests/model-isolation.test.ts +99 -0
  244. package/src/resources/extensions/gsd/tests/model-router.test.ts +167 -0
  245. package/src/resources/extensions/gsd/tests/parsers.test.ts +40 -0
  246. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +41 -1
  247. package/src/resources/extensions/gsd/tests/preferences-git.test.ts +0 -1
  248. package/src/resources/extensions/gsd/tests/preferences-hooks.test.ts +0 -1
  249. package/src/resources/extensions/gsd/tests/preferences-mode.test.ts +110 -0
  250. package/src/resources/extensions/gsd/tests/preferences-models.test.ts +0 -1
  251. package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +464 -0
  252. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +385 -0
  253. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +488 -1
  254. package/src/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +17 -29
  255. package/src/resources/extensions/gsd/tests/resolve-ts.mjs +2 -8
  256. package/src/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
  257. package/src/resources/extensions/gsd/tests/skill-lifecycle.test.ts +126 -0
  258. package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +31 -8
  259. package/src/resources/extensions/gsd/tests/token-savings.test.ts +366 -0
  260. package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +224 -0
  261. package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +215 -0
  262. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +25 -1
  263. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +145 -0
  264. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +290 -0
  265. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +120 -0
  266. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +478 -0
  267. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +205 -0
  268. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +442 -0
  269. package/src/resources/extensions/gsd/tests/worktree-post-create-hook.test.ts +165 -0
  270. package/src/resources/extensions/gsd/triage-resolution.ts +200 -0
  271. package/src/resources/extensions/gsd/triage-ui.ts +175 -0
  272. package/src/resources/extensions/gsd/types.ts +29 -0
  273. package/src/resources/extensions/gsd/undo.ts +0 -1
  274. package/src/resources/extensions/gsd/unit-runtime.ts +5 -1
  275. package/src/resources/extensions/gsd/visualizer-data.ts +505 -0
  276. package/src/resources/extensions/gsd/visualizer-overlay.ts +337 -0
  277. package/src/resources/extensions/gsd/visualizer-views.ts +755 -0
  278. package/src/resources/extensions/gsd/worktree-command.ts +18 -0
  279. package/src/resources/extensions/gsd/worktree-manager.ts +11 -4
  280. package/src/resources/extensions/remote-questions/config.ts +4 -2
  281. package/src/resources/extensions/remote-questions/discord-adapter.ts +35 -4
  282. package/src/resources/extensions/remote-questions/format.ts +166 -14
  283. package/src/resources/extensions/remote-questions/manager.ts +14 -4
  284. package/src/resources/extensions/remote-questions/remote-command.ts +100 -4
  285. package/src/resources/extensions/remote-questions/slack-adapter.ts +58 -2
  286. package/src/resources/extensions/remote-questions/telegram-adapter.ts +161 -0
  287. package/src/resources/extensions/remote-questions/types.ts +2 -1
  288. package/src/resources/extensions/ttsr/ttsr-manager.ts +26 -0
  289. package/src/resources/extensions/voice/index.ts +4 -3
@@ -0,0 +1,505 @@
1
+ // Data loader for workflow visualizer overlay — aggregates state + metrics.
2
+
3
+ import { deriveState } from './state.js';
4
+ import { parseRoadmap, parsePlan, parseSummary, loadFile } from './files.js';
5
+ import { findMilestoneIds } from './guided-flow.js';
6
+ import { resolveMilestoneFile, resolveSliceFile } from './paths.js';
7
+ import {
8
+ getLedger,
9
+ getProjectTotals,
10
+ aggregateByPhase,
11
+ aggregateBySlice,
12
+ aggregateByModel,
13
+ loadLedgerFromDisk,
14
+ classifyUnitPhase,
15
+ } from './metrics.js';
16
+
17
+ import type { Phase } from './types.js';
18
+ import type {
19
+ ProjectTotals,
20
+ PhaseAggregate,
21
+ SliceAggregate,
22
+ ModelAggregate,
23
+ UnitMetrics,
24
+ } from './metrics.js';
25
+
26
+ // ─── Visualizer Types ─────────────────────────────────────────────────────────
27
+
28
+ export interface VisualizerMilestone {
29
+ id: string;
30
+ title: string;
31
+ status: 'complete' | 'active' | 'pending';
32
+ dependsOn: string[];
33
+ slices: VisualizerSlice[];
34
+ }
35
+
36
+ export interface VisualizerSlice {
37
+ id: string;
38
+ title: string;
39
+ done: boolean;
40
+ active: boolean;
41
+ risk: string;
42
+ depends: string[];
43
+ tasks: VisualizerTask[];
44
+ }
45
+
46
+ export interface VisualizerTask {
47
+ id: string;
48
+ title: string;
49
+ done: boolean;
50
+ active: boolean;
51
+ }
52
+
53
+ export interface CriticalPathInfo {
54
+ milestonePath: string[];
55
+ slicePath: string[];
56
+ milestoneSlack: Map<string, number>;
57
+ sliceSlack: Map<string, number>;
58
+ }
59
+
60
+ export interface AgentActivityInfo {
61
+ currentUnit: { type: string; id: string; startedAt: number } | null;
62
+ elapsed: number;
63
+ completedUnits: number;
64
+ totalSlices: number;
65
+ completionRate: number;
66
+ active: boolean;
67
+ sessionCost: number;
68
+ sessionTokens: number;
69
+ }
70
+
71
+ export interface ChangelogEntry {
72
+ milestoneId: string;
73
+ sliceId: string;
74
+ title: string;
75
+ oneLiner: string;
76
+ filesModified: { path: string; description: string }[];
77
+ completedAt: string;
78
+ }
79
+
80
+ export interface ChangelogInfo {
81
+ entries: ChangelogEntry[];
82
+ }
83
+
84
+ export interface VisualizerData {
85
+ milestones: VisualizerMilestone[];
86
+ phase: Phase;
87
+ totals: ProjectTotals | null;
88
+ byPhase: PhaseAggregate[];
89
+ bySlice: SliceAggregate[];
90
+ byModel: ModelAggregate[];
91
+ units: UnitMetrics[];
92
+ criticalPath: CriticalPathInfo;
93
+ remainingSliceCount: number;
94
+ agentActivity: AgentActivityInfo | null;
95
+ changelog: ChangelogInfo;
96
+ }
97
+
98
+ // ─── Critical Path ────────────────────────────────────────────────────────────
99
+
100
+ export function computeCriticalPath(milestones: VisualizerMilestone[]): CriticalPathInfo {
101
+ const empty: CriticalPathInfo = {
102
+ milestonePath: [],
103
+ slicePath: [],
104
+ milestoneSlack: new Map(),
105
+ sliceSlack: new Map(),
106
+ };
107
+
108
+ if (milestones.length === 0) return empty;
109
+
110
+ // Milestone-level critical path (weight = number of incomplete slices)
111
+ const msMap = new Map(milestones.map(m => [m.id, m]));
112
+ const msIds = milestones.map(m => m.id);
113
+ const msAdj = new Map<string, string[]>();
114
+ const msWeight = new Map<string, number>();
115
+
116
+ for (const ms of milestones) {
117
+ msAdj.set(ms.id, []);
118
+ const incomplete = ms.slices.filter(s => !s.done).length;
119
+ msWeight.set(ms.id, ms.status === 'complete' ? 0 : Math.max(1, incomplete));
120
+ }
121
+
122
+ for (const ms of milestones) {
123
+ for (const dep of ms.dependsOn) {
124
+ if (msMap.has(dep)) {
125
+ const adj = msAdj.get(dep);
126
+ if (adj) adj.push(ms.id);
127
+ }
128
+ }
129
+ }
130
+
131
+ // Topological sort (Kahn's algorithm)
132
+ const inDegree = new Map<string, number>();
133
+ for (const id of msIds) inDegree.set(id, 0);
134
+ for (const ms of milestones) {
135
+ for (const dep of ms.dependsOn) {
136
+ if (msMap.has(dep)) inDegree.set(ms.id, (inDegree.get(ms.id) ?? 0) + 1);
137
+ }
138
+ }
139
+
140
+ const queue: string[] = [];
141
+ for (const [id, deg] of inDegree) {
142
+ if (deg === 0) queue.push(id);
143
+ }
144
+
145
+ const topoOrder: string[] = [];
146
+ while (queue.length > 0) {
147
+ const node = queue.shift()!;
148
+ topoOrder.push(node);
149
+ for (const next of (msAdj.get(node) ?? [])) {
150
+ const d = (inDegree.get(next) ?? 1) - 1;
151
+ inDegree.set(next, d);
152
+ if (d === 0) queue.push(next);
153
+ }
154
+ }
155
+
156
+ // Longest path from each root
157
+ const dist = new Map<string, number>();
158
+ const prev = new Map<string, string | null>();
159
+ for (const id of msIds) {
160
+ dist.set(id, 0);
161
+ prev.set(id, null);
162
+ }
163
+
164
+ for (const node of topoOrder) {
165
+ const w = msWeight.get(node) ?? 1;
166
+ const nodeDist = dist.get(node)! + w;
167
+ for (const next of (msAdj.get(node) ?? [])) {
168
+ if (nodeDist > dist.get(next)!) {
169
+ dist.set(next, nodeDist);
170
+ prev.set(next, node);
171
+ }
172
+ }
173
+ }
174
+
175
+ // Find the end of the critical path (node with max dist + own weight)
176
+ let maxDist = 0;
177
+ let endNode = msIds[0];
178
+ for (const id of msIds) {
179
+ const totalDist = dist.get(id)! + (msWeight.get(id) ?? 1);
180
+ if (totalDist > maxDist) {
181
+ maxDist = totalDist;
182
+ endNode = id;
183
+ }
184
+ }
185
+
186
+ // Trace back
187
+ const milestonePath: string[] = [];
188
+ let cur: string | null = endNode;
189
+ while (cur !== null) {
190
+ milestonePath.unshift(cur);
191
+ cur = prev.get(cur) ?? null;
192
+ }
193
+
194
+ // Compute milestone slack
195
+ const milestoneSlack = new Map<string, number>();
196
+ const criticalSet = new Set(milestonePath);
197
+ for (const id of msIds) {
198
+ if (criticalSet.has(id)) {
199
+ milestoneSlack.set(id, 0);
200
+ } else {
201
+ const nodeTotal = dist.get(id)! + (msWeight.get(id) ?? 1);
202
+ milestoneSlack.set(id, Math.max(0, maxDist - nodeTotal));
203
+ }
204
+ }
205
+
206
+ // Slice-level critical path within active milestone
207
+ const activeMs = milestones.find(m => m.status === 'active');
208
+ let slicePath: string[] = [];
209
+ const sliceSlack = new Map<string, number>();
210
+
211
+ if (activeMs && activeMs.slices.length > 0) {
212
+ const slMap = new Map(activeMs.slices.map(s => [s.id, s]));
213
+ const slAdj = new Map<string, string[]>();
214
+ for (const s of activeMs.slices) slAdj.set(s.id, []);
215
+ for (const s of activeMs.slices) {
216
+ for (const dep of s.depends) {
217
+ if (slMap.has(dep)) {
218
+ const adj = slAdj.get(dep);
219
+ if (adj) adj.push(s.id);
220
+ }
221
+ }
222
+ }
223
+
224
+ // Topo sort slices
225
+ const slIn = new Map<string, number>();
226
+ for (const s of activeMs.slices) slIn.set(s.id, 0);
227
+ for (const s of activeMs.slices) {
228
+ for (const dep of s.depends) {
229
+ if (slMap.has(dep)) slIn.set(s.id, (slIn.get(s.id) ?? 0) + 1);
230
+ }
231
+ }
232
+
233
+ const slQueue: string[] = [];
234
+ for (const [id, d] of slIn) {
235
+ if (d === 0) slQueue.push(id);
236
+ }
237
+
238
+ const slTopo: string[] = [];
239
+ while (slQueue.length > 0) {
240
+ const n = slQueue.shift()!;
241
+ slTopo.push(n);
242
+ for (const next of (slAdj.get(n) ?? [])) {
243
+ const d = (slIn.get(next) ?? 1) - 1;
244
+ slIn.set(next, d);
245
+ if (d === 0) slQueue.push(next);
246
+ }
247
+ }
248
+
249
+ const slDist = new Map<string, number>();
250
+ const slPrev = new Map<string, string | null>();
251
+ for (const s of activeMs.slices) {
252
+ const w = s.done ? 0 : 1;
253
+ slDist.set(s.id, 0);
254
+ slPrev.set(s.id, null);
255
+ }
256
+
257
+ for (const n of slTopo) {
258
+ const w = (slMap.get(n)?.done ? 0 : 1);
259
+ const nd = slDist.get(n)! + w;
260
+ for (const next of (slAdj.get(n) ?? [])) {
261
+ if (nd > slDist.get(next)!) {
262
+ slDist.set(next, nd);
263
+ slPrev.set(next, n);
264
+ }
265
+ }
266
+ }
267
+
268
+ let slMax = 0;
269
+ let slEnd = activeMs.slices[0].id;
270
+ for (const s of activeMs.slices) {
271
+ const totalDist = slDist.get(s.id)! + (s.done ? 0 : 1);
272
+ if (totalDist > slMax) {
273
+ slMax = totalDist;
274
+ slEnd = s.id;
275
+ }
276
+ }
277
+
278
+ let slCur: string | null = slEnd;
279
+ while (slCur !== null) {
280
+ slicePath.unshift(slCur);
281
+ slCur = slPrev.get(slCur) ?? null;
282
+ }
283
+
284
+ const slCritSet = new Set(slicePath);
285
+ for (const s of activeMs.slices) {
286
+ if (slCritSet.has(s.id)) {
287
+ sliceSlack.set(s.id, 0);
288
+ } else {
289
+ const nodeTotal = slDist.get(s.id)! + (s.done ? 0 : 1);
290
+ sliceSlack.set(s.id, Math.max(0, slMax - nodeTotal));
291
+ }
292
+ }
293
+ }
294
+
295
+ return { milestonePath, slicePath, milestoneSlack, sliceSlack };
296
+ }
297
+
298
+ // ─── Agent Activity ──────────────────────────────────────────────────────────
299
+
300
+ function loadAgentActivity(units: UnitMetrics[], milestones: VisualizerMilestone[]): AgentActivityInfo | null {
301
+ if (units.length === 0) return null;
302
+
303
+ // Find currently running unit (finishedAt === 0)
304
+ const running = units.find(u => u.finishedAt === 0);
305
+ const now = Date.now();
306
+
307
+ const completedUnits = units.filter(u => u.finishedAt > 0).length;
308
+ const totalSlices = milestones.reduce((sum, m) => sum + m.slices.length, 0);
309
+
310
+ // Completion rate from finished units
311
+ const finished = units.filter(u => u.finishedAt > 0);
312
+ let completionRate = 0;
313
+ if (finished.length >= 2) {
314
+ const earliest = Math.min(...finished.map(u => u.startedAt));
315
+ const latest = Math.max(...finished.map(u => u.finishedAt));
316
+ const totalHours = (latest - earliest) / 3_600_000;
317
+ completionRate = totalHours > 0 ? finished.length / totalHours : 0;
318
+ }
319
+
320
+ const sessionCost = units.reduce((sum, u) => sum + u.cost, 0);
321
+ const sessionTokens = units.reduce((sum, u) => sum + u.tokens.total, 0);
322
+
323
+ return {
324
+ currentUnit: running
325
+ ? { type: running.type, id: running.id, startedAt: running.startedAt }
326
+ : null,
327
+ elapsed: running ? now - running.startedAt : 0,
328
+ completedUnits,
329
+ totalSlices,
330
+ completionRate,
331
+ active: !!running,
332
+ sessionCost,
333
+ sessionTokens,
334
+ };
335
+ }
336
+
337
+ // ─── Changelog ───────────────────────────────────────────────────────────────
338
+
339
+ const changelogCache = new Map<string, { mtime: number; entry: ChangelogEntry }>();
340
+
341
+ async function loadChangelog(basePath: string, milestones: VisualizerMilestone[]): Promise<ChangelogInfo> {
342
+ const entries: ChangelogEntry[] = [];
343
+
344
+ for (const ms of milestones) {
345
+ for (const sl of ms.slices) {
346
+ if (!sl.done) continue;
347
+
348
+ const summaryFile = resolveSliceFile(basePath, ms.id, sl.id, 'SUMMARY');
349
+ if (!summaryFile) continue;
350
+
351
+ // Check cache by file path
352
+ const cacheKey = `${ms.id}/${sl.id}`;
353
+ const cached = changelogCache.get(cacheKey);
354
+
355
+ // Check mtime for cache invalidation
356
+ let mtime = 0;
357
+ try {
358
+ const { statSync } = await import('node:fs');
359
+ mtime = statSync(summaryFile).mtimeMs;
360
+ } catch {
361
+ continue;
362
+ }
363
+
364
+ if (cached && cached.mtime === mtime) {
365
+ entries.push(cached.entry);
366
+ continue;
367
+ }
368
+
369
+ const content = await loadFile(summaryFile);
370
+ if (!content) continue;
371
+
372
+ const summary = parseSummary(content);
373
+ const entry: ChangelogEntry = {
374
+ milestoneId: ms.id,
375
+ sliceId: sl.id,
376
+ title: sl.title,
377
+ oneLiner: summary.oneLiner,
378
+ filesModified: summary.filesModified.map(f => ({
379
+ path: f.path,
380
+ description: f.description,
381
+ })),
382
+ completedAt: summary.frontmatter.completed_at ?? '',
383
+ };
384
+
385
+ changelogCache.set(cacheKey, { mtime, entry });
386
+ entries.push(entry);
387
+ }
388
+ }
389
+
390
+ // Sort by completedAt descending
391
+ entries.sort((a, b) => (b.completedAt || '').localeCompare(a.completedAt || ''));
392
+
393
+ return { entries };
394
+ }
395
+
396
+ // ─── Loader ───────────────────────────────────────────────────────────────────
397
+
398
+ export async function loadVisualizerData(basePath: string): Promise<VisualizerData> {
399
+ const state = await deriveState(basePath);
400
+ const milestoneIds = findMilestoneIds(basePath);
401
+
402
+ const milestones: VisualizerMilestone[] = [];
403
+
404
+ for (const mid of milestoneIds) {
405
+ const entry = state.registry.find(r => r.id === mid);
406
+ const status = entry?.status ?? 'pending';
407
+ const dependsOn = entry?.dependsOn ?? [];
408
+
409
+ const slices: VisualizerSlice[] = [];
410
+
411
+ const roadmapFile = resolveMilestoneFile(basePath, mid, 'ROADMAP');
412
+ const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null;
413
+
414
+ if (roadmapContent) {
415
+ const roadmap = parseRoadmap(roadmapContent);
416
+
417
+ for (const s of roadmap.slices) {
418
+ const isActiveSlice =
419
+ state.activeMilestone?.id === mid &&
420
+ state.activeSlice?.id === s.id;
421
+
422
+ const tasks: VisualizerTask[] = [];
423
+
424
+ if (isActiveSlice) {
425
+ const planFile = resolveSliceFile(basePath, mid, s.id, 'PLAN');
426
+ const planContent = planFile ? await loadFile(planFile) : null;
427
+
428
+ if (planContent) {
429
+ const plan = parsePlan(planContent);
430
+ for (const t of plan.tasks) {
431
+ tasks.push({
432
+ id: t.id,
433
+ title: t.title,
434
+ done: t.done,
435
+ active: state.activeTask?.id === t.id,
436
+ });
437
+ }
438
+ }
439
+ }
440
+
441
+ slices.push({
442
+ id: s.id,
443
+ title: s.title,
444
+ done: s.done,
445
+ active: isActiveSlice,
446
+ risk: s.risk,
447
+ depends: s.depends,
448
+ tasks,
449
+ });
450
+ }
451
+ }
452
+
453
+ milestones.push({
454
+ id: mid,
455
+ title: entry?.title ?? mid,
456
+ status,
457
+ dependsOn,
458
+ slices,
459
+ });
460
+ }
461
+
462
+ // Metrics
463
+ let totals: ProjectTotals | null = null;
464
+ let byPhase: PhaseAggregate[] = [];
465
+ let bySlice: SliceAggregate[] = [];
466
+ let byModel: ModelAggregate[] = [];
467
+ let units: UnitMetrics[] = [];
468
+
469
+ const ledger = getLedger() ?? loadLedgerFromDisk(basePath);
470
+
471
+ if (ledger && ledger.units.length > 0) {
472
+ units = [...ledger.units].sort((a, b) => a.startedAt - b.startedAt);
473
+ totals = getProjectTotals(units);
474
+ byPhase = aggregateByPhase(units);
475
+ bySlice = aggregateBySlice(units);
476
+ byModel = aggregateByModel(units);
477
+ }
478
+
479
+ // Compute new fields
480
+ const criticalPath = computeCriticalPath(milestones);
481
+
482
+ let remainingSliceCount = 0;
483
+ for (const ms of milestones) {
484
+ for (const sl of ms.slices) {
485
+ if (!sl.done) remainingSliceCount++;
486
+ }
487
+ }
488
+
489
+ const agentActivity = loadAgentActivity(units, milestones);
490
+ const changelog = await loadChangelog(basePath, milestones);
491
+
492
+ return {
493
+ milestones,
494
+ phase: state.phase,
495
+ totals,
496
+ byPhase,
497
+ bySlice,
498
+ byModel,
499
+ units,
500
+ criticalPath,
501
+ remainingSliceCount,
502
+ agentActivity,
503
+ changelog,
504
+ };
505
+ }