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
@@ -150,22 +150,13 @@ export interface KeyStatus {
150
150
  */
151
151
  export function getAllKeyStatuses(auth: AuthStorage): KeyStatus[] {
152
152
  return PROVIDER_REGISTRY.map((provider) => {
153
- const creds = auth.getCredentialsForProvider(provider.id);
153
+ const rawCreds = auth.getCredentialsForProvider(provider.id);
154
+ // Filter out empty-key entries (left by legacy removeProviderToken or skipped onboarding)
155
+ const creds = rawCreds.filter((c) => !(c.type === "api_key" && !(c as ApiKeyCredential).key));
154
156
  const envKey = provider.envVar ? process.env[provider.envVar] : undefined;
155
157
 
156
158
  if (creds.length > 0) {
157
159
  const firstCred = creds[0];
158
- // Skip empty keys (from skipped onboarding)
159
- if (firstCred.type === "api_key" && !(firstCred as ApiKeyCredential).key) {
160
- return {
161
- provider,
162
- configured: false,
163
- source: "none" as const,
164
- credentialCount: 0,
165
- description: "empty key (skipped setup)",
166
- backedOff: false,
167
- };
168
- }
169
160
  const desc =
170
161
  creds.length > 1
171
162
  ? `${creds.length} keys (round-robin)`
@@ -275,7 +266,7 @@ export async function handleAddKey(
275
266
  } else {
276
267
  // Interactive provider picker
277
268
  const options = PROVIDER_REGISTRY.map((p) => {
278
- const creds = auth.getCredentialsForProvider(p.id);
269
+ const creds = auth.getCredentialsForProvider(p.id).filter((c) => !(c.type === "api_key" && !(c as ApiKeyCredential).key));
279
270
  const existing = creds.length > 0 ? " (configured)" : "";
280
271
  return `[${p.category}] ${p.label}${existing}`;
281
272
  });
@@ -360,7 +351,7 @@ export async function handleRemoveKey(
360
351
  } else {
361
352
  // Show only configured providers
362
353
  const configured = PROVIDER_REGISTRY.filter((p) => {
363
- const creds = auth.getCredentialsForProvider(p.id);
354
+ const creds = auth.getCredentialsForProvider(p.id).filter((c) => !(c.type === "api_key" && !(c as ApiKeyCredential).key));
364
355
  return creds.length > 0;
365
356
  });
366
357
 
@@ -619,7 +610,7 @@ export async function handleRotateKey(
619
610
  // Show only configured API key providers
620
611
  const configured = PROVIDER_REGISTRY.filter((p) => {
621
612
  const creds = auth.getCredentialsForProvider(p.id);
622
- return creds.some((c) => c.type === "api_key");
613
+ return creds.some((c) => c.type === "api_key" && (c as ApiKeyCredential).key);
623
614
  });
624
615
 
625
616
  if (configured.length === 0) {
@@ -788,7 +779,7 @@ export function runKeyDoctor(auth: AuthStorage): DoctorFinding[] {
788
779
  if (!envValue) continue;
789
780
 
790
781
  const creds = auth.getCredentialsForProvider(provider.id);
791
- const apiKey = creds.find((c) => c.type === "api_key") as ApiKeyCredential | undefined;
782
+ const apiKey = creds.find((c) => c.type === "api_key" && (c as ApiKeyCredential).key) as ApiKeyCredential | undefined;
792
783
  if (apiKey?.key && apiKey.key !== envValue) {
793
784
  findings.push({
794
785
  severity: "warning",
@@ -9,6 +9,7 @@
9
9
  // parseRoadmap(), parsePlan(), parseSummary() in files.ts.
10
10
 
11
11
  import { readFileSync, existsSync, mkdirSync } from "node:fs";
12
+ import { isClosedStatus } from "./status-guards.js";
12
13
  import { join, relative } from "node:path";
13
14
  import { createRequire } from "node:module";
14
15
  import {
@@ -337,7 +338,7 @@ function renderSlicePlanMarkdown(slice: SliceRow, tasks: TaskRow[], gates: GateR
337
338
  lines.push("## Tasks");
338
339
  lines.push("");
339
340
  for (const task of tasks) {
340
- const done = task.status === "done" || task.status === "complete" ? "x" : " ";
341
+ const done = isClosedStatus(task.status) ? "x" : " ";
341
342
  const estimate = task.estimate.trim() ? ` \`est:${task.estimate.trim()}\`` : "";
342
343
  lines.push(`- [${done}] **${task.id}: ${task.title || task.id}**${estimate}`);
343
344
  if (task.description.trim()) {
@@ -573,7 +574,7 @@ export async function renderPlanCheckboxes(
573
574
  // Apply checkbox patches for each task
574
575
  let updated = content;
575
576
  for (const task of tasks) {
576
- const isDone = task.status === "done" || task.status === "complete";
577
+ const isDone = isClosedStatus(task.status);
577
578
  const tid = task.id;
578
579
 
579
580
  if (isDone) {
@@ -857,7 +858,7 @@ export function detectStaleRenders(basePath: string): StaleEntry[] {
857
858
  const parsed = parsePlan(content);
858
859
 
859
860
  for (const task of tasks) {
860
- const isDoneInDb = task.status === "done" || task.status === "complete";
861
+ const isDoneInDb = isClosedStatus(task.status);
861
862
  const planTask = parsed.tasks.find((t: { id: string }) => t.id === task.id);
862
863
  if (!planTask) continue;
863
864
 
@@ -880,7 +881,7 @@ export function detectStaleRenders(basePath: string): StaleEntry[] {
880
881
 
881
882
  // Check missing task summary files
882
883
  for (const task of tasks) {
883
- if ((task.status === "done" || task.status === "complete") && task.full_summary_md) {
884
+ if (isClosedStatus(task.status) && task.full_summary_md) {
884
885
  const slicePath = resolveSlicePath(basePath, milestone.id, slice.id);
885
886
  if (slicePath) {
886
887
  const tasksDir = join(slicePath, "tasks");
@@ -125,6 +125,9 @@ export function getActiveMemoriesRanked(limit = 30): Memory[] {
125
125
  /**
126
126
  * Generate the next memory ID: MEM + zero-padded 3-digit from MAX(seq).
127
127
  * Returns MEM001 if no memories exist.
128
+ *
129
+ * NOTE: For race-safe creation, prefer createMemory() which inserts with a
130
+ * placeholder ID then updates to the seq-derived ID atomically.
128
131
  */
129
132
  export function nextMemoryId(): string {
130
133
  if (!isDbAvailable()) return 'MEM001';
@@ -147,7 +150,9 @@ export function nextMemoryId(): string {
147
150
  // ─── Mutation Functions ─────────────────────────────────────────────────────
148
151
 
149
152
  /**
150
- * Insert a new memory with auto-assigned ID.
153
+ * Insert a new memory with a race-safe auto-assigned ID.
154
+ * Uses AUTOINCREMENT seq to derive the ID after insert, avoiding
155
+ * the read-then-write race in concurrent scenarios (e.g. worktrees).
151
156
  * Returns the assigned ID, or null on failure.
152
157
  */
153
158
  export function createMemory(fields: {
@@ -162,13 +167,14 @@ export function createMemory(fields: {
162
167
  if (!adapter) return null;
163
168
 
164
169
  try {
165
- const id = nextMemoryId();
166
170
  const now = new Date().toISOString();
171
+ // Insert with a temporary placeholder ID — seq is auto-assigned
172
+ const placeholder = `_TMP_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
167
173
  adapter.prepare(
168
174
  `INSERT INTO memories (id, category, content, confidence, source_unit_type, source_unit_id, created_at, updated_at)
169
175
  VALUES (:id, :category, :content, :confidence, :source_unit_type, :source_unit_id, :created_at, :updated_at)`,
170
176
  ).run({
171
- ':id': id,
177
+ ':id': placeholder,
172
178
  ':category': fields.category,
173
179
  ':content': fields.content,
174
180
  ':confidence': fields.confidence ?? 0.8,
@@ -177,7 +183,16 @@ export function createMemory(fields: {
177
183
  ':created_at': now,
178
184
  ':updated_at': now,
179
185
  });
180
- return id;
186
+ // Derive the real ID from the assigned seq
187
+ const row = adapter.prepare('SELECT seq FROM memories WHERE id = :id').get({ ':id': placeholder });
188
+ if (!row) return placeholder; // fallback — should not happen
189
+ const seq = row['seq'] as number;
190
+ const realId = `MEM${String(seq).padStart(3, '0')}`;
191
+ adapter.prepare('UPDATE memories SET id = :real_id WHERE id = :placeholder').run({
192
+ ':real_id': realId,
193
+ ':placeholder': placeholder,
194
+ });
195
+ return realId;
181
196
  } catch {
182
197
  return null;
183
198
  }
@@ -331,20 +346,16 @@ export function enforceMemoryCap(max = 50): void {
331
346
  if (count <= max) return;
332
347
 
333
348
  const excess = count - max;
334
- // Find the IDs of the lowest-ranked active memories
335
- const rows = adapter.prepare(
336
- `SELECT id FROM memories
337
- WHERE superseded_by IS NULL
338
- ORDER BY (confidence * (1.0 + hit_count * 0.1)) ASC
339
- LIMIT :limit`,
340
- ).all({ ':limit': excess });
341
-
342
- const now = new Date().toISOString();
343
- for (const row of rows) {
344
- adapter.prepare(
345
- 'UPDATE memories SET superseded_by = :reason, updated_at = :now WHERE id = :id',
346
- ).run({ ':reason': 'CAP_EXCEEDED', ':now': now, ':id': row['id'] as string });
347
- }
349
+ // Batch update: supersede lowest-ranked active memories in a single statement
350
+ adapter.prepare(
351
+ `UPDATE memories SET superseded_by = 'CAP_EXCEEDED', updated_at = :now
352
+ WHERE id IN (
353
+ SELECT id FROM memories
354
+ WHERE superseded_by IS NULL
355
+ ORDER BY (confidence * (1.0 + hit_count * 0.1)) ASC
356
+ LIMIT :limit
357
+ )`,
358
+ ).run({ ':now': new Date().toISOString(), ':limit': excess });
348
359
  } catch {
349
360
  // non-fatal
350
361
  }
@@ -20,6 +20,7 @@ import {
20
20
  } from "./paths.js";
21
21
  import { invalidateAllCaches } from "./cache.js";
22
22
  import { loadQueueOrder, saveQueueOrder } from "./queue-order.js";
23
+ import { isDbAvailable, updateMilestoneStatus } from "./gsd-db.js";
23
24
 
24
25
  // ─── Park ──────────────────────────────────────────────────────────────────
25
26
 
@@ -52,6 +53,14 @@ export function parkMilestone(basePath: string, milestoneId: string, reason: str
52
53
  ].join("\n");
53
54
 
54
55
  writeFileSync(parkedPath, content, "utf-8");
56
+ // Sync DB status so deriveStateFromDb also skips this milestone (#2694)
57
+ if (isDbAvailable()) {
58
+ try {
59
+ updateMilestoneStatus(milestoneId, "parked");
60
+ } catch (err) {
61
+ process.stderr.write(`gsd: parkMilestone DB sync failed for ${milestoneId}: ${(err as Error).message}\n`);
62
+ }
63
+ }
55
64
  invalidateAllCaches();
56
65
  return true;
57
66
  }
@@ -70,6 +79,14 @@ export function unparkMilestone(basePath: string, milestoneId: string): boolean
70
79
  if (!existsSync(parkedPath)) return false; // not parked
71
80
 
72
81
  unlinkSync(parkedPath);
82
+ // Sync DB status so deriveStateFromDb picks up the unparked milestone (#2694)
83
+ if (isDbAvailable()) {
84
+ try {
85
+ updateMilestoneStatus(milestoneId, "active");
86
+ } catch (err) {
87
+ process.stderr.write(`gsd: unparkMilestone DB sync failed for ${milestoneId}: ${(err as Error).message}\n`);
88
+ }
89
+ }
73
90
  invalidateAllCaches();
74
91
  return true;
75
92
  }
@@ -196,7 +196,17 @@ function appendWorkerLog(basePath: string, milestoneId: string, chunk: string):
196
196
  }
197
197
 
198
198
  function restoreRuntimeState(basePath: string): boolean {
199
- if (state?.active) return true;
199
+ if (state?.active) {
200
+ // Verify at least one worker is alive — if all are in terminal states,
201
+ // the cached state is stale and we should fall through to cleanup.
202
+ const hasLiveWorker = [...state.workers.values()].some(
203
+ (w) => w.state !== "error" && w.state !== "stopped",
204
+ );
205
+ if (hasLiveWorker) return true;
206
+
207
+ // All workers dead — clear stale state so restoreState() can clean up.
208
+ state = null;
209
+ }
200
210
 
201
211
  const restored = restoreState(basePath);
202
212
  if (restored && restored.workers.length > 0) {
@@ -932,6 +942,18 @@ export function refreshWorkerStatuses(
932
942
  state.totalCost += worker.cost;
933
943
  }
934
944
 
945
+ // If all workers are in a terminal state (error/stopped), the orchestration
946
+ // is finished — deactivate and clean up so zombie workers don't persist.
947
+ const allDead = [...state.workers.values()].every(
948
+ (w) => w.state === "error" || w.state === "stopped",
949
+ );
950
+ if (allDead) {
951
+ state.active = false;
952
+ removeStateFile(basePath);
953
+ state = null;
954
+ return;
955
+ }
956
+
935
957
  // Persist updated state for crash recovery
936
958
  persistState(basePath);
937
959
  }
@@ -125,18 +125,6 @@ export function getNextFallbackModel(
125
125
  }
126
126
  }
127
127
 
128
- /**
129
- * Detect whether an error message indicates a transient network error
130
- * (worth retrying the same model) vs a permanent provider error
131
- * (auth failure, quota exceeded, etc. -- should fall back immediately).
132
- */
133
- export function isTransientNetworkError(errorMsg: string): boolean {
134
- if (!errorMsg) return false;
135
- const hasNetworkSignal = /network|ECONNRESET|ETIMEDOUT|ECONNREFUSED|socket hang up|fetch failed|connection.*reset|dns/i.test(errorMsg);
136
- const hasPermanentSignal = /auth|unauthorized|forbidden|invalid.*key|quota|billing/i.test(errorMsg);
137
- return hasNetworkSignal && !hasPermanentSignal;
138
- }
139
-
140
128
  /**
141
129
  * Validate a model ID string.
142
130
  * Returns true if the ID looks like a valid model identifier.
@@ -308,7 +296,7 @@ export function resolveContextSelection(): import("./types.js").ContextSelection
308
296
  }
309
297
 
310
298
  /**
311
- * Resolve the search provider preference from preferences.md.
299
+ * Resolve the search provider preference from PREFERENCES.md.
312
300
  * Returns undefined if not configured (caller falls back to existing behavior).
313
301
  */
314
302
  export function resolveSearchProviderFromPreferences(): GSDPreferences["search_provider"] | undefined {
@@ -33,7 +33,7 @@ export const MODE_DEFAULTS: Record<WorkflowMode, Partial<GSDPreferences>> = {
33
33
  git: {
34
34
  auto_push: true,
35
35
  push_branches: false,
36
- pre_merge_check: false,
36
+ pre_merge_check: "auto",
37
37
  merge_strategy: "squash",
38
38
  isolation: "none",
39
39
  },
@@ -68,7 +68,6 @@ export {
68
68
  resolveModelForUnit,
69
69
  resolveModelWithFallbacksForUnit,
70
70
  getNextFallbackModel,
71
- isTransientNetworkError,
72
71
  validateModelId,
73
72
  updatePreferencesModels,
74
73
  resolveDynamicRoutingConfig,
@@ -87,7 +86,7 @@ function gsdHome(): string {
87
86
  }
88
87
 
89
88
  function globalPreferencesPath(): string {
90
- return join(gsdHome(), "preferences.md");
89
+ return join(gsdHome(), "PREFERENCES.md");
91
90
  }
92
91
 
93
92
  function legacyGlobalPreferencesPath(): string {
@@ -95,15 +94,15 @@ function legacyGlobalPreferencesPath(): string {
95
94
  }
96
95
 
97
96
  function projectPreferencesPath(): string {
98
- return join(gsdRoot(process.cwd()), "preferences.md");
97
+ return join(gsdRoot(process.cwd()), "PREFERENCES.md");
99
98
  }
100
- // Bootstrap in gitignore.ts historically created PREFERENCES.md (uppercase) by mistake.
101
- // Check uppercase as a fallback so those files aren't silently ignored.
102
- function globalPreferencesPathUppercase(): string {
103
- return join(gsdHome(), "PREFERENCES.md");
99
+ // Legacy: older versions used lowercase preferences.md.
100
+ // Check lowercase as a fallback so those files aren't silently ignored.
101
+ function globalPreferencesPathLegacy(): string {
102
+ return join(gsdHome(), "preferences.md");
104
103
  }
105
- function projectPreferencesPathUppercase(): string {
106
- return join(gsdRoot(process.cwd()), "PREFERENCES.md");
104
+ function projectPreferencesPathLegacy(): string {
105
+ return join(gsdRoot(process.cwd()), "preferences.md");
107
106
  }
108
107
 
109
108
  export function getGlobalGSDPreferencesPath(): string {
@@ -122,13 +121,13 @@ export function getProjectGSDPreferencesPath(): string {
122
121
 
123
122
  export function loadGlobalGSDPreferences(): LoadedGSDPreferences | null {
124
123
  return loadPreferencesFile(globalPreferencesPath(), "global")
125
- ?? loadPreferencesFile(globalPreferencesPathUppercase(), "global")
124
+ ?? loadPreferencesFile(globalPreferencesPathLegacy(), "global")
126
125
  ?? loadPreferencesFile(legacyGlobalPreferencesPath(), "global");
127
126
  }
128
127
 
129
128
  export function loadProjectGSDPreferences(): LoadedGSDPreferences | null {
130
129
  return loadPreferencesFile(projectPreferencesPath(), "project")
131
- ?? loadPreferencesFile(projectPreferencesPathUppercase(), "project");
130
+ ?? loadPreferencesFile(projectPreferencesPathLegacy(), "project");
132
131
  }
133
132
 
134
133
  export function loadEffectiveGSDPreferences(): LoadedGSDPreferences | null {
@@ -223,7 +222,7 @@ export function parsePreferencesMarkdown(content: string): GSDPreferences | null
223
222
 
224
223
  if (!_warnedUnrecognizedFormat) {
225
224
  _warnedUnrecognizedFormat = true;
226
- console.warn("[parsePreferencesMarkdown] preferences.md exists but uses an unrecognized format — skipping.");
225
+ console.warn("[parsePreferencesMarkdown] PREFERENCES.md exists but uses an unrecognized format — skipping.");
227
226
  }
228
227
  return null;
229
228
  }
@@ -502,7 +501,7 @@ export function resolvePreDispatchHooks(): PreDispatchHookConfig[] {
502
501
  * Resolve the effective git isolation mode from preferences.
503
502
  * Returns "none" (default), "worktree", or "branch".
504
503
  *
505
- * Default is "none" so GSD works out of the box without preferences.md.
504
+ * Default is "none" so GSD works out of the box without PREFERENCES.md.
506
505
  * Worktree isolation requires explicit opt-in because it depends on git
507
506
  * branch infrastructure that must be set up before use.
508
507
  */
@@ -92,7 +92,7 @@ Titles live inside file content (headings, frontmatter), not in file or director
92
92
 
93
93
  ### Isolation Model
94
94
 
95
- Auto-mode supports three isolation modes (configured in `.gsd/preferences.md` under `taskIsolation.mode`):
95
+ Auto-mode supports three isolation modes (configured in `.gsd/PREFERENCES.md` under `taskIsolation.mode`):
96
96
 
97
97
  - **worktree** (default): Work happens in `.gsd/worktrees/<MID>/`, a full git worktree on the `milestone/<MID>` branch. Each worktree has its own working copy and `.gsd/` directory. Squash-merged back to the integration branch on milestone completion.
98
98
  - **branch**: Work happens in the project root on a `milestone/<MID>` branch. No worktree directory — files are checked out in-place.
@@ -2,63 +2,6 @@ export type ProviderErrorPauseUI = {
2
2
  notify(message: string, level?: "info" | "warning" | "error" | "success"): void;
3
3
  };
4
4
 
5
- /**
6
- * Classify a provider error as transient (auto-resume) or permanent (manual resume).
7
- *
8
- * Transient: rate limits, server errors (500/502/503), overloaded, internal errors.
9
- * These are expected to self-resolve and should auto-resume after a delay.
10
- *
11
- * Permanent: auth errors, invalid API key, billing issues.
12
- * These require user intervention and should pause indefinitely.
13
- */
14
- export function classifyProviderError(errorMsg: string): {
15
- isTransient: boolean;
16
- isRateLimit: boolean;
17
- suggestedDelayMs: number;
18
- } {
19
- const isRateLimit = /rate.?limit|too many requests|429/i.test(errorMsg);
20
- const isServerError = /internal server error|500|502|503|overloaded|server_error|api_error|service.?unavailable/i.test(errorMsg);
21
-
22
- // Connection/process errors — transient, auto-resume after brief backoff (#2309).
23
- // These indicate the process was killed, the connection was reset, or a network
24
- // blip occurred. They are NOT permanent failures.
25
- const isConnectionError = /terminated|connection.?reset|connection.?refused|other side closed|fetch failed|network.?(?:is\s+)?unavailable|ECONNREFUSED|ECONNRESET|EPIPE/i.test(errorMsg);
26
-
27
- // Permanent errors — never auto-resume
28
- const isPermanent = /auth|unauthorized|forbidden|invalid.*key|invalid.*api|billing|quota exceeded|account/i.test(errorMsg);
29
-
30
- if (isPermanent && !isRateLimit) {
31
- return { isTransient: false, isRateLimit: false, suggestedDelayMs: 0 };
32
- }
33
-
34
- if (isRateLimit) {
35
- // Try to extract retry-after from the message
36
- const resetMatch = errorMsg.match(/reset in (\d+)s/i);
37
- const delayMs = resetMatch ? Number(resetMatch[1]) * 1000 : 60_000; // default 60s for rate limits
38
- return { isTransient: true, isRateLimit: true, suggestedDelayMs: delayMs };
39
- }
40
-
41
- if (isServerError) {
42
- return { isTransient: true, isRateLimit: false, suggestedDelayMs: 30_000 }; // 30s for server errors
43
- }
44
-
45
- if (isConnectionError) {
46
- return { isTransient: true, isRateLimit: false, suggestedDelayMs: 15_000 }; // 15s for connection errors
47
- }
48
-
49
- // Stream-truncation JSON parse errors — transient (#2572).
50
- // When the API stream is cut mid-chunk, pi tries to reassemble the partial
51
- // tool-call JSON and gets a SyntaxError. This is the downstream symptom of
52
- // a connection drop — same root cause as ECONNRESET, one layer up.
53
- const isMalformedStream = /Unexpected end of JSON|Unexpected token.*JSON|Expected double-quoted property name|SyntaxError.*JSON/i.test(errorMsg);
54
- if (isMalformedStream) {
55
- return { isTransient: true, isRateLimit: false, suggestedDelayMs: 15_000 }; // 15s, same as connection errors
56
- }
57
-
58
- // Unknown error — treat as permanent (user reviews)
59
- return { isTransient: false, isRateLimit: false, suggestedDelayMs: 0 };
60
- }
61
-
62
5
  /**
63
6
  * Pause auto-mode due to a provider error.
64
7
  *
@@ -524,7 +524,7 @@ export class RuleRegistry {
524
524
  formatHookStatus(): string {
525
525
  const entries = this.getHookStatus();
526
526
  if (entries.length === 0) {
527
- return "No hooks configured. Add post_unit_hooks or pre_dispatch_hooks to .gsd/preferences.md";
527
+ return "No hooks configured. Add post_unit_hooks or pre_dispatch_hooks to .gsd/PREFERENCES.md";
528
528
  }
529
529
 
530
530
  const lines: string[] = ["Configured Hooks:", ""];
@@ -27,15 +27,27 @@ const SERVICE_TIER_SCOPE_NOTE = "Only affects gpt-5.4 models, regardless of prov
27
27
 
28
28
  // ─── Gating ──────────────────────────────────────────────────────────────────
29
29
 
30
+ /**
31
+ * Model ID prefixes (bare, without provider) that support OpenAI service tiers.
32
+ *
33
+ * This list is the fallback for callers that only have a model ID string.
34
+ * The authoritative source of truth is `model.capabilities.supportsServiceTier`
35
+ * (set via CAPABILITY_PATCHES in packages/pi-ai/src/models.ts). When callers
36
+ * have access to the full Model object, prefer reading capabilities directly.
37
+ *
38
+ * See: https://github.com/gsd-build/gsd-2/issues/2546
39
+ */
40
+ const SERVICE_TIER_MODEL_PREFIXES = ["gpt-5.4"] as const;
41
+
30
42
  /**
31
43
  * Returns true when the given model ID supports OpenAI service tiers.
32
- * Currently only gpt-5.4 variants qualify.
44
+ * Reads from SERVICE_TIER_MODEL_PREFIXES update that list, not this function.
33
45
  */
34
46
  export function supportsServiceTier(modelId: string): boolean {
35
47
  if (!modelId) return false;
36
48
  // Strip provider prefix if present (e.g. "openai/gpt-5.4" → "gpt-5.4")
37
49
  const bare = modelId.includes("/") ? modelId.split("/").pop()! : modelId;
38
- return bare.startsWith("gpt-5.4");
50
+ return SERVICE_TIER_MODEL_PREFIXES.some((prefix) => bare.startsWith(prefix));
39
51
  }
40
52
 
41
53
  // ─── Status Formatting ───────────────────────────────────────────────────────