gsd-pi 2.38.0-dev.eeb3520 → 2.39.0-dev.20aba06

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 (346) hide show
  1. package/README.md +15 -11
  2. package/dist/app-paths.js +1 -1
  3. package/dist/cli.js +9 -0
  4. package/dist/extension-discovery.d.ts +5 -3
  5. package/dist/extension-discovery.js +14 -9
  6. package/dist/extension-registry.js +2 -2
  7. package/dist/remote-questions-config.js +2 -2
  8. package/dist/resource-loader.js +100 -3
  9. package/dist/resources/extensions/async-jobs/index.js +10 -0
  10. package/dist/resources/extensions/browser-tools/index.js +3 -1
  11. package/dist/resources/extensions/browser-tools/package.json +3 -1
  12. package/dist/resources/extensions/browser-tools/tools/verify.js +97 -0
  13. package/dist/resources/extensions/cmux/index.js +55 -1
  14. package/dist/resources/extensions/context7/package.json +1 -1
  15. package/dist/resources/extensions/get-secrets-from-user.js +5 -24
  16. package/dist/resources/extensions/github-sync/cli.js +284 -0
  17. package/dist/resources/extensions/github-sync/index.js +73 -0
  18. package/dist/resources/extensions/github-sync/mapping.js +67 -0
  19. package/dist/resources/extensions/github-sync/sync.js +424 -0
  20. package/dist/resources/extensions/github-sync/templates.js +118 -0
  21. package/dist/resources/extensions/github-sync/types.js +7 -0
  22. package/dist/resources/extensions/google-search/package.json +3 -1
  23. package/dist/resources/extensions/gsd/auto/session.js +6 -23
  24. package/dist/resources/extensions/gsd/auto-dashboard.js +7 -0
  25. package/dist/resources/extensions/gsd/auto-dispatch.js +8 -9
  26. package/dist/resources/extensions/gsd/auto-loop.js +923 -787
  27. package/dist/resources/extensions/gsd/auto-post-unit.js +107 -70
  28. package/dist/resources/extensions/gsd/auto-prompts.js +205 -51
  29. package/dist/resources/extensions/gsd/auto-start.js +19 -3
  30. package/dist/resources/extensions/gsd/auto-worktree-sync.js +13 -5
  31. package/dist/resources/extensions/gsd/auto-worktree.js +3 -3
  32. package/dist/resources/extensions/gsd/auto.js +149 -100
  33. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +126 -0
  34. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +233 -0
  35. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +59 -0
  36. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +38 -0
  37. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +156 -0
  38. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +46 -0
  39. package/dist/resources/extensions/gsd/bootstrap/system-context.js +300 -0
  40. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +38 -0
  41. package/dist/resources/extensions/gsd/captures.js +9 -1
  42. package/dist/resources/extensions/gsd/commands/catalog.js +278 -0
  43. package/dist/resources/extensions/gsd/commands/context.js +84 -0
  44. package/dist/resources/extensions/gsd/commands/dispatcher.js +21 -0
  45. package/dist/resources/extensions/gsd/commands/handlers/auto.js +72 -0
  46. package/dist/resources/extensions/gsd/commands/handlers/core.js +246 -0
  47. package/dist/resources/extensions/gsd/commands/handlers/ops.js +166 -0
  48. package/dist/resources/extensions/gsd/commands/handlers/parallel.js +94 -0
  49. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +102 -0
  50. package/dist/resources/extensions/gsd/commands/index.js +11 -0
  51. package/dist/resources/extensions/gsd/commands-extensions.js +3 -2
  52. package/dist/resources/extensions/gsd/commands-handlers.js +17 -4
  53. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
  54. package/dist/resources/extensions/gsd/commands.js +8 -1169
  55. package/dist/resources/extensions/gsd/context-budget.js +2 -10
  56. package/dist/resources/extensions/gsd/dashboard-overlay.js +9 -0
  57. package/dist/resources/extensions/gsd/detection.js +1 -2
  58. package/dist/resources/extensions/gsd/docs/preferences-reference.md +0 -2
  59. package/dist/resources/extensions/gsd/doctor-checks.js +82 -0
  60. package/dist/resources/extensions/gsd/doctor-environment.js +78 -0
  61. package/dist/resources/extensions/gsd/doctor-format.js +15 -0
  62. package/dist/resources/extensions/gsd/doctor-proactive.js +80 -10
  63. package/dist/resources/extensions/gsd/doctor-providers.js +30 -11
  64. package/dist/resources/extensions/gsd/doctor.js +234 -12
  65. package/dist/resources/extensions/gsd/env-utils.js +29 -0
  66. package/dist/resources/extensions/gsd/exit-command.js +2 -1
  67. package/dist/resources/extensions/gsd/export-html.js +46 -0
  68. package/dist/resources/extensions/gsd/export.js +1 -1
  69. package/dist/resources/extensions/gsd/files.js +48 -9
  70. package/dist/resources/extensions/gsd/forensics.js +1 -1
  71. package/dist/resources/extensions/gsd/git-service.js +30 -12
  72. package/dist/resources/extensions/gsd/gitignore.js +16 -3
  73. package/dist/resources/extensions/gsd/guided-flow.js +149 -38
  74. package/dist/resources/extensions/gsd/health-widget-core.js +32 -70
  75. package/dist/resources/extensions/gsd/health-widget.js +4 -87
  76. package/dist/resources/extensions/gsd/index.js +4 -1111
  77. package/dist/resources/extensions/gsd/migrate/parsers.js +1 -1
  78. package/dist/resources/extensions/gsd/migrate-external.js +18 -1
  79. package/dist/resources/extensions/gsd/native-git-bridge.js +37 -0
  80. package/dist/resources/extensions/gsd/package.json +1 -1
  81. package/dist/resources/extensions/gsd/paths.js +3 -0
  82. package/dist/resources/extensions/gsd/preferences-models.js +0 -12
  83. package/dist/resources/extensions/gsd/preferences-types.js +1 -1
  84. package/dist/resources/extensions/gsd/preferences-validation.js +59 -11
  85. package/dist/resources/extensions/gsd/preferences.js +22 -11
  86. package/dist/resources/extensions/gsd/progress-score.js +20 -1
  87. package/dist/resources/extensions/gsd/prompt-loader.js +6 -2
  88. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  89. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  90. package/dist/resources/extensions/gsd/prompts/discuss.md +11 -14
  91. package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -3
  92. package/dist/resources/extensions/gsd/prompts/forensics.md +121 -46
  93. package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  94. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
  95. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
  96. package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  97. package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  98. package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  99. package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  100. package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  101. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  102. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  103. package/dist/resources/extensions/gsd/prompts/queue.md +4 -8
  104. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +11 -8
  105. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  106. package/dist/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  107. package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
  108. package/dist/resources/extensions/gsd/prompts/run-uat.md +28 -11
  109. package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -2
  110. package/dist/resources/extensions/gsd/repo-identity.js +21 -4
  111. package/dist/resources/extensions/gsd/resource-version.js +2 -1
  112. package/dist/resources/extensions/gsd/roadmap-mutations.js +24 -0
  113. package/dist/resources/extensions/gsd/state.js +42 -23
  114. package/dist/resources/extensions/gsd/templates/runtime.md +21 -0
  115. package/dist/resources/extensions/gsd/templates/task-plan.md +3 -0
  116. package/dist/resources/extensions/gsd/visualizer-data.js +27 -2
  117. package/dist/resources/extensions/gsd/visualizer-views.js +52 -0
  118. package/dist/resources/extensions/gsd/worktree.js +35 -16
  119. package/dist/resources/extensions/mcp-client/index.js +14 -1
  120. package/dist/resources/extensions/remote-questions/status.js +4 -1
  121. package/dist/resources/extensions/remote-questions/store.js +4 -1
  122. package/dist/resources/extensions/search-the-web/provider.js +2 -1
  123. package/dist/resources/extensions/shared/frontmatter.js +1 -1
  124. package/dist/resources/extensions/subagent/index.js +12 -3
  125. package/dist/resources/extensions/subagent/isolation.js +2 -1
  126. package/dist/resources/extensions/ttsr/rule-loader.js +2 -1
  127. package/dist/resources/extensions/universal-config/package.json +1 -1
  128. package/dist/welcome-screen.d.ts +13 -0
  129. package/dist/welcome-screen.js +97 -0
  130. package/package.json +1 -1
  131. package/packages/pi-ai/dist/utils/oauth/anthropic.js +2 -2
  132. package/packages/pi-ai/dist/utils/oauth/anthropic.js.map +1 -1
  133. package/packages/pi-ai/src/utils/oauth/anthropic.ts +2 -2
  134. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +12 -0
  135. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  136. package/packages/pi-coding-agent/dist/core/agent-session.js +107 -24
  137. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  138. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  139. package/packages/pi-coding-agent/dist/core/extensions/loader.js +205 -7
  140. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  141. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  142. package/packages/pi-coding-agent/dist/core/package-manager.js +8 -4
  143. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  144. package/packages/pi-coding-agent/dist/core/skill-tool.test.d.ts +2 -0
  145. package/packages/pi-coding-agent/dist/core/skill-tool.test.d.ts.map +1 -0
  146. package/packages/pi-coding-agent/dist/core/skill-tool.test.js +70 -0
  147. package/packages/pi-coding-agent/dist/core/skill-tool.test.js.map +1 -0
  148. package/packages/pi-coding-agent/dist/core/skills.d.ts +1 -0
  149. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  150. package/packages/pi-coding-agent/dist/core/skills.js +8 -2
  151. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  152. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  153. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  154. package/packages/pi-coding-agent/dist/index.js +1 -1
  155. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  156. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts +17 -0
  157. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -0
  158. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +244 -0
  159. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -0
  160. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.d.ts +3 -0
  161. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.d.ts.map +1 -0
  162. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js +58 -0
  163. package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js.map +1 -0
  164. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts +12 -0
  165. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -0
  166. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +54 -0
  167. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -0
  168. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts +6 -0
  169. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts.map +1 -0
  170. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js +63 -0
  171. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js.map +1 -0
  172. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +38 -0
  173. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -0
  174. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js +2 -0
  175. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -0
  176. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +1 -1
  177. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  178. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +15 -457
  179. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  180. package/packages/pi-coding-agent/package.json +1 -1
  181. package/packages/pi-coding-agent/src/core/agent-session.ts +122 -23
  182. package/packages/pi-coding-agent/src/core/extensions/loader.ts +223 -7
  183. package/packages/pi-coding-agent/src/core/package-manager.ts +8 -4
  184. package/packages/pi-coding-agent/src/core/skill-tool.test.ts +89 -0
  185. package/packages/pi-coding-agent/src/core/skills.ts +11 -2
  186. package/packages/pi-coding-agent/src/index.ts +1 -0
  187. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +302 -0
  188. package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +59 -0
  189. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +68 -0
  190. package/packages/pi-coding-agent/src/modes/interactive/controllers/model-controller.ts +71 -0
  191. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +37 -0
  192. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +18 -510
  193. package/pkg/package.json +1 -1
  194. package/src/resources/extensions/async-jobs/index.ts +11 -0
  195. package/src/resources/extensions/browser-tools/index.ts +3 -0
  196. package/src/resources/extensions/browser-tools/tools/verify.ts +117 -0
  197. package/src/resources/extensions/cmux/index.ts +57 -1
  198. package/src/resources/extensions/get-secrets-from-user.ts +5 -24
  199. package/src/resources/extensions/github-sync/cli.ts +364 -0
  200. package/src/resources/extensions/github-sync/index.ts +93 -0
  201. package/src/resources/extensions/github-sync/mapping.ts +81 -0
  202. package/src/resources/extensions/github-sync/sync.ts +556 -0
  203. package/src/resources/extensions/github-sync/templates.ts +183 -0
  204. package/src/resources/extensions/github-sync/tests/cli.test.ts +20 -0
  205. package/src/resources/extensions/github-sync/tests/commit-linking.test.ts +39 -0
  206. package/src/resources/extensions/github-sync/tests/mapping.test.ts +104 -0
  207. package/src/resources/extensions/github-sync/tests/templates.test.ts +110 -0
  208. package/src/resources/extensions/github-sync/types.ts +47 -0
  209. package/src/resources/extensions/gsd/auto/session.ts +7 -25
  210. package/src/resources/extensions/gsd/auto-dashboard.ts +10 -0
  211. package/src/resources/extensions/gsd/auto-dispatch.ts +7 -9
  212. package/src/resources/extensions/gsd/auto-loop.ts +1285 -1138
  213. package/src/resources/extensions/gsd/auto-post-unit.ts +90 -46
  214. package/src/resources/extensions/gsd/auto-prompts.ts +250 -53
  215. package/src/resources/extensions/gsd/auto-start.ts +24 -3
  216. package/src/resources/extensions/gsd/auto-worktree-sync.ts +15 -4
  217. package/src/resources/extensions/gsd/auto-worktree.ts +3 -3
  218. package/src/resources/extensions/gsd/auto.ts +152 -111
  219. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +142 -0
  220. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +238 -0
  221. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +90 -0
  222. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +46 -0
  223. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +167 -0
  224. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +55 -0
  225. package/src/resources/extensions/gsd/bootstrap/system-context.ts +340 -0
  226. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +51 -0
  227. package/src/resources/extensions/gsd/captures.ts +10 -1
  228. package/src/resources/extensions/gsd/commands/catalog.ts +301 -0
  229. package/src/resources/extensions/gsd/commands/context.ts +101 -0
  230. package/src/resources/extensions/gsd/commands/dispatcher.ts +32 -0
  231. package/src/resources/extensions/gsd/commands/handlers/auto.ts +74 -0
  232. package/src/resources/extensions/gsd/commands/handlers/core.ts +274 -0
  233. package/src/resources/extensions/gsd/commands/handlers/ops.ts +169 -0
  234. package/src/resources/extensions/gsd/commands/handlers/parallel.ts +118 -0
  235. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +109 -0
  236. package/src/resources/extensions/gsd/commands/index.ts +14 -0
  237. package/src/resources/extensions/gsd/commands-extensions.ts +4 -2
  238. package/src/resources/extensions/gsd/commands-handlers.ts +18 -3
  239. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
  240. package/src/resources/extensions/gsd/commands.ts +10 -1307
  241. package/src/resources/extensions/gsd/context-budget.ts +2 -12
  242. package/src/resources/extensions/gsd/dashboard-overlay.ts +10 -0
  243. package/src/resources/extensions/gsd/detection.ts +2 -2
  244. package/src/resources/extensions/gsd/docs/preferences-reference.md +0 -2
  245. package/src/resources/extensions/gsd/doctor-checks.ts +75 -0
  246. package/src/resources/extensions/gsd/doctor-environment.ts +82 -1
  247. package/src/resources/extensions/gsd/doctor-format.ts +20 -0
  248. package/src/resources/extensions/gsd/doctor-proactive.ts +106 -10
  249. package/src/resources/extensions/gsd/doctor-providers.ts +30 -9
  250. package/src/resources/extensions/gsd/doctor-types.ts +16 -1
  251. package/src/resources/extensions/gsd/doctor.ts +243 -14
  252. package/src/resources/extensions/gsd/env-utils.ts +31 -0
  253. package/src/resources/extensions/gsd/exit-command.ts +2 -2
  254. package/src/resources/extensions/gsd/export-html.ts +51 -0
  255. package/src/resources/extensions/gsd/export.ts +1 -1
  256. package/src/resources/extensions/gsd/files.ts +51 -11
  257. package/src/resources/extensions/gsd/forensics.ts +1 -1
  258. package/src/resources/extensions/gsd/git-service.ts +44 -10
  259. package/src/resources/extensions/gsd/gitignore.ts +17 -3
  260. package/src/resources/extensions/gsd/guided-flow.ts +177 -44
  261. package/src/resources/extensions/gsd/health-widget-core.ts +28 -80
  262. package/src/resources/extensions/gsd/health-widget.ts +4 -89
  263. package/src/resources/extensions/gsd/index.ts +12 -1307
  264. package/src/resources/extensions/gsd/migrate/parsers.ts +1 -1
  265. package/src/resources/extensions/gsd/migrate-external.ts +18 -1
  266. package/src/resources/extensions/gsd/native-git-bridge.ts +37 -0
  267. package/src/resources/extensions/gsd/paths.ts +4 -0
  268. package/src/resources/extensions/gsd/preferences-models.ts +0 -12
  269. package/src/resources/extensions/gsd/preferences-types.ts +4 -4
  270. package/src/resources/extensions/gsd/preferences-validation.ts +51 -11
  271. package/src/resources/extensions/gsd/preferences.ts +25 -11
  272. package/src/resources/extensions/gsd/progress-score.ts +23 -0
  273. package/src/resources/extensions/gsd/prompt-loader.ts +7 -2
  274. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  275. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  276. package/src/resources/extensions/gsd/prompts/discuss.md +11 -14
  277. package/src/resources/extensions/gsd/prompts/execute-task.md +5 -3
  278. package/src/resources/extensions/gsd/prompts/forensics.md +121 -46
  279. package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  280. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +11 -12
  281. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -10
  282. package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  283. package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  284. package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  285. package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  286. package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  287. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  288. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  289. package/src/resources/extensions/gsd/prompts/queue.md +4 -8
  290. package/src/resources/extensions/gsd/prompts/reactive-execute.md +11 -8
  291. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  292. package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  293. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  294. package/src/resources/extensions/gsd/prompts/run-uat.md +28 -11
  295. package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -2
  296. package/src/resources/extensions/gsd/repo-identity.ts +23 -4
  297. package/src/resources/extensions/gsd/resource-version.ts +3 -1
  298. package/src/resources/extensions/gsd/roadmap-mutations.ts +29 -0
  299. package/src/resources/extensions/gsd/state.ts +39 -21
  300. package/src/resources/extensions/gsd/templates/runtime.md +21 -0
  301. package/src/resources/extensions/gsd/templates/task-plan.md +3 -0
  302. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +21 -18
  303. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +135 -77
  304. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +4 -3
  305. package/src/resources/extensions/gsd/tests/cmux.test.ts +93 -0
  306. package/src/resources/extensions/gsd/tests/derive-state.test.ts +43 -0
  307. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +266 -0
  308. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +3 -3
  309. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +86 -3
  310. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +50 -0
  311. package/src/resources/extensions/gsd/tests/health-widget.test.ts +16 -54
  312. package/src/resources/extensions/gsd/tests/parsers.test.ts +131 -14
  313. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +209 -0
  314. package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -7
  315. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +59 -0
  316. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +16 -16
  317. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +21 -1
  318. package/src/resources/extensions/gsd/tests/run-uat.test.ts +16 -4
  319. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +140 -0
  320. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +10 -10
  321. package/src/resources/extensions/gsd/tests/worktree.test.ts +47 -0
  322. package/src/resources/extensions/gsd/types.ts +18 -1
  323. package/src/resources/extensions/gsd/verification-evidence.ts +16 -0
  324. package/src/resources/extensions/gsd/visualizer-data.ts +52 -2
  325. package/src/resources/extensions/gsd/visualizer-views.ts +58 -0
  326. package/src/resources/extensions/gsd/worktree.ts +35 -15
  327. package/src/resources/extensions/mcp-client/index.ts +17 -1
  328. package/src/resources/extensions/remote-questions/status.ts +5 -1
  329. package/src/resources/extensions/remote-questions/store.ts +5 -1
  330. package/src/resources/extensions/search-the-web/provider.ts +2 -1
  331. package/src/resources/extensions/shared/frontmatter.ts +1 -1
  332. package/src/resources/extensions/subagent/index.ts +12 -3
  333. package/src/resources/extensions/subagent/isolation.ts +3 -1
  334. package/src/resources/extensions/ttsr/rule-loader.ts +3 -1
  335. package/dist/resources/extensions/gsd/prompt-compressor.js +0 -393
  336. package/dist/resources/extensions/gsd/semantic-chunker.js +0 -254
  337. package/dist/resources/extensions/gsd/summary-distiller.js +0 -212
  338. package/src/resources/extensions/gsd/prompt-compressor.ts +0 -508
  339. package/src/resources/extensions/gsd/semantic-chunker.ts +0 -336
  340. package/src/resources/extensions/gsd/summary-distiller.ts +0 -258
  341. package/src/resources/extensions/gsd/tests/context-compression.test.ts +0 -193
  342. package/src/resources/extensions/gsd/tests/prompt-compressor.test.ts +0 -529
  343. package/src/resources/extensions/gsd/tests/semantic-chunker.test.ts +0 -426
  344. package/src/resources/extensions/gsd/tests/summary-distiller.test.ts +0 -323
  345. package/src/resources/extensions/gsd/tests/token-optimization-benchmark.test.ts +0 -1272
  346. package/src/resources/extensions/gsd/tests/token-optimization-prefs.test.ts +0 -164
