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
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, unlinkSync } from "node:fs";
2
2
  import { join, sep } from "node:path";
3
3
 
4
4
  import { loadFile, parsePlan, parseRoadmap, parseSummary, saveFile, parseTaskPlanMustHaves, countMustHavesMentionedInSummary } from "./files.js";
@@ -9,6 +9,8 @@ import { listWorktrees } from "./worktree-manager.js";
9
9
  import { abortAndReset } from "./git-self-heal.js";
10
10
  import { RUNTIME_EXCLUSION_PATHS } from "./git-service.js";
11
11
  import { nativeIsRepo, nativeWorktreeRemove, nativeBranchList, nativeBranchDelete, nativeLsFiles, nativeRmCached } from "./native-git-bridge.js";
12
+ import { readCrashLock, isLockProcessAlive, clearLock } from "./crash-recovery.js";
13
+ import { ensureGitignore } from "./gitignore.js";
12
14
 
13
15
  export type DoctorSeverity = "info" | "warning" | "error";
14
16
  export type DoctorIssueCode =
@@ -32,7 +34,14 @@ export type DoctorIssueCode =
32
34
  | "stale_milestone_branch"
33
35
  | "corrupt_merge_state"
34
36
  | "tracked_runtime_files"
35
- | "legacy_slice_branches";
37
+ | "legacy_slice_branches"
38
+ | "stale_crash_lock"
39
+ | "orphaned_completed_units"
40
+ | "stale_hook_state"
41
+ | "activity_log_bloat"
42
+ | "state_file_stale"
43
+ | "state_file_missing"
44
+ | "gitignore_missing_patterns";
36
45
 
37
46
  export interface DoctorIssue {
38
47
  severity: DoctorSeverity;
@@ -657,6 +666,275 @@ async function checkGitHealth(
657
666
  }
658
667
  }
659
668
 
