gsd-pi 2.65.0-dev.5c8557b → 2.65.0-dev.d0517ff

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 (316) 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/run-unit.js +13 -2
  5. package/dist/resources/extensions/gsd/auto/session.js +4 -0
  6. package/dist/resources/extensions/gsd/auto-dispatch.js +99 -9
  7. package/dist/resources/extensions/gsd/auto-model-selection.js +7 -5
  8. package/dist/resources/extensions/gsd/auto-post-unit.js +17 -6
  9. package/dist/resources/extensions/gsd/auto-prompts.js +24 -0
  10. package/dist/resources/extensions/gsd/auto-recovery.js +40 -22
  11. package/dist/resources/extensions/gsd/auto-start.js +42 -11
  12. package/dist/resources/extensions/gsd/auto-tool-tracking.js +10 -0
  13. package/dist/resources/extensions/gsd/auto-worktree.js +29 -7
  14. package/dist/resources/extensions/gsd/auto.js +21 -15
  15. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -4
  16. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +10 -0
  17. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +6 -4
  18. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -1
  19. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -3
  20. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +31 -1
  21. package/dist/resources/extensions/gsd/commands/context.js +8 -1
  22. package/dist/resources/extensions/gsd/commands/handlers/core.js +20 -0
  23. package/dist/resources/extensions/gsd/commands-extensions.js +1 -1
  24. package/dist/resources/extensions/gsd/config-overlay.js +312 -0
  25. package/dist/resources/extensions/gsd/db-writer.js +13 -3
  26. package/dist/resources/extensions/gsd/detection.js +1 -1
  27. package/dist/resources/extensions/gsd/dispatch-guard.js +2 -1
  28. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  29. package/dist/resources/extensions/gsd/doctor.js +2 -1
  30. package/dist/resources/extensions/gsd/gitignore.js +1 -0
  31. package/dist/resources/extensions/gsd/gsd-db.js +47 -4
  32. package/dist/resources/extensions/gsd/guided-flow.js +220 -29
  33. package/dist/resources/extensions/gsd/index.js +1 -1
  34. package/dist/resources/extensions/gsd/json-persistence.js +5 -2
  35. package/dist/resources/extensions/gsd/md-importer.js +14 -7
  36. package/dist/resources/extensions/gsd/parallel-orchestrator.js +17 -11
  37. package/dist/resources/extensions/gsd/pre-execution-checks.js +12 -5
  38. package/dist/resources/extensions/gsd/preferences-types.js +3 -0
  39. package/dist/resources/extensions/gsd/preferences-validation.js +45 -1
  40. package/dist/resources/extensions/gsd/preferences.js +9 -2
  41. package/dist/resources/extensions/gsd/preparation.js +1092 -0
  42. package/dist/resources/extensions/gsd/prompt-validation.js +67 -0
  43. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
  44. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  45. package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
  46. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -0
  47. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +6 -1
  48. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
  49. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
  50. package/dist/resources/extensions/gsd/prompts/queue.md +2 -0
  51. package/dist/resources/extensions/gsd/prompts/rethink.md +2 -1
  52. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
  53. package/dist/resources/extensions/gsd/quick.js +19 -15
  54. package/dist/resources/extensions/gsd/reactive-graph.js +12 -0
  55. package/dist/resources/extensions/gsd/roadmap-slices.js +24 -5
  56. package/dist/resources/extensions/gsd/safety/content-validator.js +3 -3
  57. package/dist/resources/extensions/gsd/session-lock.js +23 -1
  58. package/dist/resources/extensions/gsd/state.js +115 -28
  59. package/dist/resources/extensions/gsd/templates/context-enhanced.md +138 -0
  60. package/dist/resources/extensions/gsd/tools/complete-milestone.js +15 -3
  61. package/dist/resources/extensions/gsd/tools/complete-slice.js +27 -6
  62. package/dist/resources/extensions/gsd/tools/complete-task.js +31 -7
  63. package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -5
  64. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +5 -2
  65. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +119 -0
  66. package/dist/resources/extensions/gsd/tools/reopen-slice.js +30 -0
  67. package/dist/resources/extensions/gsd/tools/reopen-task.js +18 -0
  68. package/dist/resources/extensions/gsd/triage-resolution.js +33 -16
  69. package/dist/resources/extensions/gsd/undo.js +3 -2
  70. package/dist/resources/extensions/gsd/workflow-events.js +1 -0
  71. package/dist/resources/extensions/gsd/workflow-logger.js +1 -1
  72. package/dist/resources/extensions/gsd/workflow-projections.js +7 -9
  73. package/dist/resources/extensions/gsd/workflow-reconcile.js +100 -9
  74. package/dist/resources/extensions/gsd/workflow-templates.js +11 -2
  75. package/dist/resources/extensions/gsd/worktree-manager.js +5 -2
  76. package/dist/resources/extensions/gsd/worktree.js +9 -0
  77. package/dist/resources/extensions/shared/interview-ui.js +1 -1
  78. package/dist/web/standalone/.next/BUILD_ID +1 -1
  79. package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
  80. package/dist/web/standalone/.next/build-manifest.json +3 -3
  81. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  82. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  83. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  84. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  92. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/index.html +1 -1
  100. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
  107. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  110. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  111. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  112. package/dist/web/standalone/.next/static/chunks/6502.8874bcae249c02e1.js +9 -0
  113. package/dist/web/standalone/.next/static/chunks/{webpack-a1c1e452c6b32d04.js → webpack-9fed74684e1c5bb1.js} +1 -1
  114. package/package.json +1 -1
  115. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  116. package/packages/pi-coding-agent/dist/core/retry-handler.js +30 -19
  117. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  118. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +51 -0
  119. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  120. package/packages/pi-coding-agent/dist/core/sdk.js +9 -9
  121. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  122. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +2 -1
  123. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
  124. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +10 -1
  125. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
  126. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  127. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  128. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +20 -5
  129. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  130. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +15 -1
  131. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  132. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +18 -0
  133. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  134. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  135. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +4 -0
  136. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  137. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +80 -0
  138. package/packages/pi-coding-agent/src/core/retry-handler.ts +37 -25
  139. package/packages/pi-coding-agent/src/core/sdk.ts +9 -9
  140. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +10 -0
  141. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +20 -4
  142. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +27 -0
  143. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +16 -1
  144. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +5 -0
  145. package/packages/pi-tui/dist/components/image.d.ts +2 -0
  146. package/packages/pi-tui/dist/components/image.d.ts.map +1 -1
  147. package/packages/pi-tui/dist/components/image.js +4 -0
  148. package/packages/pi-tui/dist/components/image.js.map +1 -1
  149. package/packages/pi-tui/dist/components/image.test.d.ts +6 -0
  150. package/packages/pi-tui/dist/components/image.test.d.ts.map +1 -0
  151. package/packages/pi-tui/dist/components/image.test.js +32 -0
  152. package/packages/pi-tui/dist/components/image.test.js.map +1 -0
  153. package/packages/pi-tui/src/components/image.test.ts +36 -0
  154. package/packages/pi-tui/src/components/image.ts +5 -0
  155. package/src/resources/extensions/browser-tools/capture.ts +19 -1
  156. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
  157. package/src/resources/extensions/gsd/auto/run-unit.ts +12 -2
  158. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  159. package/src/resources/extensions/gsd/auto-dispatch.ts +110 -9
  160. package/src/resources/extensions/gsd/auto-model-selection.ts +7 -5
  161. package/src/resources/extensions/gsd/auto-post-unit.ts +16 -6
  162. package/src/resources/extensions/gsd/auto-prompts.ts +31 -0
  163. package/src/resources/extensions/gsd/auto-recovery.ts +29 -23
  164. package/src/resources/extensions/gsd/auto-start.ts +45 -10
  165. package/src/resources/extensions/gsd/auto-tool-tracking.ts +10 -0
  166. package/src/resources/extensions/gsd/auto-worktree.ts +28 -7
  167. package/src/resources/extensions/gsd/auto.ts +19 -8
  168. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -4
  169. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +10 -0
  170. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +5 -4
  171. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -1
  172. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +11 -3
  173. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +36 -1
  174. package/src/resources/extensions/gsd/commands/context.ts +7 -1
  175. package/src/resources/extensions/gsd/commands/handlers/core.ts +23 -0
  176. package/src/resources/extensions/gsd/commands-extensions.ts +1 -1
  177. package/src/resources/extensions/gsd/config-overlay.ts +331 -0
  178. package/src/resources/extensions/gsd/db-writer.ts +11 -3
  179. package/src/resources/extensions/gsd/detection.ts +1 -1
  180. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -1
  181. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  182. package/src/resources/extensions/gsd/doctor.ts +2 -1
  183. package/src/resources/extensions/gsd/gitignore.ts +1 -0
  184. package/src/resources/extensions/gsd/gsd-db.ts +46 -4
  185. package/src/resources/extensions/gsd/guided-flow.ts +254 -30
  186. package/src/resources/extensions/gsd/index.ts +1 -0
  187. package/src/resources/extensions/gsd/json-persistence.ts +6 -3
  188. package/src/resources/extensions/gsd/md-importer.ts +13 -6
  189. package/src/resources/extensions/gsd/parallel-orchestrator.ts +19 -11
  190. package/src/resources/extensions/gsd/pre-execution-checks.ts +15 -7
  191. package/src/resources/extensions/gsd/preferences-types.ts +25 -0
  192. package/src/resources/extensions/gsd/preferences-validation.ts +45 -1
  193. package/src/resources/extensions/gsd/preferences.ts +9 -2
  194. package/src/resources/extensions/gsd/preparation.ts +1419 -0
  195. package/src/resources/extensions/gsd/prompt-validation.ts +88 -0
  196. package/src/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
  197. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  198. package/src/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
  199. package/src/resources/extensions/gsd/prompts/discuss.md +2 -0
  200. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +6 -1
  201. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
  202. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
  203. package/src/resources/extensions/gsd/prompts/queue.md +2 -0
  204. package/src/resources/extensions/gsd/prompts/rethink.md +2 -1
  205. package/src/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
  206. package/src/resources/extensions/gsd/quick.ts +20 -15
  207. package/src/resources/extensions/gsd/reactive-graph.ts +18 -0
  208. package/src/resources/extensions/gsd/roadmap-slices.ts +21 -5
  209. package/src/resources/extensions/gsd/safety/content-validator.ts +3 -3
  210. package/src/resources/extensions/gsd/session-lock.ts +17 -1
  211. package/src/resources/extensions/gsd/state.ts +115 -26
  212. package/src/resources/extensions/gsd/templates/context-enhanced.md +138 -0
  213. package/src/resources/extensions/gsd/tests/adversarial-review-fixes.test.ts +223 -0
  214. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +33 -2
  215. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +56 -0
  216. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +41 -0
  217. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +72 -0
  218. package/src/resources/extensions/gsd/tests/complete-task-normalize-lists.test.ts +54 -0
  219. package/src/resources/extensions/gsd/tests/defer-milestone-stamp.test.ts +30 -0
  220. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -3
  221. package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +36 -0
  222. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +46 -0
  223. package/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts +33 -0
  224. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +37 -0
  225. package/src/resources/extensions/gsd/tests/error-success-mask.test.ts +37 -0
  226. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +48 -0
  227. package/src/resources/extensions/gsd/tests/frontmatter-parse-noise.test.ts +42 -0
  228. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +38 -0
  229. package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +103 -0
  230. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +42 -0
  231. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +11 -9
  232. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
  233. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +28 -30
  234. package/src/resources/extensions/gsd/tests/integration/test-isolation.ts +53 -0
  235. package/src/resources/extensions/gsd/tests/integration-prepared-discussion.test.ts +525 -0
  236. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +62 -0
  237. package/src/resources/extensions/gsd/tests/needs-remediation-revalidation.test.ts +48 -0
  238. package/src/resources/extensions/gsd/tests/note-captures-executed.test.ts +46 -0
  239. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +77 -0
  240. package/src/resources/extensions/gsd/tests/phantom-ghost-detection.test.ts +55 -0
  241. package/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts +39 -0
  242. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +68 -0
  243. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +218 -20
  244. package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +2 -2
  245. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +2 -2
  246. package/src/resources/extensions/gsd/tests/preparation.test.ts +1211 -0
  247. package/src/resources/extensions/gsd/tests/project-root-cwd-crash.test.ts +53 -0
  248. package/src/resources/extensions/gsd/tests/projection-no-plan-overwrite.test.ts +83 -0
  249. package/src/resources/extensions/gsd/tests/prompt-builder.test.ts +669 -0
  250. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +7 -4
  251. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +85 -0
  252. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -1
  253. package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +47 -0
  254. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +107 -0
  255. package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +45 -0
  256. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +63 -0
  257. package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +4 -5
  258. package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +51 -0
  259. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +56 -0
  260. package/src/resources/extensions/gsd/tests/skip-slice-state-rebuild.test.ts +31 -0
  261. package/src/resources/extensions/gsd/tests/skipped-validation-completion.test.ts +39 -0
  262. package/src/resources/extensions/gsd/tests/slice-sequence-insert.test.ts +51 -0
  263. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +1 -1
  264. package/src/resources/extensions/gsd/tests/stale-lockfile-recovery.test.ts +36 -0
  265. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +147 -0
  266. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +13 -0
  267. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +21 -0
  268. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +21 -0
  269. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +6 -7
  270. package/src/resources/extensions/gsd/tests/status-db-open.test.ts +47 -0
  271. package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +1 -0
  272. package/src/resources/extensions/gsd/tests/symlink-extension-discovery.test.ts +125 -0
  273. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +65 -0
  274. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +29 -1
  275. package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +2 -1
  276. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +3 -4
  277. package/src/resources/extensions/gsd/tests/verification-operational-gate.test.ts +15 -0
  278. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +89 -0
  279. package/src/resources/extensions/gsd/tests/wave1-critical-regressions.test.ts +49 -0
  280. package/src/resources/extensions/gsd/tests/wave2-events-regressions.test.ts +48 -0
  281. package/src/resources/extensions/gsd/tests/wave3-session-regressions.test.ts +47 -0
  282. package/src/resources/extensions/gsd/tests/wave4-write-safety-regressions.test.ts +70 -0
  283. package/src/resources/extensions/gsd/tests/wave5-consistency-regressions.test.ts +165 -0
  284. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +48 -0
  285. package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +6 -3
  286. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +38 -0
  287. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +16 -0
  288. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +20 -0
  289. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +16 -17
  290. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +13 -9
  291. package/src/resources/extensions/gsd/tests/worktree.test.ts +26 -9
  292. package/src/resources/extensions/gsd/tests/write-gate.test.ts +127 -2
  293. package/src/resources/extensions/gsd/tests/zero-slice-roadmap-guided.test.ts +19 -0
  294. package/src/resources/extensions/gsd/tools/complete-milestone.ts +13 -3
  295. package/src/resources/extensions/gsd/tools/complete-slice.ts +26 -6
  296. package/src/resources/extensions/gsd/tools/complete-task.ts +29 -7
  297. package/src/resources/extensions/gsd/tools/plan-milestone.ts +11 -9
  298. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +5 -2
  299. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +152 -0
  300. package/src/resources/extensions/gsd/tools/reopen-slice.ts +27 -0
  301. package/src/resources/extensions/gsd/tools/reopen-task.ts +17 -0
  302. package/src/resources/extensions/gsd/triage-resolution.ts +37 -17
  303. package/src/resources/extensions/gsd/types.ts +4 -0
  304. package/src/resources/extensions/gsd/undo.ts +3 -2
  305. package/src/resources/extensions/gsd/workflow-events.ts +5 -3
  306. package/src/resources/extensions/gsd/workflow-logger.ts +1 -1
  307. package/src/resources/extensions/gsd/workflow-projections.ts +7 -8
  308. package/src/resources/extensions/gsd/workflow-reconcile.ts +109 -8
  309. package/src/resources/extensions/gsd/workflow-templates.ts +11 -2
  310. package/src/resources/extensions/gsd/worktree-manager.ts +4 -2
  311. package/src/resources/extensions/gsd/worktree.ts +10 -0
  312. package/src/resources/extensions/shared/interview-ui.ts +1 -1
  313. package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +8 -10
  314. package/dist/web/standalone/.next/static/chunks/6502.7593d7797a4b3999.js +0 -9
  315. /package/dist/web/standalone/.next/static/{qq3YfHPfyqvh3DIMVmsRH → JwdBI3y1H8vtBKiYvWfEK}/_buildManifest.js +0 -0
  316. /package/dist/web/standalone/.next/static/{qq3YfHPfyqvh3DIMVmsRH → JwdBI3y1H8vtBKiYvWfEK}/_ssgManifest.js +0 -0