@@ -9,7 +9,6 @@
9
9
  */
10
10
 
11
11
  import { type TokenProvider, getCharsPerToken } from "./token-counter.js";
12
- import { compressToTarget } from "./prompt-compressor.js";
13
12
 
14
13
  // ─── Budget ratio constants ──────────────────────────────────────────────────
15
14
  // Percentages of total context window allocated to each budget category.
@@ -202,22 +201,13 @@ export function resolveExecutorContextWindow(
202
201
  }
203
202
 
204
203
  /**
205
- * Smart context reduction: compress first, then truncate if still over budget.
206
- * Returns the content within budget with maximum information preservation.
204
+ * Reduce content to fit within budget using section-boundary truncation.
207
205
  */
208
206
  export function reduceToFit(content: string, budgetChars: number): TruncationResult {
209
207
  if (!content || content.length <= budgetChars) {
210
208
  return { content, droppedSections: 0 };
211
209
  }
212
-
213
- // Step 1: Try compression
214
- const compressed = compressToTarget(content, budgetChars);
215
- if (compressed.compressedChars <= budgetChars) {
216
- return { content: compressed.content, droppedSections: 0 };
217
- }
218
-
219
- // Step 2: Truncate the compressed content at section boundaries
220
- return truncateAtSectionBoundary(compressed.content, budgetChars);
210
+ return truncateAtSectionBoundary(content, budgetChars);
221
211
  }