669
+ // ── Runtime Health Checks ──────────────────────────────────────────────────
670
+ // Checks for stale crash locks, orphaned completed-units, stale hook state,
671
+ // activity log bloat, STATE.md drift, and gitignore drift.
672
+
673
+ async function checkRuntimeHealth(
674
+ basePath: string,
675
+ issues: DoctorIssue[],
676
+ fixesApplied: string[],
677
+ shouldFix: (code: DoctorIssueCode) => boolean,
678
+ ): Promise<void> {
679
+ const root = gsdRoot(basePath);
680
+
681
+ // ── Stale crash lock ──────────────────────────────────────────────────
682
+ try {
683
+ const lock = readCrashLock(basePath);
684
+ if (lock) {
685
+ const alive = isLockProcessAlive(lock);
686
+ if (!alive) {
687
+ issues.push({
688
+ severity: "error",
689
+ code: "stale_crash_lock",
690
+ scope: "project",
691
+ unitId: "project",
692
+ message: `Stale auto.lock from PID ${lock.pid} (started ${lock.startedAt}, was executing ${lock.unitType} ${lock.unitId}) — process is no longer running`,
693
+ file: ".gsd/auto.lock",
694
+ fixable: true,
695
+ });
696
+
697
+ if (shouldFix("stale_crash_lock")) {
698
+ clearLock(basePath);
699
+ fixesApplied.push("cleared stale auto.lock");
700
+ }
701
+ }
702
+ }
703
+ } catch {
704
+ // Non-fatal — crash lock check failed
705
+ }
706
+
707
+ // ── Orphaned completed-units keys ─────────────────────────────────────
708
+ try {
709
+ const completedKeysFile = join(root, "completed-units.json");
710
+ if (existsSync(completedKeysFile)) {
711
+ const raw = readFileSync(completedKeysFile, "utf-8");
712
+ const keys: string[] = JSON.parse(raw);
713
+ const orphaned: string[] = [];
714
+
715
+ for (const key of keys) {
716
+ // Key format: "unitType/unitId" e.g. "execute-task/M001/S01/T01"
717
+ const slashIdx = key.indexOf("/");
718
+ if (slashIdx === -1) continue;
719
+ const unitType = key.slice(0, slashIdx);
720
+ const unitId = key.slice(slashIdx + 1);
721
+
722
+ // Only validate artifact-producing unit types
723
+ const { verifyExpectedArtifact } = await import("./auto-recovery.js");
724
+ if (!verifyExpectedArtifact(unitType, unitId, basePath)) {
725
+ orphaned.push(key);
726
+ }
727
+ }
728
+
729
+ if (orphaned.length > 0) {
730
+ issues.push({
731
+ severity: "warning",
732
+ code: "orphaned_completed_units",
733
+ scope: "project",
734
+ unitId: "project",
735
+ message: `${orphaned.length} completed-unit key(s) reference missing artifacts: ${orphaned.slice(0, 3).join(", ")}${orphaned.length > 3 ? "..." : ""}`,
736
+ file: ".gsd/completed-units.json",
737
+ fixable: true,
738
+ });
739
+
740
+ if (shouldFix("orphaned_completed_units")) {
741
+ const { removePersistedKey } = await import("./auto-recovery.js");
742
+ for (const key of orphaned) {
743
+ removePersistedKey(basePath, key);
744
+ }
745
+ fixesApplied.push(`removed ${orphaned.length} orphaned completed-unit key(s)`);
746
+ }
747
+ }
748
+ }
749
+ } catch {
750
+ // Non-fatal — completed-units check failed
751
+ }
752
+
753
+ // ── Stale hook state ──────────────────────────────────────────────────
754
+ try {
755
+ const hookStateFile = join(root, "hook-state.json");
756
+ if (existsSync(hookStateFile)) {
757
+ const raw = readFileSync(hookStateFile, "utf-8");
758
+ const state = JSON.parse(raw);
759
+ const hasCycleCounts = state.cycleCounts && typeof state.cycleCounts === "object"
760
+ && Object.keys(state.cycleCounts).length > 0;
761
+
762
+ // Only flag if there are actual cycle counts AND no auto-mode is running
763
+ if (hasCycleCounts) {
764
+ const lock = readCrashLock(basePath);
765
+ const autoRunning = lock ? isLockProcessAlive(lock) : false;
766
+
767
+ if (!autoRunning) {
768
+ issues.push({
769
+ severity: "info",
770
+ code: "stale_hook_state",
771
+ scope: "project",
772
+ unitId: "project",
773
+ message: `hook-state.json has ${Object.keys(state.cycleCounts).length} residual cycle count(s) from a previous session`,
774
+ file: ".gsd/hook-state.json",
775
+ fixable: true,
776
+ });
777
+
778
+ if (shouldFix("stale_hook_state")) {
779
+ const { clearPersistedHookState } = await import("./post-unit-hooks.js");
780
+ clearPersistedHookState(basePath);
781
+ fixesApplied.push("cleared stale hook-state.json");
782
+ }
783
+ }
784
+ }
785
+ }
786
+ } catch {
787
+ // Non-fatal — hook state check failed
788
+ }
789
+
790
+ // ── Activity log bloat ────────────────────────────────────────────────
791
+ try {
792
+ const activityDir = join(root, "activity");
793
+ if (existsSync(activityDir)) {
794
+ const files = readdirSync(activityDir);
795
+ let totalSize = 0;
796
+ for (const f of files) {
797
+ try {
798
+ totalSize += statSync(join(activityDir, f)).size;
799
+ } catch {
800
+ // stat failed — skip
801
+ }
802
+ }
803
+
804
+ const totalMB = totalSize / (1024 * 1024);
805
+ const BLOAT_FILE_THRESHOLD = 500;
806
+ const BLOAT_SIZE_MB = 100;
807
+
808
+ if (files.length > BLOAT_FILE_THRESHOLD || totalMB > BLOAT_SIZE_MB) {
809
+ issues.push({
810
+ severity: "warning",
811
+ code: "activity_log_bloat",
812
+ scope: "project",
813
+ unitId: "project",
814
+ message: `Activity logs: ${files.length} files, ${totalMB.toFixed(1)}MB (thresholds: ${BLOAT_FILE_THRESHOLD} files / ${BLOAT_SIZE_MB}MB)`,
815
+ file: ".gsd/activity/",
816
+ fixable: true,
817
+ });
818
+
819
+ if (shouldFix("activity_log_bloat")) {
820
+ const { pruneActivityLogs } = await import("./activity-log.js");
821
+ pruneActivityLogs(activityDir, 7); // 7-day retention
822
+ fixesApplied.push("pruned activity logs (7-day retention)");
823
+ }
824
+ }
825
+ }
826
+ } catch {
827
+ // Non-fatal — activity log check failed
828
+ }
829
+
830
+ // ── STATE.md health ───────────────────────────────────────────────────
831
+ try {
832
+ const stateFilePath = resolveGsdRootFile(basePath, "STATE");
833
+ const milestonesPath = milestonesDir(basePath);
834
+
835
+ if (existsSync(milestonesPath)) {
836
+ if (!existsSync(stateFilePath)) {
837
+ issues.push({
838
+ severity: "warning",
839
+ code: "state_file_missing",
840
+ scope: "project",
841
+ unitId: "project",
842
+ message: "STATE.md is missing — state display will not work",
843
+ file: ".gsd/STATE.md",
844
+ fixable: true,
845
+ });
846
+
847
+ if (shouldFix("state_file_missing")) {
848
+ const state = await deriveState(basePath);
849
+ await saveFile(stateFilePath, buildStateMarkdown(state));
850
+ fixesApplied.push("created STATE.md from derived state");
851
+ }
852
+ } else {
853
+ // Check if STATE.md is stale by comparing active milestone/slice/phase
854
+ const currentContent = readFileSync(stateFilePath, "utf-8");
855
+ const state = await deriveState(basePath);
856
+ const freshContent = buildStateMarkdown(state);
857
+
858
+ // Extract key fields for comparison — don't compare full content
859
+ // since timestamp/formatting differences are normal
860
+ const extractFields = (content: string) => {
861
+ const milestone = content.match(/\*\*Active Milestone:\*\*\s*(.+)/)?.[1]?.trim() ?? "";
862
+ const slice = content.match(/\*\*Active Slice:\*\*\s*(.+)/)?.[1]?.trim() ?? "";
863
+ const phase = content.match(/\*\*Phase:\*\*\s*(.+)/)?.[1]?.trim() ?? "";
864
+ return { milestone, slice, phase };
865
+ };
866
+
867
+ const current = extractFields(currentContent);
868
+ const fresh = extractFields(freshContent);
869
+
870
+ if (current.milestone !== fresh.milestone || current.slice !== fresh.slice || current.phase !== fresh.phase) {
871
+ issues.push({
872
+ severity: "warning",
873
+ code: "state_file_stale",
874
+ scope: "project",
875
+ unitId: "project",
876
+ message: `STATE.md is stale — shows "${current.phase}" but derived state is "${fresh.phase}"`,
877
+ file: ".gsd/STATE.md",
878
+ fixable: true,
879
+ });
880
+
881
+ if (shouldFix("state_file_stale")) {
882
+ await saveFile(stateFilePath, freshContent);
883
+ fixesApplied.push("rebuilt STATE.md from derived state");
884
+ }
885
+ }
886
+ }
887
+ }
888
+ } catch {
889
+ // Non-fatal — STATE.md check failed
890
+ }
891
+
892
+ // ── Gitignore drift ───────────────────────────────────────────────────
893
+ try {
894
+ const gitignorePath = join(basePath, ".gitignore");
895
+ if (existsSync(gitignorePath) && nativeIsRepo(basePath)) {
896
+ const content = readFileSync(gitignorePath, "utf-8");
897
+ const existingLines = new Set(
898
+ content.split("\n").map(l => l.trim()).filter(l => l && !l.startsWith("#")),
899
+ );
900
+
901
+ // Check for critical runtime patterns that must be present
902
+ const criticalPatterns = [
903
+ ".gsd/activity/",
904
+ ".gsd/runtime/",
905
+ ".gsd/auto.lock",
906
+ ".gsd/gsd.db",
907
+ ".gsd/completed-units.json",
908
+ ];
909
+
910
+ // If blanket .gsd/ or .gsd is present, all patterns are covered
911
+ const hasBlanketIgnore = existingLines.has(".gsd/") || existingLines.has(".gsd");
912
+
913
+ if (!hasBlanketIgnore) {
914
+ const missing = criticalPatterns.filter(p => !existingLines.has(p));
915
+ if (missing.length > 0) {
916
+ issues.push({
917
+ severity: "warning",
918
+ code: "gitignore_missing_patterns",
919
+ scope: "project",
920
+ unitId: "project",
921
+ message: `${missing.length} critical GSD runtime pattern(s) missing from .gitignore: ${missing.join(", ")}`,
922
+ file: ".gitignore",
923
+ fixable: true,
924
+ });
925
+
926
+ if (shouldFix("gitignore_missing_patterns")) {
927
+ ensureGitignore(basePath);
928
+ fixesApplied.push("added missing GSD runtime patterns to .gitignore");
929
+ }
930
+ }
931
+ }
932
+ }
933
+ } catch {
934
+ // Non-fatal — gitignore check failed
935
+ }
936
+ }
937
+
660
938
  export async function runGSDDoctor(basePath: string, options?: { fix?: boolean; scope?: string; fixLevel?: "task" | "all" }): Promise<DoctorReport> {
661
939
  const issues: DoctorIssue[] = [];
662
940
  const fixesApplied: string[] = [];
@@ -700,6 +978,9 @@ export async function runGSDDoctor(basePath: string, options?: { fix?: boolean;
700
978
  // Git health checks (orphaned worktrees, stale branches, corrupt merge state, tracked runtime files)
701
979
  await checkGitHealth(basePath, issues, fixesApplied, shouldFix);
702
980
 
981
+ // Runtime health checks (crash locks, completed-units, hook state, activity logs, STATE.md, gitignore)
982
+ await checkRuntimeHealth(basePath, issues, fixesApplied, shouldFix);
983
+
703
984
  const milestonesPath = milestonesDir(basePath);
704
985
  if (!existsSync(milestonesPath)) {
705
986
  return { ok: issues.every(issue => issue.severity !== "error"), basePath, issues, fixesApplied };
@@ -1,18 +1,97 @@
1
1
  // GSD Extension — Session/Milestone Export
2
2
  // Generate shareable reports of milestone work in JSON or markdown format.
3
- // Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
4
3
 
5
4
  import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
6
5
  import { writeFileSync, mkdirSync } from "node:fs";
7
6
  import { join, basename } from "node:path";
8
7
  import {
9
8
  getLedger, getProjectTotals, aggregateByPhase, aggregateBySlice,
10
- aggregateByModel, formatCost, formatTokenCount,
9
+ aggregateByModel, formatCost, formatTokenCount, loadLedgerFromDisk,
11
10
  } from "./metrics.js";
12
11
  import type { UnitMetrics } from "./metrics.js";
13
12
  import { gsdRoot } from "./paths.js";
14
13
  import { formatDuration } from "./history.js";
15
14
 
15
+ /**
16
+ * Write an export file directly, without requiring an ExtensionCommandContext.
17
+ * Used by the visualizer overlay export tab.
18
+ * Returns the output file path, or null on failure.
19
+ */
20
+ export function writeExportFile(
21
+ basePath: string,
22
+ format: "markdown" | "json",
23
+ visualizerData?: { totals: any; byPhase: any[]; bySlice: any[]; byModel: any[]; units: any[]; criticalPath?: any; remainingSliceCount?: number },
24
+ ): string | null {
25
+ const ledger = getLedger();
26
+ let units: UnitMetrics[];
27
+
28
+ if (visualizerData && visualizerData.units.length > 0) {
29
+ units = visualizerData.units;
30
+ } else if (ledger && ledger.units.length > 0) {
31
+ units = ledger.units;
32
+ } else {
33
+ const diskLedger = loadLedgerFromDisk(basePath);
34
+ if (!diskLedger || diskLedger.units.length === 0) return null;
35
+ units = diskLedger.units;
36
+ }
37
+
38
+ const projectName = basename(basePath);
39
+ const exportDir = gsdRoot(basePath);
40
+ mkdirSync(exportDir, { recursive: true });
41
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
42
+
43
+ if (format === "json") {
44
+ const report = {
45
+ exportedAt: new Date().toISOString(),
46
+ project: projectName,
47
+ totals: visualizerData?.totals ?? getProjectTotals(units),
48
+ byPhase: visualizerData?.byPhase ?? aggregateByPhase(units),
49
+ bySlice: visualizerData?.bySlice ?? aggregateBySlice(units),
50
+ byModel: visualizerData?.byModel ?? aggregateByModel(units),
51
+ units,
52
+ };
53
+ const outPath = join(exportDir, `export-${timestamp}.json`);
54
+ writeFileSync(outPath, JSON.stringify(report, null, 2) + "\n", "utf-8");
55
+ return outPath;
56
+ } else {
57
+ const totals = visualizerData?.totals ?? getProjectTotals(units);
58
+ const phases = visualizerData?.byPhase ?? aggregateByPhase(units);
59
+ const slices = visualizerData?.bySlice ?? aggregateBySlice(units);
60
+
61
+ const md = [
62
+ `# GSD Session Report — ${projectName}`,
63
+ ``,
64
+ `**Generated**: ${new Date().toISOString()}`,
65
+ `**Units completed**: ${totals.units}`,
66
+ `**Total cost**: ${formatCost(totals.cost)}`,
67
+ `**Total tokens**: ${formatTokenCount(totals.tokens.total)}`,
68
+ `**Total duration**: ${formatDuration(totals.duration)}`,
69
+ `**Tool calls**: ${totals.toolCalls}`,
70
+ ``,
71
+ `## Cost by Phase`,
72
+ ``,
73
+ `| Phase | Units | Cost | Tokens | Duration |`,
74
+ `|-------|-------|------|--------|----------|`,
75
+ ...phases.map((p: any) =>
76
+ `| ${p.phase} | ${p.units} | ${formatCost(p.cost)} | ${formatTokenCount(p.tokens.total)} | ${formatDuration(p.duration)} |`,
77
+ ),
78
+ ``,
79
+ `## Cost by Slice`,
80
+ ``,
81
+ `| Slice | Units | Cost | Tokens | Duration |`,
82
+ `|-------|-------|------|--------|----------|`,
83
+ ...slices.map((s: any) =>
84
+ `| ${s.sliceId} | ${s.units} | ${formatCost(s.cost)} | ${formatTokenCount(s.tokens.total)} | ${formatDuration(s.duration)} |`,
85
+ ),
86
+ ``,
87
+ ].join("\n");
88
+
89
+ const outPath = join(exportDir, `export-${timestamp}.md`);
90
+ writeFileSync(outPath, md, "utf-8");
91
+ return outPath;
92
+ }
93
+ }
94
+
16
95
  /**
17
96
  * Export session/milestone data to JSON or markdown.
18
97
  */
@@ -21,6 +21,7 @@ import type {
21
21
  import { checkExistingEnvKeys } from '../get-secrets-from-user.js';
22
22
  import { parseRoadmapSlices } from './roadmap-slices.js';
23
23
  import { nativeParseRoadmap, nativeExtractSection, nativeParsePlanFile, nativeParseSummaryFile, NATIVE_UNAVAILABLE } from './native-parser-bridge.js';
24
+ import { debugTime, debugCount } from './debug-logger.js';
24
25
 
25
26
  // ─── Parse Cache ──────────────────────────────────────────────────────────
26
27
 
@@ -224,9 +225,14 @@ export function parseRoadmap(content: string): Roadmap {
224
225
  }
225
226
 
226
227
  function _parseRoadmapImpl(content: string): Roadmap {
228
+ const stopTimer = debugTime("parse-roadmap");
227
229
  // Try native parser first for better performance
228
230
  const nativeResult = nativeParseRoadmap(content);
229
- if (nativeResult) return nativeResult;
231
+ if (nativeResult) {
232
+ stopTimer({ native: true, slices: nativeResult.slices.length, boundaryEntries: nativeResult.boundaryMap.length });
233
+ debugCount("parseRoadmapCalls");
234
+ return nativeResult;
235
+ }
230
236
 
231
237
  const lines = content.split('\n');
232
238
 
@@ -265,21 +271,40 @@ function _parseRoadmapImpl(content: string): Roadmap {
265
271
  let produces = '';
266
272
  let consumes = '';
267
273
 
268
- const prodMatch = sectionContent.match(/^Produces:\s*\n([\s\S]*?)(?=^Consumes|$)/m);
269
- if (prodMatch) produces = prodMatch[1].trim();
274
+ // Use indexOf-based parsing instead of [\s\S]*? regex to avoid
275
+ // catastrophic backtracking on content with code fences (#468).
276
+ const prodIdx = sectionContent.search(/^Produces:\s*$/m);
277
+ if (prodIdx !== -1) {
278
+ const afterProd = sectionContent.indexOf('\n', prodIdx);
279
+ if (afterProd !== -1) {
280
+ const consIdx = sectionContent.search(/^Consumes/m);
281
+ const endIdx = consIdx !== -1 && consIdx > afterProd ? consIdx : sectionContent.length;
282
+ produces = sectionContent.slice(afterProd + 1, endIdx).trim();
283
+ }
284
+ }
270
285
 
271
- const consMatch = sectionContent.match(/^Consumes[^:]*:\s*\n?([\s\S]*?)$/m);
272
- if (consMatch) consumes = consMatch[1].trim();
286
+ const consLineMatch = sectionContent.match(/^Consumes[^:]*:\s*(.+)$/m);
287
+ if (consLineMatch) {
288
+ consumes = consLineMatch[1].trim();
289
+ }
273
290
  if (!consumes) {
274
- const singleCons = sectionContent.match(/^Consumes[^:]*:\s*(.+)$/m);
275
- if (singleCons) consumes = singleCons[1].trim();
291
+ const consIdx = sectionContent.search(/^Consumes[^:]*:\s*$/m);
292
+ if (consIdx !== -1) {
293
+ const afterCons = sectionContent.indexOf('\n', consIdx);
294
+ if (afterCons !== -1) {
295
+ consumes = sectionContent.slice(afterCons + 1).trim();
296
+ }
297
+ }
276
298
  }
277
299
 
278
300
  boundaryMap.push({ fromSlice, toSlice, produces, consumes });
279
301
  }
280
302
  }
281
303
 
282
- return { title, vision, successCriteria, slices, boundaryMap };
304
+ const result = { title, vision, successCriteria, slices, boundaryMap };
305
+ stopTimer({ native: false, slices: slices.length, boundaryEntries: boundaryMap.length });
306
+ debugCount("parseRoadmapCalls");
307
+ return result;
283
308
  }
284
309
 
285
310
  // ─── Secrets Manifest Parser ───────────────────────────────────────────────
@@ -358,9 +383,11 @@ export function parsePlan(content: string): SlicePlan {
358
383
  }
359
384
 
360
385
  function _parsePlanImpl(content: string): SlicePlan {
386
+ const stopTimer = debugTime("parse-plan");
361
387
  // Try native parser first for better performance
362
388
  const nativeResult = nativeParsePlanFile(content);
363
389
  if (nativeResult) {
390
+ stopTimer({ native: true });
364
391
  return {
365
392
  id: nativeResult.id,
366
393
  title: nativeResult.title,
@@ -452,7 +479,10 @@ function _parsePlanImpl(content: string): SlicePlan {
452
479
  const filesSection = extractSection(content, 'Files Likely Touched');
453
480
  const filesLikelyTouched = filesSection ? parseBullets(filesSection) : [];
454
481
 
455
- return { id, title, goal, demo, mustHaves, tasks, filesLikelyTouched };
482
+ const result = { id, title, goal, demo, mustHaves, tasks, filesLikelyTouched };
483
+ stopTimer({ tasks: tasks.length });
484
+ debugCount("parsePlanCalls");
485
+ return result;
456
486
  }
457
487
 
458
488
  // ─── Summary Parser ────────────────────────────────────────────────────────
@@ -52,6 +52,12 @@ export interface GitPreferences {
52
52
  * Default: true (planning docs are tracked in git).
53
53
  */
54
54
  commit_docs?: boolean;
55
+ /** Script to run after a worktree is created (#597).
56
+ * Receives SOURCE_DIR and WORKTREE_DIR as environment variables.
57
+ * Can be an absolute path or relative to the project root.
58
+ * Failure is non-fatal — logged as a warning.
59
+ */
60
+ worktree_post_create?: string;
55
61
  }
56
62
 
57
63
  export const VALID_BRANCH_NAME = /^[a-zA-Z0-9_\-\/.]+$/;