gsd-pi 2.65.0 → 2.66.0-dev.1b4e601

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 (463) hide show
  1. package/dist/mcp-server.js +6 -2
  2. package/dist/resources/extensions/browser-tools/capture.js +20 -1
  3. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
  4. package/dist/resources/extensions/gsd/auto/finalize-timeout.js +2 -0
  5. package/dist/resources/extensions/gsd/auto/loop.js +2 -2
  6. package/dist/resources/extensions/gsd/auto/phases.js +48 -5
  7. package/dist/resources/extensions/gsd/auto/run-unit.js +13 -2
  8. package/dist/resources/extensions/gsd/auto/session.js +4 -0
  9. package/dist/resources/extensions/gsd/auto/types.js +2 -0
  10. package/dist/resources/extensions/gsd/auto-dashboard.js +2 -1
  11. package/dist/resources/extensions/gsd/auto-dispatch.js +99 -9
  12. package/dist/resources/extensions/gsd/auto-model-selection.js +7 -5
  13. package/dist/resources/extensions/gsd/auto-post-unit.js +17 -6
  14. package/dist/resources/extensions/gsd/auto-prompts.js +24 -0
  15. package/dist/resources/extensions/gsd/auto-recovery.js +40 -22
  16. package/dist/resources/extensions/gsd/auto-start.js +175 -12
  17. package/dist/resources/extensions/gsd/auto-tool-tracking.js +10 -0
  18. package/dist/resources/extensions/gsd/auto-worktree.js +29 -7
  19. package/dist/resources/extensions/gsd/auto.js +21 -15
  20. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -4
  21. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +10 -0
  22. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +6 -4
  23. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -1
  24. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -3
  25. package/dist/resources/extensions/gsd/bootstrap/system-context.js +3 -1
  26. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +31 -1
  27. package/dist/resources/extensions/gsd/commands/context.js +8 -1
  28. package/dist/resources/extensions/gsd/commands/handlers/core.js +23 -2
  29. package/dist/resources/extensions/gsd/commands-extensions.js +1 -1
  30. package/dist/resources/extensions/gsd/config-overlay.js +312 -0
  31. package/dist/resources/extensions/gsd/db-writer.js +13 -3
  32. package/dist/resources/extensions/gsd/detection.js +1 -1
  33. package/dist/resources/extensions/gsd/dispatch-guard.js +2 -1
  34. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  35. package/dist/resources/extensions/gsd/doctor.js +2 -1
  36. package/dist/resources/extensions/gsd/files.js +17 -0
  37. package/dist/resources/extensions/gsd/gitignore.js +1 -0
  38. package/dist/resources/extensions/gsd/gsd-db.js +47 -4
  39. package/dist/resources/extensions/gsd/guided-flow.js +220 -29
  40. package/dist/resources/extensions/gsd/index.js +1 -1
  41. package/dist/resources/extensions/gsd/json-persistence.js +5 -2
  42. package/dist/resources/extensions/gsd/md-importer.js +14 -7
  43. package/dist/resources/extensions/gsd/notification-overlay.js +1 -1
  44. package/dist/resources/extensions/gsd/notification-widget.js +2 -1
  45. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +1 -1
  46. package/dist/resources/extensions/gsd/parallel-orchestrator.js +17 -11
  47. package/dist/resources/extensions/gsd/pre-execution-checks.js +26 -5
  48. package/dist/resources/extensions/gsd/preferences-types.js +3 -0
  49. package/dist/resources/extensions/gsd/preferences-validation.js +45 -1
  50. package/dist/resources/extensions/gsd/preferences.js +9 -2
  51. package/dist/resources/extensions/gsd/preparation.js +1092 -0
  52. package/dist/resources/extensions/gsd/prompt-validation.js +67 -0
  53. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
  54. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  55. package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
  56. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -0
  57. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +6 -1
  58. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
  59. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
  60. package/dist/resources/extensions/gsd/prompts/queue.md +2 -0
  61. package/dist/resources/extensions/gsd/prompts/rethink.md +2 -1
  62. package/dist/resources/extensions/gsd/prompts/system.md +2 -2
  63. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
  64. package/dist/resources/extensions/gsd/quick.js +19 -15
  65. package/dist/resources/extensions/gsd/reactive-graph.js +12 -0
  66. package/dist/resources/extensions/gsd/roadmap-slices.js +24 -5
  67. package/dist/resources/extensions/gsd/safety/content-validator.js +3 -3
  68. package/dist/resources/extensions/gsd/session-lock.js +23 -1
  69. package/dist/resources/extensions/gsd/state.js +115 -28
  70. package/dist/resources/extensions/gsd/templates/context-enhanced.md +138 -0
  71. package/dist/resources/extensions/gsd/tools/complete-milestone.js +15 -3
  72. package/dist/resources/extensions/gsd/tools/complete-slice.js +27 -6
  73. package/dist/resources/extensions/gsd/tools/complete-task.js +31 -7
  74. package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -5
  75. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +5 -2
  76. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +119 -0
  77. package/dist/resources/extensions/gsd/tools/reopen-slice.js +30 -0
  78. package/dist/resources/extensions/gsd/tools/reopen-task.js +18 -0
  79. package/dist/resources/extensions/gsd/triage-resolution.js +33 -16
  80. package/dist/resources/extensions/gsd/undo.js +3 -2
  81. package/dist/resources/extensions/gsd/workflow-events.js +1 -0
  82. package/dist/resources/extensions/gsd/workflow-logger.js +1 -1
  83. package/dist/resources/extensions/gsd/workflow-projections.js +7 -9
  84. package/dist/resources/extensions/gsd/workflow-reconcile.js +100 -9
  85. package/dist/resources/extensions/gsd/workflow-templates.js +11 -2
  86. package/dist/resources/extensions/gsd/worktree-manager.js +5 -2
  87. package/dist/resources/extensions/gsd/worktree.js +9 -0
  88. package/dist/resources/extensions/shared/interview-ui.js +1 -1
  89. package/dist/resources/extensions/subagent/agents.js +19 -5
  90. package/dist/web/standalone/.next/BUILD_ID +1 -1
  91. package/dist/web/standalone/.next/app-path-routes-manifest.json +19 -19
  92. package/dist/web/standalone/.next/build-manifest.json +4 -4
  93. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  94. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  95. package/dist/web/standalone/.next/required-server-files.json +3 -3
  96. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  97. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  99. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  107. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  109. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  110. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  111. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  113. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  115. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  123. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  135. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  155. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  165. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  171. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  184. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  185. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  186. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  187. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  188. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  189. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  190. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  191. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  192. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  193. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  194. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  195. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  196. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  197. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  198. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  199. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  200. package/dist/web/standalone/.next/server/app/index.html +1 -1
  201. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  202. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  203. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  204. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  205. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  206. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  207. package/dist/web/standalone/.next/server/app/page.js +2 -2
  208. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  209. package/dist/web/standalone/.next/server/app-paths-manifest.json +19 -19
  210. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  211. package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
  212. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  213. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  214. package/dist/web/standalone/.next/server/middleware.js +2 -2
  215. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  216. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  217. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  218. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  219. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  220. package/dist/web/standalone/.next/static/chunks/6502.8874bcae249c02e1.js +9 -0
  221. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  222. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  223. package/dist/web/standalone/.next/static/chunks/app/page-0c485498795110d6.js +1 -0
  224. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  225. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  226. package/dist/web/standalone/.next/static/chunks/{webpack-a1c1e452c6b32d04.js → webpack-9fed74684e1c5bb1.js} +1 -1
  227. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  228. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  229. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  230. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  231. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  232. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  233. package/dist/web/standalone/server.js +1 -1
  234. package/package.json +1 -1
  235. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  236. package/packages/pi-coding-agent/dist/core/retry-handler.js +30 -19
  237. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  238. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +51 -0
  239. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  240. package/packages/pi-coding-agent/dist/core/sdk.js +9 -9
  241. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  242. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +2 -1
  243. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
  244. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +10 -1
  245. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
  246. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  247. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  248. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +20 -5
  249. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  250. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +15 -1
  251. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  252. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +18 -0
  253. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  254. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  255. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +4 -0
  256. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  257. package/packages/pi-coding-agent/package.json +1 -1
  258. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +80 -0
  259. package/packages/pi-coding-agent/src/core/retry-handler.ts +37 -25
  260. package/packages/pi-coding-agent/src/core/sdk.ts +9 -9
  261. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +10 -0
  262. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +20 -4
  263. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +27 -0
  264. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +16 -1
  265. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +5 -0
  266. package/packages/pi-tui/dist/components/image.d.ts +2 -0
  267. package/packages/pi-tui/dist/components/image.d.ts.map +1 -1
  268. package/packages/pi-tui/dist/components/image.js +4 -0
  269. package/packages/pi-tui/dist/components/image.js.map +1 -1
  270. package/packages/pi-tui/dist/components/image.test.d.ts +6 -0
  271. package/packages/pi-tui/dist/components/image.test.d.ts.map +1 -0
  272. package/packages/pi-tui/dist/components/image.test.js +32 -0
  273. package/packages/pi-tui/dist/components/image.test.js.map +1 -0
  274. package/packages/pi-tui/dist/tui.d.ts +1 -0
  275. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  276. package/packages/pi-tui/dist/tui.js +8 -2
  277. package/packages/pi-tui/dist/tui.js.map +1 -1
  278. package/packages/pi-tui/src/components/image.test.ts +36 -0
  279. package/packages/pi-tui/src/components/image.ts +5 -0
  280. package/packages/pi-tui/src/tui.ts +8 -2
  281. package/pkg/package.json +1 -1
  282. package/src/resources/extensions/browser-tools/capture.ts +19 -1
  283. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
  284. package/src/resources/extensions/gsd/auto/finalize-timeout.ts +3 -0
  285. package/src/resources/extensions/gsd/auto/loop.ts +2 -2
  286. package/src/resources/extensions/gsd/auto/phases.ts +68 -3
  287. package/src/resources/extensions/gsd/auto/run-unit.ts +12 -2
  288. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  289. package/src/resources/extensions/gsd/auto/types.ts +5 -0
  290. package/src/resources/extensions/gsd/auto-dashboard.ts +2 -1
  291. package/src/resources/extensions/gsd/auto-dispatch.ts +110 -9
  292. package/src/resources/extensions/gsd/auto-model-selection.ts +7 -5
  293. package/src/resources/extensions/gsd/auto-post-unit.ts +16 -6
  294. package/src/resources/extensions/gsd/auto-prompts.ts +31 -0
  295. package/src/resources/extensions/gsd/auto-recovery.ts +29 -23
  296. package/src/resources/extensions/gsd/auto-start.ts +188 -10
  297. package/src/resources/extensions/gsd/auto-tool-tracking.ts +10 -0
  298. package/src/resources/extensions/gsd/auto-worktree.ts +28 -7
  299. package/src/resources/extensions/gsd/auto.ts +19 -8
  300. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -4
  301. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +10 -0
  302. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +5 -4
  303. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -1
  304. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +11 -3
  305. package/src/resources/extensions/gsd/bootstrap/system-context.ts +3 -1
  306. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +36 -1
  307. package/src/resources/extensions/gsd/commands/context.ts +7 -1
  308. package/src/resources/extensions/gsd/commands/handlers/core.ts +26 -2
  309. package/src/resources/extensions/gsd/commands-extensions.ts +1 -1
  310. package/src/resources/extensions/gsd/config-overlay.ts +331 -0
  311. package/src/resources/extensions/gsd/db-writer.ts +11 -3
  312. package/src/resources/extensions/gsd/detection.ts +1 -1
  313. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -1
  314. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  315. package/src/resources/extensions/gsd/doctor.ts +2 -1
  316. package/src/resources/extensions/gsd/files.ts +19 -0
  317. package/src/resources/extensions/gsd/gitignore.ts +1 -0
  318. package/src/resources/extensions/gsd/gsd-db.ts +46 -4
  319. package/src/resources/extensions/gsd/guided-flow.ts +254 -30
  320. package/src/resources/extensions/gsd/index.ts +1 -0
  321. package/src/resources/extensions/gsd/json-persistence.ts +6 -3
  322. package/src/resources/extensions/gsd/md-importer.ts +13 -6
  323. package/src/resources/extensions/gsd/notification-overlay.ts +1 -1
  324. package/src/resources/extensions/gsd/notification-widget.ts +2 -1
  325. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +1 -1
  326. package/src/resources/extensions/gsd/parallel-orchestrator.ts +19 -11
  327. package/src/resources/extensions/gsd/pre-execution-checks.ts +32 -7
  328. package/src/resources/extensions/gsd/preferences-types.ts +25 -0
  329. package/src/resources/extensions/gsd/preferences-validation.ts +45 -1
  330. package/src/resources/extensions/gsd/preferences.ts +9 -2
  331. package/src/resources/extensions/gsd/preparation.ts +1419 -0
  332. package/src/resources/extensions/gsd/prompt-validation.ts +88 -0
  333. package/src/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
  334. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  335. package/src/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
  336. package/src/resources/extensions/gsd/prompts/discuss.md +2 -0
  337. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +6 -1
  338. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
  339. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
  340. package/src/resources/extensions/gsd/prompts/queue.md +2 -0
  341. package/src/resources/extensions/gsd/prompts/rethink.md +2 -1
  342. package/src/resources/extensions/gsd/prompts/system.md +2 -2
  343. package/src/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
  344. package/src/resources/extensions/gsd/quick.ts +20 -15
  345. package/src/resources/extensions/gsd/reactive-graph.ts +18 -0
  346. package/src/resources/extensions/gsd/roadmap-slices.ts +21 -5
  347. package/src/resources/extensions/gsd/safety/content-validator.ts +3 -3
  348. package/src/resources/extensions/gsd/session-lock.ts +17 -1
  349. package/src/resources/extensions/gsd/state.ts +115 -26
  350. package/src/resources/extensions/gsd/templates/context-enhanced.md +138 -0
  351. package/src/resources/extensions/gsd/tests/adversarial-review-fixes.test.ts +223 -0
  352. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +33 -2
  353. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +56 -0
  354. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +41 -0
  355. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +72 -0
  356. package/src/resources/extensions/gsd/tests/complete-task-normalize-lists.test.ts +54 -0
  357. package/src/resources/extensions/gsd/tests/defer-milestone-stamp.test.ts +30 -0
  358. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -3
  359. package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +36 -0
  360. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +46 -0
  361. package/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts +33 -0
  362. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +37 -0
  363. package/src/resources/extensions/gsd/tests/error-success-mask.test.ts +37 -0
  364. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +125 -0
  365. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +48 -0
  366. package/src/resources/extensions/gsd/tests/format-shortcut.test.ts +69 -0
  367. package/src/resources/extensions/gsd/tests/frontmatter-parse-noise.test.ts +42 -0
  368. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +38 -0
  369. package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +103 -0
  370. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +42 -0
  371. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +11 -9
  372. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
  373. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +28 -30
  374. package/src/resources/extensions/gsd/tests/integration/test-isolation.ts +53 -0
  375. package/src/resources/extensions/gsd/tests/integration-prepared-discussion.test.ts +525 -0
  376. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +62 -0
  377. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +11 -10
  378. package/src/resources/extensions/gsd/tests/needs-remediation-revalidation.test.ts +48 -0
  379. package/src/resources/extensions/gsd/tests/note-captures-executed.test.ts +46 -0
  380. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +189 -0
  381. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +77 -0
  382. package/src/resources/extensions/gsd/tests/phantom-ghost-detection.test.ts +55 -0
  383. package/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts +39 -0
  384. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +68 -0
  385. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +284 -20
  386. package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +2 -2
  387. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +2 -2
  388. package/src/resources/extensions/gsd/tests/preparation.test.ts +1211 -0
  389. package/src/resources/extensions/gsd/tests/project-root-cwd-crash.test.ts +53 -0
  390. package/src/resources/extensions/gsd/tests/projection-no-plan-overwrite.test.ts +83 -0
  391. package/src/resources/extensions/gsd/tests/prompt-builder.test.ts +669 -0
  392. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +7 -4
  393. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +85 -0
  394. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -1
  395. package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +47 -0
  396. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +107 -0
  397. package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +45 -0
  398. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +63 -0
  399. package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +4 -5
  400. package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +51 -0
  401. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +56 -0
  402. package/src/resources/extensions/gsd/tests/skip-slice-state-rebuild.test.ts +31 -0
  403. package/src/resources/extensions/gsd/tests/skipped-validation-completion.test.ts +39 -0
  404. package/src/resources/extensions/gsd/tests/slice-sequence-insert.test.ts +51 -0
  405. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +1 -1
  406. package/src/resources/extensions/gsd/tests/stale-lockfile-recovery.test.ts +36 -0
  407. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +147 -0
  408. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +13 -0
  409. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +21 -0
  410. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +21 -0
  411. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +6 -7
  412. package/src/resources/extensions/gsd/tests/status-db-open.test.ts +47 -0
  413. package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +1 -0
  414. package/src/resources/extensions/gsd/tests/subagent-agent-discovery.test.ts +47 -0
  415. package/src/resources/extensions/gsd/tests/symlink-extension-discovery.test.ts +125 -0
  416. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +65 -0
  417. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +29 -1
  418. package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +2 -1
  419. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +3 -4
  420. package/src/resources/extensions/gsd/tests/verification-operational-gate.test.ts +15 -0
  421. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +89 -0
  422. package/src/resources/extensions/gsd/tests/wave1-critical-regressions.test.ts +49 -0
  423. package/src/resources/extensions/gsd/tests/wave2-events-regressions.test.ts +48 -0
  424. package/src/resources/extensions/gsd/tests/wave3-session-regressions.test.ts +47 -0
  425. package/src/resources/extensions/gsd/tests/wave4-write-safety-regressions.test.ts +70 -0
  426. package/src/resources/extensions/gsd/tests/wave5-consistency-regressions.test.ts +165 -0
  427. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +48 -0
  428. package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +6 -3
  429. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +38 -0
  430. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +16 -0
  431. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +20 -0
  432. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +16 -17
  433. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +13 -9
  434. package/src/resources/extensions/gsd/tests/worktree.test.ts +26 -9
  435. package/src/resources/extensions/gsd/tests/write-gate.test.ts +127 -2
  436. package/src/resources/extensions/gsd/tests/zero-slice-roadmap-guided.test.ts +19 -0
  437. package/src/resources/extensions/gsd/tools/complete-milestone.ts +13 -3
  438. package/src/resources/extensions/gsd/tools/complete-slice.ts +26 -6
  439. package/src/resources/extensions/gsd/tools/complete-task.ts +29 -7
  440. package/src/resources/extensions/gsd/tools/plan-milestone.ts +11 -9
  441. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +5 -2
  442. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +152 -0
  443. package/src/resources/extensions/gsd/tools/reopen-slice.ts +27 -0
  444. package/src/resources/extensions/gsd/tools/reopen-task.ts +17 -0
  445. package/src/resources/extensions/gsd/triage-resolution.ts +37 -17
  446. package/src/resources/extensions/gsd/types.ts +4 -0
  447. package/src/resources/extensions/gsd/undo.ts +3 -2
  448. package/src/resources/extensions/gsd/workflow-events.ts +5 -3
  449. package/src/resources/extensions/gsd/workflow-logger.ts +1 -1
  450. package/src/resources/extensions/gsd/workflow-projections.ts +7 -8
  451. package/src/resources/extensions/gsd/workflow-reconcile.ts +109 -8
  452. package/src/resources/extensions/gsd/workflow-templates.ts +11 -2
  453. package/src/resources/extensions/gsd/worktree-manager.ts +4 -2
  454. package/src/resources/extensions/gsd/worktree.ts +10 -0
  455. package/src/resources/extensions/shared/interview-ui.ts +1 -1
  456. package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +8 -10
  457. package/src/resources/extensions/subagent/agents.ts +30 -6
  458. package/dist/web/standalone/.next/static/chunks/6502.7593d7797a4b3999.js +0 -9
  459. package/dist/web/standalone/.next/static/chunks/app/page-62be3b5fa91e4c8f.js +0 -1
  460. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  461. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  462. /package/dist/web/standalone/.next/static/{MRM3OSYIAa4HMDqVGQ9nt → fcV2z87tmOazTEreFWNdG}/_buildManifest.js +0 -0
  463. /package/dist/web/standalone/.next/static/{MRM3OSYIAa4HMDqVGQ9nt → fcV2z87tmOazTEreFWNdG}/_ssgManifest.js +0 -0