@@ -44,6 +44,9 @@ import {
44
44
  nativeInit,
45
45
  nativeAddAll,
46
46
  nativeCommit,
47
+ nativeGetCurrentBranch,
48
+ nativeDetectMainBranch,
49
+ nativeCheckoutBranch,
47
50
  } from "./native-git-bridge.js";
48
51
  import { GitServiceImpl } from "./git-service.js";
49
52
  import {
@@ -99,11 +102,8 @@ export interface BootstrapDeps {
99
102
  * concurrent session detected). Returns true when ready to dispatch.
100
103
  */
101
104
 
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;
105
+ // Guard constant for consecutive bootstrap attempts that found phase === "complete".
106
+ // Counter moved to AutoSession.consecutiveCompleteBootstraps so s.reset() clears it.
107
107
  const MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS = 2;
108
108
 
109
109
  export async function openProjectDbIfPresent(basePath: string): Promise<void> {
@@ -389,9 +389,9 @@ export async function bootstrapAutoSession(
389
389
  // Guard against recursive dialog loop (#1348):
390
390
  // If we've entered this branch multiple times in quick succession,
391
391
  // the discuss workflow isn't producing a milestone. Break the cycle.
392
- _consecutiveCompleteBootstraps++;
393
- if (_consecutiveCompleteBootstraps > MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS) {
394
- _consecutiveCompleteBootstraps = 0;
392
+ s.consecutiveCompleteBootstraps++;
393
+ if (s.consecutiveCompleteBootstraps > MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS) {
394
+ s.consecutiveCompleteBootstraps = 0;
395
395
  ctx.ui.notify(
396
396
  "All milestones are complete and the discussion didn't produce a new one. " +
397
397
  "Run /gsd to start a new milestone manually.",
@@ -410,7 +410,7 @@ export async function bootstrapAutoSession(
410
410
  postState.phase !== "complete" &&
411
411
  postState.phase !== "pre-planning"
412
412
  ) {
413
- _consecutiveCompleteBootstraps = 0; // Successfully advanced past "complete"
413
+ s.consecutiveCompleteBootstraps = 0; // Successfully advanced past "complete"
414
414
  state = postState;
415
415
  } else if (
416
416
  postState.activeMilestone &&
@@ -489,7 +489,7 @@ export async function bootstrapAutoSession(
489
489
  }
490
490
 
491
491
  // Successfully resolved an active milestone — reset the re-entry guard
492
- _consecutiveCompleteBootstraps = 0;
492
+ s.consecutiveCompleteBootstraps = 0;
493
493
 
494
494
  // ── Initialize session state ──
495
495
  s.active = true;
@@ -528,6 +528,22 @@ export async function bootstrapAutoSession(
528
528
  setActiveMilestoneId(base, s.currentMilestoneId);
529
529
  }
530
530
 
531
+ // Guard against stale milestone branch when isolation:none (#3613).
532
+ // A prior session with isolation:branch/worktree may have left HEAD on
533
+ // milestone/<MID>. Auto-checkout back to the integration branch.
534
+ if (getIsolationMode() === "none" && nativeIsRepo(base)) {
535
+ try {
536
+ const currentBranch = nativeGetCurrentBranch(base);
537
+ if (currentBranch.startsWith("milestone/")) {
538
+ const integrationBranch = nativeDetectMainBranch(base);
539
+ nativeCheckoutBranch(base, integrationBranch);
540
+ logWarning("bootstrap", `Returned to "${integrationBranch}" — HEAD was on stale milestone branch "${currentBranch}" (isolation: none does not use milestone branches).`);
541
+ }
542
+ } catch (err) {
543
+ logWarning("bootstrap", `Could not auto-checkout from stale milestone branch: ${err instanceof Error ? err.message : String(err)}`);
544
+ }
545
+ }
546
+
531
547
  // ── Auto-worktree setup ──
532
548
  s.originalBasePath = base;
533
549
 
@@ -614,6 +630,25 @@ export async function bootstrapAutoSession(
614
630
  };
615
631
  }
616
632
 
633
+ // Apply worker model override from parallel orchestrator (#worker-model).
634
+ // GSD_WORKER_MODEL is injected by the coordinator when parallel.worker_model
635
+ // is configured, so parallel milestone workers use a cheaper model than the
636
+ // coordinator session (e.g. Haiku for execution, Sonnet for planning).
637
+ const workerModelOverride = process.env.GSD_WORKER_MODEL;
638
+ if (workerModelOverride && process.env.GSD_PARALLEL_WORKER === "1") {
639
+ const availableModels = ctx.modelRegistry.getAvailable();
640
+ const { resolveModelId } = await import("./auto-model-selection.js");
641
+ const overrideModel = resolveModelId(workerModelOverride, availableModels, ctx.model?.provider);
642
+ if (overrideModel) {
643
+ const ok = await pi.setModel(overrideModel, { persist: false });
644
+ if (ok) {
645
+ // Update start model so all subsequent units use this as the baseline
646
+ s.autoModeStartModel = { provider: overrideModel.provider, id: overrideModel.id };
647
+ ctx.ui.notify(`Worker model override: ${overrideModel.provider}/${overrideModel.id}`, "info");
648
+ }
649
+ }
650
+ }
651
+
617
652
  // Snapshot installed skills
618
653
  if (resolveSkillDiscoveryMode() !== "off") {
619
654
  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
  }
@@ -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);
@@ -52,6 +52,7 @@ export function showHelp(ctx: ExtensionCommandContext): void {
52
52
  " /gsd cmux Manage cmux integration [status|on|off|notifications|sidebar|splits|browser]",
53
53
  " /gsd config Set API keys for external tools",
54
54
  " /gsd keys API key manager [list|add|remove|test|rotate|doctor]",
55
+ " /gsd show-config Show effective configuration (models, routing, toggles)",
55
56
  " /gsd hooks Show post-unit hook configuration",
56
57
  " /gsd extensions Manage extensions [list|enable|disable|info]",
57
58
  " /gsd fast Toggle OpenAI service tier [on|off|flex|status]",
@@ -71,6 +72,9 @@ export function showHelp(ctx: ExtensionCommandContext): void {
71
72
 
72
73
  export async function handleStatus(ctx: ExtensionCommandContext): Promise<void> {
73
74
  const basePath = projectRoot();
75
+ // Open DB in cold sessions so status uses DB-backed state, not filesystem fallback (#3385)
76
+ const { ensureDbOpen } = await import("../../bootstrap/dynamic-tools.js");
77
+ await ensureDbOpen();
74
78
  const state = await deriveState(basePath);
75
79
 
76
80
  if (state.registry.length === 0) {
@@ -214,6 +218,25 @@ export async function handleCoreCommand(trimmed: string, ctx: ExtensionCommandCo
214
218
  await handleCmux(trimmed.replace(/^cmux\s*/, "").trim(), ctx);
215
219
  return true;
216
220
  }
221
+ if (trimmed === "show-config") {
222
+ const { GSDConfigOverlay, formatConfigText } = await import("../../config-overlay.js");
223
+ const result = await ctx.ui.custom<void>(
224
+ (tui, theme, _kb, done) => new GSDConfigOverlay(tui, theme, () => done()),
225
+ {
226
+ overlay: true,
227
+ overlayOptions: {
228
+ width: "65%",
229
+ minWidth: 55,
230
+ maxHeight: "85%",
231
+ anchor: "center",
232
+ },
233
+ },
234
+ );
235
+ if (result === undefined) {
236
+ ctx.ui.notify(formatConfigText(), "info");
237
+ }
238
+ return true;
239
+ }
217
240
  if (trimmed === "setup" || trimmed.startsWith("setup ")) {
218
241
  await handleSetup(trimmed.replace(/^setup\s*/, "").trim(), ctx);
219
242
  return true;
@@ -105,7 +105,7 @@ function discoverManifests(): Map<string, ExtensionManifest> {
105
105
  const manifests = new Map<string, ExtensionManifest>();
106
106
  if (!existsSync(extDir)) return manifests;
107
107
  for (const entry of readdirSync(extDir, { withFileTypes: true })) {
108
- if (!entry.isDirectory()) continue;
108
+ if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
109
109
  const m = readManifest(join(extDir, entry.name));
110
110
  if (m) manifests.set(m.id, m);
111
111
  }