gsd-pi 2.51.0 → 2.52.0-dev.655ad8a

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 (419) hide show
  1. package/README.md +59 -36
  2. package/dist/headless-events.d.ts +18 -0
  3. package/dist/headless-events.js +36 -0
  4. package/dist/headless-query.js +1 -1
  5. package/dist/headless-types.d.ts +28 -0
  6. package/dist/headless-types.js +7 -0
  7. package/dist/headless.d.ts +8 -3
  8. package/dist/headless.js +47 -16
  9. package/dist/help-text.js +16 -5
  10. package/dist/onboarding.js +5 -4
  11. package/dist/remote-questions-config.js +1 -1
  12. package/dist/resources/extensions/async-jobs/async-bash-tool.js +29 -17
  13. package/dist/resources/extensions/async-jobs/job-manager.js +4 -1
  14. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +18 -19
  15. package/dist/resources/extensions/get-secrets-from-user.js +7 -0
  16. package/dist/resources/extensions/gsd/auto/phases.js +34 -8
  17. package/dist/resources/extensions/gsd/auto-dispatch.js +23 -1
  18. package/dist/resources/extensions/gsd/auto-start.js +2 -0
  19. package/dist/resources/extensions/gsd/auto-timers.js +24 -2
  20. package/dist/resources/extensions/gsd/auto-tool-tracking.js +25 -7
  21. package/dist/resources/extensions/gsd/auto-worktree.js +91 -14
  22. package/dist/resources/extensions/gsd/auto.js +30 -4
  23. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +99 -70
  24. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +12 -2
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +1 -1
  26. package/dist/resources/extensions/gsd/claude-import.js +60 -9
  27. package/dist/resources/extensions/gsd/commands/handlers/auto.js +69 -6
  28. package/dist/resources/extensions/gsd/commands-config.js +10 -5
  29. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +4 -4
  30. package/dist/resources/extensions/gsd/detection.js +6 -6
  31. package/dist/resources/extensions/gsd/docs/preferences-reference.md +5 -5
  32. package/dist/resources/extensions/gsd/error-classifier.js +105 -0
  33. package/dist/resources/extensions/gsd/git-service.js +4 -3
  34. package/dist/resources/extensions/gsd/gitignore.js +7 -7
  35. package/dist/resources/extensions/gsd/gsd-db.js +298 -45
  36. package/dist/resources/extensions/gsd/guided-flow.js +4 -3
  37. package/dist/resources/extensions/gsd/init-wizard.js +2 -2
  38. package/dist/resources/extensions/gsd/key-manager.js +7 -16
  39. package/dist/resources/extensions/gsd/markdown-renderer.js +5 -4
  40. package/dist/resources/extensions/gsd/memory-store.js +28 -13
  41. package/dist/resources/extensions/gsd/milestone-actions.js +19 -0
  42. package/dist/resources/extensions/gsd/parallel-orchestrator.js +18 -2
  43. package/dist/resources/extensions/gsd/preferences-models.js +1 -13
  44. package/dist/resources/extensions/gsd/preferences-types.js +1 -1
  45. package/dist/resources/extensions/gsd/preferences.js +13 -13
  46. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  47. package/dist/resources/extensions/gsd/provider-error-pause.js +0 -44
  48. package/dist/resources/extensions/gsd/rule-registry.js +1 -1
  49. package/dist/resources/extensions/gsd/service-tier.js +13 -2
  50. package/dist/resources/extensions/gsd/state.js +38 -30
  51. package/dist/resources/extensions/gsd/status-guards.js +12 -0
  52. package/dist/resources/extensions/gsd/tools/complete-milestone.js +7 -13
  53. package/dist/resources/extensions/gsd/tools/complete-slice.js +7 -20
  54. package/dist/resources/extensions/gsd/tools/complete-task.js +11 -21
  55. package/dist/resources/extensions/gsd/tools/plan-milestone.js +28 -29
  56. package/dist/resources/extensions/gsd/tools/plan-slice.js +27 -26
  57. package/dist/resources/extensions/gsd/tools/plan-task.js +23 -23
  58. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +50 -41
  59. package/dist/resources/extensions/gsd/tools/reopen-slice.js +4 -3
  60. package/dist/resources/extensions/gsd/tools/reopen-task.js +5 -4
  61. package/dist/resources/extensions/gsd/tools/replan-slice.js +51 -41
  62. package/dist/resources/extensions/gsd/tools/validate-milestone.js +23 -16
  63. package/dist/resources/extensions/gsd/validation.js +21 -0
  64. package/dist/resources/extensions/gsd/workflow-logger.js +0 -1
  65. package/dist/resources/extensions/remote-questions/config.js +1 -1
  66. package/dist/resources/extensions/remote-questions/remote-command.js +1 -1
  67. package/dist/resources/extensions/search-the-web/native-search.js +1 -1
  68. package/dist/resources/extensions/search-the-web/provider.js +1 -1
  69. package/dist/resources/extensions/shared/rtk.js +14 -4
  70. package/dist/rtk.js +3 -1
  71. package/dist/web/standalone/.next/BUILD_ID +1 -1
  72. package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
  73. package/dist/web/standalone/.next/build-manifest.json +4 -4
  74. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  75. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  76. package/dist/web/standalone/.next/required-server-files.json +3 -3
  77. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  78. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  79. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  80. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  88. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  90. package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -4
  91. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
  92. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
  94. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  97. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  104. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  116. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  144. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  150. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  164. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  166. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  168. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  170. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  179. package/dist/web/standalone/.next/server/app/index.html +1 -1
  180. package/dist/web/standalone/.next/server/app/index.rsc +5 -5
  181. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  182. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +5 -5
  183. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  184. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -4
  185. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  186. package/dist/web/standalone/.next/server/app/page.js +2 -2
  187. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  188. package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
  189. package/dist/web/standalone/.next/server/chunks/2229.js +3 -3
  190. package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
  191. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  192. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  193. package/dist/web/standalone/.next/server/middleware.js +2 -2
  194. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  195. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  196. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  197. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  198. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  199. package/dist/web/standalone/.next/static/chunks/4024.87fd909ae0110f50.js +9 -0
  200. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  201. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  202. package/dist/web/standalone/.next/static/chunks/app/page-b950e4e384cc62b3.js +1 -0
  203. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  204. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  205. package/dist/web/standalone/.next/static/chunks/{webpack-cfc9a116e6450a6b.js → webpack-bca0e732db0dcec3.js} +1 -1
  206. package/dist/web/standalone/.next/static/css/a58ef8a151aa0493.css +1 -0
  207. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  208. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  209. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  210. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  211. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  212. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  213. package/dist/web/standalone/server.js +1 -1
  214. package/dist/wizard.js +4 -1
  215. package/package.json +2 -2
  216. package/packages/mcp-server/README.md +202 -0
  217. package/packages/mcp-server/package.json +36 -0
  218. package/packages/mcp-server/src/cli.ts +68 -0
  219. package/packages/mcp-server/src/index.ts +14 -0
  220. package/packages/mcp-server/src/mcp-server.test.ts +628 -0
  221. package/packages/mcp-server/src/server.ts +278 -0
  222. package/packages/mcp-server/src/session-manager.ts +328 -0
  223. package/packages/mcp-server/src/types.ts +107 -0
  224. package/packages/mcp-server/tsconfig.json +24 -0
  225. package/packages/pi-ai/dist/models.d.ts +14 -3
  226. package/packages/pi-ai/dist/models.d.ts.map +1 -1
  227. package/packages/pi-ai/dist/models.js +53 -10
  228. package/packages/pi-ai/dist/models.js.map +1 -1
  229. package/packages/pi-ai/dist/models.test.js +102 -1
  230. package/packages/pi-ai/dist/models.test.js.map +1 -1
  231. package/packages/pi-ai/dist/types.d.ts +30 -0
  232. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  233. package/packages/pi-ai/dist/types.js.map +1 -1
  234. package/packages/pi-ai/src/models.test.ts +114 -1
  235. package/packages/pi-ai/src/models.ts +70 -13
  236. package/packages/pi-ai/src/types.ts +31 -0
  237. package/packages/pi-coding-agent/dist/cli/args.d.ts +2 -0
  238. package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
  239. package/packages/pi-coding-agent/dist/cli/args.js +3 -0
  240. package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
  241. package/packages/pi-coding-agent/dist/core/bash-executor.d.ts.map +1 -1
  242. package/packages/pi-coding-agent/dist/core/bash-executor.js +5 -1
  243. package/packages/pi-coding-agent/dist/core/bash-executor.js.map +1 -1
  244. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  245. package/packages/pi-coding-agent/dist/core/model-registry.js +9 -4
  246. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  247. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.d.ts +19 -0
  248. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.d.ts.map +1 -0
  249. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js +83 -0
  250. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js.map +1 -0
  251. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  252. package/packages/pi-coding-agent/dist/core/tools/bash.js +5 -1
  253. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  254. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  255. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  256. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  257. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  258. package/packages/pi-coding-agent/dist/main.js +5 -3
  259. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  260. package/packages/pi-coding-agent/dist/modes/index.d.ts +1 -1
  261. package/packages/pi-coding-agent/dist/modes/index.d.ts.map +1 -1
  262. package/packages/pi-coding-agent/dist/modes/index.js.map +1 -1
  263. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  264. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +0 -2
  265. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  266. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts +28 -1
  267. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  268. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +49 -0
  269. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
  270. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts +1 -1
  271. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  272. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +114 -6
  273. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  274. package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.d.ts +9 -0
  275. package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.d.ts.map +1 -0
  276. package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.js +831 -0
  277. package/packages/pi-coding-agent/dist/modes/rpc/rpc-protocol-v2.test.js.map +1 -0
  278. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +66 -0
  279. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  280. package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
  281. package/packages/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
  282. package/packages/pi-coding-agent/dist/utils/shell.js +0 -1
  283. package/packages/pi-coding-agent/dist/utils/shell.js.map +1 -1
  284. package/packages/pi-coding-agent/package.json +1 -1
  285. package/packages/pi-coding-agent/src/cli/args.ts +4 -0
  286. package/packages/pi-coding-agent/src/core/bash-executor.ts +5 -1
  287. package/packages/pi-coding-agent/src/core/model-registry.ts +10 -3
  288. package/packages/pi-coding-agent/src/core/tools/bash-spawn-windows.test.ts +101 -0
  289. package/packages/pi-coding-agent/src/core/tools/bash.ts +5 -1
  290. package/packages/pi-coding-agent/src/index.ts +3 -0
  291. package/packages/pi-coding-agent/src/main.ts +5 -3
  292. package/packages/pi-coding-agent/src/modes/index.ts +8 -1
  293. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +0 -2
  294. package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +54 -1
  295. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +124 -6
  296. package/packages/pi-coding-agent/src/modes/rpc/rpc-protocol-v2.test.ts +971 -0
  297. package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +61 -4
  298. package/packages/pi-coding-agent/src/utils/shell.ts +0 -1
  299. package/packages/rpc-client/package.json +20 -0
  300. package/pkg/package.json +1 -1
  301. package/scripts/ensure-workspace-builds.cjs +36 -8
  302. package/src/resources/extensions/async-jobs/async-bash-tool.ts +22 -11
  303. package/src/resources/extensions/async-jobs/job-manager.ts +4 -1
  304. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +19 -20
  305. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +21 -0
  306. package/src/resources/extensions/get-secrets-from-user.ts +8 -0
  307. package/src/resources/extensions/gsd/auto/phases.ts +44 -7
  308. package/src/resources/extensions/gsd/auto-dispatch.ts +25 -1
  309. package/src/resources/extensions/gsd/auto-start.ts +2 -0
  310. package/src/resources/extensions/gsd/auto-timers.ts +25 -1
  311. package/src/resources/extensions/gsd/auto-tool-tracking.ts +30 -6
  312. package/src/resources/extensions/gsd/auto-worktree.ts +94 -14
  313. package/src/resources/extensions/gsd/auto.ts +31 -4
  314. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +118 -73
  315. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +11 -2
  316. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +1 -1
  317. package/src/resources/extensions/gsd/claude-import.ts +58 -9
  318. package/src/resources/extensions/gsd/commands/handlers/auto.ts +73 -6
  319. package/src/resources/extensions/gsd/commands-config.ts +11 -5
  320. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +4 -4
  321. package/src/resources/extensions/gsd/detection.ts +6 -6
  322. package/src/resources/extensions/gsd/docs/preferences-reference.md +5 -5
  323. package/src/resources/extensions/gsd/error-classifier.ts +139 -0
  324. package/src/resources/extensions/gsd/git-service.ts +4 -3
  325. package/src/resources/extensions/gsd/gitignore.ts +7 -7
  326. package/src/resources/extensions/gsd/gsd-db.ts +355 -63
  327. package/src/resources/extensions/gsd/guided-flow.ts +4 -3
  328. package/src/resources/extensions/gsd/init-wizard.ts +2 -2
  329. package/src/resources/extensions/gsd/key-manager.ts +7 -16
  330. package/src/resources/extensions/gsd/markdown-renderer.ts +5 -4
  331. package/src/resources/extensions/gsd/memory-store.ts +29 -18
  332. package/src/resources/extensions/gsd/milestone-actions.ts +17 -0
  333. package/src/resources/extensions/gsd/parallel-orchestrator.ts +23 -1
  334. package/src/resources/extensions/gsd/preferences-models.ts +1 -13
  335. package/src/resources/extensions/gsd/preferences-types.ts +1 -1
  336. package/src/resources/extensions/gsd/preferences.ts +12 -13
  337. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  338. package/src/resources/extensions/gsd/provider-error-pause.ts +0 -57
  339. package/src/resources/extensions/gsd/rule-registry.ts +1 -1
  340. package/src/resources/extensions/gsd/service-tier.ts +14 -2
  341. package/src/resources/extensions/gsd/state.ts +39 -30
  342. package/src/resources/extensions/gsd/status-guards.ts +13 -0
  343. package/src/resources/extensions/gsd/tests/active-milestone-id-guard.test.ts +91 -0
  344. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +1 -1
  345. package/src/resources/extensions/gsd/tests/auto-milestone-target.test.ts +61 -0
  346. package/src/resources/extensions/gsd/tests/auto-stale-lock-self-kill.test.ts +87 -0
  347. package/src/resources/extensions/gsd/tests/auto-worktree-auto-resolve.test.ts +80 -0
  348. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +1 -1
  349. package/src/resources/extensions/gsd/tests/claude-import-marketplace-discovery.test.ts +191 -0
  350. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +1 -1
  351. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +39 -0
  352. package/src/resources/extensions/gsd/tests/commands-config.test.ts +24 -0
  353. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
  354. package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +106 -0
  355. package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
  356. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +35 -7
  357. package/src/resources/extensions/gsd/tests/detection.test.ts +1 -1
  358. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +4 -4
  359. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +1 -1
  360. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +2 -2
  361. package/src/resources/extensions/gsd/tests/empty-db-reconciliation.test.ts +79 -0
  362. package/src/resources/extensions/gsd/tests/git-service.test.ts +65 -31
  363. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
  364. package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +125 -0
  365. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +1 -1
  366. package/src/resources/extensions/gsd/tests/interactive-tool-idle-exemption.test.ts +119 -0
  367. package/src/resources/extensions/gsd/tests/key-manager.test.ts +16 -1
  368. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  369. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  370. package/src/resources/extensions/gsd/tests/milestone-report-path.test.ts +51 -0
  371. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +7 -7
  372. package/src/resources/extensions/gsd/tests/parallel-orchestrator-zombie-cleanup.test.ts +277 -0
  373. package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +85 -0
  374. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +103 -0
  375. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +91 -0
  376. package/src/resources/extensions/gsd/tests/preferences.test.ts +2 -2
  377. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +77 -70
  378. package/src/resources/extensions/gsd/tests/rate-limit-model-fallback.test.ts +90 -0
  379. package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +110 -0
  380. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +29 -0
  381. package/src/resources/extensions/gsd/tests/session-lock-transient-read.test.ts +9 -8
  382. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +125 -0
  383. package/src/resources/extensions/gsd/tests/status-guards.test.ts +30 -0
  384. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +42 -31
  385. package/src/resources/extensions/gsd/tests/token-cost-display.test.ts +2 -2
  386. package/src/resources/extensions/gsd/tests/vacuous-truth-slices.test.ts +115 -0
  387. package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +90 -0
  388. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +12 -2
  389. package/src/resources/extensions/gsd/tests/validation-gate-patterns.test.ts +124 -0
  390. package/src/resources/extensions/gsd/tests/validation.test.ts +72 -0
  391. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +81 -1
  392. package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +130 -0
  393. package/src/resources/extensions/gsd/tools/complete-milestone.ts +7 -17
  394. package/src/resources/extensions/gsd/tools/complete-slice.ts +7 -24
  395. package/src/resources/extensions/gsd/tools/complete-task.ts +13 -25
  396. package/src/resources/extensions/gsd/tools/plan-milestone.ts +30 -32
  397. package/src/resources/extensions/gsd/tools/plan-slice.ts +30 -30
  398. package/src/resources/extensions/gsd/tools/plan-task.ts +26 -26
  399. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +57 -46
  400. package/src/resources/extensions/gsd/tools/reopen-slice.ts +4 -3
  401. package/src/resources/extensions/gsd/tools/reopen-task.ts +5 -4
  402. package/src/resources/extensions/gsd/tools/replan-slice.ts +55 -44
  403. package/src/resources/extensions/gsd/tools/validate-milestone.ts +26 -20
  404. package/src/resources/extensions/gsd/validation.ts +23 -0
  405. package/src/resources/extensions/gsd/workflow-logger.ts +0 -1
  406. package/src/resources/extensions/remote-questions/config.ts +1 -1
  407. package/src/resources/extensions/remote-questions/remote-command.ts +1 -1
  408. package/src/resources/extensions/search-the-web/native-search.ts +1 -1
  409. package/src/resources/extensions/search-the-web/provider.ts +1 -1
  410. package/src/resources/extensions/shared/rtk.ts +22 -4
  411. package/dist/web/standalone/.next/static/chunks/4024.9ad5def014d90ce4.js +0 -9
  412. package/dist/web/standalone/.next/static/chunks/app/page-fbecd1237e2d6d1f.js +0 -1
  413. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  414. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  415. package/dist/web/standalone/.next/static/css/de141508b083f922.css +0 -1
  416. /package/dist/resources/extensions/gsd/templates/{preferences.md → PREFERENCES.md} +0 -0
  417. /package/dist/web/standalone/.next/static/{vkr67v-utm1dgZnbrBWQh → zpvUPKoW5jRAMB_fWHlPi}/_buildManifest.js +0 -0
  418. /package/dist/web/standalone/.next/static/{vkr67v-utm1dgZnbrBWQh → zpvUPKoW5jRAMB_fWHlPi}/_ssgManifest.js +0 -0
  419. /package/src/resources/extensions/gsd/templates/{preferences.md → PREFERENCES.md} +0 -0