@@ -44,6 +44,13 @@ import {
44
44
  nativeInit,
45
45
  nativeAddAll,
46
46
  nativeCommit,
47
+ nativeGetCurrentBranch,
48
+ nativeDetectMainBranch,
49
+ nativeCheckoutBranch,
50
+ nativeBranchList,
51
+ nativeBranchListMerged,
52
+ nativeBranchDelete,
53
+ nativeWorktreeRemove,
47
54
  } from "./native-git-bridge.js";
48
55
  import { GitServiceImpl } from "./git-service.js";
49
56
  import {
@@ -53,6 +60,7 @@ import {
53
60
  } from "./worktree.js";
54
61
  import { getAutoWorktreePath, isInAutoWorktree } from "./auto-worktree.js";
55
62
  import { readResourceVersion, cleanStaleRuntimeUnits } from "./auto-worktree.js";
63
+ import { worktreePath as getWorktreeDir, isInsideWorktreesDir } from "./worktree-manager.js";
56
64
  import { initMetrics } from "./metrics.js";
57
65
  import { initRoutingHistory } from "./routing-history.js";
58
66
  import { restoreHookState, resetHookState } from "./post-unit-hooks.js";
@@ -73,6 +81,7 @@ import {
73
81
  existsSync,
74
82
  mkdirSync,
75
83
  readdirSync,
84
+ rmSync,
76
85
  statSync,
77
86
  unlinkSync,
78
87
  } from "node:fs";
@@ -99,11 +108,8 @@ export interface BootstrapDeps {
99
108
  * concurrent session detected). Returns true when ready to dispatch.
100
109
  */
101
110
 
102
- /** Guard: tracks consecutive bootstrap attempts that found phase === "complete".
103
- * Prevents the recursive dialog loop described in #1348 where
104
- * bootstrapAutoSession → showSmartEntry → checkAutoStartAfterDiscuss → startAuto
105
- * cycles indefinitely when the discuss workflow doesn't produce a milestone. */
106
- let _consecutiveCompleteBootstraps = 0;
111
+ // Guard constant for consecutive bootstrap attempts that found phase === "complete".
112
+ // Counter moved to AutoSession.consecutiveCompleteBootstraps so s.reset() clears it.
107
113
  const MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS = 2;
108
114
 
109
115
  export async function openProjectDbIfPresent(basePath: string): Promise<void> {
@@ -117,6 +123,123 @@ export async function openProjectDbIfPresent(basePath: string): Promise<void> {
117
123
  }
118
124
  }
119
125
 
126
+ /**
127
+ * Audit for orphaned milestone branches at bootstrap.
128
+ *
129
+ * After a milestone completes, the teardown step (merge branch → main,
130
+ * delete branch, remove worktree) runs as a post-completion engine step.
131
+ * If the session ends between completion and teardown, the branch and
132
+ * worktree are orphaned — the DB says "complete" so auto-mode won't
133
+ * re-enter the milestone, and the teardown is never retried.
134
+ *
135
+ * This audit runs on every fresh bootstrap to catch that gap:
136
+ * 1. Lists all local `milestone/*` branches.
137
+ * 2. For each, checks if the milestone's DB status is "complete".
138
+ * 3. If the branch is already merged into main → deletes the branch
139
+ * and cleans up any orphaned worktree directory (safe, no data loss).
140
+ * 4. If the branch is NOT merged → preserves it and warns the user
141
+ * so they can merge manually (data safety first).
142
+ *
143
+ * Returns a summary of actions taken for the caller to surface via notify.
144
+ */
145
+ export function auditOrphanedMilestoneBranches(
146
+ basePath: string,
147
+ isolationMode: "worktree" | "branch" | "none",
148
+ ): { recovered: string[]; warnings: string[] } {
149
+ const recovered: string[] = [];
150
+ const warnings: string[] = [];
151
+
152
+ // Skip in none mode — no milestone branches are created
153
+ if (isolationMode === "none") return { recovered, warnings };
154
+
155
+ // Skip if DB not available — can't determine completion status
156
+ if (!isDbAvailable()) return { recovered, warnings };
157
+
158
+ let milestoneBranches: string[];
159
+ try {
160
+ milestoneBranches = nativeBranchList(basePath, "milestone/*");
161
+ } catch {
162
+ // git branch list failed — skip audit
163
+ return { recovered, warnings };
164
+ }
165
+
166
+ if (milestoneBranches.length === 0) return { recovered, warnings };
167
+
168
+ // Detect main branch for merge-check
169
+ let mainBranch: string;
170
+ try {
171
+ mainBranch = nativeDetectMainBranch(basePath);
172
+ } catch {
173
+ mainBranch = "main";
174
+ }
175
+
176
+ // Get branches already merged into main
177
+ let mergedBranches: Set<string>;
178
+ try {
179
+ mergedBranches = new Set(nativeBranchListMerged(basePath, mainBranch, "milestone/*"));
180
+ } catch {
181
+ mergedBranches = new Set();
182
+ }
183
+
184
+ for (const branch of milestoneBranches) {
185
+ const milestoneId = branch.replace(/^milestone\//, "");
186
+ const milestone = getMilestone(milestoneId);
187
+
188
+ // Only audit completed milestones
189
+ if (!milestone || milestone.status !== "complete") continue;
190
+
191
+ const isMerged = mergedBranches.has(branch);
192
+
193
+ if (isMerged) {
194
+ // Branch is merged — safe to delete branch and clean up worktree dir
195
+ try {
196
+ nativeBranchDelete(basePath, branch, true);
197
+ recovered.push(`Deleted merged branch ${branch} for completed milestone ${milestoneId}.`);
198
+ } catch (err) {
199
+ warnings.push(`Failed to delete merged branch ${branch}: ${err instanceof Error ? err.message : String(err)}`);
200
+ }
201
+
202
+ // Clean up orphaned worktree directory if it exists
203
+ const wtDir = getWorktreeDir(basePath, milestoneId);
204
+ if (existsSync(wtDir)) {
205
+ // Try git worktree remove first (handles registered worktrees)
206
+ try {
207
+ nativeWorktreeRemove(basePath, wtDir, true);
208
+ } catch (e) {
209
+ // Not a registered worktree — expected for orphaned dirs
210
+ logWarning("engine", `worktree remove failed (expected for orphaned dirs): ${e instanceof Error ? e.message : String(e)}`);
211
+ }
212
+
213
+ // If the directory still exists after git worktree remove (either it
214
+ // wasn't registered or the remove was a noop), fall back to direct
215
+ // filesystem removal — but only inside .gsd/worktrees/ for safety (#2365).
216
+ if (existsSync(wtDir)) {
217
+ if (isInsideWorktreesDir(basePath, wtDir)) {
218
+ try {
219
+ rmSync(wtDir, { recursive: true, force: true });
220
+ recovered.push(`Removed orphaned worktree directory for ${milestoneId}.`);
221
+ } catch (err2) {
222
+ warnings.push(`Failed to remove worktree directory for ${milestoneId}: ${err2 instanceof Error ? err2.message : String(err2)}`);
223
+ }
224
+ } else {
225
+ warnings.push(`Orphaned worktree directory for ${milestoneId} is outside .gsd/worktrees/ — skipping removal for safety.`);
226
+ }
227
+ } else {
228
+ recovered.push(`Removed orphaned worktree directory for ${milestoneId}.`);
229
+ }
230
+ }
231
+ } else {
232
+ // Branch is NOT merged — preserve for safety, warn the user
233
+ warnings.push(
234
+ `Branch ${branch} exists for completed milestone ${milestoneId} but is NOT merged into ${mainBranch}. ` +
235
+ `This may contain unmerged work. Merge manually or run \`/gsd health --fix\` to resolve.`,
236
+ );
237
+ }
238
+ }
239
+
240
+ return { recovered, warnings };
241
+ }
242
+
120
243
  export async function bootstrapAutoSession(
121
244
  s: AutoSession,
122
245
  ctx: ExtensionCommandContext,
@@ -300,6 +423,26 @@ export async function bootstrapAutoSession(
300
423
  // derivation (queue-order, task status) works on a cold start (#2841).
301
424
  await openProjectDbIfPresent(base);
302
425
 
426
+ // ── Orphaned milestone branch audit ──
427
+ // Catches completed milestones whose teardown (merge + branch delete)
428
+ // was lost due to session ending between completion and teardown.
429
+ // Must run after DB open and before worktree entry.
430
+ try {
431
+ const auditResult = auditOrphanedMilestoneBranches(base, getIsolationMode());
432
+ for (const msg of auditResult.recovered) {
433
+ ctx.ui.notify(`Orphan audit: ${msg}`, "info");
434
+ }
435
+ for (const msg of auditResult.warnings) {
436
+ ctx.ui.notify(`Orphan audit: ${msg}`, "warning");
437
+ }
438
+ if (auditResult.recovered.length > 0) {
439
+ debugLog("orphan-audit", { recovered: auditResult.recovered, warnings: auditResult.warnings });
440
+ }
441
+ } catch (err) {
442
+ // Non-fatal — the audit is defensive, never block bootstrap
443
+ logWarning("bootstrap", `orphaned milestone branch audit failed: ${err instanceof Error ? err.message : String(err)}`);
444
+ }
445
+
303
446
  let state = await deriveState(base);
304
447
 
305
448
  // Stale worktree state recovery (#654)
@@ -389,9 +532,9 @@ export async function bootstrapAutoSession(
389
532
  // Guard against recursive dialog loop (#1348):
390
533
  // If we've entered this branch multiple times in quick succession,
391
534
  // the discuss workflow isn't producing a milestone. Break the cycle.
392
- _consecutiveCompleteBootstraps++;
393
- if (_consecutiveCompleteBootstraps > MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS) {
394
- _consecutiveCompleteBootstraps = 0;
535
+ s.consecutiveCompleteBootstraps++;
536
+ if (s.consecutiveCompleteBootstraps > MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS) {
537
+ s.consecutiveCompleteBootstraps = 0;
395
538
  ctx.ui.notify(
396
539
  "All milestones are complete and the discussion didn't produce a new one. " +
397
540
  "Run /gsd to start a new milestone manually.",
@@ -410,7 +553,7 @@ export async function bootstrapAutoSession(
410
553
  postState.phase !== "complete" &&
411
554
  postState.phase !== "pre-planning"
412
555
  ) {
413
- _consecutiveCompleteBootstraps = 0; // Successfully advanced past "complete"
556
+ s.consecutiveCompleteBootstraps = 0; // Successfully advanced past "complete"
414
557
  state = postState;
415
558
  } else if (
416
559
  postState.activeMilestone &&
@@ -489,7 +632,7 @@ export async function bootstrapAutoSession(
489
632
  }
490
633
 
491
634
  // Successfully resolved an active milestone — reset the re-entry guard
492
- _consecutiveCompleteBootstraps = 0;
635
+ s.consecutiveCompleteBootstraps = 0;
493
636
 
494
637
  // ── Initialize session state ──
495
638
  s.active = true;
@@ -528,6 +671,22 @@ export async function bootstrapAutoSession(
528
671
  setActiveMilestoneId(base, s.currentMilestoneId);
529
672
  }
530
673
 
674
+ // Guard against stale milestone branch when isolation:none (#3613).
675
+ // A prior session with isolation:branch/worktree may have left HEAD on
676
+ // milestone/<MID>. Auto-checkout back to the integration branch.
677
+ if (getIsolationMode() === "none" && nativeIsRepo(base)) {
678
+ try {
679
+ const currentBranch = nativeGetCurrentBranch(base);
680
+ if (currentBranch.startsWith("milestone/")) {
681
+ const integrationBranch = nativeDetectMainBranch(base);
682
+ nativeCheckoutBranch(base, integrationBranch);
683
+ logWarning("bootstrap", `Returned to "${integrationBranch}" — HEAD was on stale milestone branch "${currentBranch}" (isolation: none does not use milestone branches).`);
684
+ }
685
+ } catch (err) {
686
+ logWarning("bootstrap", `Could not auto-checkout from stale milestone branch: ${err instanceof Error ? err.message : String(err)}`);
687
+ }
688
+ }
689
+
531
690
  // ── Auto-worktree setup ──
532
691
  s.originalBasePath = base;
533
692
 
@@ -614,6 +773,25 @@ export async function bootstrapAutoSession(
614
773
  };
615
774
  }
616
775
 
776
+ // Apply worker model override from parallel orchestrator (#worker-model).
777
+ // GSD_WORKER_MODEL is injected by the coordinator when parallel.worker_model
778
+ // is configured, so parallel milestone workers use a cheaper model than the
779
+ // coordinator session (e.g. Haiku for execution, Sonnet for planning).
780
+ const workerModelOverride = process.env.GSD_WORKER_MODEL;
781
+ if (workerModelOverride && process.env.GSD_PARALLEL_WORKER === "1") {
782
+ const availableModels = ctx.modelRegistry.getAvailable();
783
+ const { resolveModelId } = await import("./auto-model-selection.js");
784
+ const overrideModel = resolveModelId(workerModelOverride, availableModels, ctx.model?.provider);
785
+ if (overrideModel) {
786
+ const ok = await pi.setModel(overrideModel, { persist: false });
787
+ if (ok) {
788
+ // Update start model so all subsequent units use this as the baseline
789
+ s.autoModeStartModel = { provider: overrideModel.provider, id: overrideModel.id };
790
+ ctx.ui.notify(`Worker model override: ${overrideModel.provider}/${overrideModel.id}`, "info");
791
+ }
792
+ }
793
+ }
794
+
617
795
  // Snapshot installed skills
618
796
  if (resolveSkillDiscoveryMode() !== "off") {
619
797
  snapshotSkills();
@@ -102,3 +102,13 @@ export function isToolInvocationError(errorMsg: string): boolean {
102
102
  if (!errorMsg) return false;
103
103
  return TOOL_INVOCATION_ERROR_RE.test(errorMsg);
104
104
  }
105
+
106
+ /**
107
+ * Returns true if the error message indicates the tool was skipped because
108
+ * a queued user message interrupted the turn (#3595). Retrying will produce
109
+ * the same skip, so the unit should be paused rather than retried.
110
+ */
111
+ export function isQueuedUserMessageSkip(errorMsg: string): boolean {
112
+ if (!errorMsg) return false;
113
+ return /^Skipped due to queued user message\.?$/i.test(errorMsg.trim());
114
+ }
@@ -188,8 +188,10 @@ function clearProjectRootStateFiles(basePath: string, milestoneId: string): void
188
188
  try {
189
189
  unlinkSync(file);
190
190
  } catch (err) {
191
- /* non-fatal — file may not exist */
192
- logWarning("worktree", `file unlink failed: ${err instanceof Error ? err.message : String(err)}`);
191
+ // ENOENT is expected — file may not exist (#3597)
192
+ if ((err as NodeJS.ErrnoException).code !== "ENOENT") {
193
+ logWarning("worktree", `file unlink failed: ${err instanceof Error ? err.message : String(err)}`);
194
+ }
193
195
  }
194
196
  }
195
197
 
@@ -218,8 +220,11 @@ function clearProjectRootStateFiles(basePath: string, milestoneId: string): void
218
220
  try {
219
221
  unlinkSync(join(basePath, f));
220
222
  } catch (err) {
221
- /* non-fatal */
222
- logWarning("worktree", `untracked file unlink failed: ${err instanceof Error ? err.message : String(err)}`);
223
+ // ENOENT/EISDIR are expected for already-removed or directory entries (#3597)
224
+ const code = (err as NodeJS.ErrnoException).code;
225
+ if (code !== "ENOENT" && code !== "EISDIR") {
226
+ logWarning("worktree", `untracked file unlink failed: ${err instanceof Error ? err.message : String(err)}`);
227
+ }
223
228
  }
224
229
  }
225
230
  }
@@ -770,6 +775,9 @@ export function syncWorktreeStateBack(
770
775
  .map((d) => d.name);
771
776
 
772
777
  for (const mid of wtMilestones) {
778
+ // Skip the current milestone being merged — its files are already in the
779
+ // milestone branch and would conflict with the squash merge (#3641).
780
+ if (mid === milestoneId) continue;
773
781
  syncMilestoneDir(wtGsd, mainGsd, mid, synced);
774
782
  }
775
783
  } catch (err) {
@@ -1049,12 +1057,20 @@ export function createAutoWorktree(
1049
1057
  reuseExistingBranch: true,
1050
1058
  });
1051
1059
  } else {
1052
- // Fresh start — create branch from integration branch
1060
+ // Fresh start — create branch from integration branch.
1061
+ // Use the same 3-tier fallback as mergeMilestoneToMain (#3461):
1062
+ // 1. META.json integration branch (explicit per-milestone override)
1063
+ // 2. git.main_branch preference (user's configured working branch)
1064
+ // 3. nativeDetectMainBranch (origin/HEAD auto-detection)
1065
+ // Without tier 2, projects with main_branch=dev but origin/HEAD→master
1066
+ // would fork worktrees from the wrong (stale) branch.
1053
1067
  const integrationBranch =
1054
1068
  readIntegrationBranch(basePath, milestoneId) ?? undefined;
1069
+ const gitPrefs = loadEffectiveGSDPreferences()?.preferences?.git;
1070
+ const startPoint = integrationBranch ?? gitPrefs?.main_branch ?? undefined;
1055
1071
  info = createWorktree(basePath, milestoneId, {
1056
1072
  branch,
1057
- startPoint: integrationBranch,
1073
+ startPoint,
1058
1074
  });
1059
1075
  }
1060
1076
 
@@ -1453,8 +1469,13 @@ export function mergeMilestoneToMain(
1453
1469
  originalBasePath_,
1454
1470
  milestoneId,
1455
1471
  );
1472
+ // Validate prefs.main_branch exists before using it — a stale preference
1473
+ // (e.g. "master" when repo uses "main") causes merge failure (#3589).
1474
+ const validatedPrefBranch = prefs.main_branch && nativeBranchExists(originalBasePath_, prefs.main_branch)
1475
+ ? prefs.main_branch
1476
+ : undefined;
1456
1477
  const mainBranch =
1457
- integrationBranch ?? prefs.main_branch ?? nativeDetectMainBranch(originalBasePath_);
1478
+ integrationBranch ?? validatedPrefBranch ?? nativeDetectMainBranch(originalBasePath_);
1458
1479
 
1459
1480
  // Remove transient project-root state files before any branch or merge
1460
1481
  // operation. Untracked milestone metadata can otherwise block squash merges.
@@ -76,6 +76,7 @@ import {
76
76
  hasInteractiveToolInFlight,
77
77
  clearInFlightTools,
78
78
  isToolInvocationError,
79
+ isQueuedUserMessageSkip,
79
80
  } from "./auto-tool-tracking.js";
80
81
  import { closeoutUnit } from "./auto-unit-closeout.js";
81
82
  import { recoverTimedOutUnit } from "./auto-timeout-recovery.js";
@@ -397,7 +398,7 @@ export function markToolEnd(toolCallId: string): void {
397
398
  */
398
399
  export function recordToolInvocationError(toolName: string, errorMsg: string): void {
399
400
  if (!s.active) return;
400
- if (isToolInvocationError(errorMsg)) {
401
+ if (isToolInvocationError(errorMsg) || isQueuedUserMessageSkip(errorMsg)) {
401
402
  s.lastToolInvocationError = `${toolName}: ${errorMsg}`;
402
403
  }
403
404
  }
@@ -1140,9 +1141,9 @@ export async function startAuto(
1140
1141
  s.stepMode = meta.stepMode ?? requestedStepMode;
1141
1142
  s.autoStartTime = meta.autoStartTime || Date.now();
1142
1143
  s.paused = true;
1143
- try { unlinkSync(pausedPath); } catch (err) { /* non-fatal */
1144
- logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1145
- }
1144
+ // Don't delete pause file yet defer until lock is acquired.
1145
+ // If lock fails, the file must survive for retry.
1146
+ s.pausedSessionFile = pausedPath;
1146
1147
  ctx.ui.notify(
1147
1148
  `Resuming paused custom workflow${meta.activeRunDir ? ` (${meta.activeRunDir})` : ""}.`,
1148
1149
  "info",
@@ -1166,10 +1167,9 @@ export async function startAuto(
1166
1167
  s.stepMode = meta.stepMode ?? requestedStepMode;
1167
1168
  s.autoStartTime = meta.autoStartTime || Date.now();
1168
1169
  s.paused = true;
1169
- // Clean up the persisted file — we're consuming it
1170
- try { unlinkSync(pausedPath); } catch (err) { /* non-fatal */
1171
- logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1172
- }
1170
+ // Don't delete pause file yet defer until lock is acquired.
1171
+ // If lock fails, the file must survive for retry.
1172
+ s.pausedSessionFile = pausedPath;
1173
1173
  ctx.ui.notify(
1174
1174
  `Resuming paused session for ${meta.milestoneId}${meta.worktreePath ? ` (worktree)` : ""}.`,
1175
1175
  "info",
@@ -1186,10 +1186,21 @@ export async function startAuto(
1186
1186
  if (s.paused) {
1187
1187
  const resumeLock = acquireSessionLock(base);
1188
1188
  if (!resumeLock.acquired) {
1189
+ // Reset paused state so isAutoPaused() doesn't stick true after lock failure.
1190
+ // Pause file is preserved on disk for retry — not deleted.
1191
+ s.paused = false;
1189
1192
  ctx.ui.notify(`Cannot resume: ${resumeLock.reason}`, "error");
1190
1193
  return;
1191
1194
  }
1192
1195
 
1196
+ // Lock acquired — now safe to delete the pause file
1197
+ if (s.pausedSessionFile) {
1198
+ try { unlinkSync(s.pausedSessionFile); } catch (err) {
1199
+ logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1200
+ }
1201
+ s.pausedSessionFile = null;
1202
+ }
1203
+
1193
1204
  s.paused = false;
1194
1205
  s.active = true;
1195
1206
  s.verbose = verboseMode;
@@ -95,12 +95,24 @@ export async function handleAgentEnd(
95
95
  return;
96
96
  }
97
97
  if (lastMsg && "stopReason" in lastMsg && lastMsg.stopReason === "error") {
98
- const errorDetail = "errorMessage" in lastMsg && lastMsg.errorMessage ? `: ${lastMsg.errorMessage}` : "";
99
- const errorMsg = ("errorMessage" in lastMsg && lastMsg.errorMessage) ? String(lastMsg.errorMessage) : "";
98
+ // #3588: errorMessage can be useless (e.g. "success") while the real error
99
+ // is in the assistant message text content. Fall back to content when
100
+ // errorMessage looks uninformative.
101
+ const rawErrorMsg = ("errorMessage" in lastMsg && lastMsg.errorMessage) ? String(lastMsg.errorMessage) : "";
102
+ const isUseless = !rawErrorMsg || /^(success|ok|true|error|unknown)$/i.test(rawErrorMsg.trim());
103
+ // #3588: When errorMessage is uninformative, extract the real error from
104
+ // the assistant message text content for display purposes only.
105
+ // Classification still uses rawErrorMsg to avoid false positives from prose.
106
+ let displayMsg = rawErrorMsg;
107
+ if (isUseless && "content" in lastMsg && Array.isArray(lastMsg.content)) {
108
+ const textBlock = lastMsg.content.find((b: any) => b.type === "text" && b.text);
109
+ if (textBlock) displayMsg = (textBlock as any).text.slice(0, 300);
110
+ }
111
+ const errorDetail = displayMsg ? `: ${displayMsg}` : "";
100
112
  const explicitRetryAfterMs = ("retryAfterMs" in lastMsg && typeof lastMsg.retryAfterMs === "number") ? lastMsg.retryAfterMs : undefined;
101
113
 
102
- // ── 1. Classify ──────────────────────────────────────────────────────
103
- const cls = classifyError(errorMsg, explicitRetryAfterMs);
114
+ // ── 1. Classify using rawErrorMsg to avoid prose false-positives ────
115
+ const cls = classifyError(rawErrorMsg, explicitRetryAfterMs);
104
116
 
105
117
  // Cap rate-limit backoff for CLI-style providers (openai-codex, google-gemini-cli)
106
118
  // which use per-user quotas with shorter windows (#2922).
@@ -1000,6 +1000,16 @@ export function registerDbTools(pi: ExtensionAPI): void {
1000
1000
  updateSliceStatus(params.milestoneId, params.sliceId, "skipped");
1001
1001
  invalidateStateCache();
1002
1002
 
1003
+ // Rebuild STATE.md so it reflects the skip immediately (#3477).
1004
+ // Without this, /gsd auto reads stale STATE.md and resumes the skipped slice.
1005
+ try {
1006
+ const basePath = process.cwd();
1007
+ const { rebuildState } = await import("../doctor.js");
1008
+ await rebuildState(basePath);
1009
+ } catch (err) {
1010
+ logError("tool", `skip_slice rebuildState failed: ${(err as Error).message}`, { tool: "gsd_skip_slice" });
1011
+ }
1012
+
1003
1013
  return {
1004
1014
  content: [{ type: "text" as const, text: `Skipped slice ${params.sliceId} (${params.milestoneId}). Reason: ${params.reason ?? "User-directed skip"}. Auto-mode will advance past this slice.` }],
1005
1015
  details: {
@@ -22,17 +22,18 @@ export function registerQueryTools(pi: ExtensionAPI): void {
22
22
  }),
23
23
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
24
24
  try {
25
- // Strictly read-only: only use an already-open DB connection.
26
- // Do NOT call ensureDbOpen() it can create/migrate the DB as a side effect.
25
+ // Open the DB if not already open safe for read-only use since
26
+ // ensureDbOpen() only creates/migrates when .gsd/ has content (#3644).
27
+ const { ensureDbOpen } = await import("./dynamic-tools.js");
28
+ const dbAvailable = await ensureDbOpen();
27
29
  const {
28
- isDbAvailable,
29
30
  getMilestone,
30
31
  getSliceStatusSummary,
31
32
  getSliceTaskCounts,
32
33
  _getAdapter,
33
34
  } = await import("../gsd-db.js");
34
35
 
35
- if (!isDbAvailable()) {
36
+ if (!dbAvailable) {
36
37
  return {
37
38
  content: [{ type: "text" as const, text: "Error: GSD database is not available." }],
38
39
  details: { operation: "milestone_status", error: "db_unavailable" } as any,
@@ -36,7 +36,10 @@ function installEpipeGuard(): void {
36
36
  if (handleRecoverableExtensionProcessError(err)) {
37
37
  return;
38
38
  }
39
- throw err;
39
+ // Log unhandled errors instead of re-throwing — throwing inside an
40
+ // uncaughtException handler is a fatal double-fault in Node.js (#3163).
41
+ process.stderr.write(`[gsd] uncaught extension error (non-fatal): ${err.message}\n`);
42
+ if (err.stack) process.stderr.write(`${err.stack}\n`);
40
43
  };
41
44
  process.on("uncaughtException", _gsdEpipeGuard);
42
45
  }
@@ -6,7 +6,7 @@ import { isToolCallEventType } from "@gsd/pi-coding-agent";
6
6
  import { buildMilestoneFileName, resolveMilestonePath, resolveSliceFile, resolveSlicePath } from "../paths.js";
7
7
  import { buildBeforeAgentStartResult } from "./system-context.js";
8
8
  import { handleAgentEnd } from "./agent-end-recovery.js";
9
- import { clearDiscussionFlowState, isDepthVerified, isQueuePhaseActive, markDepthVerified, resetWriteGateState, shouldBlockContextWrite, shouldBlockQueueExecution } from "./write-gate.js";
9
+ import { clearDiscussionFlowState, isDepthVerified, isDepthConfirmationAnswer, isQueuePhaseActive, markDepthVerified, resetWriteGateState, shouldBlockContextWrite, shouldBlockQueueExecution } from "./write-gate.js";
10
10
  import { isBlockedStateFile, isBashWriteToStateFile, BLOCKED_WRITE_ERROR } from "../write-intercept.js";
11
11
  import { cleanupQuickBranch } from "../quick.js";
12
12
  import { getDiscussionMilestoneId } from "../guided-flow.js";
@@ -108,7 +108,10 @@ export function registerHooks(pi: ExtensionAPI): void {
108
108
  });
109
109
 
110
110
  pi.on("session_before_compact", async () => {
111
- if (isAutoActive() || isAutoPaused()) {
111
+ // Only cancel compaction while auto-mode is actively running.
112
+ // Paused auto-mode should allow compaction — the user may be doing
113
+ // interactive work (#3165).
114
+ if (isAutoActive()) {
112
115
  return { cancel: true };
113
116
  }
114
117
  const basePath = process.cwd();
@@ -246,7 +249,12 @@ export function registerHooks(pi: ExtensionAPI): void {
246
249
  const questions: any[] = (event.input as any)?.questions ?? [];
247
250
  for (const question of questions) {
248
251
  if (typeof question.id === "string" && question.id.includes("depth_verification")) {
249
- markDepthVerified();
252
+ // Only unlock the gate if the user selected the first option (confirmation).
253
+ // Cross-references against the question's defined options to reject free-form "Other" text.
254
+ const answer = details.response?.answers?.[question.id];
255
+ if (isDepthConfirmationAnswer(answer?.selected, question.options)) {
256
+ markDepthVerified();
257
+ }
250
258
  break;
251
259
  }
252
260
  }
@@ -15,7 +15,7 @@ import { hasSkillSnapshot, detectNewSkills, formatSkillsXml } from "../skill-dis
15
15
  import { getActiveAutoWorktreeContext } from "../auto-worktree.js";
16
16
  import { getActiveWorktreeName, getWorktreeOriginalCwd } from "../worktree-command.js";
17
17
  import { deriveState } from "../state.js";
18
- import { formatOverridesSection, loadActiveOverrides, loadFile, parseContinue, parseSummary } from "../files.js";
18
+ import { formatOverridesSection, formatShortcut, loadActiveOverrides, loadFile, parseContinue, parseSummary } from "../files.js";
19
19
  import { toPosixPath } from "../../shared/mod.js";
20
20
  import { markCmuxPromptShown, shouldPromptToEnableCmux } from "../../cmux/index.js";
21
21
 
@@ -72,6 +72,8 @@ export async function buildBeforeAgentStartResult(
72
72
  const systemContent = loadPrompt("system", {
73
73
  bundledSkillsTable: buildBundledSkillsTable(),
74
74
  templatesDir: getTemplatesDir(),
75
+ shortcutDashboard: formatShortcut("Ctrl+Alt+G"),
76
+ shortcutShell: formatShortcut("Ctrl+Alt+B"),
75
77
  });
76
78
  const loadedPreferences = loadEffectiveGSDPreferences();
77
79
  if (shouldPromptToEnableCmux(loadedPreferences?.preferences)) {
@@ -54,6 +54,35 @@ export function markDepthVerified(): void {
54
54
  depthVerificationDone = true;
55
55
  }
56
56
 
57
+ /**
58
+ * Check whether a depth_verification answer confirms the discussion is complete.
59
+ * Uses structural validation: the selected answer must exactly match the first
60
+ * option label from the question definition (the confirmation option by convention).
61
+ * This rejects free-form "Other" text, decline options, and garbage input without
62
+ * coupling to any specific label substring.
63
+ *
64
+ * @param selected The answer's selected value from details.response.answers[id].selected
65
+ * @param options The question's options array from event.input.questions[n].options
66
+ */
67
+ export function isDepthConfirmationAnswer(
68
+ selected: unknown,
69
+ options?: Array<{ label?: string }>,
70
+ ): boolean {
71
+ const value = Array.isArray(selected) ? selected[0] : selected;
72
+ if (typeof value !== "string" || !value) return false;
73
+
74
+ // If options are available, structurally validate: selected must exactly match
75
+ // the first option (confirmation) label. Rejects free-form "Other" and decline options.
76
+ if (Array.isArray(options) && options.length > 0) {
77
+ const confirmLabel = options[0]?.label;
78
+ return typeof confirmLabel === "string" && value === confirmLabel;
79
+ }
80
+
81
+ // Fallback when options aren't available (e.g., older call sites):
82
+ // accept only if it contains "(Recommended)" — the prompt convention suffix.
83
+ return value.includes("(Recommended)");
84
+ }
85
+
57
86
  export function shouldBlockContextWrite(
58
87
  toolName: string,
59
88
  inputPath: string,
@@ -71,7 +100,13 @@ export function shouldBlockContextWrite(
71
100
 
72
101
  return {
73
102
  block: true,
74
- reason: `Blocked: Cannot write to milestone CONTEXT.md during discussion phase without depth verification. Call ask_user_questions with question id "depth_verification" first to confirm discussion depth before writing context.`,
103
+ reason: [
104
+ `HARD BLOCK: Cannot write to milestone CONTEXT.md without depth verification.`,
105
+ `This is a mechanical gate — you MUST NOT proceed, retry, or rationalize past this block.`,
106
+ `Required action: call ask_user_questions with question id containing "depth_verification".`,
107
+ `The user MUST select the "(Recommended)" confirmation option to unlock this gate.`,
108
+ `If the user declines, cancels, or the tool fails, you must re-ask — not bypass.`,
109
+ ].join(" "),
75
110
  };
76
111
  }
77
112
 
@@ -13,7 +13,13 @@ export interface GsdDispatchContext {
13
13
  }
14
14
 
15
15
  export function projectRoot(): string {
16
- const cwd = process.cwd();
16
+ let cwd: string;
17
+ try {
18
+ cwd = process.cwd();
19
+ } catch {
20
+ // cwd directory was deleted (e.g. worktree teardown) — fall back to HOME (#3598)
21
+ cwd = process.env.HOME ?? "/";
22
+ }
17
23
  const root = resolveProjectRoot(cwd);
18
24
  if (root !== cwd) {
19
25
  assertSafeDirectory(cwd);