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
@@ -20,7 +20,7 @@ import { synthesizeCrashRecovery } from "./session-forensics.js";
20
20
  import { writeLock, clearLock, readCrashLock, formatCrashInfo, isLockProcessAlive, } from "./crash-recovery.js";
21
21
  import { acquireSessionLock, releaseSessionLock, updateSessionLock, } from "./session-lock.js";
22
22
  import { ensureGitignore, untrackRuntimeFiles } from "./gitignore.js";
23
- import { nativeInit, nativeAddAll, nativeCommit, } from "./native-git-bridge.js";
23
+ import { nativeIsRepo, nativeInit, nativeAddAll, nativeCommit, nativeGetCurrentBranch, nativeDetectMainBranch, nativeCheckoutBranch, } from "./native-git-bridge.js";
24
24
  import { GitServiceImpl } from "./git-service.js";
25
25
  import { captureIntegrationBranch, detectWorktreeName, setActiveMilestoneId, } from "./worktree.js";
26
26
  import { getAutoWorktreePath } from "./auto-worktree.js";
@@ -48,11 +48,8 @@ import { resolveDefaultSessionModel } from "./preferences-models.js";
48
48
  * Returns false if the bootstrap aborted (e.g., guided flow returned,
49
49
  * concurrent session detected). Returns true when ready to dispatch.
50
50
  */
51
- /** Guard: tracks consecutive bootstrap attempts that found phase === "complete".
52
- * Prevents the recursive dialog loop described in #1348 where
53
- * bootstrapAutoSession → showSmartEntry → checkAutoStartAfterDiscuss → startAuto
54
- * cycles indefinitely when the discuss workflow doesn't produce a milestone. */
55
- let _consecutiveCompleteBootstraps = 0;
51
+ // Guard constant for consecutive bootstrap attempts that found phase === "complete".
52
+ // Counter moved to AutoSession.consecutiveCompleteBootstraps so s.reset() clears it.
56
53
  const MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS = 2;
57
54
  export async function openProjectDbIfPresent(basePath) {
58
55
  const gsdDbPath = resolveProjectRootDbPath(basePath);
@@ -263,9 +260,9 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
263
260
  // Guard against recursive dialog loop (#1348):
264
261
  // If we've entered this branch multiple times in quick succession,
265
262
  // the discuss workflow isn't producing a milestone. Break the cycle.
266
- _consecutiveCompleteBootstraps++;
267
- if (_consecutiveCompleteBootstraps > MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS) {
268
- _consecutiveCompleteBootstraps = 0;
263
+ s.consecutiveCompleteBootstraps++;
264
+ if (s.consecutiveCompleteBootstraps > MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS) {
265
+ s.consecutiveCompleteBootstraps = 0;
269
266
  ctx.ui.notify("All milestones are complete and the discussion didn't produce a new one. " +
270
267
  "Run /gsd to start a new milestone manually.", "warning");
271
268
  return releaseLockAndReturn();
@@ -277,7 +274,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
277
274
  if (postState.activeMilestone &&
278
275
  postState.phase !== "complete" &&
279
276
  postState.phase !== "pre-planning") {
280
- _consecutiveCompleteBootstraps = 0; // Successfully advanced past "complete"
277
+ s.consecutiveCompleteBootstraps = 0; // Successfully advanced past "complete"
281
278
  state = postState;
282
279
  }
283
280
  else if (postState.activeMilestone &&
@@ -338,7 +335,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
338
335
  return releaseLockAndReturn();
339
336
  }
340
337
  // Successfully resolved an active milestone — reset the re-entry guard
341
- _consecutiveCompleteBootstraps = 0;
338
+ s.consecutiveCompleteBootstraps = 0;
342
339
  // ── Initialize session state ──
343
340
  s.active = true;
344
341
  s.stepMode = requestedStepMode;
@@ -373,6 +370,22 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
373
370
  }
374
371
  setActiveMilestoneId(base, s.currentMilestoneId);
375
372
  }
373
+ // Guard against stale milestone branch when isolation:none (#3613).
374
+ // A prior session with isolation:branch/worktree may have left HEAD on
375
+ // milestone/<MID>. Auto-checkout back to the integration branch.
376
+ if (getIsolationMode() === "none" && nativeIsRepo(base)) {
377
+ try {
378
+ const currentBranch = nativeGetCurrentBranch(base);
379
+ if (currentBranch.startsWith("milestone/")) {
380
+ const integrationBranch = nativeDetectMainBranch(base);
381
+ nativeCheckoutBranch(base, integrationBranch);
382
+ logWarning("bootstrap", `Returned to "${integrationBranch}" — HEAD was on stale milestone branch "${currentBranch}" (isolation: none does not use milestone branches).`);
383
+ }
384
+ }
385
+ catch (err) {
386
+ logWarning("bootstrap", `Could not auto-checkout from stale milestone branch: ${err instanceof Error ? err.message : String(err)}`);
387
+ }
388
+ }
376
389
  // ── Auto-worktree setup ──
377
390
  s.originalBasePath = base;
378
391
  const isUnderGsdWorktrees = (p) => {
@@ -448,6 +461,24 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
448
461
  id: startModelSnapshot.id,
449
462
  };
450
463
  }