222
212
 
223
213
  // ─── Internal helpers ────────────────────────────────────────────────────────
@@ -320,6 +320,16 @@ export class GSDDashboardOverlay {
320
320
  : progressScore.level === "yellow" ? th.fg("warning", "●")
321
321
  : th.fg("error", "●");
322
322
  lines.push(row(`${progressIcon} ${th.fg("text", progressScore.summary)}`));
323
+
324
+ // Show signal details when degraded — real-time visibility into what doctor found
325
+ if (progressScore.level !== "green" && progressScore.signals.length > 0) {
326
+ for (const signal of progressScore.signals) {
327
+ const prefix = signal.kind === "positive" ? th.fg("success", " ✓")
328
+ : signal.kind === "negative" ? th.fg("error", " ✗")
329
+ : th.fg("dim", " ·");
330
+ lines.push(row(`${prefix} ${th.fg("dim", signal.label)}`));
331
+ }
332
+ }
323
333
  }
324
334
  lines.push(blank());
325
335
 
@@ -11,6 +11,8 @@ import { join } from "node:path";
11
11
  import { homedir } from "node:os";
12
12
  import { gsdRoot } from "./paths.js";
13
13
 
14
+ const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
15
+
14
16
  // ─── Types ──────────────────────────────────────────────────────────────────────
