gsd-pi 2.76.0-dev.4100bd590 → 2.76.0-dev.479ad0e78

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 (362) hide show
  1. package/dist/claude-cli-check.js +32 -3
  2. package/dist/mcp-server.d.ts +7 -0
  3. package/dist/mcp-server.js +35 -1
  4. package/dist/onboarding.js +45 -0
  5. package/dist/resource-loader.d.ts +1 -1
  6. package/dist/resource-loader.js +2 -8
  7. package/dist/resources/extensions/claude-code-cli/readiness.js +4 -3
  8. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +77 -17
  9. package/dist/resources/extensions/gsd/auto/loop.js +9 -0
  10. package/dist/resources/extensions/gsd/auto/phases.js +58 -5
  11. package/dist/resources/extensions/gsd/auto/run-unit.js +38 -2
  12. package/dist/resources/extensions/gsd/auto/session.js +22 -1
  13. package/dist/resources/extensions/gsd/auto-dispatch.js +16 -3
  14. package/dist/resources/extensions/gsd/auto-model-selection.js +14 -3
  15. package/dist/resources/extensions/gsd/auto-post-unit.js +25 -2
  16. package/dist/resources/extensions/gsd/auto-prompts.js +14 -0
  17. package/dist/resources/extensions/gsd/auto-recovery.js +32 -1
  18. package/dist/resources/extensions/gsd/auto-start.js +58 -57
  19. package/dist/resources/extensions/gsd/auto-worktree.js +51 -53
  20. package/dist/resources/extensions/gsd/auto.js +70 -28
  21. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -1
  22. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +39 -9
  23. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +93 -0
  24. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +52 -6
  26. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +34 -2
  27. package/dist/resources/extensions/gsd/clean-root-preflight.js +93 -0
  28. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +968 -23
  29. package/dist/resources/extensions/gsd/compaction-snapshot.js +121 -0
  30. package/dist/resources/extensions/gsd/complexity-classifier.js +5 -3
  31. package/dist/resources/extensions/gsd/error-classifier.js +10 -3
  32. package/dist/resources/extensions/gsd/exec-history.js +120 -0
  33. package/dist/resources/extensions/gsd/exec-sandbox.js +258 -0
  34. package/dist/resources/extensions/gsd/gitignore.js +1 -0
  35. package/dist/resources/extensions/gsd/gsd-db.js +149 -31
  36. package/dist/resources/extensions/gsd/guided-flow.js +190 -1
  37. package/dist/resources/extensions/gsd/health-widget.js +4 -1
  38. package/dist/resources/extensions/gsd/init-wizard.js +15 -1
  39. package/dist/resources/extensions/gsd/key-manager.js +28 -0
  40. package/dist/resources/extensions/gsd/model-router.js +36 -3
  41. package/dist/resources/extensions/gsd/pre-execution-checks.js +44 -9
  42. package/dist/resources/extensions/gsd/preferences-types.js +9 -0
  43. package/dist/resources/extensions/gsd/preferences-validation.js +83 -0
  44. package/dist/resources/extensions/gsd/preferences.js +17 -17
  45. package/dist/resources/extensions/gsd/prompt-loader.js +22 -7
  46. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
  47. package/dist/resources/extensions/gsd/prompts/discuss.md +29 -2
  48. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
  49. package/dist/resources/extensions/gsd/safety/evidence-collector.js +96 -0
  50. package/dist/resources/extensions/gsd/safety/file-change-validator.js +13 -5
  51. package/dist/resources/extensions/gsd/safety/safety-harness.js +5 -1
  52. package/dist/resources/extensions/gsd/token-counter.js +22 -5
  53. package/dist/resources/extensions/gsd/tools/complete-milestone.js +16 -10
  54. package/dist/resources/extensions/gsd/tools/exec-search-tool.js +59 -0
  55. package/dist/resources/extensions/gsd/tools/exec-tool.js +126 -0
  56. package/dist/resources/extensions/gsd/tools/resume-tool.js +23 -0
  57. package/dist/resources/extensions/gsd/uok/plan-v2.js +20 -3
  58. package/dist/resources/extensions/gsd/workflow-mcp.js +3 -0
  59. package/dist/resources/extensions/gsd/worktree-resolver.js +50 -10
  60. package/dist/resources/skills/verify-before-complete/SKILL.md +2 -1
  61. package/dist/resources/skills/write-docs/SKILL.md +2 -1
  62. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  63. package/dist/web/standalone/.next/BUILD_ID +1 -1
  64. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  65. package/dist/web/standalone/.next/build-manifest.json +2 -2
  66. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  67. package/dist/web/standalone/.next/required-server-files.json +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/index.html +1 -1
  85. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  92. package/dist/web/standalone/.next/server/chunks/6897.js +2 -2
  93. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  95. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  96. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  97. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  98. package/dist/web/standalone/server.js +1 -1
  99. package/dist/welcome-screen.js +6 -1
  100. package/dist/wizard.js +2 -0
  101. package/package.json +1 -1
  102. package/packages/mcp-server/dist/remote-questions.d.ts +45 -0
  103. package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -0
  104. package/packages/mcp-server/dist/remote-questions.js +732 -0
  105. package/packages/mcp-server/dist/remote-questions.js.map +1 -0
  106. package/packages/mcp-server/dist/server.d.ts +7 -0
  107. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  108. package/packages/mcp-server/dist/server.js +70 -8
  109. package/packages/mcp-server/dist/server.js.map +1 -1
  110. package/packages/mcp-server/dist/session-manager.d.ts +14 -0
  111. package/packages/mcp-server/dist/session-manager.d.ts.map +1 -1
  112. package/packages/mcp-server/dist/session-manager.js +49 -1
  113. package/packages/mcp-server/dist/session-manager.js.map +1 -1
  114. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  115. package/packages/mcp-server/dist/workflow-tools.js +64 -25
  116. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  117. package/packages/mcp-server/package.json +2 -1
  118. package/packages/mcp-server/src/mcp-server.test.ts +67 -0
  119. package/packages/mcp-server/src/remote-questions.test.ts +294 -0
  120. package/packages/mcp-server/src/remote-questions.ts +916 -0
  121. package/packages/mcp-server/src/server.ts +89 -14
  122. package/packages/mcp-server/src/session-manager.ts +43 -1
  123. package/packages/mcp-server/src/workflow-tools.test.ts +146 -1
  124. package/packages/mcp-server/src/workflow-tools.ts +84 -43
  125. package/packages/mcp-server/tsconfig.test.json +19 -0
  126. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  127. package/packages/pi-ai/dist/models/custom.d.ts +38 -0
  128. package/packages/pi-ai/dist/models/custom.d.ts.map +1 -1
  129. package/packages/pi-ai/dist/models/custom.js +41 -0
  130. package/packages/pi-ai/dist/models/custom.js.map +1 -1
  131. package/packages/pi-ai/dist/providers/anthropic-auth.test.js +1 -1
  132. package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -1
  133. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  134. package/packages/pi-ai/dist/providers/anthropic-shared.js +27 -4
  135. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  136. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  137. package/packages/pi-ai/dist/providers/anthropic.js +8 -3
  138. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  139. package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts +2 -0
  140. package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts.map +1 -0
  141. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js +80 -0
  142. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js.map +1 -0
  143. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  144. package/packages/pi-ai/dist/providers/openai-completions.js +60 -15
  145. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  146. package/packages/pi-ai/dist/providers/simple-options.d.ts +10 -0
  147. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  148. package/packages/pi-ai/dist/providers/simple-options.js +16 -1
  149. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  150. package/packages/pi-ai/dist/providers/think-tag-parser.d.ts +17 -0
  151. package/packages/pi-ai/dist/providers/think-tag-parser.d.ts.map +1 -0
  152. package/packages/pi-ai/dist/providers/think-tag-parser.js +75 -0
  153. package/packages/pi-ai/dist/providers/think-tag-parser.js.map +1 -0
  154. package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts +2 -0
  155. package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts.map +1 -0
  156. package/packages/pi-ai/dist/providers/think-tag-parser.test.js +41 -0
  157. package/packages/pi-ai/dist/providers/think-tag-parser.test.js.map +1 -0
  158. package/packages/pi-ai/src/models/custom.ts +42 -0
  159. package/packages/pi-ai/src/providers/anthropic-auth.test.ts +1 -1
  160. package/packages/pi-ai/src/providers/anthropic-shared.ts +26 -5
  161. package/packages/pi-ai/src/providers/anthropic.ts +9 -3
  162. package/packages/pi-ai/src/providers/minimax-tool-name.test.ts +98 -0
  163. package/packages/pi-ai/src/providers/openai-completions.ts +57 -16
  164. package/packages/pi-ai/src/providers/simple-options.ts +17 -1
  165. package/packages/pi-ai/src/providers/think-tag-parser.test.ts +44 -0
  166. package/packages/pi-ai/src/providers/think-tag-parser.ts +94 -0
  167. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  168. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +3 -2
  169. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  170. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +2 -0
  171. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  172. package/packages/pi-coding-agent/dist/core/agent-session.js +7 -0
  173. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  174. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -0
  175. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  176. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  177. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +7 -0
  178. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  179. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  180. package/packages/pi-coding-agent/dist/core/model-discovery.d.ts +3 -1
  181. package/packages/pi-coding-agent/dist/core/model-discovery.d.ts.map +1 -1
  182. package/packages/pi-coding-agent/dist/core/model-discovery.js +92 -12
  183. package/packages/pi-coding-agent/dist/core/model-discovery.js.map +1 -1
  184. package/packages/pi-coding-agent/dist/core/model-discovery.test.js +16 -1
  185. package/packages/pi-coding-agent/dist/core/model-discovery.test.js.map +1 -1
  186. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts +2 -0
  187. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts.map +1 -0
  188. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js +203 -0
  189. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js.map +1 -0
  190. package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js +61 -1
  191. package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js.map +1 -1
  192. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +5 -0
  193. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  194. package/packages/pi-coding-agent/dist/core/model-registry.js +90 -10
  195. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  196. package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts +2 -0
  197. package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts.map +1 -0
  198. package/packages/pi-coding-agent/dist/core/redact-secrets.js +49 -0
  199. package/packages/pi-coding-agent/dist/core/redact-secrets.js.map +1 -0
  200. package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts +2 -0
  201. package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts.map +1 -0
  202. package/packages/pi-coding-agent/dist/core/redact-secrets.test.js +67 -0
  203. package/packages/pi-coding-agent/dist/core/redact-secrets.test.js.map +1 -0
  204. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  205. package/packages/pi-coding-agent/dist/core/session-manager.js +10 -6
  206. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  207. package/packages/pi-coding-agent/dist/core/session-manager.test.js +45 -1
  208. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  209. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts +1 -1
  210. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  211. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +5 -4
  212. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  213. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
  214. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +13 -7
  215. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
  216. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts +7 -6
  217. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  218. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +29 -21
  219. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  220. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  221. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +13 -1
  222. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  223. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +3 -2
  224. package/packages/pi-coding-agent/src/core/agent-session.ts +11 -0
  225. package/packages/pi-coding-agent/src/core/extensions/runner.ts +2 -0
  226. package/packages/pi-coding-agent/src/core/extensions/types.ts +7 -0
  227. package/packages/pi-coding-agent/src/core/model-discovery.test.ts +19 -0
  228. package/packages/pi-coding-agent/src/core/model-discovery.ts +99 -12
  229. package/packages/pi-coding-agent/src/core/model-registry-custom-caps.test.ts +245 -0
  230. package/packages/pi-coding-agent/src/core/model-registry-discovery.test.ts +75 -0
  231. package/packages/pi-coding-agent/src/core/model-registry.ts +102 -10
  232. package/packages/pi-coding-agent/src/core/redact-secrets.test.ts +86 -0
  233. package/packages/pi-coding-agent/src/core/redact-secrets.ts +58 -0
  234. package/packages/pi-coding-agent/src/core/session-manager.test.ts +65 -1
  235. package/packages/pi-coding-agent/src/core/session-manager.ts +10 -6
  236. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +6 -6
  237. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +16 -7
  238. package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +36 -22
  239. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +13 -1
  240. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  241. package/scripts/link-workspace-packages.cjs +1 -0
  242. package/src/resources/extensions/claude-code-cli/readiness.ts +4 -3
  243. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +78 -17
  244. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +149 -5
  245. package/src/resources/extensions/gsd/auto/loop-deps.ts +14 -0
  246. package/src/resources/extensions/gsd/auto/loop.ts +9 -0
  247. package/src/resources/extensions/gsd/auto/phases.ts +82 -4
  248. package/src/resources/extensions/gsd/auto/run-unit.ts +40 -2
  249. package/src/resources/extensions/gsd/auto/session.ts +35 -2
  250. package/src/resources/extensions/gsd/auto-dispatch.ts +16 -3
  251. package/src/resources/extensions/gsd/auto-model-selection.ts +17 -2
  252. package/src/resources/extensions/gsd/auto-post-unit.ts +29 -3
  253. package/src/resources/extensions/gsd/auto-prompts.ts +28 -1
  254. package/src/resources/extensions/gsd/auto-recovery.ts +26 -1
  255. package/src/resources/extensions/gsd/auto-start.ts +60 -68
  256. package/src/resources/extensions/gsd/auto-worktree.ts +62 -63
  257. package/src/resources/extensions/gsd/auto.ts +73 -28
  258. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +23 -1
  259. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +40 -9
  260. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +109 -0
  261. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
  262. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +54 -6
  263. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +35 -2
  264. package/src/resources/extensions/gsd/clean-root-preflight.ts +111 -0
  265. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +898 -32
  266. package/src/resources/extensions/gsd/compaction-snapshot.ts +165 -0
  267. package/src/resources/extensions/gsd/complexity-classifier.ts +5 -3
  268. package/src/resources/extensions/gsd/error-classifier.ts +10 -3
  269. package/src/resources/extensions/gsd/exec-history.ts +153 -0
  270. package/src/resources/extensions/gsd/exec-sandbox.ts +326 -0
  271. package/src/resources/extensions/gsd/gitignore.ts +1 -1
  272. package/src/resources/extensions/gsd/gsd-db.ts +157 -33
  273. package/src/resources/extensions/gsd/guided-flow.ts +222 -1
  274. package/src/resources/extensions/gsd/health-widget.ts +3 -1
  275. package/src/resources/extensions/gsd/init-wizard.ts +15 -1
  276. package/src/resources/extensions/gsd/journal.ts +2 -1
  277. package/src/resources/extensions/gsd/key-manager.ts +28 -0
  278. package/src/resources/extensions/gsd/model-router.ts +42 -1
  279. package/src/resources/extensions/gsd/pre-execution-checks.ts +46 -10
  280. package/src/resources/extensions/gsd/preferences-types.ts +46 -0
  281. package/src/resources/extensions/gsd/preferences-validation.ts +79 -0
  282. package/src/resources/extensions/gsd/preferences.ts +17 -17
  283. package/src/resources/extensions/gsd/prompt-loader.ts +30 -7
  284. package/src/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
  285. package/src/resources/extensions/gsd/prompts/discuss.md +29 -2
  286. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
  287. package/src/resources/extensions/gsd/safety/evidence-collector.ts +119 -0
  288. package/src/resources/extensions/gsd/safety/file-change-validator.ts +17 -4
  289. package/src/resources/extensions/gsd/safety/safety-harness.ts +9 -0
  290. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +188 -2
  291. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +12 -0
  292. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +12 -0
  293. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +49 -0
  294. package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +141 -0
  295. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +33 -3
  296. package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +38 -0
  297. package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +23 -0
  298. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +186 -0
  299. package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +123 -0
  300. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +61 -1
  301. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
  302. package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
  303. package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +3 -3
  304. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +2 -0
  305. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +31 -0
  306. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +1 -1
  307. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +1 -1
  308. package/src/resources/extensions/gsd/tests/escalation.test.ts +1 -1
  309. package/src/resources/extensions/gsd/tests/exec-history.test.ts +237 -0
  310. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +210 -0
  311. package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +58 -0
  312. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +447 -1
  313. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +27 -0
  314. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +1 -0
  315. package/src/resources/extensions/gsd/tests/integration/gitignore-tracked-gsd.test.ts +1 -0
  316. package/src/resources/extensions/gsd/tests/integration/idle-recovery.test.ts +30 -0
  317. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +1 -1
  318. package/src/resources/extensions/gsd/tests/issue-4540-regressions.test.ts +288 -0
  319. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +2 -0
  320. package/src/resources/extensions/gsd/tests/key-manager.test.ts +9 -0
  321. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  322. package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +12 -0
  323. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  324. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +19 -0
  325. package/src/resources/extensions/gsd/tests/plan-gate-failed-doctor-heal-hint.test.ts +37 -0
  326. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +14 -0
  327. package/src/resources/extensions/gsd/tests/pre-exec-gate-loop.test.ts +272 -0
  328. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +356 -0
  329. package/src/resources/extensions/gsd/tests/preferences.test.ts +110 -0
  330. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +44 -0
  331. package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +49 -0
  332. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +48 -0
  333. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +388 -0
  334. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +9 -3
  335. package/src/resources/extensions/gsd/tests/resume-dispatch-worktree.test.ts +230 -0
  336. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +205 -0
  337. package/src/resources/extensions/gsd/tests/save-gate-result-render.test.ts +95 -0
  338. package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +413 -0
  339. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +32 -40
  340. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +56 -0
  341. package/src/resources/extensions/gsd/tests/token-counter.test.ts +105 -1
  342. package/src/resources/extensions/gsd/tests/tool-compatibility.test.ts +107 -0
  343. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +23 -0
  344. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +65 -2
  345. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +35 -0
  346. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +6 -1
  347. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -5
  348. package/src/resources/extensions/gsd/tests/write-gate.test.ts +64 -0
  349. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +3 -1
  350. package/src/resources/extensions/gsd/token-counter.ts +22 -5
  351. package/src/resources/extensions/gsd/tools/complete-milestone.ts +15 -9
  352. package/src/resources/extensions/gsd/tools/exec-search-tool.ts +81 -0
  353. package/src/resources/extensions/gsd/tools/exec-tool.ts +183 -0
  354. package/src/resources/extensions/gsd/tools/resume-tool.ts +40 -0
  355. package/src/resources/extensions/gsd/uok/plan-v2.ts +26 -3
  356. package/src/resources/extensions/gsd/workflow-logger.ts +3 -1
  357. package/src/resources/extensions/gsd/workflow-mcp.ts +3 -0
  358. package/src/resources/extensions/gsd/worktree-resolver.ts +54 -9
  359. package/src/resources/skills/verify-before-complete/SKILL.md +2 -1
  360. package/src/resources/skills/write-docs/SKILL.md +2 -1
  361. /package/dist/web/standalone/.next/static/{YnUwu2WWaT0_hyTLUF4nq → JgU2F-5N9mTyB7kUSSk9A}/_buildManifest.js +0 -0
  362. /package/dist/web/standalone/.next/static/{YnUwu2WWaT0_hyTLUF4nq → JgU2F-5N9mTyB7kUSSk9A}/_ssgManifest.js +0 -0
