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
@@ -408,8 +408,9 @@ export async function showDiscuss(ctx, pi, basePath) {
408
408
  // Invalidate caches to pick up artifacts written by a just-completed discuss/plan
409
409
  invalidateAllCaches();
410
410
  const state = await deriveState(basePath);
411
- // No active milestone check for pending milestones to discuss instead
412
- if (!state.activeMilestone) {
411
+ // No active milestone (or corrupted milestone with undefined id)
412
+ // check for pending milestones to discuss instead
413
+ if (!state.activeMilestone?.id) {
413
414
  const pendingMilestones = state.registry.filter(m => m.status === "pending");
414
415
  if (pendingMilestones.length === 0) {
415
416
  ctx.ui.notify("No active milestone. Run /gsd to create one first.", "warning");
@@ -864,7 +865,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
864
865
  }
865
866
  }
866
867
  const state = await deriveState(basePath);
867
- if (!state.activeMilestone) {
868
+ if (!state.activeMilestone?.id) {
868
869
  // Guard: if a discuss session is already in flight, don't re-inject the prompt.
869
870
  // Both /gsd and /gsd auto reach this branch when no milestone exists yet.
870
871
  // Without this guard, every subsequent /gsd call overwrites pendingAutoStart
@@ -336,9 +336,9 @@ function bootstrapGsdDirectory(basePath, prefs, signals) {
336
336
  assertSafeDirectory(basePath);
337
337
  const gsd = gsdRoot(basePath);
338
338
  mkdirSync(join(gsd, "milestones"), { recursive: true });
339
- // Write preferences.md from wizard answers
339
+ // Write PREFERENCES.md from wizard answers
340
340
  const preferencesContent = buildPreferencesFile(prefs);
341
- writeFileSync(join(gsd, "preferences.md"), preferencesContent, "utf-8");
341
+ writeFileSync(join(gsd, "PREFERENCES.md"), preferencesContent, "utf-8");
342
342
  // Seed CONTEXT.md with detected project signals
343
343
  const contextContent = buildContextSeed(signals);
344
344
  if (contextContent) {
@@ -110,21 +110,12 @@ export function findProvider(idOrLabel) {
110
110
  */
111
111
  export function getAllKeyStatuses(auth) {
112
112
  return PROVIDER_REGISTRY.map((provider) => {
113
- const creds = auth.getCredentialsForProvider(provider.id);
113
+ const rawCreds = auth.getCredentialsForProvider(provider.id);
114
+ // Filter out empty-key entries (left by legacy removeProviderToken or skipped onboarding)
115
+ const creds = rawCreds.filter((c) => !(c.type === "api_key" && !c.key));
114
116
  const envKey = provider.envVar ? process.env[provider.envVar] : undefined;
115
117
  if (creds.length > 0) {
116
118
  const firstCred = creds[0];
117
- // Skip empty keys (from skipped onboarding)
118
- if (firstCred.type === "api_key" && !firstCred.key) {
119
- return {
120
- provider,
121
- configured: false,
122
- source: "none",
123
- credentialCount: 0,
124
- description: "empty key (skipped setup)",
125
- backedOff: false,
126
- };
127
- }
128
119
  const desc = creds.length > 1
129
120
  ? `${creds.length} keys (round-robin)`
130
121
  : describeCredential(firstCred);
@@ -222,7 +213,7 @@ export async function handleAddKey(providerArg, ctx, auth) {
222
213
  else {
223
214
  // Interactive provider picker
224
215
  const options = PROVIDER_REGISTRY.map((p) => {
225
- const creds = auth.getCredentialsForProvider(p.id);
216
+ const creds = auth.getCredentialsForProvider(p.id).filter((c) => !(c.type === "api_key" && !c.key));
226
217
  const existing = creds.length > 0 ? " (configured)" : "";
227
218
  return `[${p.category}] ${p.label}${existing}`;
228
219
  });
@@ -285,7 +276,7 @@ export async function handleRemoveKey(providerArg, ctx, auth) {
285
276
  else {
286
277
  // Show only configured providers
287
278
  const configured = PROVIDER_REGISTRY.filter((p) => {
288
- const creds = auth.getCredentialsForProvider(p.id);
279
+ const creds = auth.getCredentialsForProvider(p.id).filter((c) => !(c.type === "api_key" && !c.key));
289
280
  return creds.length > 0;
290
281
  });
291
282
  if (configured.length === 0) {
@@ -508,7 +499,7 @@ export async function handleRotateKey(providerArg, ctx, auth) {
508
499
  // Show only configured API key providers
509
500
  const configured = PROVIDER_REGISTRY.filter((p) => {
510
501
  const creds = auth.getCredentialsForProvider(p.id);
511
- return creds.some((c) => c.type === "api_key");
502
+ return creds.some((c) => c.type === "api_key" && c.key);
512
503
  });
513
504
  if (configured.length === 0) {
514
505
  ctx.ui.notify("No API keys configured to rotate.", "info");
@@ -646,7 +637,7 @@ export function runKeyDoctor(auth) {
646
637
  if (!envValue)
647
638
  continue;
648
639
  const creds = auth.getCredentialsForProvider(provider.id);
649
- const apiKey = creds.find((c) => c.type === "api_key");
640
+ const apiKey = creds.find((c) => c.type === "api_key" && c.key);
650
641
  if (apiKey?.key && apiKey.key !== envValue) {
651
642
  findings.push({
652
643
  severity: "warning",
@@ -8,6 +8,7 @@
8
8
  // Critical invariant: rendered markdown must round-trip through
9
9
  // parseRoadmap(), parsePlan(), parseSummary() in files.ts.
10
10
  import { readFileSync, existsSync, mkdirSync } from "node:fs";
11
+ import { isClosedStatus } from "./status-guards.js";
11
12
  import { join, relative } from "node:path";
12
13
  import { createRequire } from "node:module";
13
14
  import { getAllMilestones, getMilestone, getMilestoneSlices, getSliceTasks, getTask, getSlice, getArtifact, insertArtifact, getGateResults, } from "./gsd-db.js";
@@ -262,7 +263,7 @@ function renderSlicePlanMarkdown(slice, tasks, gates = []) {
262
263
  lines.push("## Tasks");
263
264
  lines.push("");
264
265
  for (const task of tasks) {
265
- const done = task.status === "done" || task.status === "complete" ? "x" : " ";
266
+ const done = isClosedStatus(task.status) ? "x" : " ";
266
267
  const estimate = task.estimate.trim() ? ` \`est:${task.estimate.trim()}\`` : "";
267
268
  lines.push(`- [${done}] **${task.id}: ${task.title || task.id}**${estimate}`);
268
269
  if (task.description.trim()) {
@@ -435,7 +436,7 @@ export async function renderPlanCheckboxes(basePath, milestoneId, sliceId) {
435
436
  // Apply checkbox patches for each task
436
437
  let updated = content;
437
438
  for (const task of tasks) {
438
- const isDone = task.status === "done" || task.status === "complete";
439
+ const isDone = isClosedStatus(task.status);
439
440
  const tid = task.id;
440
441
  if (isDone) {
441
442
  // Set [x]
@@ -660,7 +661,7 @@ export function detectStaleRenders(basePath) {
660
661
  const content = readFileSync(planPath, "utf-8");
661
662
  const parsed = parsePlan(content);
662
663
  for (const task of tasks) {
663
- const isDoneInDb = task.status === "done" || task.status === "complete";
664
+ const isDoneInDb = isClosedStatus(task.status);
664
665
  const planTask = parsed.tasks.find((t) => t.id === task.id);
665
666
  if (!planTask)
666
667
  continue;
@@ -684,7 +685,7 @@ export function detectStaleRenders(basePath) {
684
685
  }
685
686
  // Check missing task summary files
686
687
  for (const task of tasks) {
687
- if ((task.status === "done" || task.status === "complete") && task.full_summary_md) {
688
+ if (isClosedStatus(task.status) && task.full_summary_md) {
688
689
  const slicePath = resolveSlicePath(basePath, milestone.id, slice.id);
689
690
  if (slicePath) {
690
691
  const tasksDir = join(slicePath, "tasks");
@@ -71,6 +71,9 @@ export function getActiveMemoriesRanked(limit = 30) {
71
71
  /**
72
72
  * Generate the next memory ID: MEM + zero-padded 3-digit from MAX(seq).
73
73
  * Returns MEM001 if no memories exist.
74
+ *
75
+ * NOTE: For race-safe creation, prefer createMemory() which inserts with a
76
+ * placeholder ID then updates to the seq-derived ID atomically.
74
77
  */
75
78
  export function nextMemoryId() {
76
79
  if (!isDbAvailable())
@@ -94,7 +97,9 @@ export function nextMemoryId() {
94
97
  }
95
98
  // ─── Mutation Functions ─────────────────────────────────────────────────────
96
99
  /**
97
- * Insert a new memory with auto-assigned ID.
100
+ * Insert a new memory with a race-safe auto-assigned ID.
101
+ * Uses AUTOINCREMENT seq to derive the ID after insert, avoiding
102
+ * the read-then-write race in concurrent scenarios (e.g. worktrees).
98
103
  * Returns the assigned ID, or null on failure.
99
104
  */
100
105
  export function createMemory(fields) {
@@ -104,11 +109,12 @@ export function createMemory(fields) {
104
109
  if (!adapter)
105
110
  return null;
106
111
  try {
107
- const id = nextMemoryId();
108
112
  const now = new Date().toISOString();
113
+ // Insert with a temporary placeholder ID — seq is auto-assigned
114
+ const placeholder = `_TMP_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
109
115
  adapter.prepare(`INSERT INTO memories (id, category, content, confidence, source_unit_type, source_unit_id, created_at, updated_at)
110
116
  VALUES (:id, :category, :content, :confidence, :source_unit_type, :source_unit_id, :created_at, :updated_at)`).run({
111
- ':id': id,
117
+ ':id': placeholder,
112
118
  ':category': fields.category,
113
119
  ':content': fields.content,
114
120
  ':confidence': fields.confidence ?? 0.8,
@@ -117,7 +123,17 @@ export function createMemory(fields) {
117
123
  ':created_at': now,
118
124
  ':updated_at': now,
119
125
  });
120
- return id;
126
+ // Derive the real ID from the assigned seq
127
+ const row = adapter.prepare('SELECT seq FROM memories WHERE id = :id').get({ ':id': placeholder });
128
+ if (!row)
129
+ return placeholder; // fallback — should not happen
130
+ const seq = row['seq'];
131
+ const realId = `MEM${String(seq).padStart(3, '0')}`;
132
+ adapter.prepare('UPDATE memories SET id = :real_id WHERE id = :placeholder').run({
133
+ ':real_id': realId,
134
+ ':placeholder': placeholder,
135
+ });
136
+ return realId;
121
137
  }
122
138
  catch {
123
139
  return null;
@@ -258,15 +274,14 @@ export function enforceMemoryCap(max = 50) {
258
274
  if (count <= max)
259
275
  return;
260
276
  const excess = count - max;
261
- // Find the IDs of the lowest-ranked active memories
262
- const rows = adapter.prepare(`SELECT id FROM memories
263
- WHERE superseded_by IS NULL
264
- ORDER BY (confidence * (1.0 + hit_count * 0.1)) ASC
265
- LIMIT :limit`).all({ ':limit': excess });
266
- const now = new Date().toISOString();
267
- for (const row of rows) {
268
- adapter.prepare('UPDATE memories SET superseded_by = :reason, updated_at = :now WHERE id = :id').run({ ':reason': 'CAP_EXCEEDED', ':now': now, ':id': row['id'] });
269
- }
277
+ // Batch update: supersede lowest-ranked active memories in a single statement
278
+ adapter.prepare(`UPDATE memories SET superseded_by = 'CAP_EXCEEDED', updated_at = :now
279
+ WHERE id IN (
280
+ SELECT id FROM memories
281
+ WHERE superseded_by IS NULL
282
+ ORDER BY (confidence * (1.0 + hit_count * 0.1)) ASC
283
+ LIMIT :limit
284
+ )`).run({ ':now': new Date().toISOString(), ':limit': excess });
270
285
  }
271
286
  catch {
272
287
  // non-fatal
@@ -15,6 +15,7 @@ import { join } from "node:path";
15
15
  import { resolveMilestonePath, resolveMilestoneFile, buildMilestoneFileName, } from "./paths.js";
16
16
  import { invalidateAllCaches } from "./cache.js";
17
17
  import { loadQueueOrder, saveQueueOrder } from "./queue-order.js";
18
+ import { isDbAvailable, updateMilestoneStatus } from "./gsd-db.js";
18
19
  // ─── Park ──────────────────────────────────────────────────────────────────
19
20
  /**
20
21
  * Park a milestone — creates a PARKED.md marker file with reason and timestamp.
@@ -44,6 +45,15 @@ export function parkMilestone(basePath, milestoneId, reason) {
44
45
  "",
45
46
  ].join("\n");
46
47
  writeFileSync(parkedPath, content, "utf-8");
48
+ // Sync DB status so deriveStateFromDb also skips this milestone (#2694)
49
+ if (isDbAvailable()) {
50
+ try {
51
+ updateMilestoneStatus(milestoneId, "parked");
52
+ }
53
+ catch (err) {
54
+ process.stderr.write(`gsd: parkMilestone DB sync failed for ${milestoneId}: ${err.message}\n`);
55
+ }
56
+ }
47
57
  invalidateAllCaches();
48
58
  return true;
49
59
  }
@@ -60,6 +70,15 @@ export function unparkMilestone(basePath, milestoneId) {
60
70
  if (!existsSync(parkedPath))
61
71
  return false; // not parked
62
72
  unlinkSync(parkedPath);
73
+ // Sync DB status so deriveStateFromDb picks up the unparked milestone (#2694)
74
+ if (isDbAvailable()) {
75
+ try {
76
+ updateMilestoneStatus(milestoneId, "active");
77
+ }
78
+ catch (err) {
79
+ process.stderr.write(`gsd: unparkMilestone DB sync failed for ${milestoneId}: ${err.message}\n`);
80
+ }
81
+ }
63
82
  invalidateAllCaches();
64
83
  return true;
65
84
  }
@@ -131,8 +131,15 @@ function appendWorkerLog(basePath, milestoneId, chunk) {
131
131
  }
132
132
  }
133
133
  function restoreRuntimeState(basePath) {
134
- if (state?.active)
135
- return true;
134
+ if (state?.active) {
135
+ // Verify at least one worker is alive — if all are in terminal states,
136
+ // the cached state is stale and we should fall through to cleanup.
137
+ const hasLiveWorker = [...state.workers.values()].some((w) => w.state !== "error" && w.state !== "stopped");
138
+ if (hasLiveWorker)
139
+ return true;
140
+ // All workers dead — clear stale state so restoreState() can clean up.
141
+ state = null;
142
+ }
136
143
  const restored = restoreState(basePath);
137
144
  if (restored && restored.workers.length > 0) {
138
145
  const config = resolveParallelConfig(undefined);
@@ -778,6 +785,15 @@ export function refreshWorkerStatuses(basePath, options = {}) {
778
785
  for (const worker of state.workers.values()) {
779
786
  state.totalCost += worker.cost;
780
787
  }
788
+ // If all workers are in a terminal state (error/stopped), the orchestration
789
+ // is finished — deactivate and clean up so zombie workers don't persist.
790
+ const allDead = [...state.workers.values()].every((w) => w.state === "error" || w.state === "stopped");
791
+ if (allDead) {
792
+ state.active = false;
793
+ removeStateFile(basePath);
794
+ state = null;
795
+ return;
796
+ }
781
797
  // Persist updated state for crash recovery
782
798
  persistState(basePath);
783
799
  }
@@ -98,18 +98,6 @@ export function getNextFallbackModel(currentModelId, modelConfig) {
98
98
  return modelsToTry[0];
99
99
  }
100
100
  }
101
- /**
102
- * Detect whether an error message indicates a transient network error
103
- * (worth retrying the same model) vs a permanent provider error
104
- * (auth failure, quota exceeded, etc. -- should fall back immediately).
105
- */
106
- export function isTransientNetworkError(errorMsg) {
107
- if (!errorMsg)
108
- return false;
109
- const hasNetworkSignal = /network|ECONNRESET|ETIMEDOUT|ECONNREFUSED|socket hang up|fetch failed|connection.*reset|dns/i.test(errorMsg);
110
- const hasPermanentSignal = /auth|unauthorized|forbidden|invalid.*key|quota|billing/i.test(errorMsg);
111
- return hasNetworkSignal && !hasPermanentSignal;
112
- }
113
101
  /**
114
102
  * Validate a model ID string.
115
103
  * Returns true if the ID looks like a valid model identifier.
@@ -273,7 +261,7 @@ export function resolveContextSelection() {
273
261
  return profile === "budget" ? "smart" : "full";
274
262
  }
275
263
  /**
276
- * Resolve the search provider preference from preferences.md.
264
+ * Resolve the search provider preference from PREFERENCES.md.
277
265
  * Returns undefined if not configured (caller falls back to existing behavior).
278
266
  */
279
267
  export function resolveSearchProviderFromPreferences() {
@@ -11,7 +11,7 @@ export const MODE_DEFAULTS = {
11
11
  git: {
12
12
  auto_push: true,
13
13
  push_branches: false,
14
- pre_merge_check: false,
14
+ pre_merge_check: "auto",
15
15
  merge_strategy: "squash",
16
16
  isolation: "none",
17
17
  },
@@ -24,27 +24,27 @@ export { validatePreferences } from "./preferences-validation.js";
24
24
  // ─── Re-exports: skills ─────────────────────────────────────────────────────
25
25
  export { resolveAllSkillReferences, resolveSkillDiscoveryMode, resolveSkillStalenessDays, } from "./preferences-skills.js";
26
26
  // ─── Re-exports: models ─────────────────────────────────────────────────────
27
- export { resolveModelForUnit, resolveModelWithFallbacksForUnit, getNextFallbackModel, isTransientNetworkError, validateModelId, updatePreferencesModels, resolveDynamicRoutingConfig, resolveAutoSupervisorConfig, resolveProfileDefaults, resolveEffectiveProfile, resolveInlineLevel, resolveContextSelection, resolveSearchProviderFromPreferences, } from "./preferences-models.js";
27
+ export { resolveModelForUnit, resolveModelWithFallbacksForUnit, getNextFallbackModel, validateModelId, updatePreferencesModels, resolveDynamicRoutingConfig, resolveAutoSupervisorConfig, resolveProfileDefaults, resolveEffectiveProfile, resolveInlineLevel, resolveContextSelection, resolveSearchProviderFromPreferences, } from "./preferences-models.js";
28
28
  // ─── Path Constants & Getters ───────────────────────────────────────────────
29
29
  function gsdHome() {
30
30
  return process.env.GSD_HOME || join(homedir(), ".gsd");
31
31
  }
32
32
  function globalPreferencesPath() {
33
- return join(gsdHome(), "preferences.md");
33
+ return join(gsdHome(), "PREFERENCES.md");
34
34
  }
35
35
  function legacyGlobalPreferencesPath() {
36
36
  return join(homedir(), ".pi", "agent", "gsd-preferences.md");
37
37
  }
38
38
  function projectPreferencesPath() {
39
- return join(gsdRoot(process.cwd()), "preferences.md");
39
+ return join(gsdRoot(process.cwd()), "PREFERENCES.md");
40
40
  }
41
- // Bootstrap in gitignore.ts historically created PREFERENCES.md (uppercase) by mistake.
42
- // Check uppercase as a fallback so those files aren't silently ignored.
43
- function globalPreferencesPathUppercase() {
44
- return join(gsdHome(), "PREFERENCES.md");
41
+ // Legacy: older versions used lowercase preferences.md.
42
+ // Check lowercase as a fallback so those files aren't silently ignored.
43
+ function globalPreferencesPathLegacy() {
44
+ return join(gsdHome(), "preferences.md");
45
45
  }
46
- function projectPreferencesPathUppercase() {
47
- return join(gsdRoot(process.cwd()), "PREFERENCES.md");
46
+ function projectPreferencesPathLegacy() {
47
+ return join(gsdRoot(process.cwd()), "preferences.md");
48
48
  }
49
49
  export function getGlobalGSDPreferencesPath() {
50
50
  return globalPreferencesPath();
@@ -58,12 +58,12 @@ export function getProjectGSDPreferencesPath() {
58
58
  // ─── Loading ────────────────────────────────────────────────────────────────
59
59
  export function loadGlobalGSDPreferences() {
60
60
  return loadPreferencesFile(globalPreferencesPath(), "global")
61
- ?? loadPreferencesFile(globalPreferencesPathUppercase(), "global")
61
+ ?? loadPreferencesFile(globalPreferencesPathLegacy(), "global")
62
62
  ?? loadPreferencesFile(legacyGlobalPreferencesPath(), "global");
63
63
  }
64
64
  export function loadProjectGSDPreferences() {
65
65
  return loadPreferencesFile(projectPreferencesPath(), "project")
66
- ?? loadPreferencesFile(projectPreferencesPathUppercase(), "project");
66
+ ?? loadPreferencesFile(projectPreferencesPathLegacy(), "project");
67
67
  }
68
68
  export function loadEffectiveGSDPreferences() {
69
69
  const globalPreferences = loadGlobalGSDPreferences();
@@ -149,7 +149,7 @@ export function parsePreferencesMarkdown(content) {
149
149
  }
150
150
  if (!_warnedUnrecognizedFormat) {
151
151
  _warnedUnrecognizedFormat = true;
152
- console.warn("[parsePreferencesMarkdown] preferences.md exists but uses an unrecognized format — skipping.");
152
+ console.warn("[parsePreferencesMarkdown] PREFERENCES.md exists but uses an unrecognized format — skipping.");
153
153
  }
154
154
  return null;
155
155
  }
@@ -398,7 +398,7 @@ export function resolvePreDispatchHooks() {
398
398
  * Resolve the effective git isolation mode from preferences.
399
399
  * Returns "none" (default), "worktree", or "branch".
400
400
  *
401
- * Default is "none" so GSD works out of the box without preferences.md.
401
+ * Default is "none" so GSD works out of the box without PREFERENCES.md.
402
402
  * Worktree isolation requires explicit opt-in because it depends on git
403
403
  * branch infrastructure that must be set up before use.
404
404
  */
@@ -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.
@@ -1,47 +1,3 @@
1
- /**
2
- * Classify a provider error as transient (auto-resume) or permanent (manual resume).
3
- *
4
- * Transient: rate limits, server errors (500/502/503), overloaded, internal errors.
5
- * These are expected to self-resolve and should auto-resume after a delay.
6
- *
7
- * Permanent: auth errors, invalid API key, billing issues.
8
- * These require user intervention and should pause indefinitely.
9
- */
10
- export function classifyProviderError(errorMsg) {
11
- const isRateLimit = /rate.?limit|too many requests|429/i.test(errorMsg);
12
- const isServerError = /internal server error|500|502|503|overloaded|server_error|api_error|service.?unavailable/i.test(errorMsg);
13
- // Connection/process errors — transient, auto-resume after brief backoff (#2309).
14
- // These indicate the process was killed, the connection was reset, or a network
15
- // blip occurred. They are NOT permanent failures.
16
- const isConnectionError = /terminated|connection.?reset|connection.?refused|other side closed|fetch failed|network.?(?:is\s+)?unavailable|ECONNREFUSED|ECONNRESET|EPIPE/i.test(errorMsg);
17
- // Permanent errors — never auto-resume
18
- const isPermanent = /auth|unauthorized|forbidden|invalid.*key|invalid.*api|billing|quota exceeded|account/i.test(errorMsg);
19
- if (isPermanent && !isRateLimit) {
20
- return { isTransient: false, isRateLimit: false, suggestedDelayMs: 0 };
21
- }
22
- if (isRateLimit) {
23
- // Try to extract retry-after from the message
24
- const resetMatch = errorMsg.match(/reset in (\d+)s/i);
25
- const delayMs = resetMatch ? Number(resetMatch[1]) * 1000 : 60_000; // default 60s for rate limits
26
- return { isTransient: true, isRateLimit: true, suggestedDelayMs: delayMs };
27
- }
28
- if (isServerError) {
29
- return { isTransient: true, isRateLimit: false, suggestedDelayMs: 30_000 }; // 30s for server errors
30
- }
31
- if (isConnectionError) {
32
- return { isTransient: true, isRateLimit: false, suggestedDelayMs: 15_000 }; // 15s for connection errors
33
- }
34
- // Stream-truncation JSON parse errors — transient (#2572).
35
- // When the API stream is cut mid-chunk, pi tries to reassemble the partial
36
- // tool-call JSON and gets a SyntaxError. This is the downstream symptom of
37
- // a connection drop — same root cause as ECONNRESET, one layer up.
38
- const isMalformedStream = /Unexpected end of JSON|Unexpected token.*JSON|Expected double-quoted property name|SyntaxError.*JSON/i.test(errorMsg);
39
- if (isMalformedStream) {
40
- return { isTransient: true, isRateLimit: false, suggestedDelayMs: 15_000 }; // 15s, same as connection errors
41
- }
42
- // Unknown error — treat as permanent (user reviews)
43
- return { isTransient: false, isRateLimit: false, suggestedDelayMs: 0 };
44
- }
45
1
  /**
46
2
  * Pause auto-mode due to a provider error.
47
3
  *
@@ -426,7 +426,7 @@ export class RuleRegistry {
426
426
  formatHookStatus() {
427
427
  const entries = this.getHookStatus();
428
428
  if (entries.length === 0) {
429
- return "No hooks configured. Add post_unit_hooks or pre_dispatch_hooks to .gsd/preferences.md";
429
+ return "No hooks configured. Add post_unit_hooks or pre_dispatch_hooks to .gsd/PREFERENCES.md";
430
430
  }
431
431
  const lines = ["Configured Hooks:", ""];
432
432
  const postHooks = entries.filter(e => e.type === "post");
@@ -13,16 +13,27 @@ import { getGlobalGSDPreferencesPath, loadEffectiveGSDPreferences, loadGlobalGSD
13
13
  import { ensurePreferencesFile, serializePreferencesToFrontmatter } from "./commands-prefs-wizard.js";
14
14
  const SERVICE_TIER_SCOPE_NOTE = "Only affects gpt-5.4 models, regardless of provider.";
15
15
  // ─── Gating ──────────────────────────────────────────────────────────────────
16
+ /**
17
+ * Model ID prefixes (bare, without provider) that support OpenAI service tiers.
18
+ *
19
+ * This list is the fallback for callers that only have a model ID string.
20
+ * The authoritative source of truth is `model.capabilities.supportsServiceTier`
21
+ * (set via CAPABILITY_PATCHES in packages/pi-ai/src/models.ts). When callers
22
+ * have access to the full Model object, prefer reading capabilities directly.
23
+ *
24
+ * See: https://github.com/gsd-build/gsd-2/issues/2546
25
+ */
26
+ const SERVICE_TIER_MODEL_PREFIXES = ["gpt-5.4"];
16
27
  /**
17
28
  * Returns true when the given model ID supports OpenAI service tiers.
18
- * Currently only gpt-5.4 variants qualify.
29
+ * Reads from SERVICE_TIER_MODEL_PREFIXES update that list, not this function.
19
30
  */
20
31
  export function supportsServiceTier(modelId) {
21
32
  if (!modelId)
22
33
  return false;
23
34
  // Strip provider prefix if present (e.g. "openai/gpt-5.4" → "gpt-5.4")
24
35
  const bare = modelId.includes("/") ? modelId.split("/").pop() : modelId;
25
- return bare.startsWith("gpt-5.4");
36
+ return SERVICE_TIER_MODEL_PREFIXES.some((prefix) => bare.startsWith(prefix));
26
37
  }
27
38
  // ─── Status Formatting ───────────────────────────────────────────────────────
28
39
  /**