15
17
 
16
18
  export interface ProjectDetection {
@@ -400,7 +402,6 @@ function detectVerificationCommands(
400
402
  * Check if global GSD setup exists (has ~/.gsd/ with preferences).
401
403
  */
402
404
  export function hasGlobalSetup(): boolean {
403
- const gsdHome = join(homedir(), ".gsd");
404
405
  return (
405
406
  existsSync(join(gsdHome, "preferences.md")) ||
406
407
  existsSync(join(gsdHome, "PREFERENCES.md"))
@@ -412,7 +413,6 @@ export function hasGlobalSetup(): boolean {
412
413
  * Returns true if ~/.gsd/ doesn't exist or has no preferences or auth.
413
414
  */
414
415
  export function isFirstEverLaunch(): boolean {
415
- const gsdHome = join(homedir(), ".gsd");
416
416
  if (!existsSync(gsdHome)) return true;
417
417
 
418
418
  // If we have preferences, not first launch
@@ -194,8 +194,6 @@ Setting `prefer_skills: []` does **not** disable skill discovery — it just mea
194
194
 
195
195
  - `search_provider`: `"brave"`, `"tavily"`, `"ollama"`, `"native"`, or `"auto"` — selects the search backend for research phases. `"native"` forces Anthropic's built-in web search only; provider values force that backend and disable native search; `"auto"` uses the default heuristic. Default: `"auto"`.
196
196
 
197
- - `compression_strategy`: `"truncate"` or `"compress"` — controls how context that exceeds the budget is reduced. `"truncate"` (default) drops sections from the end. `"compress"` applies heuristic compression before truncating, preserving more content at the cost of some fidelity. Default: `"truncate"`.
198
-
199
197
  - `context_selection`: `"full"` or `"smart"` — controls how files are inlined into context. `"full"` inlines entire files; `"smart"` uses semantic chunking to include only the most relevant sections. Default is derived from `token_profile`.
200
198
 
201
199
  - `parallel`: configures parallel orchestration for running multiple slices concurrently. Keys:
@@ -657,6 +657,81 @@ export async function checkRuntimeHealth(
657
657
  } catch {
658
658
  // Non-fatal — external state check failed
659
659
  }
660
+
661
+ // ── Metrics ledger integrity ───────────────────────────────────────────
662
+ try {
663
+ const metricsPath = join(root, "metrics.json");
664
+ if (existsSync(metricsPath)) {
665
+ try {
666
+ const raw = readFileSync(metricsPath, "utf-8");
667
+ const ledger = JSON.parse(raw);
668
+ if (ledger.version !== 1 || !Array.isArray(ledger.units)) {
669
+ issues.push({
670
+ severity: "warning",
671
+ code: "metrics_ledger_corrupt",
672
+ scope: "project",
673
+ unitId: "project",
674
+ message: "metrics.json has an unexpected structure (version !== 1 or units is not an array) — metrics data may be unreliable",
675
+ file: ".gsd/metrics.json",
676
+ fixable: false,
677
+ });
678
+ }
679
+ } catch {
680
+ issues.push({
681
+ severity: "warning",
682
+ code: "metrics_ledger_corrupt",
683
+ scope: "project",
684
+ unitId: "project",
685
+ message: "metrics.json is not valid JSON — metrics data may be corrupt",
686
+ file: ".gsd/metrics.json",
687
+ fixable: false,
688
+ });
689
+ }
690
+ }
691
+ } catch {
692
+ // Non-fatal — metrics check failed
693
+ }
694
+
695
+ // ── Large planning file detection ──────────────────────────────────────
696
+ // Files over 100KB can cause LLM context pressure. Report the worst offenders.
697
+ try {
698
+ const MAX_FILE_BYTES = 100 * 1024; // 100KB
699
+ const milestonesPath = milestonesDir(basePath);
700
+ if (existsSync(milestonesPath)) {
701
+ const largeFiles: Array<{ path: string; sizeKB: number }> = [];
702
+ function scanForLargeFiles(dir: string, depth = 0): void {
703
+ if (depth > 6) return;
704
+ try {
705
+ for (const entry of readdirSync(dir)) {
706
+ const full = join(dir, entry);
707
+ try {
708
+ const s = statSync(full);
709
+ if (s.isDirectory()) { scanForLargeFiles(full, depth + 1); continue; }
710
+ if (entry.endsWith(".md") && s.size > MAX_FILE_BYTES) {
711
+ largeFiles.push({ path: full.replace(basePath + "/", ""), sizeKB: Math.round(s.size / 1024) });
712
+ }
713
+ } catch { /* skip entry */ }
714
+ }
715
+ } catch { /* skip dir */ }
716
+ }
717
+ scanForLargeFiles(milestonesPath);
718
+ if (largeFiles.length > 0) {
719
+ largeFiles.sort((a, b) => b.sizeKB - a.sizeKB);
720
+ const worst = largeFiles[0]!;
721
+ issues.push({
722
+ severity: "warning",
723
+ code: "large_planning_file",
724
+ scope: "project",
725
+ unitId: "project",
726
+ message: `${largeFiles.length} planning file(s) exceed 100KB — largest: ${worst.path} (${worst.sizeKB}KB). Large files cause LLM context pressure.`,
727
+ file: worst.path,
728
+ fixable: false,
729
+ });
730
+ }
731
+ }
732
+ } catch {
733
+ // Non-fatal — large file scan failed
734
+ }
660
735
  }
661
736
 
662
737
  /**
@@ -407,6 +407,63 @@ function checkGitRemote(basePath: string): EnvironmentCheckResult | null {
407
407
  return { name: "git_remote", status: "ok", message: "Git remote reachable" };
408
408
  }
409
409
 
410
+ /**
411
+ * Check if the project build passes (opt-in slow check, use --build flag).
412
+ * Runs npm run build and reports failure as env_build.
413
+ */
414
+ function checkBuildHealth(basePath: string): EnvironmentCheckResult | null {
415
+ const pkgPath = join(basePath, "package.json");
416
+ if (!existsSync(pkgPath)) return null;
417
+
418
+ try {
419
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
420
+ const buildScript = pkg.scripts?.build;
421
+ if (!buildScript) return null;
422
+
423
+ const result = tryExec("npm run build 2>&1", basePath);
424
+ if (result === null) {
425
+ return {
426
+ name: "build",
427
+ status: "error",
428
+ message: "Build failed — npm run build exited non-zero",
429
+ detail: "Fix build errors before dispatching work",
430
+ };
431
+ }
432
+ return { name: "build", status: "ok", message: "Build passes" };
433
+ } catch {
434
+ return null;
435
+ }
436
+ }
437
+
438
+ /**
439
+ * Check if tests pass (opt-in slow check, use --test flag).
440
+ * Runs npm test and reports failures as env_test.
441
+ */
442
+ function checkTestHealth(basePath: string): EnvironmentCheckResult | null {
443
+ const pkgPath = join(basePath, "package.json");
444
+ if (!existsSync(pkgPath)) return null;
445
+
446
+ try {
447
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
448
+ const testScript = pkg.scripts?.test;
449
+ // Skip if no test script or the default placeholder
450
+ if (!testScript || testScript.includes("no test specified")) return null;
451
+
452
+ const result = tryExec("npm test 2>&1", basePath);
453
+ if (result === null) {
454
+ return {
455
+ name: "test",
456
+ status: "warning",
457
+ message: "Tests failing — npm test exited non-zero",
458
+ detail: "Fix failing tests before shipping",
459
+ };
460
+ }
461
+ return { name: "test", status: "ok", message: "Tests pass" };
462
+ } catch {
463
+ return null;
464
+ }
465
+ }
466
+
410
467
  // ── Public API ─────────────────────────────────────────────────────────────
411
468
 
412
469
  /**
@@ -454,6 +511,26 @@ export function runFullEnvironmentChecks(basePath: string): EnvironmentCheckResu
454
511
  return results;
455
512
  }
456
513
 
514
+ /**
515
+ * Run slow opt-in checks (build and/or test).
516
+ * These are never run on the pre-dispatch gate — only on explicit /gsd doctor --build/--test.
517
+ */
518
+ export function runSlowEnvironmentChecks(
519
+ basePath: string,
520
+ options?: { includeBuild?: boolean; includeTests?: boolean },
521
+ ): EnvironmentCheckResult[] {
522
+ const results: EnvironmentCheckResult[] = [];
523
+ if (options?.includeBuild) {
524
+ const buildCheck = checkBuildHealth(basePath);
525
+ if (buildCheck) results.push(buildCheck);
526
+ }
527
+ if (options?.includeTests) {
528
+ const testCheck = checkTestHealth(basePath);
529
+ if (testCheck) results.push(testCheck);
530
+ }
531
+ return results;
532
+ }
533
+
457
534
  /**
458
535
  * Convert environment check results to DoctorIssue format for the doctor pipeline.
459
536
  */
@@ -477,12 +554,16 @@ export function environmentResultsToDoctorIssues(results: EnvironmentCheckResult
477
554
  export async function checkEnvironmentHealth(
478
555
  basePath: string,
479
556
  issues: DoctorIssue[],
480
- options?: { includeRemote?: boolean },
557
+ options?: { includeRemote?: boolean; includeBuild?: boolean; includeTests?: boolean },
481
558
  ): Promise<void> {
482
559
  const results = options?.includeRemote
483
560
  ? runFullEnvironmentChecks(basePath)
484
561
  : runEnvironmentChecks(basePath);
485
562
 
563
+ if (options?.includeBuild || options?.includeTests) {
564
+ results.push(...runSlowEnvironmentChecks(basePath, options));
565
+ }
566
+
486
567
  issues.push(...environmentResultsToDoctorIssues(results));
487
568
  }
488
569
 
@@ -76,3 +76,23 @@ export function formatDoctorIssuesForPrompt(issues: DoctorIssue[]): string {
76
76
  return `- [${prefix}] ${issue.unitId} | ${issue.code} | ${issue.message}${issue.file ? ` | file: ${issue.file}` : ""} | fixable: ${issue.fixable ? "yes" : "no"}`;
77
77
  }).join("\n");
78
78
  }
79
+
80
+ /**
81
+ * Serialize a doctor report to JSON — suitable for CI/tooling integration.
82
+ * Usage: /gsd doctor --json
83
+ */
84
+ export function formatDoctorReportJson(report: DoctorReport): string {
85
+ return JSON.stringify(
86
+ {
87
+ ok: report.ok,
88
+ basePath: report.basePath,
89
+ generatedAt: new Date().toISOString(),
90
+ summary: summarizeDoctorIssues(report.issues),
91
+ issues: report.issues,
92
+ fixesApplied: report.fixesApplied,
93
+ ...(report.timing ? { timing: report.timing } : {}),
94
+ },
95
+ null,
96
+ 2,
97
+ );
98
+ }
@@ -26,12 +26,26 @@ import { nativeBranchExists, nativeIsRepo } from "./native-git-bridge.js";
26
26
 
27
27
  // ── Health Score Tracking ──────────────────────────────────────────────────
28
28
 
29
+ /** Compact issue detail stored per snapshot for real-time visibility. */
30
+ export interface HealthIssueDetail {
31
+ code: string;
32
+ message: string;
33
+ severity: "error" | "warning" | "info";
34
+ unitId: string;
35
+ }
36
+
29
37
  export interface HealthSnapshot {
30
38
  timestamp: number;
31
39
  errors: number;
32
40
  warnings: number;
33
41
  fixesApplied: number;
34
42
  unitIndex: number; // which unit dispatch triggered this snapshot
43
+ /** Top issues from the doctor run that produced this snapshot. */
44
+ issues: HealthIssueDetail[];
45
+ /** Fixes that were auto-applied during this snapshot's doctor run. */
46
+ fixes: string[];
47
+ /** Milestone/slice scope this snapshot belongs to (e.g. "M001" or "M001/S02"). */
48
+ scope?: string;
35
49
  }
36
50
 
37
51
  /** In-memory health history for the current auto-mode session. */
@@ -43,11 +57,33 @@ let consecutiveErrorUnits = 0;
43
57
  /** Unit index counter for health tracking. */
44
58
  let healthUnitIndex = 0;
45
59
 
60
+ /** Previous progress level for state transition detection. */
61
+ let previousProgressLevel: "green" | "yellow" | "red" = "green";
62
+
63
+ /** Callback for state transition notifications. Set by auto-mode. */
64
+ let onLevelChange: ((from: string, to: string, summary: string) => void) | null = null;
65
+
66
+ /**
67
+ * Register a callback for progress level transitions (green→yellow, yellow→red, etc.).
68
+ * Called once when auto-mode starts. Pass null to unregister.
69
+ */
70
+ export function setLevelChangeCallback(cb: ((from: string, to: string, summary: string) => void) | null): void {
71
+ onLevelChange = cb;
72
+ previousProgressLevel = "green";
73
+ }
74
+
46
75
  /**
47
76
  * Record a health snapshot after a doctor run.
48
- * Called from the post-unit hook in auto.ts.
77
+ * Called from the post-unit hook in auto-post-unit.ts.
49
78
  */
50
- export function recordHealthSnapshot(errors: number, warnings: number, fixesApplied: number): void {
79
+ export function recordHealthSnapshot(
80
+ errors: number,
81
+ warnings: number,
82
+ fixesApplied: number,
83
+ issues?: HealthIssueDetail[],
84
+ fixes?: string[],
85
+ scope?: string,
86
+ ): void {
51
87
  healthUnitIndex++;
52
88
  healthHistory.push({
53
89
  timestamp: Date.now(),
@@ -55,6 +91,9 @@ export function recordHealthSnapshot(errors: number, warnings: number, fixesAppl
55
91
  warnings,
56
92
  fixesApplied,
57
93
  unitIndex: healthUnitIndex,
94
+ issues: issues ?? [],
95
+ fixes: fixes ?? [],
96
+ scope,
58
97
  });
59
98
 
60
99
  // Keep only the last 50 snapshots to bound memory
@@ -67,6 +106,19 @@ export function recordHealthSnapshot(errors: number, warnings: number, fixesAppl
67
106
  } else {
68
107
  consecutiveErrorUnits = 0;
69
108
  }
109
+
110
+ // Detect progress level transitions and notify
111
+ if (onLevelChange) {
112
+ const newLevel = consecutiveErrorUnits >= 3 ? "red"
113
+ : consecutiveErrorUnits >= 1 || getHealthTrend() === "degrading" ? "yellow"
114
+ : "green";
115
+ if (newLevel !== previousProgressLevel) {
116
+ const topIssue = (issues ?? []).find(i => i.severity === "error") ?? (issues ?? [])[0];
117
+ const detail = topIssue ? `: ${topIssue.message}` : "";
118
+ onLevelChange(previousProgressLevel, newLevel, `Health ${previousProgressLevel} → ${newLevel}${detail}`);
119
+ previousProgressLevel = newLevel;
120
+ }
121
+ }
70
122
  }
71
123
 
72
124
  /**
@@ -104,6 +156,27 @@ export function getHealthHistory(): readonly HealthSnapshot[] {
104
156
  return healthHistory;
105
157
  }
106
158
 
159
+ /**
160
+ * Get the latest health issues from the most recent snapshot.
161
+ * Returns issues from the last snapshot that had any, for real-time visibility.
162
+ */
163
+ export function getLatestHealthIssues(): HealthIssueDetail[] {
164
+ for (let i = healthHistory.length - 1; i >= 0; i--) {
165
+ if (healthHistory[i]!.issues.length > 0) return healthHistory[i]!.issues;
166
+ }
167
+ return [];
168
+ }
169
+
170
+ /**
171
+ * Get the latest fixes applied from the most recent snapshot.
172
+ */
173
+ export function getLatestHealthFixes(): string[] {
174
+ for (let i = healthHistory.length - 1; i >= 0; i--) {
175
+ if (healthHistory[i]!.fixes.length > 0) return healthHistory[i]!.fixes;
176
+ }
177
+ return [];
178
+ }
179
+
107
180
  /**
108
181
  * Reset health tracking state. Called on auto-mode start/stop.
109
182
  */
@@ -111,6 +184,7 @@ export function resetHealthTracking(): void {
111
184
  healthHistory = [];
112
185
  consecutiveErrorUnits = 0;
113
186
  healthUnitIndex = 0;
187
+ previousProgressLevel = "green";
114
188
  }
115
189
 
116
190
  // ── Pre-Dispatch Health Gate ───────────────────────────────────────────────
@@ -285,26 +359,48 @@ export function resetEscalation(): void {
285
359
 
286
360
  /**
287
361
  * Format a health summary for display in the auto-mode dashboard.
362
+ * Human-readable with full words, not abbreviations.
288
363
  */
289
364
  export function formatHealthSummary(): string {
290
365
  if (healthHistory.length === 0) return "No health data yet.";
291
366
 
292
367
  const latest = healthHistory[healthHistory.length - 1]!;
293
368
  const trend = getHealthTrend();
294
- const trendIcon = trend === "improving" ? "+" : trend === "degrading" ? "-" : "=";
369
+ const trendLabel = trend === "improving" ? "improving"
370
+ : trend === "degrading" ? "degrading"
371
+ : trend === "stable" ? "stable"
372
+ : "unknown";
295
373
  const totalFixes = healthHistory.reduce((sum, s) => sum + s.fixesApplied, 0);
296
374
 
297
- const parts = [
298
- `Health: ${latest.errors}E/${latest.warnings}W`,
299
- `trend:${trendIcon}`,
300
- `fixes:${totalFixes}`,
301
- ];
375
+ const parts: string[] = [];
376
+
377
+ // Error/warning summary
378
+ if (latest.errors === 0 && latest.warnings === 0) {
379
+ parts.push("No issues");
380
+ } else {
381
+ const counts: string[] = [];
382
+ if (latest.errors > 0) counts.push(`${latest.errors} error${latest.errors > 1 ? "s" : ""}`);
383
+ if (latest.warnings > 0) counts.push(`${latest.warnings} warning${latest.warnings > 1 ? "s" : ""}`);
384
+ parts.push(counts.join(", "));
385
+ }
386
+
387
+ parts.push(`trend ${trendLabel}`);
388
+
389
+ if (totalFixes > 0) {
390
+ parts.push(`${totalFixes} fix${totalFixes > 1 ? "es" : ""} applied`);
391
+ }
302
392
 
303
393
  if (consecutiveErrorUnits > 0) {
304
- parts.push(`streak:${consecutiveErrorUnits}/${ESCALATION_THRESHOLD}`);
394
+ parts.push(`${consecutiveErrorUnits} of ${ESCALATION_THRESHOLD} consecutive errors before escalation`);
395
+ }
396
+
397
+ // Include top issue from latest snapshot
398
+ if (latest.issues.length > 0) {
399
+ const topIssue = latest.issues.find(i => i.severity === "error") ?? latest.issues[0]!;
400
+ parts.push(`latest: ${topIssue.message}`);
305
401
  }
306
402
 
307
- return parts.join(" | ");
403
+ return parts.join(" · ");
308
404
  }
309
405
 
310
406
  /**
@@ -51,10 +51,12 @@ function modelToProviderId(model: string): string | null {
51
51
  const prefix = model.split("/")[0].toLowerCase();
52
52
  // Map known prefixes to registry IDs
53
53
  const prefixMap: Record<string, string> = {
54
+ "anthropic-vertex": "anthropic-vertex",
54
55
  openrouter: "openrouter",
55
56
  groq: "groq",
56
57
  mistral: "mistral",
57
58
  google: "google",
59
+ "google-vertex": "google-vertex",
58
60
  anthropic: "anthropic",
59
61
  openai: "openai",
60
62
  "github-copilot": "github-copilot",
@@ -88,11 +90,20 @@ function collectConfiguredModelProviders(): Set<string> {
88
90
 
89
91
  const modelEntries = typeof models === "object" ? Object.values(models) : [];
90
92
  for (const entry of modelEntries) {
91
- const modelId = typeof entry === "string" ? entry
92
- : typeof entry === "object" && entry !== null && "model" in entry
93
- ? String((entry as { model: unknown }).model)
94
- : null;
95
- if (modelId) {
93
+ if (typeof entry === "string") {
94
+ const pid = modelToProviderId(entry);
95
+ if (pid) providers.add(pid);
96
+ continue;
97
+ }
98
+
99
+ if (typeof entry === "object" && entry !== null && "model" in entry) {
100
+ const configuredProvider = "provider" in entry ? (entry as { provider?: unknown }).provider : undefined;
101
+ if (typeof configuredProvider === "string" && configuredProvider.trim().length > 0) {
102
+ providers.add(configuredProvider);
103
+ continue;
104
+ }
105
+
106
+ const modelId = String((entry as { model: unknown }).model);
96
107
  const pid = modelToProviderId(modelId);
97
108
  if (pid) providers.add(pid);
98
109
  }
@@ -117,6 +128,10 @@ interface KeyLookup {
117
128
  function resolveKey(providerId: string): KeyLookup {
118
129
  const info = PROVIDER_REGISTRY.find(p => p.id === providerId);
119
130
 
131
+ if (providerId === "anthropic-vertex" && process.env.ANTHROPIC_VERTEX_PROJECT_ID) {
132
+ return { found: true, source: "env", backedOff: false };
133
+ }
134
+
120
135
  // Check auth.json
121
136
  const authPath = getAuthPath();
122
137
  if (existsSync(authPath)) {
@@ -175,7 +190,9 @@ function checkLlmProviders(): ProviderCheckResult[] {
175
190
 
176
191
  for (const providerId of required) {
177
192
  const info = PROVIDER_REGISTRY.find(p => p.id === providerId);
178
- const label = info?.label ?? providerId;
193
+ const label = providerId === "anthropic-vertex"
194
+ ? "Anthropic Vertex"
195
+ : info?.label ?? providerId;
179
196
  const lookup = resolveKey(providerId);
180
197
 
181
198
  if (!lookup.found) {
@@ -196,14 +213,18 @@ function checkLlmProviders(): ProviderCheckResult[] {
196
213
  continue;
197
214
  }
198
215
 
199
- const envVar = info?.envVar ?? `${providerId.toUpperCase()}_API_KEY`;
216
+ const envVar = providerId === "anthropic-vertex"
217
+ ? "ANTHROPIC_VERTEX_PROJECT_ID"
218
+ : info?.envVar ?? `${providerId.toUpperCase()}_API_KEY`;
200
219
  results.push({
201
220
  name: providerId,
202
221
  label,
203
222
  category: "llm",
204
223
  status: "error",
205
- message: `${label} — no API key found`,
206
- detail: info?.hasOAuth
224
+ message: `${label} — not configured`,
225
+ detail: providerId === "anthropic-vertex"
226
+ ? "Set ANTHROPIC_VERTEX_PROJECT_ID and authenticate with Google ADC"
227
+ : info?.hasOAuth
207
228
  ? `Run /gsd keys to authenticate`
208
229
  : `Set ${envVar} or run /gsd keys`,
209
230
  required: true,
@@ -53,7 +53,20 @@ export type DoctorIssueCode =
53
53
  | "stranded_lock_directory"
54
54
  // Git / worktree integrity checks
55
55
  | "integration_branch_missing"
56
- | "worktree_directory_orphaned";
56
+ | "worktree_directory_orphaned"
57
+ // GSD state structural checks
58
+ | "circular_slice_dependency"
59
+ | "orphaned_slice_directory"
60
+ | "duplicate_task_id"
61
+ | "task_file_not_in_plan"
62
+ | "stale_replan_file"
63
+ | "future_timestamp"
64
+ // Runtime data integrity
65
+ | "metrics_ledger_corrupt"
66
+ | "large_planning_file"
67
+ // Slow environment checks (opt-in via --build / --test flags)
68
+ | "env_build"
69
+ | "env_test";
57
70
 
58
71
  /**
59
72
  * Issue codes that represent expected completion-transition states.
@@ -83,6 +96,8 @@ export interface DoctorReport {
83
96
  basePath: string;
84
97
  issues: DoctorIssue[];
85
98
  fixesApplied: string[];
99
+ /** Per-domain check durations in milliseconds. Present on explicit /gsd doctor runs. */
100
+ timing?: { git: number; runtime: number; environment: number; gsdState: number };
86
101
  }
87
102
 
88
103
  export interface DoctorSummary {