@@ -238,14 +238,6 @@ function clearProjectRootStateFiles(basePath: string, milestoneId: string): void
238
238
  }
239
239
  }
240
240
 
241
- function isProjectGsdSymlink(basePath: string): boolean {
242
- try {
243
- return lstatSyncFn(join(basePath, ".gsd")).isSymbolicLink();
244
- } catch {
245
- return false;
246
- }
247
- }
248
-
249
241
  // ─── Build Artifact Auto-Resolve ─────────────────────────────────────────────
250
242
 
251
243
  /** Patterns for machine-generated build artifacts that can be safely
@@ -1664,54 +1656,15 @@ export function mergeMilestoneToMain(
1664
1656
  }
1665
1657
  }
1666
1658
 
1667
- // 7. Stash any pre-existing dirty files so the squash merge is not
1668
- // blocked by unrelated local changes (#2151). clearProjectRootStateFiles
1669
- // only removes untracked .gsd/ files; tracked dirty files elsewhere (e.g.
1670
- // .planning/work-state.json with stash conflict markers) are invisible to
1671
- // that cleanup but will cause `git merge --squash` to reject.
1672
- let stashed = false;
1673
- try {
1674
- const status = execFileSync("git", ["status", "--porcelain"], {
1675
- cwd: originalBasePath_,
1676
- stdio: ["ignore", "pipe", "pipe"],
1677
- encoding: "utf-8",
1678
- }).trim();
1679
- if (status) {
1680
- // Use --include-untracked to stash untracked files that would block
1681
- // the squash merge, but EXCLUDE .gsd/milestones/ (#2505).
1682
- // --include-untracked without exclusion sweeps queued milestone
1683
- // CONTEXT files into the stash. If stash pop later fails, those files
1684
- // are permanently trapped in the stash entry and lost on the next
1685
- // stash push or drop.
1686
- //
1687
- // When `.gsd` itself is a symlink, Git rejects pathspecs below it
1688
- // ("beyond a symbolic link"). In that layout, exclude the whole symlink
1689
- // and keep stashing real project files that could block the merge.
1690
- const stashPathspecs = isProjectGsdSymlink(originalBasePath_)
1691
- ? [".", ":(exclude).gsd"]
1692
- : [":(exclude).gsd/milestones"];
1693
- execFileSync(
1694
- "git",
1695
- [
1696
- "stash", "push", "--include-untracked",
1697
- "-m", `gsd: pre-merge stash for ${milestoneId}`,
1698
- "--", ...stashPathspecs,
1699
- ],
1700
- { cwd: originalBasePath_, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" },
1701
- );
1702
- stashed = true;
1703
- }
1704
- } catch (err) {
1705
- // Stash failure is non-fatal — proceed without stash and let the merge
1706
- // report the dirty tree if it fails.
1707
- logWarning("worktree", `git stash failed: ${err instanceof Error ? err.message : String(err)}`);
1708
- }
1709
-
1710
- // 7a. Shelter queued milestone directories before the squash merge (#2505).
1659
+ // 7. Shelter queued milestone directories before the squash merge (#2505).
1711
1660
  // The milestone branch may contain copies of queued milestone dirs (via
1712
1661
  // copyPlanningArtifacts), so `git merge --squash` rejects when those same
1713
1662
  // files exist as untracked in the working tree. Temporarily move them to
1714
1663
  // a backup location, then restore after the merge+commit.
1664
+ //
1665
+ // MUST run BEFORE the pre-merge stash (step 7a) so `--include-untracked`
1666
+ // does not sweep queued CONTEXT files into the stash. If stash pop later
1667
+ // fails, files trapped inside the stash are permanently lost (#2505).
1715
1668
  const milestonesDir = join(gsdRoot(originalBasePath_), "milestones");
1716
1669
  const shelterDir = join(gsdRoot(originalBasePath_), ".milestone-shelter");
1717
1670
  const shelteredDirs: string[] = [];
@@ -1759,6 +1712,35 @@ export function mergeMilestoneToMain(
1759
1712
  logWarning("worktree", `milestone shelter operation failed: ${err instanceof Error ? err.message : String(err)}`);
1760
1713
  }
1761
1714
 
1715
+ // 7a. Stash pre-existing dirty files so the squash merge is not blocked by
1716
+ // unrelated local changes (#2151). Includes untracked files to handle
1717
+ // locally-added files that conflict with tracked files on the milestone
1718
+ // branch. Passing NO pathspec lets git skip gitignored paths silently;
1719
+ // adding an explicit pathspec trips a `git add`-style fatal on ignored
1720
+ // entries (e.g. a gitignored `.gsd` symlink under ADR-002) (#4573).
1721
+ // Queued CONTEXT files under `.gsd/milestones/*` are already sheltered
1722
+ // in step 7 above, so they won't be swept into the stash.
1723
+ let stashed = false;
1724
+ try {
1725
+ const status = execFileSync("git", ["status", "--porcelain"], {
1726
+ cwd: originalBasePath_,
1727
+ stdio: ["ignore", "pipe", "pipe"],
1728
+ encoding: "utf-8",
1729
+ }).trim();
1730
+ if (status) {
1731
+ execFileSync(
1732
+ "git",
1733
+ ["stash", "push", "--include-untracked", "-m", `gsd: pre-merge stash for ${milestoneId}`],
1734
+ { cwd: originalBasePath_, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" },
1735
+ );
1736
+ stashed = true;
1737
+ }
1738
+ } catch (err) {
1739
+ // Stash failure is non-fatal — proceed without stash and let the merge
1740
+ // report the dirty tree if it fails.
1741
+ logWarning("worktree", `git stash failed: ${err instanceof Error ? err.message : String(err)}`);
1742
+ }
1743
+
1762
1744
  // 7b. Clean up stale merge state before attempting squash merge (#2912).
1763
1745
  // A leftover MERGE_HEAD (from a previous failed merge, libgit2 native path,
1764
1746
  // or interrupted operation) causes `git merge --squash` to refuse with
@@ -2012,21 +1994,38 @@ export function mergeMilestoneToMain(
2012
1994
  // When a milestone only produced .gsd/ metadata (summaries, roadmaps) but no
2013
1995
  // real code, the user sees "milestone complete" but nothing changed in their
2014
1996
  // codebase. Surface this so the caller can warn the user.
1997
+ //
1998
+ // Bug #4385 fix: use `git diff-tree --root` instead of `git diff HEAD~1 HEAD`.
1999
+ // `HEAD~1` does not exist on initial commits and is unreliable on shallow clones
2000
+ // and merge commits. `diff-tree --root` handles all three cases correctly.
2001
+ // The empty-tree hash (4b825dc…) is the universal fallback for refs that don't exist.
2002
+ const GIT_EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
2015
2003
  let codeFilesChanged = false;
2016
2004
  if (!nothingToCommit) {
2017
2005
  try {
2018
- const mergedFiles = nativeDiffNumstat(
2019
- originalBasePath_,
2020
- "HEAD~1",
2021
- "HEAD",
2022
- );
2023
- codeFilesChanged = mergedFiles.some(
2024
- (entry) => !entry.path.startsWith(".gsd/"),
2025
- );
2006
+ const diffTreeOutput = execFileSync(
2007
+ "git",
2008
+ ["diff-tree", "--root", "--no-commit-id", "-r", "--name-only", "HEAD"],
2009
+ { cwd: originalBasePath_, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" },
2010
+ ).trim();
2011
+ const mergedFiles = diffTreeOutput ? diffTreeOutput.split("\n").filter(Boolean) : [];
2012
+ codeFilesChanged = mergedFiles.some((f) => !f.startsWith(".gsd/"));
2026
2013
  } catch (e) {
2027
- // If HEAD~1 doesn't exist (first commit), assume code was changed
2028
- logWarning("worktree", `diff numstat failed (assuming code changed): ${(e as Error).message}`);
2029
- codeFilesChanged = true;
2014
+ // diff-tree failed (e.g. unborn HEAD in a brand-new repo) fall back to
2015
+ // comparing against the empty tree so initial-commit repos still report changes.
2016
+ try {
2017
+ const fallbackOutput = execFileSync(
2018
+ "git",
2019
+ ["diff", "--name-only", GIT_EMPTY_TREE, "HEAD"],
2020
+ { cwd: originalBasePath_, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" },
2021
+ ).trim();
2022
+ const fallbackFiles = fallbackOutput ? fallbackOutput.split("\n").filter(Boolean) : [];
2023
+ codeFilesChanged = fallbackFiles.some((f) => !f.startsWith(".gsd/"));
2024
+ } catch {
2025
+ // Truly unable to determine — assume code was changed to avoid silent data loss
2026
+ logWarning("worktree", `diff-tree and empty-tree fallback both failed (assuming code changed): ${(e as Error).message}`);
2027
+ codeFilesChanged = true;
2028
+ }
2030
2029
  }
2031
2030
  }
2032
2031
 
@@ -126,8 +126,9 @@ import {
126
126
  formatTokenCount,
127
127
  } from "./metrics.js";
128
128
  import { setLogBasePath, logWarning, logError } from "./workflow-logger.js";
129
+ import { preflightCleanRoot, postflightPopStash } from "./clean-root-preflight.js";
129
130
  import { homedir } from "node:os";
130
- import { join } from "node:path";
131
+ import { isAbsolute, join } from "node:path";
131
132
  import { pathToFileURL } from "node:url";
132
133
  import { readFileSync, existsSync, mkdirSync, writeFileSync, unlinkSync } from "node:fs";
133
134
  import { atomicWriteSync } from "./atomic-write.js";
@@ -182,7 +183,6 @@ import {
182
183
  unitVerb,
183
184
  formatAutoElapsed as _formatAutoElapsed,
184
185
  formatWidgetTokens,
185
- hideFooter,
186
186
  type WidgetStateAccessors,
187
187
  } from "./auto-dashboard.js";
188
188
  import {
@@ -310,6 +310,21 @@ function restoreMilestoneLockEnv(): void {
310
310
  s.milestoneLockEnvCaptured = false;
311
311
  }
312
312
 
313
+ function normalizeSessionFilePath(raw: unknown): string | null {
314
+ if (typeof raw !== "string") return null;
315
+ const trimmed = raw.trim();
316
+ if (!trimmed) return null;
317
+ const firstLine = trimmed.split(/\r?\n/, 1)[0]?.trim() ?? "";
318
+ if (!firstLine) return null;
319
+
320
+ // Guard against accidental message concatenation by trimming to .jsonl.
321
+ const jsonlIndex = firstLine.toLowerCase().indexOf(".jsonl");
322
+ const candidate = jsonlIndex >= 0 ? firstLine.slice(0, jsonlIndex + ".jsonl".length) : firstLine;
323
+ if (!isAbsolute(candidate)) return null;
324
+ if (!candidate.toLowerCase().endsWith(".jsonl")) return null;
325
+ return candidate;
326
+ }
327
+
313
328
  export function startAutoDetached(
314
329
  ctx: ExtensionCommandContext,
315
330
  pi: ExtensionAPI,
@@ -330,8 +345,8 @@ export function startAutoDetached(
330
345
  }
331
346
 
332
347
  /** Returns true if the project is configured for `isolation:worktree` mode. */