464
+ // Apply worker model override from parallel orchestrator (#worker-model).
465
+ // GSD_WORKER_MODEL is injected by the coordinator when parallel.worker_model
466
+ // is configured, so parallel milestone workers use a cheaper model than the
467
+ // coordinator session (e.g. Haiku for execution, Sonnet for planning).
468
+ const workerModelOverride = process.env.GSD_WORKER_MODEL;
469
+ if (workerModelOverride && process.env.GSD_PARALLEL_WORKER === "1") {
470
+ const availableModels = ctx.modelRegistry.getAvailable();
471
+ const { resolveModelId } = await import("./auto-model-selection.js");
472
+ const overrideModel = resolveModelId(workerModelOverride, availableModels, ctx.model?.provider);
473
+ if (overrideModel) {
474
+ const ok = await pi.setModel(overrideModel, { persist: false });
475
+ if (ok) {
476
+ // Update start model so all subsequent units use this as the baseline
477
+ s.autoModeStartModel = { provider: overrideModel.provider, id: overrideModel.id };
478
+ ctx.ui.notify(`Worker model override: ${overrideModel.provider}/${overrideModel.id}`, "info");
479
+ }
480
+ }
481
+ }
451
482
  // Snapshot installed skills
452
483
  if (resolveSkillDiscoveryMode() !== "off") {
453
484
  snapshotSkills();
@@ -92,3 +92,13 @@ export function isToolInvocationError(errorMsg) {
92
92
  return false;
93
93
  return TOOL_INVOCATION_ERROR_RE.test(errorMsg);
94
94
  }
95
+ /**
96
+ * Returns true if the error message indicates the tool was skipped because
97
+ * a queued user message interrupted the turn (#3595). Retrying will produce
98
+ * the same skip, so the unit should be paused rather than retried.
99
+ */
100
+ export function isQueuedUserMessageSkip(errorMsg) {
101
+ if (!errorMsg)
102
+ return false;
103
+ return /^Skipped due to queued user message\.?$/i.test(errorMsg.trim());
104
+ }
@@ -135,8 +135,10 @@ function clearProjectRootStateFiles(basePath, milestoneId) {
135
135
  unlinkSync(file);
136
136
  }
137
137
  catch (err) {
138
- /* non-fatal — file may not exist */
139
- logWarning("worktree", `file unlink failed: ${err instanceof Error ? err.message : String(err)}`);
138
+ // ENOENT is expected — file may not exist (#3597)
139
+ if (err.code !== "ENOENT") {
140
+ logWarning("worktree", `file unlink failed: ${err instanceof Error ? err.message : String(err)}`);
141
+ }
140
142
  }
141
143
  }
142
144
  // Clean up entire synced milestone directory and runtime/units.
@@ -160,8 +162,11 @@ function clearProjectRootStateFiles(basePath, milestoneId) {
160
162
  unlinkSync(join(basePath, f));
161
163
  }
162
164
  catch (err) {
163
- /* non-fatal */
164
- logWarning("worktree", `untracked file unlink failed: ${err instanceof Error ? err.message : String(err)}`);
165
+ // ENOENT/EISDIR are expected for already-removed or directory entries (#3597)
166
+ const code = err.code;
167
+ if (code !== "ENOENT" && code !== "EISDIR") {
168
+ logWarning("worktree", `untracked file unlink failed: ${err instanceof Error ? err.message : String(err)}`);
169
+ }
165
170
  }