@@ -103,16 +103,47 @@ function isMarketplacePath(pluginPath: string): boolean {
103
103
 
104
104
  /**
105
105
  * Detect which plugin roots are marketplaces and which are legacy flat paths.
106
+ *
107
+ * Claude Code stores marketplace sources under ~/.claude/plugins/marketplaces/.
108
+ * Each subdirectory (e.g. marketplaces/confluent/) is a marketplace repo that
109
+ * contains .claude-plugin/marketplace.json. The parent directory itself does not
110
+ * have a marketplace.json, so we scan one level deeper when the root isn't
111
+ * directly a marketplace.
106
112
  */
107
- function categorizePluginRoots(pluginRoots: string[]): { marketplaces: string[]; flat: string[] } {
113
+ export function categorizePluginRoots(pluginRoots: string[]): { marketplaces: string[]; flat: string[] } {
108
114
  const marketplaces: string[] = [];
109
115
  const flat: string[] = [];
116
+ const seen = new Set<string>();
110
117
 
111
118
  for (const root of pluginRoots) {
112
119
  if (isMarketplacePath(root)) {
113
- marketplaces.push(root);
120
+ if (!seen.has(root)) {
121
+ marketplaces.push(root);
122
+ seen.add(root);
123
+ }
114
124
  } else {
115
- flat.push(root);
125
+ // The root itself isn't a marketplace — check if it's a container of
126
+ // marketplaces (e.g. ~/.claude/plugins/marketplaces/ contains subdirs
127
+ // like confluent/, claude-hud/, each with their own marketplace.json).
128
+ let foundChild = false;
129
+ try {
130
+ const entries = readdirSync(root, { withFileTypes: true });
131
+ for (const entry of entries) {
132
+ if (!entry.isDirectory()) continue;
133
+ if (SKIP_DIRS.has(entry.name)) continue;
134
+ const childPath = join(root, entry.name);
135
+ if (isMarketplacePath(childPath) && !seen.has(childPath)) {
136
+ marketplaces.push(childPath);
137
+ seen.add(childPath);
138
+ foundChild = true;
139
+ }
140
+ }
141
+ } catch {
142
+ // Can't read directory — fall through to flat
143
+ }
144
+ if (!foundChild) {
145
+ flat.push(root);
146
+ }
116
147
  }
117
148
  }
118
149
 
@@ -170,18 +201,36 @@ export function discoverClaudePlugins(cwd: string): ClaudePluginCandidate[] {
170
201
 
171
202
  for (const root of pluginRoots) {
172
203
  walkDirs(root, (dir) => {
204
+ // Recognize both npm-style plugins (package.json) and Claude Code plugins
205
+ // (.claude-plugin/plugin.json). Claude marketplace-installed plugins use
206
+ // the latter format exclusively.
173
207
  const pkgPath = join(dir, "package.json");
174
- if (!existsSync(pkgPath)) return;
208
+ const claudePluginPath = join(dir, ".claude-plugin", "plugin.json");
209
+ const hasPkg = existsSync(pkgPath);
210
+ const hasClaudePlugin = existsSync(claudePluginPath);
211
+ if (!hasPkg && !hasClaudePlugin) return;
212
+
175
213
  const resolvedDir = resolve(dir);
176
214
  if (seen.has(resolvedDir)) return;
177
215
  seen.add(resolvedDir);
216
+
178
217
  let packageName: string | undefined;
179
- try {
180
- const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as { name?: string };
181
- packageName = pkg.name;
182
- } catch {
183
- packageName = undefined;
218
+ if (hasPkg) {
219
+ try {
220
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as { name?: string };
221
+ packageName = pkg.name;
222
+ } catch {
223
+ packageName = undefined;
224
+ }
225
+ } else if (hasClaudePlugin) {
226
+ try {
227
+ const manifest = JSON.parse(readFileSync(claudePluginPath, "utf8")) as { name?: string };
228
+ packageName = manifest.name;
229
+ } catch {
230
+ packageName = undefined;
231
+ }
184
232
  }
233
+
185
234
  results.push({
186
235
  type: "plugin",
187
236
  name: packageName || basename(dir),
@@ -7,6 +7,7 @@ import { enableDebug } from "../../debug-logger.js";
7
7
  import { getAutoDashboardData, isAutoActive, isAutoPaused, pauseAuto, startAuto, stopAuto, stopAutoRemote } from "../../auto.js";
8
8
  import { handleRate } from "../../commands-rate.js";
9
9
  import { guardRemoteSession, projectRoot } from "../context.js";
10
+ import { findMilestoneIds } from "../../milestone-id-utils.js";
10
11
 
11
12
  /**
12
13
  * Parse --yolo flag and optional file path from the auto command string.
@@ -28,6 +29,39 @@ function parseYoloFlag(trimmed: string): { yoloSeedFile: string | null; rest: st
28
29
  return { yoloSeedFile: filePath, rest };
29
30
  }
30
31
 
32
+ /**
33
+ * Extract a milestone ID (e.g. M016 or M001-a3b4c5) from the command string.
34
+ * Returns the matched ID and the remaining string with the ID removed.
35
+ * The milestone ID pattern matches the format used by findMilestoneIds: M\d+ with
36
+ * an optional -[a-z0-9]{6} suffix for unique milestone IDs.
37
+ */
38
+ export function parseMilestoneTarget(input: string): { milestoneId: string | null; rest: string } {
39
+ const match = input.match(/\b(M\d+(?:-[a-z0-9]{6})?)\b/);
40
+ if (!match) return { milestoneId: null, rest: input };
41
+ const rest = input.replace(match[0], "").replace(/\s+/g, " ").trim();
42
+ return { milestoneId: match[1], rest };
43
+ }
44
+
45
+ /**
46
+ * Set GSD_MILESTONE_LOCK to target a specific milestone, then run `fn`.
47
+ * Clears the env var when `fn` resolves or rejects, so the lock does not
48
+ * leak into subsequent commands in the same process.
49
+ */
50
+ async function withMilestoneLock(milestoneId: string, fn: () => Promise<void>): Promise<void> {
51
+ const previous = process.env.GSD_MILESTONE_LOCK;
52
+ process.env.GSD_MILESTONE_LOCK = milestoneId;
53
+ try {
54
+ await fn();
55
+ } finally {
56
+ // Restore previous value (undefined → delete, else restore).
57
+ if (previous === undefined) {
58
+ delete process.env.GSD_MILESTONE_LOCK;
59
+ } else {
60
+ process.env.GSD_MILESTONE_LOCK = previous;
61
+ }
62
+ }
63
+ }
64
+
31
65
  export async function handleAutoCommand(trimmed: string, ctx: ExtensionCommandContext, pi: ExtensionAPI): Promise<boolean> {
32
66
  if (trimmed === "next" || trimmed.startsWith("next ")) {
33
67
  if (trimmed.includes("--dry-run")) {
@@ -35,21 +69,48 @@ export async function handleAutoCommand(trimmed: string, ctx: ExtensionCommandCo
35
69
  await handleDryRun(ctx, projectRoot());
36
70
  return true;
37
71
  }
38
- const verboseMode = trimmed.includes("--verbose");
39
- const debugMode = trimmed.includes("--debug");
72
+ const { milestoneId, rest: afterMilestone } = parseMilestoneTarget(trimmed);
73
+ const verboseMode = afterMilestone.includes("--verbose");
74
+ const debugMode = afterMilestone.includes("--debug");
40
75
  if (debugMode) enableDebug(projectRoot());
41
76
  if (!(await guardRemoteSession(ctx, pi))) return true;
42
- await startAuto(ctx, pi, projectRoot(), verboseMode, { step: true });
77
+
78
+ // Validate the milestone target exists and is not already complete.
79
+ if (milestoneId) {
80
+ const allIds = findMilestoneIds(projectRoot());
81
+ if (!allIds.includes(milestoneId)) {
82
+ ctx.ui.notify(`Milestone ${milestoneId} does not exist. Available: ${allIds.join(", ") || "(none)"}`, "error");
83
+ return true;
84
+ }
85
+ }
86
+
87
+ if (milestoneId) {
88
+ await withMilestoneLock(milestoneId, () =>
89
+ startAuto(ctx, pi, projectRoot(), verboseMode, { step: true }),
90
+ );
91
+ } else {
92
+ await startAuto(ctx, pi, projectRoot(), verboseMode, { step: true });
93
+ }
43
94
  return true;
44
95
  }
45
96
 
46
97
  if (trimmed === "auto" || trimmed.startsWith("auto ")) {
47
- const { yoloSeedFile, rest } = parseYoloFlag(trimmed);
48
- const verboseMode = rest.includes("--verbose");
49
- const debugMode = rest.includes("--debug");
98
+ const { yoloSeedFile, rest: afterYolo } = parseYoloFlag(trimmed);
99
+ const { milestoneId, rest: afterMilestone } = parseMilestoneTarget(afterYolo);
100
+ const verboseMode = afterMilestone.includes("--verbose");
101
+ const debugMode = afterMilestone.includes("--debug");
50
102
  if (debugMode) enableDebug(projectRoot());
51
103
  if (!(await guardRemoteSession(ctx, pi))) return true;
52
104
 
105
+ // Validate the milestone target exists and is not already complete.
106
+ if (milestoneId) {
107
+ const allIds = findMilestoneIds(projectRoot());
108
+ if (!allIds.includes(milestoneId)) {
109
+ ctx.ui.notify(`Milestone ${milestoneId} does not exist. Available: ${allIds.join(", ") || "(none)"}`, "error");
110
+ return true;
111
+ }
112
+ }
113
+
53
114
  if (yoloSeedFile) {
54
115
  const resolved = resolve(projectRoot(), yoloSeedFile);
55
116
  if (!existsSync(resolved)) {
@@ -66,6 +127,12 @@ export async function handleAutoCommand(trimmed: string, ctx: ExtensionCommandCo
66
127
  // when the LLM says "Milestone X ready."
67
128
  const { showHeadlessMilestoneCreation } = await import("../../guided-flow.js");
68
129
  await showHeadlessMilestoneCreation(ctx, pi, projectRoot(), seedContent);
130
+ } else if (milestoneId) {
131
+ // Target a specific milestone — use GSD_MILESTONE_LOCK so state
132
+ // derivation only sees this milestone (#2521).
133
+ await withMilestoneLock(milestoneId, () =>
134
+ startAuto(ctx, pi, projectRoot(), verboseMode),
135
+ );
69
136
  } else {
70
137
  await startAuto(ctx, pi, projectRoot(), verboseMode);
71
138
  }
@@ -22,6 +22,12 @@ export const TOOL_KEYS = [
22
22
  { id: "groq", env: "GROQ_API_KEY", label: "Groq Voice", hint: "console.groq.com" },
23
23
  ] as const;
24
24
 
25
+ function getStoredToolKey(auth: AuthStorage, providerId: string): string | undefined {
26
+ const creds = auth.getCredentialsForProvider(providerId);
27
+ const cred = creds.find((c) => c.type === "api_key" && c.key);
28
+ return cred?.type === "api_key" ? cred.key : undefined;
29
+ }
30
+
25
31
  /**
26
32
  * Load tool API keys from auth.json into environment variables.
27
33
  * Called at session startup to ensure tools have access to their credentials.
@@ -33,9 +39,9 @@ export function loadToolApiKeys(): void {
33
39
 
34
40
  const auth = AuthStorage.create(authPath);
35
41
  for (const tool of TOOL_KEYS) {
36
- const cred = auth.get(tool.id);
37
- if (cred && cred.type === "api_key" && cred.key && !process.env[tool.env]) {
38
- process.env[tool.env] = cred.key;
42
+ const key = getStoredToolKey(auth, tool.id);
43
+ if (key && !process.env[tool.env]) {
44
+ process.env[tool.env] = key;
39
45
  }
40
46
  }
41
47
  } catch {
@@ -55,14 +61,14 @@ export async function handleConfig(ctx: ExtensionCommandContext): Promise<void>
55
61
  // Show current status
56
62
  const statusLines = ["GSD Tool Configuration\n"];
57
63
  for (const tool of TOOL_KEYS) {
58
- const hasKey = !!process.env[tool.env] || !!(auth.get(tool.id) as { key?: string })?.key;
64
+ const hasKey = !!process.env[tool.env] || !!getStoredToolKey(auth, tool.id);
59
65
  statusLines.push(` ${hasKey ? "\u2713" : "\u2717"} ${tool.label}${hasKey ? "" : ` \u2014 get key at ${tool.hint}`}`);
60
66
  }
61
67
  ctx.ui.notify(statusLines.join("\n"), "info");
62
68
 
63
69
  // Ask which tools to configure
64
70
  const options = TOOL_KEYS.map(t => {
65
- const hasKey = !!process.env[t.env] || !!(auth.get(t.id) as { key?: string })?.key;
71
+ const hasKey = !!process.env[t.env] || !!getStoredToolKey(auth, t.id);
66
72
  return `${t.label} ${hasKey ? "(configured \u2713)" : "(not set)"}`;
67
73
  });
68
74
  options.push("(done)");
@@ -390,7 +390,7 @@ async function configureGit(ctx: ExtensionCommandContext, prefs: Record<string,
390
390
  const gitBooleanFields = [
391
391
  { key: "auto_push", label: "Auto-push commits after committing", defaultVal: false },
392
392
  { key: "push_branches", label: "Push milestone branches to remote", defaultVal: false },
393
- { key: "snapshots", label: "Create WIP snapshot commits during long tasks", defaultVal: false },
393
+ { key: "snapshots", label: "Create WIP snapshot commits during long tasks", defaultVal: true },
394
394
  ] as const;
395
395
 
396
396
  for (const field of gitBooleanFields) {
@@ -423,7 +423,7 @@ async function configureGit(ctx: ExtensionCommandContext, prefs: Record<string,
423
423
  // pre_merge_check
424
424
  const currentPreMerge = git.pre_merge_check !== undefined ? String(git.pre_merge_check) : "";
425
425
  const preMergeChoice = await ctx.ui.select(
426
- `Pre-merge check${currentPreMerge ? ` (current: ${currentPreMerge})` : " (default: false)"}:`,
426
+ `Pre-merge check${currentPreMerge ? ` (current: ${currentPreMerge})` : " (default: auto)"}:`,
427
427
  ["true", "false", "auto", "(keep current)"],
428
428
  );
429
429
  if (preMergeChoice && preMergeChoice !== "(keep current)") {
@@ -588,7 +588,7 @@ export async function configureMode(ctx: ExtensionCommandContext, prefs: Record<
588
588
  if (modeStr.startsWith("solo")) {
589
589
  prefs.mode = "solo";
590
590
  ctx.ui.notify(
591
- "Mode: solo — defaults: auto_push=true, push_branches=false, pre_merge_check=false, merge_strategy=squash, isolation=worktree, unique_milestone_ids=false",
591
+ "Mode: solo — defaults: auto_push=true, push_branches=false, pre_merge_check=auto, merge_strategy=squash, isolation=worktree, unique_milestone_ids=false",
592
592
  "info",
593
593
  );
594
594
  } else if (modeStr.startsWith("team")) {
@@ -771,7 +771,7 @@ export async function ensurePreferencesFile(
771
771
  scope: "global" | "project",
772
772
  ): Promise<void> {
773
773
  if (!existsSync(path)) {
774
- const template = await loadFile(join(dirname(fileURLToPath(import.meta.url)), "templates", "preferences.md"));
774
+ const template = await loadFile(join(dirname(fileURLToPath(import.meta.url)), "templates", "PREFERENCES.md"));
775
775
  if (!template) {
776
776
  ctx.ui.notify("Could not load GSD preferences template.", "error");
777
777
  return;
@@ -359,8 +359,8 @@ function detectV2Gsd(basePath: string): V2Detection | null {
359
359
  if (!existsSync(gsdPath)) return null;
360
360
 
361
361
  const hasPreferences =
362
- existsSync(join(gsdPath, "preferences.md")) ||
363
- existsSync(join(gsdPath, "PREFERENCES.md"));
362
+ existsSync(join(gsdPath, "PREFERENCES.md")) ||
363
+ existsSync(join(gsdPath, "preferences.md"));
364
364
 
365
365
  const hasContext = existsSync(join(gsdPath, "CONTEXT.md"));
366
366
 
@@ -714,8 +714,8 @@ function detectVerificationCommands(
714
714
  */
715
715
  export function hasGlobalSetup(): boolean {
716
716
  return (
717
- existsSync(join(gsdHome, "preferences.md")) ||
718
- existsSync(join(gsdHome, "PREFERENCES.md"))
717
+ existsSync(join(gsdHome, "PREFERENCES.md")) ||
718
+ existsSync(join(gsdHome, "preferences.md"))
719
719
  );
720
720
  }
721
721
 
@@ -728,8 +728,8 @@ export function isFirstEverLaunch(): boolean {
728
728
 
729
729
  // If we have preferences, not first launch
730
730
  if (
731
- existsSync(join(gsdHome, "preferences.md")) ||
732
- existsSync(join(gsdHome, "PREFERENCES.md"))
731
+ existsSync(join(gsdHome, "PREFERENCES.md")) ||
732
+ existsSync(join(gsdHome, "preferences.md"))
733
733
  ) {
734
734
  return false;
735
735
  }
@@ -1,6 +1,6 @@
1
1
  # GSD Preferences Reference
2
2
 
3
- Full documentation for `~/.gsd/preferences.md` (global) and `.gsd/preferences.md` (project).
3
+ Full documentation for `~/.gsd/PREFERENCES.md` (global) and `.gsd/PREFERENCES.md` (project).
4
4
 
5
5
  ---
6
6
 
@@ -51,8 +51,8 @@ skill_rules: []
51
51
 
52
52
  Preferences are loaded from two locations and merged:
53
53
 
54
- 1. **Global:** `~/.gsd/preferences.md` — applies to all projects
55
- 2. **Project:** `.gsd/preferences.md` — applies to the current project only
54
+ 1. **Global:** `~/.gsd/PREFERENCES.md` — applies to all projects
55
+ 2. **Project:** `.gsd/PREFERENCES.md` — applies to the current project only
56
56
 
57
57
  **Merge behavior** (see `mergePreferences()` in `preferences.ts`):
58
58
 
@@ -126,8 +126,8 @@ Setting `prefer_skills: []` does **not** disable skill discovery — it just mea
126
126
  - `auto_push`: boolean — automatically push commits to the remote after committing. Default: `false`.
127
127
  - `push_branches`: boolean — push the milestone branch to the remote after commits. Default: `false`.
128
128
  - `remote`: string — git remote name to push to. Default: `"origin"`.
129
- - `snapshots`: boolean — create snapshot commits (WIP saves) during long-running tasks. Default: `false`.
130
- - `pre_merge_check`: boolean or `"auto"` — run pre-merge checks before merging a worktree back to the integration branch. `true` always runs, `false` never runs, `"auto"` runs when CI is detected. Default: `false`.
129
+ - `snapshots`: boolean — create snapshot commits (WIP saves) during long-running tasks. Default: `true`.
130
+ - `pre_merge_check`: boolean or `"auto"` — run pre-merge checks before merging a worktree back to the integration branch. `true` always runs, `false` never runs, `"auto"` runs when CI is detected. Default: `"auto"`.
131
131
  - `commit_type`: string — override the conventional commit type prefix. Must be one of: `feat`, `fix`, `refactor`, `docs`, `test`, `chore`, `perf`, `ci`, `build`, `style`. Default: inferred from diff content.
132
132
  - `main_branch`: string — the primary branch name for new git repos (e.g., `"main"`, `"master"`, `"trunk"`). Also used by `getMainBranch()` as the preferred branch when auto-detection is ambiguous. Default: `"main"`.
133
133
  - `merge_strategy`: `"squash"` or `"merge"` — controls how worktree branches are merged back. `"squash"` combines all commits into one; `"merge"` preserves individual commits. Default: `"squash"`.
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Unified error classifier for provider/network/server errors.
3
+ *
4
+ * Consolidates patterns from:
5
+ * - isTransientNetworkError() in preferences-models.ts
6
+ * - classifyProviderError() in provider-error-pause.ts
7
+ *
8
+ * Single entry point: classifyError(errorMsg, retryAfterMs?)
9
+ *
10
+ * @see https://github.com/gsd-build/gsd/issues/2577
11
+ */
12
+
13
+ // ── ErrorClass discriminated union ──────────────────────────────────────────
14
+
15
+ export type ErrorClass =
16
+ | { kind: "network"; retryAfterMs: number }
17
+ | { kind: "rate-limit"; retryAfterMs: number }
18
+ | { kind: "server"; retryAfterMs: number }
19
+ | { kind: "stream"; retryAfterMs: number }
20
+ | { kind: "connection"; retryAfterMs: number }
21
+ | { kind: "model-error" }
22
+ | { kind: "permanent" }
23
+ | { kind: "unknown" };
24
+
25
+ // ── RetryState ──────────────────────────────────────────────────────────────
26
+
27
+ export interface RetryState {
28
+ networkRetryCount: number;
29
+ consecutiveTransientCount: number;
30
+ currentRetryModelId: string | undefined;
31
+ }
32
+
33
+ export function createRetryState(): RetryState {
34
+ return { networkRetryCount: 0, consecutiveTransientCount: 0, currentRetryModelId: undefined };
35
+ }
36
+
37
+ export function resetRetryState(state: RetryState): void {
38
+ state.networkRetryCount = 0;
39
+ state.consecutiveTransientCount = 0;
40
+ state.currentRetryModelId = undefined;
41
+ }
42
+
43
+ // ── Classification ──────────────────────────────────────────────────────────
44
+
45
+ const PERMANENT_RE = /auth|unauthorized|forbidden|invalid.*key|invalid.*api|billing|quota exceeded|account/i;
46
+ const RATE_LIMIT_RE = /rate.?limit|too many requests|429/i;
47
+ const NETWORK_RE = /network|ECONNRESET|ETIMEDOUT|ECONNREFUSED|socket hang up|fetch failed|connection.*reset|dns/i;
48
+ const SERVER_RE = /internal server error|500|502|503|overloaded|server_error|api_error|service.?unavailable/i;
49
+ // ECONNRESET/ECONNREFUSED are in NETWORK_RE (same-model retry first).
50
+ const CONNECTION_RE = /terminated|connection.?refused|other side closed|EPIPE|network.?(?:is\s+)?unavailable|stream_exhausted(?:_without_result)?/i;
51
+ const STREAM_RE = /Unexpected end of JSON|Unexpected token.*JSON|Expected double-quoted property name|SyntaxError.*JSON/i;
52
+ const RESET_DELAY_RE = /reset in (\d+)s/i;
53
+
54
+ /**
55
+ * Classify an error message into one of the ErrorClass kinds.
56
+ *
57
+ * Classification order:
58
+ * 1. Permanent (auth/billing/quota) — unless also rate-limited
59
+ * 2. Rate limit (429, rate.?limit, too many requests)
60
+ * 3. Network (ECONNRESET, ETIMEDOUT, socket hang up, fetch failed, dns)
61
+ * 4. Server (500/502/503, overloaded, server_error)
62
+ * 5. Connection (terminated, ECONNREFUSED, EPIPE, other side closed)
63
+ * 6. Stream truncation (malformed JSON from mid-stream cut)
64
+ * 7. Unknown
65
+ */
66
+ export function classifyError(errorMsg: string, retryAfterMs?: number): ErrorClass {
67
+ const isPermanent = PERMANENT_RE.test(errorMsg);
68
+ const isRateLimit = RATE_LIMIT_RE.test(errorMsg);
69
+
70
+ // 1. Permanent — but rate limit takes precedence
71
+ if (isPermanent && !isRateLimit) {
72
+ return { kind: "permanent" };
73
+ }
74
+
75
+ // 2. Rate limit
76
+ if (isRateLimit) {
77
+ if (retryAfterMs != null && retryAfterMs > 0) {
78
+ return { kind: "rate-limit", retryAfterMs };
79
+ }
80
+ const resetMatch = errorMsg.match(RESET_DELAY_RE);
81
+ const delayMs = resetMatch ? Number(resetMatch[1]) * 1000 : 60_000;
82
+ return { kind: "rate-limit", retryAfterMs: delayMs };
83
+ }
84
+
85
+ // 3. Network errors — same-model retry candidate
86
+ if (NETWORK_RE.test(errorMsg)) {
87
+ // Exclude if also matches permanent signals (already handled above for
88
+ // rate-limit, but double-check for non-rate-limit permanent overlap like
89
+ // "billing" appearing alongside "network").
90
+ return { kind: "network", retryAfterMs: retryAfterMs ?? 3_000 };
91
+ }
92
+
93
+ // 4. Server errors — try fallback model
94
+ if (SERVER_RE.test(errorMsg)) {
95
+ return { kind: "server", retryAfterMs: retryAfterMs ?? 30_000 };
96
+ }
97
+
98
+ // 5. Connection errors — try fallback model
99
+ if (CONNECTION_RE.test(errorMsg)) {
100
+ return { kind: "connection", retryAfterMs: retryAfterMs ?? 15_000 };
101
+ }
102
+
103
+ // 6. Stream truncation — downstream symptom of connection drop
104
+ if (STREAM_RE.test(errorMsg)) {
105
+ return { kind: "stream", retryAfterMs: retryAfterMs ?? 15_000 };
106
+ }
107
+
108
+ // 7. Unknown
109
+ return { kind: "unknown" };
110
+ }
111
+
112
+ // ── Helpers ─────────────────────────────────────────────────────────────────
113
+
114
+ /** Returns true for all transient (auto-resumable) error kinds. */
115
+ export function isTransient(cls: ErrorClass): boolean {
116
+ switch (cls.kind) {
117
+ case "network":
118
+ case "rate-limit":
119
+ case "server":
120
+ case "stream":
121
+ case "connection":
122
+ return true;
123
+ default:
124
+ return false;
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Backward-compatible thin wrapper.
130
+ *
131
+ * Returns true when the error is a transient *network* error specifically
132
+ * (worth retrying the same model). Permanent signals (auth, billing, quota)
133
+ * cause this to return false even if a network keyword is present.
134
+ */
135
+ export function isTransientNetworkError(errorMsg: string): boolean {
136
+ if (!errorMsg) return false;
137
+ const cls = classifyError(errorMsg);
138
+ return cls.kind === "network";
139
+ }
@@ -605,11 +605,12 @@ export class GitServiceImpl {
605
605
 
606
606
  /**
607
607
  * Create a snapshot ref for the given label (typically a slice branch name).
608
- * Gated on prefs.snapshots === true. Ref path: refs/gsd/snapshots/<label>/<timestamp>
608
+ * Enabled by default; opt out with prefs.snapshots === false.
609
+ * Ref path: refs/gsd/snapshots/<label>/<timestamp>
609
610
  * The ref points at HEAD, capturing the current commit before destructive operations.
610
611
  */
611
612
  createSnapshot(label: string): void {
612
- if (this.prefs.snapshots !== true) return;
613
+ if (this.prefs.snapshots === false) return;
613
614
 
614
615
  const now = new Date();
615
616
  const ts = now.getFullYear().toString()
@@ -631,7 +632,7 @@ export class GitServiceImpl {
631
632
  * Stub: to be implemented in T03.
632
633
  */
633
634
  runPreMergeCheck(): PreMergeCheckResult {
634
- if (this.prefs.pre_merge_check === false || this.prefs.pre_merge_check === undefined) {
635
+ if (this.prefs.pre_merge_check === false) {
635
636
  return { passed: true, skipped: true };
636
637
  }
637
638
 
@@ -1,8 +1,8 @@
1
1
  /**
2
- * GSD bootstrappers for .gitignore and preferences.md
2
+ * GSD bootstrappers for .gitignore and PREFERENCES.md
3
3
  *
4
4
  * Ensures baseline .gitignore exists with universally-correct patterns.
5
- * Creates an empty preferences.md template if it doesn't exist.
5
+ * Creates an empty PREFERENCES.md template if it doesn't exist.
6
6
  * Both idempotent — non-destructive if already present.
7
7
  */
8
8
 
@@ -216,16 +216,16 @@ export function untrackRuntimeFiles(basePath: string): void {
216
216
  }
217
217
 
218
218
  /**
219
- * Ensure basePath/.gsd/preferences.md exists as an empty template.
219
+ * Ensure basePath/.gsd/PREFERENCES.md exists as an empty template.
220
220
  * Creates the file with frontmatter only if it doesn't exist.
221
221
  * Returns true if created, false if already exists.
222
222
  *
223
- * Checks both lowercase (canonical) and uppercase (legacy) to avoid
224
- * creating a duplicate when an uppercase file already exists.
223
+ * Checks both uppercase (canonical) and lowercase (legacy) to avoid
224
+ * creating a duplicate when a lowercase file already exists.
225
225
  */
226
226
  export function ensurePreferences(basePath: string): boolean {
227
- const preferencesPath = join(gsdRoot(basePath), "preferences.md");
228
- const legacyPath = join(gsdRoot(basePath), "PREFERENCES.md");
227
+ const preferencesPath = join(gsdRoot(basePath), "PREFERENCES.md");
228
+ const legacyPath = join(gsdRoot(basePath), "preferences.md");
229
229
 
230
230
  if (existsSync(preferencesPath) || existsSync(legacyPath)) {
231
231
  return false;