333
- export function shouldUseWorktreeIsolation(): boolean {
334
- const prefs = loadEffectiveGSDPreferences()?.preferences?.git;
348
+ export function shouldUseWorktreeIsolation(basePath?: string): boolean {
349
+ const prefs = loadEffectiveGSDPreferences(basePath)?.preferences?.git;
335
350
  if (prefs?.isolation === "worktree") return true;
336
351
  // Default is false — worktree isolation requires explicit opt-in
337
352
  return false;
@@ -424,7 +439,7 @@ export function getAutoDashboardData(): AutoDashboardData {
424
439
  const rtkSavings = sessionId && s.basePath
425
440
  ? getRtkSessionSavings(s.basePath, sessionId)
426
441
  : null;
427
- const rtkEnabled = loadEffectiveGSDPreferences()?.preferences.experimental?.rtk === true;
442
+ const rtkEnabled = loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences.experimental?.rtk === true;
428
443
  // Pending capture count — lazy check, non-fatal
429
444
  let pendingCaptureCount = 0;
430
445
  try {
@@ -648,7 +663,7 @@ function buildSnapshotOpts(
648
663
  gitStatus?: "ok" | "failed";
649
664
  gitError?: string;
650
665
  } & Record<string, unknown> {
651
- const prefs = loadEffectiveGSDPreferences()?.preferences;
666
+ const prefs = loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences;
652
667
  const uokFlags = resolveUokFlags(prefs);
653
668
  return {
654
669
  ...(s.autoStartTime > 0 ? { autoSessionKey: String(s.autoStartTime) } : {}),
@@ -686,7 +701,7 @@ function handleLostSessionLock(
686
701
  restoreProjectRootEnv();
687
702
  restoreMilestoneLockEnv();
688
703
  deregisterSigtermHandler();
689
- clearCmuxSidebar(loadEffectiveGSDPreferences()?.preferences);
704
+ clearCmuxSidebar(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences);
690
705
  const base = lockBase();
691
706
  const lockFilePath = base ? join(gsdRoot(base), "auto.lock") : "unknown";
692
707
  const recoverySuggestion = "\nTo recover, run: gsd doctor --fix";
@@ -706,7 +721,6 @@ function handleLostSessionLock(
706
721
  );
707
722
  ctx?.ui.setStatus("gsd-auto", undefined);
708
723
  ctx?.ui.setWidget("gsd-progress", undefined);
709
- ctx?.ui.setFooter(undefined);
710
724
  if (ctx) initHealthWidget(ctx);
711
725
  }
712
726
 
@@ -742,7 +756,6 @@ function cleanupAfterLoopExit(ctx: ExtensionContext): void {
742
756
  if (!s.paused) {
743
757
  ctx.ui.setStatus("gsd-auto", undefined);
744
758
  ctx.ui.setWidget("gsd-progress", undefined);
745
- ctx.ui.setFooter(undefined);
746
759
  initHealthWidget(ctx);
747
760
  }
748
761
 
@@ -764,7 +777,7 @@ export async function stopAuto(
764
777
  reason?: string,
765
778
  ): Promise<void> {
766
779
  if (!s.active && !s.paused) return;
767
- const loadedPreferences = loadEffectiveGSDPreferences()?.preferences;
780
+ const loadedPreferences = loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences;
768
781
  const reasonSuffix = reason ? ` — ${reason}` : "";
769
782
 
770
783
  try {
@@ -969,7 +982,7 @@ export async function stopAuto(
969
982
  logWarning("engine", `file unlink failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
970
983
  }
971
984
 
972
- // ── Step 13: Restore original model (before reset clears IDs) ──
985
+ // ── Step 13: Restore original model + thinking (before reset clears IDs) ──
973
986
  try {
974
987
  if (pi && ctx && s.originalModelId && s.originalModelProvider) {
975
988
  const original = ctx.modelRegistry.find(
@@ -978,6 +991,9 @@ export async function stopAuto(
978
991
  );
979
992
  if (original) await pi.setModel(original);
980
993
  }
994
+ if (pi && s.originalThinkingLevel) {
995
+ pi.setThinkingLevel(s.originalThinkingLevel);
996
+ }
981
997
  } catch (e) {
982
998
  debugLog("stop-cleanup-model", { error: e instanceof Error ? e.message : String(e) });
983
999
  }
@@ -1015,7 +1031,6 @@ export async function stopAuto(
1015
1031
  // UI cleanup
1016
1032
  ctx?.ui.setStatus("gsd-auto", undefined);
1017
1033
  ctx?.ui.setWidget("gsd-progress", undefined);
1018
- ctx?.ui.setFooter(undefined);
1019
1034
  if (ctx) initHealthWidget(ctx);
1020
1035
  restoreProjectRootEnv();
1021
1036
  restoreMilestoneLockEnv();
@@ -1056,7 +1071,7 @@ export async function pauseAuto(
1056
1071
  // from provider-error pause and avoid hard-stopping (#2762).
1057
1072
  resolveAgentEndCancelled(_errorContext);
1058
1073
 
1059
- s.pausedSessionFile = ctx?.sessionManager?.getSessionFile() ?? null;
1074
+ s.pausedSessionFile = normalizeSessionFilePath(ctx?.sessionManager?.getSessionFile() ?? null);
1060
1075
 
1061
1076
  // Persist paused-session metadata so resume survives /exit (#1383).
1062
1077
  // The fresh-start bootstrap checks for this file and restores worktree context.
@@ -1118,7 +1133,6 @@ export async function pauseAuto(
1118
1133
  s.verificationRetryCount.clear();
1119
1134
  ctx?.ui.setStatus("gsd-auto", "paused");
1120
1135
  ctx?.ui.setWidget("gsd-progress", undefined);
1121
- ctx?.ui.setFooter(undefined);
1122
1136
  if (ctx) initHealthWidget(ctx);
1123
1137
  const resumeCmd = s.stepMode ? "/gsd next" : "/gsd auto";
1124
1138
  ctx?.ui.notify(
@@ -1289,6 +1303,10 @@ function buildLoopDeps(): LoopDeps {
1289
1303
 
1290
1304
  // Journal
1291
1305
  emitJournalEvent: (entry: JournalEntry) => _emitJournalEvent(s.basePath, entry),
1306
+
1307
+ // Clean-root preflight gate (#2909)
1308
+ preflightCleanRoot,
1309
+ postflightPopStash,
1292
1310
  } as unknown as LoopDeps;
1293
1311
  }
1294
1312
 
@@ -1361,7 +1379,11 @@ export async function startAuto(
1361
1379
  s.autoStartTime = meta.autoStartTime || Date.now();
1362
1380
  s.sessionMilestoneLock = meta.milestoneLock ?? null;
1363
1381
  s.paused = true;
1364
- try { unlinkSync(pausedPath); } catch (e) { logWarning("session", `pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" }); }
1382
+ try { unlinkSync(pausedPath); } catch (e) {
1383
+ if ((e as NodeJS.ErrnoException).code !== "ENOENT") {
1384
+ logWarning("session", `pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
1385
+ }
1386
+ }
1365
1387
  ctx.ui.notify(
1366
1388
  `Resuming paused custom workflow${meta.activeRunDir ? ` (${meta.activeRunDir})` : ""}.`,
1367
1389
  "info",
@@ -1380,7 +1402,9 @@ export async function startAuto(
1380
1402
  const summaryFile = resolveMilestoneFile(base, meta.milestoneId, "SUMMARY");
1381
1403
  if (!mDir || summaryFile) {
1382
1404
  try { unlinkSync(pausedPath); } catch (err) {
1383
- logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1405
+ if ((err as NodeJS.ErrnoException).code !== "ENOENT") {
1406
+ logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1407
+ }
1384
1408
  }
1385
1409
  ctx.ui.notify(
1386
1410
  `Paused milestone ${meta.milestoneId} is ${!mDir ? "missing" : "already complete"}. Starting fresh.`,
@@ -1390,20 +1414,28 @@ export async function startAuto(
1390
1414
  s.currentMilestoneId = meta.milestoneId;
1391
1415
  s.originalBasePath = meta.originalBasePath || base;
1392
1416
  s.stepMode = meta.stepMode ?? requestedStepMode;
1393
- s.pausedSessionFile = meta.sessionFile ?? null;
1417
+ s.pausedSessionFile = normalizeSessionFilePath(meta.sessionFile ?? null);
1394
1418
  s.pausedUnitType = meta.unitType ?? null;
1395
1419
  s.pausedUnitId = meta.unitId ?? null;
1396
1420
  s.autoStartTime = meta.autoStartTime || Date.now();
1397
1421
  s.sessionMilestoneLock = meta.milestoneLock ?? null;
1398
1422
  s.paused = true;
1399
- try { unlinkSync(pausedPath); } catch (e) { logWarning("session", `pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" }); }
1423
+ try { unlinkSync(pausedPath); } catch (e) {
1424
+ if ((e as NodeJS.ErrnoException).code !== "ENOENT") {
1425
+ logWarning("session", `pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
1426
+ }
1427
+ }
1400
1428
  ctx.ui.notify(
1401
1429
  `Resuming paused session for ${meta.milestoneId}${meta.worktreePath && existsSync(meta.worktreePath) ? ` (worktree)` : ""}.`,
1402
1430
  "info",
1403
1431
  );
1404
1432
  }
1405
1433
  } else if (existsSync(pausedPath)) {
1406
- try { unlinkSync(pausedPath); } catch (e) { logWarning("session", `stale pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" }); }
1434
+ try { unlinkSync(pausedPath); } catch (e) {
1435
+ if ((e as NodeJS.ErrnoException).code !== "ENOENT") {
1436
+ logWarning("session", `stale pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
1437
+ }
1438
+ }
1407
1439
  }
1408
1440
  }
1409
1441
  } catch (err) {
@@ -1462,7 +1494,9 @@ export async function startAuto(
1462
1494
  // Lock acquired — now safe to delete the pause file
1463
1495
  if (s.pausedSessionFile) {
1464
1496
  try { unlinkSync(s.pausedSessionFile); } catch (err) {
1465
- logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1497
+ if ((err as NodeJS.ErrnoException).code !== "ENOENT") {
1498
+ logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1499
+ }
1466
1500
  }
1467
1501
  s.pausedSessionFile = null;
1468
1502
  }
@@ -1473,6 +1507,17 @@ export async function startAuto(
1473
1507
  s.stepMode = requestedStepMode;
1474
1508
  s.cmdCtx = ctx;
1475
1509
  s.basePath = base;
1510
+ // ── Resume worktree: if the paused session was inside a milestone worktree,
1511
+ // apply that path as the dispatch basePath immediately (#3723).
1512
+ // This ensures the dispatch loop runs from the worktree directory even when
1513
+ // enterMilestone guard conditions differ between the original and resumed
1514
+ // session (e.g. isolation mode changed, detectWorktreeName differs across
1515
+ // process restarts). We guard with existsSync so a stale or deleted
1516
+ // worktree directory safely falls back to the project root.
1517
+ const resumeWorktreePath = freshStartAssessment.pausedSession?.worktreePath;
1518
+ if (resumeWorktreePath && existsSync(resumeWorktreePath)) {
1519
+ s.basePath = resumeWorktreePath;
1520
+ }
1476
1521
  // Ensure the workflow-logger audit log is pinned to the project root
1477
1522
  // even when auto-mode is entered via a path that bypasses the
1478
1523
  // bootstrap/dynamic-tools ensureDbOpen() → setLogBasePath() chain
@@ -1492,7 +1537,7 @@ export async function startAuto(
1492
1537
  // ── Auto-worktree / branch-mode: re-enter on resume ──
1493
1538
  if (
1494
1539
  s.currentMilestoneId &&
1495
- getIsolationMode() !== "none" &&
1540
+ getIsolationMode(s.originalBasePath || s.basePath) !== "none" &&
1496
1541
  s.originalBasePath &&
1497
1542
  !isInAutoWorktree(s.basePath) &&
1498
1543
  !detectWorktreeName(s.basePath) &&
@@ -1506,7 +1551,7 @@ export async function startAuto(
1506
1551
  registerSigtermHandler(lockBase());
1507
1552
 
1508
1553
  ctx.ui.setStatus("gsd-auto", s.stepMode ? "next" : "auto");
1509
- ctx.ui.setFooter(hideFooter);
1554
+ ctx.ui.setWidget("gsd-health", undefined);
1510
1555
  ctx.ui.notify(
1511
1556
  s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.",
1512
1557
  "info",
@@ -1531,7 +1576,7 @@ export async function startAuto(
1531
1576
  await openProjectDbIfPresent(s.basePath);
1532
1577
  try {
1533
1578
  await rebuildState(s.basePath);
1534
- syncCmuxSidebar(loadEffectiveGSDPreferences()?.preferences, await deriveState(s.basePath));
1579
+ syncCmuxSidebar(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, await deriveState(s.basePath));
1535
1580
  } catch (e) {
1536
1581
  debugLog("resume-rebuild-state-failed", {
1537
1582
  error: e instanceof Error ? e.message : String(e),
@@ -1581,7 +1626,7 @@ export async function startAuto(
1581
1626
  "resuming",
1582
1627
  s.currentMilestoneId ?? "unknown",
1583
1628
  );
1584
- logCmuxEvent(loadEffectiveGSDPreferences()?.preferences, s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "progress");
1629
+ logCmuxEvent(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "progress");
1585
1630
 
1586
1631
  captureProjectRootEnv(s.originalBasePath || s.basePath);
1587
1632
  startAutoCommandPolling(s.basePath);
@@ -1619,12 +1664,12 @@ export async function startAuto(
1619
1664
 
1620
1665
  captureProjectRootEnv(s.originalBasePath || s.basePath);
1621
1666
  try {
1622
- syncCmuxSidebar(loadEffectiveGSDPreferences()?.preferences, await deriveState(s.basePath));
1667
+ syncCmuxSidebar(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, await deriveState(s.basePath));
1623
1668
  } catch (err) {
1624
1669
  // Best-effort only — sidebar sync must never block auto-mode startup
1625
1670
  logWarning("engine", `cmux sync failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1626
1671
  }
1627
- logCmuxEvent(loadEffectiveGSDPreferences()?.preferences, requestedStepMode ? "Step-mode started." : "Auto-mode started.", "progress");
1672
+ logCmuxEvent(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, requestedStepMode ? "Step-mode started." : "Auto-mode started.", "progress");
1628
1673
 
1629
1674
  startAutoCommandPolling(s.basePath);
1630
1675
 
@@ -1773,12 +1818,12 @@ export async function dispatchHookUnit(
1773
1818
  }
1774
1819
  }
1775
1820
 
1776
- const sessionFile = ctx.sessionManager.getSessionFile();
1821
+ const sessionFile = normalizeSessionFilePath(ctx.sessionManager.getSessionFile());
1777
1822
  writeLock(
1778
1823
  lockBase(),
1779
1824
  hookUnitType,
1780
1825
  triggerUnitId,
1781
- sessionFile,
1826
+ sessionFile ?? undefined,
1782
1827
  );
1783
1828
 
1784
1829
  clearUnitTimeout();
@@ -1,7 +1,12 @@
1
1
  import type { ExtensionAPI, ExtensionContext } from "@gsd/pi-coding-agent";
2
2
 
3
3
  import { logWarning } from "../workflow-logger.js";
4
- import { checkAutoStartAfterDiscuss } from "../guided-flow.js";
4
+ import {
5
+ checkAutoStartAfterDiscuss,
6
+ maybeHandleReadyPhraseWithoutFiles,
7
+ maybeHandleEmptyIntentTurn,
8
+ resetEmptyTurnCounter,
9
+ } from "../guided-flow.js";
5
10
  import { getAutoDashboardData, getAutoModeStartModel, isAutoActive, pauseAuto, setCurrentDispatchedModelId } from "../auto.js";
6
11
  import { getNextFallbackModel, resolveModelWithFallbacksForUnit } from "../preferences.js";
7
12
  import { pauseAutoForProviderError } from "../provider-error-pause.js";
@@ -75,6 +80,20 @@ export async function handleAgentEnd(
75
80
  clearDiscussionFlowState();
76
81
  return;
77
82
  }
83
+
84
+ // #4573 — When the LLM emits "Milestone X ready." but the required files
85
+ // are missing, `checkAutoStartAfterDiscuss` returns false silently. Surface
86
+ // that and nudge the LLM to complete the writes before the user hits the
87
+ // downstream "All milestones complete" warning loop.
88
+ if (maybeHandleReadyPhraseWithoutFiles(event)) return;
89
+
90
+ // #4573 — Empty-turn recovery: if the LLM announced intent in prose but
91
+ // emitted no tool calls, nudge it to execute. Fires only when auto-mode is
92
+ // active or a discussion autostart is pending (non-auto interactive discuss
93
+ // is user-driven). Runs before `isAutoActive` early return so pending
94
+ // discussions (where isAutoActive may be false) still get recovered.
95
+ if (maybeHandleEmptyIntentTurn(event, isAutoActive())) return;
96
+
78
97
  if (!isAutoActive()) return;
79
98
  if (isSessionSwitchInFlight()) return;
80
99
 
@@ -336,6 +355,9 @@ export async function handleAgentEnd(
336
355
  // ── Success path ─────────────────────────────────────────────────────────
337
356
  try {
338
357
  resetRetryState(retryState);
358
+ // #4573 — Reset the empty-turn counter on any successful agent turn so
359
+ // transient stalls don't accumulate across independent units.
360
+ resetEmptyTurnCounter();
339
361
  resolveAgentEnd(event);
340
362
  } catch (err) {
341
363
  const message = err instanceof Error ? err.message : String(err);
@@ -35,6 +35,19 @@ function registerAlias(pi: ExtensionAPI, toolDef: any, aliasName: string, canoni
35
35
  });
36
36
  }
37
37
 
38
+ /**
39
+ * Read a tool result's structured payload, accommodating MCP's `details` →
40
+ * `structuredContent` rename (#4472, #4477). In-process executions still
41
+ * deliver the payload on `result.details`; MCP-routed executions deliver it
42
+ * on `result.structuredContent` (post `adaptExecutorResult` transform). All
43
+ * `renderResult` callbacks in this file route through this helper so a future
44
+ * field rename only needs to be applied in one place.
45
+ */
46
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- result shape varies by tool
47
+ function readDetails(result: any): any {
48
+ return result?.details ?? result?.structuredContent;
49
+ }
50
+
38
51
  export function registerDbTools(pi: ExtensionAPI): void {
39
52
  // ─── gsd_decision_save (formerly gsd_save_decision) ─────────────────────
40
53
 
@@ -110,7 +123,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
110
123
  return new Text(text, 0, 0);
111
124
  },
112
125
  renderResult(result: any, _options: any, theme: any) {
113
- const d = result.details;
126
+ const d = readDetails(result);
114
127
  if (result.isError || d?.error) {
115
128
  return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
116
129
  }
@@ -188,7 +201,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
188
201
  return new Text(text, 0, 0);
189
202
  },
190
203
  renderResult(result: any, _options: any, theme: any) {
191
- const d = result.details;
204
+ const d = readDetails(result);
192
205
  if (result.isError || d?.error) {
193
206
  return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
194
207
  }
@@ -273,7 +286,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
273
286
  return new Text(text, 0, 0);
274
287
  },
275
288
  renderResult(result: any, _options: any, theme: any) {
276
- const d = result.details;
289
+ const d = readDetails(result);
277
290
  if (result.isError || d?.error) {
278
291
  return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
279
292
  }
@@ -322,7 +335,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
322
335
  return new Text(text, 0, 0);
323
336
  },
324
337
  renderResult(result: any, _options: any, theme: any) {
325
- const d = result.details;
338
+ const d = readDetails(result);
326
339
  if (result.isError || d?.error) {
327
340
  return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
328
341
  }
@@ -406,7 +419,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
406
419
  return new Text(theme.fg("toolTitle", theme.bold("milestone_generate_id")), 0, 0);
407
420
  },
408
421
  renderResult(result: any, _options: any, theme: any) {
409
- const d = result.details;
422
+ const d = readDetails(result);
410
423
  if (result.isError || d?.error) {
411
424
  return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
412
425
  }
@@ -1074,13 +1087,31 @@ export function registerDbTools(pi: ExtensionAPI): void {
1074
1087
  text += theme.fg("dim", ` → ${args.verdict ?? ""}`);
1075
1088
  return new Text(text, 0, 0);
1076
1089
  },
1090
+ /**
1091
+ * Render the save_gate_result tool output for the TUI.
1092
+ *
1093
+ * Prefers structured fields, but falls back to `content[0].text` when the
1094
+ * structured payload is empty. Defensive: the structural fix on this
1095
+ * branch plumbs `details` through MCP via `structuredContent`, but older
1096
+ * hosts, a future handler that forgets `structuredContent`, or any drop
1097
+ * of non-standard return fields would otherwise render as
1098
+ * "undefined: undefined". Same fallback applies to error rendering, and
1099
+ * we strip a leading `Error:` from the fallback text to avoid producing
1100
+ * `Error: Error: ...`.
1101
+ */
1077
1102
  renderResult(result: any, _options: any, theme: any) {
1078
- const d = result.details;
1103
+ const d = readDetails(result);
1079
1104
  if (result.isError || d?.error) {
1080
- return new Text(theme.fg("error", `Error: ${d?.error ?? "unknown"}`), 0, 0);
1105
+ const rawMsg = d?.error ?? result.content?.[0]?.text ?? "unknown";
1106
+ const msg = rawMsg.replace(/^\s*Error:\s*/i, "");
1107
+ return new Text(theme.fg("error", `Error: ${msg}`), 0, 0);
1108
+ }
1109
+ if (!d?.gateId || !d?.verdict) {
1110
+ const text = result.content?.[0]?.text ?? "Gate result saved";
1111
+ return new Text(theme.fg("success", text), 0, 0);
1081
1112
  }
1082
- const color = d?.verdict === "flag" ? "warning" : "success";
1083
- return new Text(theme.fg(color, `${d?.gateId}: ${d?.verdict}`), 0, 0);
1113
+ const color = d.verdict === "flag" ? "warning" : "success";
1114
+ return new Text(theme.fg(color, `${d.gateId}: ${d.verdict}`), 0, 0);
1084
1115
  },
1085
1116
  };
1086
1117