166
171
  }
167
172
  }
@@ -656,6 +661,10 @@ export function syncWorktreeStateBack(mainBasePath, worktreePath, milestoneId) {
656
661
  .filter((d) => d.isDirectory())
657
662
  .map((d) => d.name);
658
663
  for (const mid of wtMilestones) {
664
+ // Skip the current milestone being merged — its files are already in the
665
+ // milestone branch and would conflict with the squash merge (#3641).
666
+ if (mid === milestoneId)
667
+ continue;
659
668
  syncMilestoneDir(wtGsd, mainGsd, mid, synced);
660
669
  }
661
670
  }
@@ -901,11 +910,19 @@ export function createAutoWorktree(basePath, milestoneId) {
901
910
  });
902
911
  }
903
912
  else {
904
- // Fresh start — create branch from integration branch
913
+ // Fresh start — create branch from integration branch.
914
+ // Use the same 3-tier fallback as mergeMilestoneToMain (#3461):
915
+ // 1. META.json integration branch (explicit per-milestone override)
916
+ // 2. git.main_branch preference (user's configured working branch)
917
+ // 3. nativeDetectMainBranch (origin/HEAD auto-detection)
918
+ // Without tier 2, projects with main_branch=dev but origin/HEAD→master
919
+ // would fork worktrees from the wrong (stale) branch.
905
920
  const integrationBranch = readIntegrationBranch(basePath, milestoneId) ?? undefined;
921
+ const gitPrefs = loadEffectiveGSDPreferences()?.preferences?.git;
922
+ const startPoint = integrationBranch ?? gitPrefs?.main_branch ?? undefined;
906
923
  info = createWorktree(basePath, milestoneId, {
907
924
  branch,
908
- startPoint: integrationBranch,
925
+ startPoint,
909
926
  });
910
927
  }
911
928
  // Copy .gsd/ planning artifacts from the source repo into the new worktree.
@@ -1235,7 +1252,12 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1235
1252
  // checkout and leave the user with a broken merge state (#1668).
1236
1253
  const prefs = loadEffectiveGSDPreferences()?.preferences?.git ?? {};
1237
1254
  const integrationBranch = readIntegrationBranch(originalBasePath_, milestoneId);
1238
- const mainBranch = integrationBranch ?? prefs.main_branch ?? nativeDetectMainBranch(originalBasePath_);
1255
+ // Validate prefs.main_branch exists before using it — a stale preference
1256
+ // (e.g. "master" when repo uses "main") causes merge failure (#3589).
1257
+ const validatedPrefBranch = prefs.main_branch && nativeBranchExists(originalBasePath_, prefs.main_branch)
1258
+ ? prefs.main_branch
1259
+ : undefined;
1260
+ const mainBranch = integrationBranch ?? validatedPrefBranch ?? nativeDetectMainBranch(originalBasePath_);
1239
1261
  // Remove transient project-root state files before any branch or merge
1240
1262
  // operation. Untracked milestone metadata can otherwise block squash merges.
1241
1263
  clearProjectRootStateFiles(originalBasePath_, milestoneId);
@@ -23,7 +23,7 @@ import { acquireSessionLock, getSessionLockStatus, releaseSessionLock, updateSes
23
23
  import { resolveAutoSupervisorConfig, loadEffectiveGSDPreferences, getIsolationMode, } from "./preferences.js";
24
24
  import { sendDesktopNotification } from "./notifications.js";
25
25
  import { getBudgetAlertLevel, getNewBudgetAlertLevel, getBudgetEnforcementAction, } from "./auto-budget.js";
26
- import { markToolStart as _markToolStart, markToolEnd as _markToolEnd, getOldestInFlightToolAgeMs as _getOldestInFlightToolAgeMs, clearInFlightTools, isToolInvocationError, } from "./auto-tool-tracking.js";
26
+ import { markToolStart as _markToolStart, markToolEnd as _markToolEnd, getOldestInFlightToolAgeMs as _getOldestInFlightToolAgeMs, clearInFlightTools, isToolInvocationError, isQueuedUserMessageSkip, } from "./auto-tool-tracking.js";
27
27
  import { closeoutUnit } from "./auto-unit-closeout.js";
28
28
  import { selectAndApplyModel, resolveModelId } from "./auto-model-selection.js";
29
29
  import { resetRoutingHistory, recordOutcome } from "./routing-history.js";
@@ -201,7 +201,7 @@ export function markToolEnd(toolCallId) {
201
201
  export function recordToolInvocationError(toolName, errorMsg) {
202
202
  if (!s.active)
203
203
  return;
204
- if (isToolInvocationError(errorMsg)) {
204
+ if (isToolInvocationError(errorMsg) || isQueuedUserMessageSkip(errorMsg)) {
205
205
  s.lastToolInvocationError = `${toolName}: ${errorMsg}`;
206
206
  }
207
207
  }
@@ -845,12 +845,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
845
845
  s.stepMode = meta.stepMode ?? requestedStepMode;
846
846
  s.autoStartTime = meta.autoStartTime || Date.now();
847
847
  s.paused = true;
848
- try {
849
- unlinkSync(pausedPath);
850
- }
851
- catch (err) { /* non-fatal */
852
- logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
853
- }
848
+ // Don't delete pause file yet — defer until lock is acquired.
849
+ // If lock fails, the file must survive for retry.
850
+ s.pausedSessionFile = pausedPath;
854
851
  ctx.ui.notify(`Resuming paused custom workflow${meta.activeRunDir ? ` (${meta.activeRunDir})` : ""}.`, "info");
855
852
  }
856
853
  else if (meta.milestoneId) {
@@ -873,13 +870,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
873
870
  s.stepMode = meta.stepMode ?? requestedStepMode;
874
871
  s.autoStartTime = meta.autoStartTime || Date.now();
875
872
  s.paused = true;
876
- // Clean up the persisted file — we're consuming it
877
- try {
878
- unlinkSync(pausedPath);
879
- }
880
- catch (err) { /* non-fatal */
881
- logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
882
- }
873
+ // Don't delete pause file yet defer until lock is acquired.
874
+ // If lock fails, the file must survive for retry.
875
+ s.pausedSessionFile = pausedPath;
883
876
  ctx.ui.notify(`Resuming paused session for ${meta.milestoneId}${meta.worktreePath ? ` (worktree)` : ""}.`, "info");
884
877
  }
885
878
  }
@@ -893,9 +886,22 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
893
886
  if (s.paused) {
894
887
  const resumeLock = acquireSessionLock(base);
895
888
  if (!resumeLock.acquired) {
889
+ // Reset paused state so isAutoPaused() doesn't stick true after lock failure.
890
+ // Pause file is preserved on disk for retry — not deleted.
891
+ s.paused = false;
896
892
  ctx.ui.notify(`Cannot resume: ${resumeLock.reason}`, "error");
897
893
  return;
898
894
  }
895
+ // Lock acquired — now safe to delete the pause file
896
+ if (s.pausedSessionFile) {
897
+ try {
898
+ unlinkSync(s.pausedSessionFile);
899
+ }
900
+ catch (err) {
901
+ logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
902
+ }
903
+ s.pausedSessionFile = null;
904
+ }
899
905
  s.paused = false;
900
906
  s.active = true;
901
907
  s.verbose = verboseMode;
@@ -79,11 +79,24 @@ export async function handleAgentEnd(pi, event, ctx) {
79
79
  return;
80
80
  }
81
81
  if (lastMsg && "stopReason" in lastMsg && lastMsg.stopReason === "error") {
82
- const errorDetail = "errorMessage" in lastMsg && lastMsg.errorMessage ? `: ${lastMsg.errorMessage}` : "";
83
- const errorMsg = ("errorMessage" in lastMsg && lastMsg.errorMessage) ? String(lastMsg.errorMessage) : "";
82
+ // #3588: errorMessage can be useless (e.g. "success") while the real error
83
+ // is in the assistant message text content. Fall back to content when
84
+ // errorMessage looks uninformative.
85
+ const rawErrorMsg = ("errorMessage" in lastMsg && lastMsg.errorMessage) ? String(lastMsg.errorMessage) : "";
86
+ const isUseless = !rawErrorMsg || /^(success|ok|true|error|unknown)$/i.test(rawErrorMsg.trim());
87
+ // #3588: When errorMessage is uninformative, extract the real error from
88
+ // the assistant message text content for display purposes only.
89
+ // Classification still uses rawErrorMsg to avoid false positives from prose.
90
+ let displayMsg = rawErrorMsg;
91
+ if (isUseless && "content" in lastMsg && Array.isArray(lastMsg.content)) {
92
+ const textBlock = lastMsg.content.find((b) => b.type === "text" && b.text);
93
+ if (textBlock)
94
+ displayMsg = textBlock.text.slice(0, 300);
95
+ }
96
+ const errorDetail = displayMsg ? `: ${displayMsg}` : "";
84
97
  const explicitRetryAfterMs = ("retryAfterMs" in lastMsg && typeof lastMsg.retryAfterMs === "number") ? lastMsg.retryAfterMs : undefined;
85
- // ── 1. Classify ──────────────────────────────────────────────────────
86
- const cls = classifyError(errorMsg, explicitRetryAfterMs);
98
+ // ── 1. Classify using rawErrorMsg to avoid prose false-positives ────
99
+ const cls = classifyError(rawErrorMsg, explicitRetryAfterMs);
87
100
  // Cap rate-limit backoff for CLI-style providers (openai-codex, google-gemini-cli)
88
101
  // which use per-user quotas with shorter windows (#2922).
89
102
  if (cls.kind === "rate-limit") {
@@ -943,6 +943,16 @@ export function registerDbTools(pi) {
943
943
  }
944
944
  updateSliceStatus(params.milestoneId, params.sliceId, "skipped");
945
945
  invalidateStateCache();
946
+ // Rebuild STATE.md so it reflects the skip immediately (#3477).
947
+ // Without this, /gsd auto reads stale STATE.md and resumes the skipped slice.
948
+ try {
949
+ const basePath = process.cwd();
950
+ const { rebuildState } = await import("../doctor.js");
951
+ await rebuildState(basePath);
952
+ }
953
+ catch (err) {
954
+ logError("tool", `skip_slice rebuildState failed: ${err.message}`, { tool: "gsd_skip_slice" });
955
+ }
946
956
  return {
947
957
  content: [{ type: "text", text: `Skipped slice ${params.sliceId} (${params.milestoneId}). Reason: ${params.reason ?? "User-directed skip"}. Auto-mode will advance past this slice.` }],
948
958
  details: {
@@ -17,10 +17,12 @@ export function registerQueryTools(pi) {
17
17
  }),
18
18
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
19
19
  try {
20
- // Strictly read-only: only use an already-open DB connection.
21
- // Do NOT call ensureDbOpen() it can create/migrate the DB as a side effect.
22
- const { isDbAvailable, getMilestone, getSliceStatusSummary, getSliceTaskCounts, _getAdapter, } = await import("../gsd-db.js");
23
- if (!isDbAvailable()) {
20
+ // Open the DB if not already open safe for read-only use since
21
+ // ensureDbOpen() only creates/migrates when .gsd/ has content (#3644).
22
+ const { ensureDbOpen } = await import("./dynamic-tools.js");
23
+ const dbAvailable = await ensureDbOpen();
24
+ const { getMilestone, getSliceStatusSummary, getSliceTaskCounts, _getAdapter, } = await import("../gsd-db.js");
25
+ if (!dbAvailable) {
24
26
  return {
25
27
  content: [{ type: "text", text: "Error: GSD database is not available." }],
26
28
  details: { operation: "milestone_status", error: "db_unavailable" },
@@ -31,7 +31,11 @@ function installEpipeGuard() {
31
31
  if (handleRecoverableExtensionProcessError(err)) {
32
32
  return;
33
33
  }
34
- throw err;
34
+ // Log unhandled errors instead of re-throwing — throwing inside an
35
+ // uncaughtException handler is a fatal double-fault in Node.js (#3163).
36
+ process.stderr.write(`[gsd] uncaught extension error (non-fatal): ${err.message}\n`);
37
+ if (err.stack)
38
+ process.stderr.write(`${err.stack}\n`);
35
39
  };
36
40
  process.on("uncaughtException", _gsdEpipeGuard);
37
41
  }
@@ -3,7 +3,7 @@ import { isToolCallEventType } from "@gsd/pi-coding-agent";
3
3
  import { buildMilestoneFileName, resolveMilestonePath, resolveSliceFile, resolveSlicePath } from "../paths.js";
4
4
  import { buildBeforeAgentStartResult } from "./system-context.js";
5
5
  import { handleAgentEnd } from "./agent-end-recovery.js";
6
- import { clearDiscussionFlowState, isDepthVerified, isQueuePhaseActive, markDepthVerified, resetWriteGateState, shouldBlockContextWrite, shouldBlockQueueExecution } from "./write-gate.js";
6
+ import { clearDiscussionFlowState, isDepthVerified, isDepthConfirmationAnswer, isQueuePhaseActive, markDepthVerified, resetWriteGateState, shouldBlockContextWrite, shouldBlockQueueExecution } from "./write-gate.js";
7
7
  import { isBlockedStateFile, isBashWriteToStateFile, BLOCKED_WRITE_ERROR } from "../write-intercept.js";
8
8
  import { cleanupQuickBranch } from "../quick.js";
9
9
  import { getDiscussionMilestoneId } from "../guided-flow.js";
@@ -98,7 +98,10 @@ export function registerHooks(pi) {
98
98
  }
99
99
  });
100
100
  pi.on("session_before_compact", async () => {
101
- if (isAutoActive() || isAutoPaused()) {
101
+ // Only cancel compaction while auto-mode is actively running.
102
+ // Paused auto-mode should allow compaction — the user may be doing
103
+ // interactive work (#3165).
104
+ if (isAutoActive()) {
102
105
  return { cancel: true };
103
106
  }
104
107
  const basePath = process.cwd();
@@ -227,7 +230,12 @@ export function registerHooks(pi) {
227
230
  const questions = event.input?.questions ?? [];
228
231
  for (const question of questions) {
229
232
  if (typeof question.id === "string" && question.id.includes("depth_verification")) {
230
- markDepthVerified();
233
+ // Only unlock the gate if the user selected the first option (confirmation).
234
+ // Cross-references against the question's defined options to reject free-form "Other" text.
235
+ const answer = details.response?.answers?.[question.id];
236
+ if (isDepthConfirmationAnswer(answer?.selected, question.options)) {
237
+ markDepthVerified();
238
+ }
231
239
  break;
232
240
  }
233
241
  }
@@ -43,6 +43,30 @@ export function clearDiscussionFlowState() {
43
43
  export function markDepthVerified() {
44
44
  depthVerificationDone = true;
45
45
  }
46
+ /**
47
+ * Check whether a depth_verification answer confirms the discussion is complete.
48
+ * Uses structural validation: the selected answer must exactly match the first
49
+ * option label from the question definition (the confirmation option by convention).
50
+ * This rejects free-form "Other" text, decline options, and garbage input without
51
+ * coupling to any specific label substring.
52
+ *
53
+ * @param selected The answer's selected value from details.response.answers[id].selected
54
+ * @param options The question's options array from event.input.questions[n].options
55
+ */
56
+ export function isDepthConfirmationAnswer(selected, options) {
57
+ const value = Array.isArray(selected) ? selected[0] : selected;
58
+ if (typeof value !== "string" || !value)
59
+ return false;
60
+ // If options are available, structurally validate: selected must exactly match
61
+ // the first option (confirmation) label. Rejects free-form "Other" and decline options.
62
+ if (Array.isArray(options) && options.length > 0) {
63
+ const confirmLabel = options[0]?.label;
64
+ return typeof confirmLabel === "string" && value === confirmLabel;
65
+ }
66
+ // Fallback when options aren't available (e.g., older call sites):
67
+ // accept only if it contains "(Recommended)" — the prompt convention suffix.
68
+ return value.includes("(Recommended)");
69
+ }
46
70
  export function shouldBlockContextWrite(toolName, inputPath, milestoneId, depthVerified, queuePhaseActive) {
47
71
  if (toolName !== "write")
48
72
  return { block: false };
@@ -56,7 +80,13 @@ export function shouldBlockContextWrite(toolName, inputPath, milestoneId, depthV
56
80
  return { block: false };
57
81
  return {
58
82
  block: true,
59
- 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.`,
83
+ reason: [
84
+ `HARD BLOCK: Cannot write to milestone CONTEXT.md without depth verification.`,
85
+ `This is a mechanical gate — you MUST NOT proceed, retry, or rationalize past this block.`,
86
+ `Required action: call ask_user_questions with question id containing "depth_verification".`,
87
+ `The user MUST select the "(Recommended)" confirmation option to unlock this gate.`,
88
+ `If the user declines, cancels, or the tool fails, you must re-ask — not bypass.`,
89
+ ].join(" "),
60
90
  };
61
91
  }
62
92
  /**
@@ -4,7 +4,14 @@ import { resolveProjectRoot } from "../worktree.js";
4
4
  import { showNextAction } from "../../shared/tui.js";
5
5
  import { handleStatus } from "./handlers/core.js";
6
6
  export function projectRoot() {
7
- const cwd = process.cwd();
7
+ let cwd;
8
+ try {
9
+ cwd = process.cwd();
10
+ }
11
+ catch {
12
+ // cwd directory was deleted (e.g. worktree teardown) — fall back to HOME (#3598)
13
+ cwd = process.env.HOME ?? "/";
14
+ }
8
15
  const root = resolveProjectRoot(cwd);
9
16
  if (root !== cwd) {
10
17
  assertSafeDirectory(cwd);
@@ -48,6 +48,7 @@ export function showHelp(ctx) {
48
48
  " /gsd cmux Manage cmux integration [status|on|off|notifications|sidebar|splits|browser]",
49
49
  " /gsd config Set API keys for external tools",
50
50
  " /gsd keys API key manager [list|add|remove|test|rotate|doctor]",
51
+ " /gsd show-config Show effective configuration (models, routing, toggles)",
51
52
  " /gsd hooks Show post-unit hook configuration",
52
53
  " /gsd extensions Manage extensions [list|enable|disable|info]",
53
54
  " /gsd fast Toggle OpenAI service tier [on|off|flex|status]",
@@ -66,6 +67,9 @@ export function showHelp(ctx) {
66
67
  }
67
68
  export async function handleStatus(ctx) {
68
69
  const basePath = projectRoot();
70
+ // Open DB in cold sessions so status uses DB-backed state, not filesystem fallback (#3385)
71
+ const { ensureDbOpen } = await import("../../bootstrap/dynamic-tools.js");
72
+ await ensureDbOpen();
69
73
  const state = await deriveState(basePath);
70
74
  if (state.registry.length === 0) {
71
75
  ctx.ui.notify("No GSD milestones found. Run /gsd to start.", "info");
@@ -188,6 +192,22 @@ export async function handleCoreCommand(trimmed, ctx) {
188
192
  await handleCmux(trimmed.replace(/^cmux\s*/, "").trim(), ctx);
189
193
  return true;
190
194
  }
195
+ if (trimmed === "show-config") {
196
+ const { GSDConfigOverlay, formatConfigText } = await import("../../config-overlay.js");
197
+ const result = await ctx.ui.custom((tui, theme, _kb, done) => new GSDConfigOverlay(tui, theme, () => done()), {
198
+ overlay: true,
199
+ overlayOptions: {
200
+ width: "65%",
201
+ minWidth: 55,
202
+ maxHeight: "85%",
203
+ anchor: "center",
204
+ },
205
+ });
206
+ if (result === undefined) {
207
+ ctx.ui.notify(formatConfigText(), "info");
208
+ }
209
+ return true;
210
+ }
191
211
  if (trimmed === "setup" || trimmed.startsWith("setup ")) {
192
212
  await handleSetup(trimmed.replace(/^setup\s*/, "").trim(), ctx);
193
213
  return true;
@@ -68,7 +68,7 @@ function discoverManifests() {
68
68
  if (!existsSync(extDir))
69
69
  return manifests;
70
70
  for (const entry of readdirSync(extDir, { withFileTypes: true })) {
71
- if (!entry.isDirectory())
71
+ if (!entry.isDirectory() && !entry.isSymbolicLink())
72
72
  continue;
73
73
  const m = readManifest(join(extDir, entry.name));
74
74
  if (m)