gsd-pi 2.76.0-dev.82e249f7b → 2.76.0-dev.97f5583d9

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 (271) 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/resource-loader.d.ts +1 -1
  5. package/dist/resource-loader.js +2 -8
  6. package/dist/resources/extensions/claude-code-cli/readiness.js +4 -3
  7. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +77 -17
  8. package/dist/resources/extensions/gsd/auto/phases.js +42 -1
  9. package/dist/resources/extensions/gsd/auto/run-unit.js +27 -0
  10. package/dist/resources/extensions/gsd/auto/session.js +12 -0
  11. package/dist/resources/extensions/gsd/auto-dispatch.js +16 -3
  12. package/dist/resources/extensions/gsd/auto-model-selection.js +1 -1
  13. package/dist/resources/extensions/gsd/auto-post-unit.js +25 -2
  14. package/dist/resources/extensions/gsd/auto-prompts.js +14 -0
  15. package/dist/resources/extensions/gsd/auto-recovery.js +13 -0
  16. package/dist/resources/extensions/gsd/auto-start.js +27 -18
  17. package/dist/resources/extensions/gsd/auto-worktree.js +51 -53
  18. package/dist/resources/extensions/gsd/auto.js +55 -27
  19. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -1
  20. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +39 -9
  21. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +93 -0
  22. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
  23. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +51 -5
  24. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +34 -2
  25. package/dist/resources/extensions/gsd/clean-root-preflight.js +93 -0
  26. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +968 -23
  27. package/dist/resources/extensions/gsd/compaction-snapshot.js +121 -0
  28. package/dist/resources/extensions/gsd/error-classifier.js +10 -3
  29. package/dist/resources/extensions/gsd/exec-history.js +120 -0
  30. package/dist/resources/extensions/gsd/exec-sandbox.js +258 -0
  31. package/dist/resources/extensions/gsd/gsd-db.js +115 -7
  32. package/dist/resources/extensions/gsd/guided-flow.js +189 -0
  33. package/dist/resources/extensions/gsd/health-widget.js +4 -1
  34. package/dist/resources/extensions/gsd/init-wizard.js +15 -1
  35. package/dist/resources/extensions/gsd/key-manager.js +6 -0
  36. package/dist/resources/extensions/gsd/model-router.js +36 -3
  37. package/dist/resources/extensions/gsd/pre-execution-checks.js +35 -9
  38. package/dist/resources/extensions/gsd/preferences-types.js +9 -0
  39. package/dist/resources/extensions/gsd/preferences-validation.js +83 -0
  40. package/dist/resources/extensions/gsd/preferences.js +17 -17
  41. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
  42. package/dist/resources/extensions/gsd/prompts/discuss.md +29 -2
  43. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
  44. package/dist/resources/extensions/gsd/safety/evidence-collector.js +96 -0
  45. package/dist/resources/extensions/gsd/safety/file-change-validator.js +13 -5
  46. package/dist/resources/extensions/gsd/safety/safety-harness.js +5 -1
  47. package/dist/resources/extensions/gsd/token-counter.js +22 -5
  48. package/dist/resources/extensions/gsd/tools/exec-search-tool.js +59 -0
  49. package/dist/resources/extensions/gsd/tools/exec-tool.js +126 -0
  50. package/dist/resources/extensions/gsd/tools/resume-tool.js +23 -0
  51. package/dist/resources/extensions/gsd/uok/plan-v2.js +20 -3
  52. package/dist/resources/extensions/gsd/workflow-mcp.js +3 -0
  53. package/dist/resources/skills/verify-before-complete/SKILL.md +2 -1
  54. package/dist/resources/skills/write-docs/SKILL.md +2 -1
  55. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  56. package/dist/web/standalone/.next/BUILD_ID +1 -1
  57. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  58. package/dist/web/standalone/.next/build-manifest.json +2 -2
  59. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  60. package/dist/web/standalone/.next/required-server-files.json +1 -1
  61. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  62. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/index.html +1 -1
  78. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  85. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  86. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  87. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  88. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  89. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  90. package/dist/web/standalone/server.js +1 -1
  91. package/package.json +1 -1
  92. package/packages/mcp-server/dist/remote-questions.d.ts +45 -0
  93. package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -0
  94. package/packages/mcp-server/dist/remote-questions.js +732 -0
  95. package/packages/mcp-server/dist/remote-questions.js.map +1 -0
  96. package/packages/mcp-server/dist/server.d.ts +7 -0
  97. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  98. package/packages/mcp-server/dist/server.js +41 -4
  99. package/packages/mcp-server/dist/server.js.map +1 -1
  100. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  101. package/packages/mcp-server/dist/workflow-tools.js +64 -25
  102. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  103. package/packages/mcp-server/package.json +2 -1
  104. package/packages/mcp-server/src/mcp-server.test.ts +30 -0
  105. package/packages/mcp-server/src/remote-questions.test.ts +294 -0
  106. package/packages/mcp-server/src/remote-questions.ts +916 -0
  107. package/packages/mcp-server/src/server.ts +62 -10
  108. package/packages/mcp-server/src/workflow-tools.test.ts +146 -1
  109. package/packages/mcp-server/src/workflow-tools.ts +84 -43
  110. package/packages/mcp-server/tsconfig.test.json +19 -0
  111. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  112. package/packages/pi-ai/dist/providers/anthropic-auth.test.js +1 -1
  113. package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -1
  114. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  115. package/packages/pi-ai/dist/providers/anthropic-shared.js +27 -4
  116. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  117. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  118. package/packages/pi-ai/dist/providers/anthropic.js +8 -3
  119. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  120. package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts +2 -0
  121. package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts.map +1 -0
  122. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js +80 -0
  123. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js.map +1 -0
  124. package/packages/pi-ai/dist/providers/simple-options.d.ts +10 -0
  125. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  126. package/packages/pi-ai/dist/providers/simple-options.js +16 -1
  127. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  128. package/packages/pi-ai/src/providers/anthropic-auth.test.ts +1 -1
  129. package/packages/pi-ai/src/providers/anthropic-shared.ts +26 -5
  130. package/packages/pi-ai/src/providers/anthropic.ts +9 -3
  131. package/packages/pi-ai/src/providers/minimax-tool-name.test.ts +98 -0
  132. package/packages/pi-ai/src/providers/simple-options.ts +17 -1
  133. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  134. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts +2 -0
  135. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts.map +1 -0
  136. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js +203 -0
  137. package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js.map +1 -0
  138. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  139. package/packages/pi-coding-agent/dist/core/model-registry.js +14 -0
  140. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  141. package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts +2 -0
  142. package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts.map +1 -0
  143. package/packages/pi-coding-agent/dist/core/redact-secrets.js +49 -0
  144. package/packages/pi-coding-agent/dist/core/redact-secrets.js.map +1 -0
  145. package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts +2 -0
  146. package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts.map +1 -0
  147. package/packages/pi-coding-agent/dist/core/redact-secrets.test.js +67 -0
  148. package/packages/pi-coding-agent/dist/core/redact-secrets.test.js.map +1 -0
  149. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  150. package/packages/pi-coding-agent/dist/core/session-manager.js +9 -5
  151. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  152. package/packages/pi-coding-agent/dist/core/session-manager.test.js +25 -1
  153. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  154. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts +1 -1
  155. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
  156. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +5 -4
  157. package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
  158. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts +7 -6
  159. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  160. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +29 -21
  161. package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  162. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  163. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +13 -1
  164. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  165. package/packages/pi-coding-agent/src/core/model-registry-custom-caps.test.ts +245 -0
  166. package/packages/pi-coding-agent/src/core/model-registry.ts +16 -0
  167. package/packages/pi-coding-agent/src/core/redact-secrets.test.ts +86 -0
  168. package/packages/pi-coding-agent/src/core/redact-secrets.ts +58 -0
  169. package/packages/pi-coding-agent/src/core/session-manager.test.ts +36 -1
  170. package/packages/pi-coding-agent/src/core/session-manager.ts +9 -5
  171. package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +6 -6
  172. package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +36 -22
  173. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +13 -1
  174. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  175. package/src/resources/extensions/claude-code-cli/readiness.ts +4 -3
  176. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +78 -17
  177. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +149 -5
  178. package/src/resources/extensions/gsd/auto/loop-deps.ts +13 -0
  179. package/src/resources/extensions/gsd/auto/phases.ts +66 -1
  180. package/src/resources/extensions/gsd/auto/run-unit.ts +29 -0
  181. package/src/resources/extensions/gsd/auto/session.ts +22 -0
  182. package/src/resources/extensions/gsd/auto-dispatch.ts +16 -3
  183. package/src/resources/extensions/gsd/auto-model-selection.ts +1 -1
  184. package/src/resources/extensions/gsd/auto-post-unit.ts +29 -3
  185. package/src/resources/extensions/gsd/auto-prompts.ts +28 -1
  186. package/src/resources/extensions/gsd/auto-recovery.ts +15 -0
  187. package/src/resources/extensions/gsd/auto-start.ts +29 -19
  188. package/src/resources/extensions/gsd/auto-worktree.ts +62 -63
  189. package/src/resources/extensions/gsd/auto.ts +58 -27
  190. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +23 -1
  191. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +40 -9
  192. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +109 -0
  193. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
  194. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +53 -5
  195. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +35 -2
  196. package/src/resources/extensions/gsd/clean-root-preflight.ts +111 -0
  197. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +898 -32
  198. package/src/resources/extensions/gsd/compaction-snapshot.ts +165 -0
  199. package/src/resources/extensions/gsd/error-classifier.ts +10 -3
  200. package/src/resources/extensions/gsd/exec-history.ts +153 -0
  201. package/src/resources/extensions/gsd/exec-sandbox.ts +326 -0
  202. package/src/resources/extensions/gsd/gsd-db.ts +122 -7
  203. package/src/resources/extensions/gsd/guided-flow.ts +221 -0
  204. package/src/resources/extensions/gsd/health-widget.ts +3 -1
  205. package/src/resources/extensions/gsd/init-wizard.ts +15 -1
  206. package/src/resources/extensions/gsd/journal.ts +2 -1
  207. package/src/resources/extensions/gsd/key-manager.ts +6 -0
  208. package/src/resources/extensions/gsd/model-router.ts +42 -1
  209. package/src/resources/extensions/gsd/pre-execution-checks.ts +36 -10
  210. package/src/resources/extensions/gsd/preferences-types.ts +46 -0
  211. package/src/resources/extensions/gsd/preferences-validation.ts +79 -0
  212. package/src/resources/extensions/gsd/preferences.ts +17 -17
  213. package/src/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
  214. package/src/resources/extensions/gsd/prompts/discuss.md +29 -2
  215. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
  216. package/src/resources/extensions/gsd/safety/evidence-collector.ts +119 -0
  217. package/src/resources/extensions/gsd/safety/file-change-validator.ts +17 -4
  218. package/src/resources/extensions/gsd/safety/safety-harness.ts +9 -0
  219. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +119 -1
  220. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +12 -0
  221. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +49 -0
  222. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +186 -0
  223. package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +123 -0
  224. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
  225. package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
  226. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +2 -0
  227. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +31 -0
  228. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +1 -1
  229. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +1 -1
  230. package/src/resources/extensions/gsd/tests/escalation.test.ts +1 -1
  231. package/src/resources/extensions/gsd/tests/exec-history.test.ts +124 -0
  232. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +210 -0
  233. package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +58 -0
  234. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +152 -1
  235. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +27 -0
  236. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +1 -1
  237. package/src/resources/extensions/gsd/tests/issue-4540-regressions.test.ts +288 -0
  238. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +2 -0
  239. package/src/resources/extensions/gsd/tests/key-manager.test.ts +7 -0
  240. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  241. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  242. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +19 -0
  243. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +14 -0
  244. package/src/resources/extensions/gsd/tests/pre-exec-gate-loop.test.ts +272 -0
  245. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +234 -0
  246. package/src/resources/extensions/gsd/tests/preferences.test.ts +110 -0
  247. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +44 -0
  248. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +48 -0
  249. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +388 -0
  250. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +9 -3
  251. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +205 -0
  252. package/src/resources/extensions/gsd/tests/save-gate-result-render.test.ts +95 -0
  253. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +32 -40
  254. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +56 -0
  255. package/src/resources/extensions/gsd/tests/token-counter.test.ts +105 -1
  256. package/src/resources/extensions/gsd/tests/tool-compatibility.test.ts +107 -0
  257. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +23 -0
  258. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +65 -2
  259. package/src/resources/extensions/gsd/tests/write-gate.test.ts +64 -0
  260. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +3 -1
  261. package/src/resources/extensions/gsd/token-counter.ts +22 -5
  262. package/src/resources/extensions/gsd/tools/exec-search-tool.ts +81 -0
  263. package/src/resources/extensions/gsd/tools/exec-tool.ts +183 -0
  264. package/src/resources/extensions/gsd/tools/resume-tool.ts +40 -0
  265. package/src/resources/extensions/gsd/uok/plan-v2.ts +26 -3
  266. package/src/resources/extensions/gsd/workflow-logger.ts +3 -1
  267. package/src/resources/extensions/gsd/workflow-mcp.ts +3 -0
  268. package/src/resources/skills/verify-before-complete/SKILL.md +2 -1
  269. package/src/resources/skills/write-docs/SKILL.md +2 -1
  270. /package/dist/web/standalone/.next/static/{ecSsu49rxxcpbNmVP4mLD → lLdDRDspgYzfz0bJAmUSz}/_buildManifest.js +0 -0
  271. /package/dist/web/standalone/.next/static/{ecSsu49rxxcpbNmVP4mLD → lLdDRDspgYzfz0bJAmUSz}/_ssgManifest.js +0 -0
@@ -30,8 +30,7 @@ import { initRoutingHistory } from "./routing-history.js";
30
30
  import { restoreHookState, resetHookState } from "./post-unit-hooks.js";
31
31
  import { resetProactiveHealing, setLevelChangeCallback } from "./doctor-proactive.js";
32
32
  import { snapshotSkills } from "./skill-discovery.js";
33
- import { isDbAvailable, getMilestone, openDatabase } from "./gsd-db.js";
34
- import { hideFooter } from "./auto-dashboard.js";
33
+ import { isDbAvailable, getMilestone, openDatabase, getDbStatus } from "./gsd-db.js";
35
34
  import { debugLog, enableDebug, isDebugEnabled, getDebugLogPath, } from "./debug-logger.js";
36
35
  import { logWarning, logError } from "./workflow-logger.js";
37
36
  import { existsSync, mkdirSync, readdirSync, rmSync, statSync, unlinkSync, } from "node:fs";
@@ -247,7 +246,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
247
246
  // the parent git root). See #2393 and related issue.
248
247
  const hasLocalGit = existsSync(join(base, ".git"));
249
248
  if (!hasLocalGit || isInheritedRepo(base)) {
250
- const mainBranch = loadEffectiveGSDPreferences()?.preferences?.git?.main_branch || "main";
249
+ const mainBranch = loadEffectiveGSDPreferences(base)?.preferences?.git?.main_branch || "main";
251
250
  nativeInit(base, mainBranch);
252
251
  }
253
252
  // Migrate legacy in-project .gsd/ to external state directory.
@@ -263,7 +262,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
263
262
  // Ensure .gitignore has baseline patterns.
264
263
  // ensureGitignore checks for git-tracked .gsd/ files and skips the
265
264
  // ".gsd" pattern if the project intentionally tracks .gsd/ in git.
266
- const gitPrefs = loadEffectiveGSDPreferences()?.preferences?.git;
265
+ const gitPrefs = loadEffectiveGSDPreferences(base)?.preferences?.git;
267
266
  const manageGitignore = gitPrefs?.manage_gitignore;
268
267
  ensureGitignore(base, { manageGitignore });
269
268
  if (manageGitignore !== false)
@@ -289,7 +288,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
289
288
  prepareWorkflowMcpForProject(ctx, base);
290
289
  }
291
290
  // Initialize GitServiceImpl
292
- s.gitService = new GitServiceImpl(s.basePath, loadEffectiveGSDPreferences()?.preferences?.git ?? {});
291
+ s.gitService = new GitServiceImpl(s.basePath, loadEffectiveGSDPreferences(base)?.preferences?.git ?? {});
293
292
  // ── Debug mode ──
294
293
  if (!isDebugEnabled() && process.env.GSD_DEBUG === "1") {
295
294
  enableDebug(base);
@@ -322,7 +321,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
322
321
  // was lost due to session ending between completion and teardown.
323
322
  // Must run after DB open and before worktree entry.
324
323
  try {
325
- const auditResult = auditOrphanedMilestoneBranches(base, getIsolationMode());
324
+ const auditResult = auditOrphanedMilestoneBranches(base, getIsolationMode(base));
326
325
  for (const msg of auditResult.recovered) {
327
326
  ctx.ui.notify(`Orphan audit: ${msg}`, "info");
328
327
  }
@@ -340,7 +339,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
340
339
  let state = await deriveState(base);
341
340
  // Stale worktree state recovery (#654)
342
341
  if (state.activeMilestone &&
343
- shouldUseWorktreeIsolation() &&
342
+ shouldUseWorktreeIsolation(base) &&
344
343
  !detectWorktreeName(base)) {
345
344
  const wtPath = getAutoWorktreePath(base, state.activeMilestone.id);
346
345
  if (wtPath) {
@@ -355,7 +354,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
355
354
  let hasSurvivorBranch = false;
356
355
  if (state.activeMilestone &&
357
356
  (state.phase === "pre-planning" || state.phase === "complete") &&
358
- getIsolationMode() !== "none" &&
357
+ getIsolationMode(base) !== "none" &&
359
358
  !detectWorktreeName(base) &&
360
359
  !base.includes(`${pathSep}.gsd${pathSep}worktrees${pathSep}`)) {
361
360
  const milestoneBranch = `milestone/${state.activeMilestone.id}`;
@@ -516,7 +515,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
516
515
  registerSigtermHandler(base);
517
516
  // Capture integration branch
518
517
  if (s.currentMilestoneId) {
519
- if (getIsolationMode() !== "none") {
518
+ if (getIsolationMode(base) !== "none") {
520
519
  captureIntegrationBranch(base, s.currentMilestoneId);
521
520
  }
522
521
  setActiveMilestoneId(base, s.currentMilestoneId);
@@ -524,7 +523,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
524
523
  // Guard against stale milestone branch when isolation:none (#3613).
525
524
  // A prior session with isolation:branch/worktree may have left HEAD on
526
525
  // milestone/<MID>. Auto-checkout back to the integration branch.
527
- if (getIsolationMode() === "none" && nativeIsRepo(base)) {
526
+ if (getIsolationMode(base) === "none" && nativeIsRepo(base)) {
528
527
  try {
529
528
  const currentBranch = nativeGetCurrentBranch(base);
530
529
  if (currentBranch.startsWith("milestone/")) {
@@ -552,7 +551,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
552
551
  return symlinkRe.test(p);
553
552
  };
554
553
  if (s.currentMilestoneId &&
555
- getIsolationMode() !== "none" &&
554
+ getIsolationMode(base) !== "none" &&
556
555
  !detectWorktreeName(base) &&
557
556
  !isUnderGsdWorktrees(base)) {
558
557
  buildResolver().enterMilestone(s.currentMilestoneId, {
@@ -597,8 +596,21 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
597
596
  // call returns "db_unavailable", triggering artifact-retry which
598
597
  // re-dispatches the same task — producing an infinite loop (#2419).
599
598
  if (existsSync(gsdDbPath) && !isDbAvailable()) {
600
- ctx.ui.notify("SQLite database exists but failed to open. Auto-mode cannot proceed without a working database provider. " +
601
- "Check for corrupt gsd.db or missing native SQLite bindings.", "error");
599
+ const dbStatus = getDbStatus();
600
+ const phaseHint = dbStatus.lastPhase === "open"
601
+ ? "The database file could not be opened"
602
+ : dbStatus.lastPhase === "initSchema"
603
+ ? "The database schema could not be initialized"
604
+ : dbStatus.lastPhase === "vacuum-recovery"
605
+ ? "Corruption recovery (VACUUM) failed"
606
+ : dbStatus.attempted
607
+ ? "The database could not be opened (phase unknown)"
608
+ : "The database provider could not be loaded";
609
+ const errorDetail = dbStatus.lastError ? ` (${dbStatus.lastError.message})` : "";
610
+ const providerHint = dbStatus.provider
611
+ ? ` Provider: ${dbStatus.provider}.`
612
+ : " No SQLite provider available — check Node >= 22 or install better-sqlite3.";
613
+ ctx.ui.notify(`SQLite database exists but failed to open: ${gsdDbPath}. ${phaseHint}${errorDetail}.${providerHint}`, "error");
602
614
  return releaseLockAndReturn();
603
615
  }
604
616
  // Initialize metrics
@@ -633,13 +645,10 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
633
645
  }
634
646
  }
635
647
  // Snapshot installed skills
636
- if (resolveSkillDiscoveryMode() !== "off") {
648
+ if (resolveSkillDiscoveryMode(base) !== "off") {
637
649
  snapshotSkills();
638
650
  }
639
651
  ctx.ui.setStatus("gsd-auto", s.stepMode ? "next" : "auto");
640
- ctx.ui.setFooter(hideFooter);
641
- // Hide gsd-health during AUTO — gsd-progress is the single source of truth
642
- // for last-commit / cost / health signal while auto is running.
643
652
  ctx.ui.setWidget("gsd-health", undefined);
644
653
  const modeLabel = s.stepMode ? "Step-mode" : "Auto-mode";
645
654
  const pendingCount = (state.registry ?? []).filter((m) => m.status !== "complete" && m.status !== "parked").length;
@@ -661,7 +670,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
661
670
  // FlatRateContext used by selectAndApplyModel so user-declared
662
671
  // flat-rate providers and externalCli auto-detection are respected.
663
672
  const { isFlatRateProvider, buildFlatRateContext } = await import("./auto-model-selection.js");
664
- const bannerPrefs = loadEffectiveGSDPreferences()?.preferences;
673
+ const bannerPrefs = loadEffectiveGSDPreferences(base)?.preferences;
665
674
  const effectiveProvider = s.autoModeStartModel?.provider ?? ctx.model?.provider;
666
675
  const effectivelyEnabled = routingConfig.enabled
667
676
  && (routingConfig.allow_flat_rate_providers
@@ -180,14 +180,6 @@ function clearProjectRootStateFiles(basePath, milestoneId) {
180
180
  }
181
181
  }
182
182
  }
183
- function isProjectGsdSymlink(basePath) {
184
- try {
185
- return lstatSyncFn(join(basePath, ".gsd")).isSymbolicLink();
186
- }
187
- catch {
188
- return false;
189
- }
190
- }
191
183
  // ─── Build Artifact Auto-Resolve ─────────────────────────────────────────────
192
184
  /** Patterns for machine-generated build artifacts that can be safely
193
185
  * auto-resolved by accepting --theirs during merge. These files are
@@ -1429,50 +1421,15 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1429
1421
  });
1430
1422
  }
1431
1423
  }
1432
- // 7. Stash any pre-existing dirty files so the squash merge is not
1433
- // blocked by unrelated local changes (#2151). clearProjectRootStateFiles
1434
- // only removes untracked .gsd/ files; tracked dirty files elsewhere (e.g.
1435
- // .planning/work-state.json with stash conflict markers) are invisible to
1436
- // that cleanup but will cause `git merge --squash` to reject.
1437
- let stashed = false;
1438
- try {
1439
- const status = execFileSync("git", ["status", "--porcelain"], {
1440
- cwd: originalBasePath_,
1441
- stdio: ["ignore", "pipe", "pipe"],
1442
- encoding: "utf-8",
1443
- }).trim();
1444
- if (status) {
1445
- // Use --include-untracked to stash untracked files that would block
1446
- // the squash merge, but EXCLUDE .gsd/milestones/ (#2505).
1447
- // --include-untracked without exclusion sweeps queued milestone
1448
- // CONTEXT files into the stash. If stash pop later fails, those files
1449
- // are permanently trapped in the stash entry and lost on the next
1450
- // stash push or drop.
1451
- //
1452
- // When `.gsd` itself is a symlink, Git rejects pathspecs below it
1453
- // ("beyond a symbolic link"). In that layout, exclude the whole symlink
1454
- // and keep stashing real project files that could block the merge.
1455
- const stashPathspecs = isProjectGsdSymlink(originalBasePath_)
1456
- ? [".", ":(exclude).gsd"]
1457
- : [":(exclude).gsd/milestones"];
1458
- execFileSync("git", [
1459
- "stash", "push", "--include-untracked",
1460
- "-m", `gsd: pre-merge stash for ${milestoneId}`,
1461
- "--", ...stashPathspecs,
1462
- ], { cwd: originalBasePath_, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" });
1463
- stashed = true;
1464
- }
1465
- }
1466
- catch (err) {
1467
- // Stash failure is non-fatal — proceed without stash and let the merge
1468
- // report the dirty tree if it fails.
1469
- logWarning("worktree", `git stash failed: ${err instanceof Error ? err.message : String(err)}`);
1470
- }
1471
- // 7a. Shelter queued milestone directories before the squash merge (#2505).
1424
+ // 7. Shelter queued milestone directories before the squash merge (#2505).
1472
1425
  // The milestone branch may contain copies of queued milestone dirs (via
1473
1426
  // copyPlanningArtifacts), so `git merge --squash` rejects when those same
1474
1427
  // files exist as untracked in the working tree. Temporarily move them to
1475
1428
  // a backup location, then restore after the merge+commit.
1429
+ //
1430
+ // MUST run BEFORE the pre-merge stash (step 7a) so `--include-untracked`
1431
+ // does not sweep queued CONTEXT files into the stash. If stash pop later
1432
+ // fails, files trapped inside the stash are permanently lost (#2505).
1476
1433
  const milestonesDir = join(gsdRoot(originalBasePath_), "milestones");
1477
1434
  const shelterDir = join(gsdRoot(originalBasePath_), ".milestone-shelter");
1478
1435
  const shelteredDirs = [];
@@ -1526,6 +1483,31 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1526
1483
  // Non-fatal — proceed with merge; untracked files may block it
1527
1484
  logWarning("worktree", `milestone shelter operation failed: ${err instanceof Error ? err.message : String(err)}`);
1528
1485
  }
1486
+ // 7a. Stash pre-existing dirty files so the squash merge is not blocked by
1487
+ // unrelated local changes (#2151). Includes untracked files to handle
1488
+ // locally-added files that conflict with tracked files on the milestone
1489
+ // branch. Passing NO pathspec lets git skip gitignored paths silently;
1490
+ // adding an explicit pathspec trips a `git add`-style fatal on ignored
1491
+ // entries (e.g. a gitignored `.gsd` symlink under ADR-002) (#4573).
1492
+ // Queued CONTEXT files under `.gsd/milestones/*` are already sheltered
1493
+ // in step 7 above, so they won't be swept into the stash.
1494
+ let stashed = false;
1495
+ try {
1496
+ const status = execFileSync("git", ["status", "--porcelain"], {
1497
+ cwd: originalBasePath_,
1498
+ stdio: ["ignore", "pipe", "pipe"],
1499
+ encoding: "utf-8",
1500
+ }).trim();
1501
+ if (status) {
1502
+ execFileSync("git", ["stash", "push", "--include-untracked", "-m", `gsd: pre-merge stash for ${milestoneId}`], { cwd: originalBasePath_, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" });
1503
+ stashed = true;
1504
+ }
1505
+ }
1506
+ catch (err) {
1507
+ // Stash failure is non-fatal — proceed without stash and let the merge
1508
+ // report the dirty tree if it fails.
1509
+ logWarning("worktree", `git stash failed: ${err instanceof Error ? err.message : String(err)}`);
1510
+ }
1529
1511
  // 7b. Clean up stale merge state before attempting squash merge (#2912).
1530
1512
  // A leftover MERGE_HEAD (from a previous failed merge, libgit2 native path,
1531
1513
  // or interrupted operation) causes `git merge --squash` to refuse with
@@ -1761,16 +1743,32 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1761
1743
  // When a milestone only produced .gsd/ metadata (summaries, roadmaps) but no
1762
1744
  // real code, the user sees "milestone complete" but nothing changed in their
1763
1745
  // codebase. Surface this so the caller can warn the user.
1746
+ //
1747
+ // Bug #4385 fix: use `git diff-tree --root` instead of `git diff HEAD~1 HEAD`.
1748
+ // `HEAD~1` does not exist on initial commits and is unreliable on shallow clones
1749
+ // and merge commits. `diff-tree --root` handles all three cases correctly.
1750
+ // The empty-tree hash (4b825dc…) is the universal fallback for refs that don't exist.
1751
+ const GIT_EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
1764
1752
  let codeFilesChanged = false;
1765
1753
  if (!nothingToCommit) {
1766
1754
  try {
1767
- const mergedFiles = nativeDiffNumstat(originalBasePath_, "HEAD~1", "HEAD");
1768
- codeFilesChanged = mergedFiles.some((entry) => !entry.path.startsWith(".gsd/"));
1755
+ const diffTreeOutput = execFileSync("git", ["diff-tree", "--root", "--no-commit-id", "-r", "--name-only", "HEAD"], { cwd: originalBasePath_, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
1756
+ const mergedFiles = diffTreeOutput ? diffTreeOutput.split("\n").filter(Boolean) : [];
1757
+ codeFilesChanged = mergedFiles.some((f) => !f.startsWith(".gsd/"));
1769
1758
  }
1770
1759
  catch (e) {
1771
- // If HEAD~1 doesn't exist (first commit), assume code was changed
1772
- logWarning("worktree", `diff numstat failed (assuming code changed): ${e.message}`);
1773
- codeFilesChanged = true;
1760
+ // diff-tree failed (e.g. unborn HEAD in a brand-new repo) fall back to
1761
+ // comparing against the empty tree so initial-commit repos still report changes.
1762
+ try {
1763
+ const fallbackOutput = execFileSync("git", ["diff", "--name-only", GIT_EMPTY_TREE, "HEAD"], { cwd: originalBasePath_, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
1764
+ const fallbackFiles = fallbackOutput ? fallbackOutput.split("\n").filter(Boolean) : [];
1765
+ codeFilesChanged = fallbackFiles.some((f) => !f.startsWith(".gsd/"));
1766
+ }
1767
+ catch {
1768
+ // Truly unable to determine — assume code was changed to avoid silent data loss
1769
+ logWarning("worktree", `diff-tree and empty-tree fallback both failed (assuming code changed): ${e.message}`);
1770
+ codeFilesChanged = true;
1771
+ }
1774
1772
  }
1775
1773
  }
1776
1774
  // 10. Auto-push if enabled
@@ -37,8 +37,9 @@ import { getRtkSessionSavings } from "../shared/rtk-session-stats.js";
37
37
  import { deactivateGSD } from "../shared/gsd-phase-state.js";
38
38
  import { initMetrics, resetMetrics, getLedger, getProjectTotals, formatCost, formatTokenCount, } from "./metrics.js";
39
39
  import { setLogBasePath, logWarning } from "./workflow-logger.js";
40
+ import { preflightCleanRoot, postflightPopStash } from "./clean-root-preflight.js";
40
41
  import { homedir } from "node:os";
41
- import { join } from "node:path";
42
+ import { isAbsolute, join } from "node:path";
42
43
  import { pathToFileURL } from "node:url";
43
44
  import { readFileSync, existsSync, mkdirSync, writeFileSync, unlinkSync } from "node:fs";
44
45
  import { atomicWriteSync } from "./atomic-write.js";
@@ -55,7 +56,7 @@ import { getErrorMessage } from "./error-utils.js";
55
56
  import { recoverFailedMigration } from "./migrate-external.js";
56
57
  import { initRegistry, convertDispatchRules } from "./rule-registry.js";
57
58
  import { emitJournalEvent as _emitJournalEvent } from "./journal.js";
58
- import { updateProgressWidget as _updateProgressWidget, updateSliceProgressCache, clearSliceProgressCache, hideFooter, } from "./auto-dashboard.js";
59
+ import { updateProgressWidget as _updateProgressWidget, updateSliceProgressCache, clearSliceProgressCache, } from "./auto-dashboard.js";
59
60
  import { registerSigtermHandler as _registerSigtermHandler, deregisterSigtermHandler as _deregisterSigtermHandler, } from "./auto-supervisor.js";
60
61
  import { isDbAvailable, getMilestone } from "./gsd-db.js";
61
62
  import { countPendingCaptures } from "./captures.js";
@@ -136,6 +137,24 @@ function restoreMilestoneLockEnv() {
136
137
  s.hadMilestoneLockEnv = false;
137
138
  s.milestoneLockEnvCaptured = false;
138
139
  }
140
+ function normalizeSessionFilePath(raw) {
141
+ if (typeof raw !== "string")
142
+ return null;
143
+ const trimmed = raw.trim();
144
+ if (!trimmed)
145
+ return null;
146
+ const firstLine = trimmed.split(/\r?\n/, 1)[0]?.trim() ?? "";
147
+ if (!firstLine)
148
+ return null;
149
+ // Guard against accidental message concatenation by trimming to .jsonl.
150
+ const jsonlIndex = firstLine.toLowerCase().indexOf(".jsonl");
151
+ const candidate = jsonlIndex >= 0 ? firstLine.slice(0, jsonlIndex + ".jsonl".length) : firstLine;
152
+ if (!isAbsolute(candidate))
153
+ return null;
154
+ if (!candidate.toLowerCase().endsWith(".jsonl"))
155
+ return null;
156
+ return candidate;
157
+ }
139
158
  export function startAutoDetached(ctx, pi, base, verboseMode, options) {
140
159
  void startAuto(ctx, pi, base, verboseMode, options).catch((err) => {
141
160
  const message = getErrorMessage(err);
@@ -145,8 +164,8 @@ export function startAutoDetached(ctx, pi, base, verboseMode, options) {
145
164
  });
146
165
  }
147
166
  /** Returns true if the project is configured for `isolation:worktree` mode. */
148
- export function shouldUseWorktreeIsolation() {
149
- const prefs = loadEffectiveGSDPreferences()?.preferences?.git;
167
+ export function shouldUseWorktreeIsolation(basePath) {
168
+ const prefs = loadEffectiveGSDPreferences(basePath)?.preferences?.git;
150
169
  if (prefs?.isolation === "worktree")
151
170
  return true;
152
171
  // Default is false — worktree isolation requires explicit opt-in
@@ -215,7 +234,7 @@ export function getAutoDashboardData() {
215
234
  const rtkSavings = sessionId && s.basePath
216
235
  ? getRtkSessionSavings(s.basePath, sessionId)
217
236
  : null;
218
- const rtkEnabled = loadEffectiveGSDPreferences()?.preferences.experimental?.rtk === true;
237
+ const rtkEnabled = loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences.experimental?.rtk === true;
219
238
  // Pending capture count — lazy check, non-fatal
220
239
  let pendingCaptureCount = 0;
221
240
  try {
@@ -393,7 +412,7 @@ function clearUnitTimeout() {
393
412
  }
394
413
  /** Build snapshot metric opts. */
395
414
  function buildSnapshotOpts(_unitType, _unitId) {
396
- const prefs = loadEffectiveGSDPreferences()?.preferences;
415
+ const prefs = loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences;
397
416
  const uokFlags = resolveUokFlags(prefs);
398
417
  return {
399
418
  ...(s.autoStartTime > 0 ? { autoSessionKey: String(s.autoStartTime) } : {}),
@@ -427,7 +446,7 @@ function handleLostSessionLock(ctx, lockStatus) {
427
446
  restoreProjectRootEnv();
428
447
  restoreMilestoneLockEnv();
429
448
  deregisterSigtermHandler();
430
- clearCmuxSidebar(loadEffectiveGSDPreferences()?.preferences);
449
+ clearCmuxSidebar(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences);
431
450
  const base = lockBase();
432
451
  const lockFilePath = base ? join(gsdRoot(base), "auto.lock") : "unknown";
433
452
  const recoverySuggestion = "\nTo recover, run: gsd doctor --fix";
@@ -443,7 +462,6 @@ function handleLostSessionLock(ctx, lockStatus) {
443
462
  ctx?.ui.notify(message, "error");
444
463
  ctx?.ui.setStatus("gsd-auto", undefined);
445
464
  ctx?.ui.setWidget("gsd-progress", undefined);
446
- ctx?.ui.setFooter(undefined);
447
465
  if (ctx)
448
466
  initHealthWidget(ctx);
449
467
  }
@@ -480,7 +498,6 @@ function cleanupAfterLoopExit(ctx) {
480
498
  if (!s.paused) {
481
499
  ctx.ui.setStatus("gsd-auto", undefined);
482
500
  ctx.ui.setWidget("gsd-progress", undefined);
483
- ctx.ui.setFooter(undefined);
484
501
  initHealthWidget(ctx);
485
502
  }
486
503
  // Restore CWD out of worktree back to original project root
@@ -498,7 +515,7 @@ function cleanupAfterLoopExit(ctx) {
498
515
  export async function stopAuto(ctx, pi, reason) {
499
516
  if (!s.active && !s.paused)
500
517
  return;
501
- const loadedPreferences = loadEffectiveGSDPreferences()?.preferences;
518
+ const loadedPreferences = loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences;
502
519
  const reasonSuffix = reason ? ` — ${reason}` : "";
503
520
  try {
504
521
  // ── Step 1: Timers and locks ──
@@ -743,7 +760,6 @@ export async function stopAuto(ctx, pi, reason) {
743
760
  // UI cleanup
744
761
  ctx?.ui.setStatus("gsd-auto", undefined);
745
762
  ctx?.ui.setWidget("gsd-progress", undefined);
746
- ctx?.ui.setFooter(undefined);
747
763
  if (ctx)
748
764
  initHealthWidget(ctx);
749
765
  restoreProjectRootEnv();
@@ -778,7 +794,7 @@ export async function pauseAuto(ctx, _pi, _errorContext) {
778
794
  // Pass errorContext so runUnitPhase can distinguish user-initiated pause
779
795
  // from provider-error pause and avoid hard-stopping (#2762).
780
796
  resolveAgentEndCancelled(_errorContext);
781
- s.pausedSessionFile = ctx?.sessionManager?.getSessionFile() ?? null;
797
+ s.pausedSessionFile = normalizeSessionFilePath(ctx?.sessionManager?.getSessionFile() ?? null);
782
798
  // Persist paused-session metadata so resume survives /exit (#1383).
783
799
  // The fresh-start bootstrap checks for this file and restores worktree context.
784
800
  try {
@@ -832,7 +848,6 @@ export async function pauseAuto(ctx, _pi, _errorContext) {
832
848
  s.verificationRetryCount.clear();
833
849
  ctx?.ui.setStatus("gsd-auto", "paused");
834
850
  ctx?.ui.setWidget("gsd-progress", undefined);
835
- ctx?.ui.setFooter(undefined);
836
851
  if (ctx)
837
852
  initHealthWidget(ctx);
838
853
  const resumeCmd = s.stepMode ? "/gsd next" : "/gsd auto";
@@ -978,6 +993,9 @@ function buildLoopDeps() {
978
993
  },
979
994
  // Journal
980
995
  emitJournalEvent: (entry) => _emitJournalEvent(s.basePath, entry),
996
+ // Clean-root preflight gate (#2909)
997
+ preflightCleanRoot,
998
+ postflightPopStash,
981
999
  };
982
1000
  }
983
1001
  /**
@@ -1034,7 +1052,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1034
1052
  unlinkSync(pausedPath);
1035
1053
  }
1036
1054
  catch (e) {
1037
- logWarning("session", `pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
1055
+ if (e.code !== "ENOENT") {
1056
+ logWarning("session", `pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
1057
+ }
1038
1058
  }
1039
1059
  ctx.ui.notify(`Resuming paused custom workflow${meta.activeRunDir ? ` (${meta.activeRunDir})` : ""}.`, "info");
1040
1060
  }
@@ -1052,7 +1072,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1052
1072
  unlinkSync(pausedPath);
1053
1073
  }
1054
1074
  catch (err) {
1055
- logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1075
+ if (err.code !== "ENOENT") {
1076
+ logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1077
+ }
1056
1078
  }
1057
1079
  ctx.ui.notify(`Paused milestone ${meta.milestoneId} is ${!mDir ? "missing" : "already complete"}. Starting fresh.`, "info");
1058
1080
  }
@@ -1060,7 +1082,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1060
1082
  s.currentMilestoneId = meta.milestoneId;
1061
1083
  s.originalBasePath = meta.originalBasePath || base;
1062
1084
  s.stepMode = meta.stepMode ?? requestedStepMode;
1063
- s.pausedSessionFile = meta.sessionFile ?? null;
1085
+ s.pausedSessionFile = normalizeSessionFilePath(meta.sessionFile ?? null);
1064
1086
  s.pausedUnitType = meta.unitType ?? null;
1065
1087
  s.pausedUnitId = meta.unitId ?? null;
1066
1088
  s.autoStartTime = meta.autoStartTime || Date.now();
@@ -1070,7 +1092,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1070
1092
  unlinkSync(pausedPath);
1071
1093
  }
1072
1094
  catch (e) {
1073
- logWarning("session", `pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
1095
+ if (e.code !== "ENOENT") {
1096
+ logWarning("session", `pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
1097
+ }
1074
1098
  }
1075
1099
  ctx.ui.notify(`Resuming paused session for ${meta.milestoneId}${meta.worktreePath && existsSync(meta.worktreePath) ? ` (worktree)` : ""}.`, "info");
1076
1100
  }
@@ -1080,7 +1104,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1080
1104
  unlinkSync(pausedPath);
1081
1105
  }
1082
1106
  catch (e) {
1083
- logWarning("session", `stale pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
1107
+ if (e.code !== "ENOENT") {
1108
+ logWarning("session", `stale pause file cleanup failed: ${e instanceof Error ? e.message : String(e)}`, { file: "auto.ts" });
1109
+ }
1084
1110
  }
1085
1111
  }
1086
1112
  }
@@ -1136,7 +1162,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1136
1162
  unlinkSync(s.pausedSessionFile);
1137
1163
  }
1138
1164
  catch (err) {
1139
- logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1165
+ if (err.code !== "ENOENT") {
1166
+ logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1167
+ }
1140
1168
  }
1141
1169
  s.pausedSessionFile = null;
1142
1170
  }
@@ -1164,7 +1192,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1164
1192
  });
1165
1193
  // ── Auto-worktree / branch-mode: re-enter on resume ──
1166
1194
  if (s.currentMilestoneId &&
1167
- getIsolationMode() !== "none" &&
1195
+ getIsolationMode(s.originalBasePath || s.basePath) !== "none" &&
1168
1196
  s.originalBasePath &&
1169
1197
  !isInAutoWorktree(s.basePath) &&
1170
1198
  !detectWorktreeName(s.basePath) &&
@@ -1175,7 +1203,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1175
1203
  }
1176
1204
  registerSigtermHandler(lockBase());
1177
1205
  ctx.ui.setStatus("gsd-auto", s.stepMode ? "next" : "auto");
1178
- ctx.ui.setFooter(hideFooter);
1206
+ ctx.ui.setWidget("gsd-health", undefined);
1179
1207
  ctx.ui.notify(s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "info");
1180
1208
  restoreHookState(s.basePath);
1181
1209
  // Re-sync managed resources on resume so long-lived auto sessions pick up
@@ -1197,7 +1225,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1197
1225
  await openProjectDbIfPresent(s.basePath);
1198
1226
  try {
1199
1227
  await rebuildState(s.basePath);
1200
- syncCmuxSidebar(loadEffectiveGSDPreferences()?.preferences, await deriveState(s.basePath));
1228
+ syncCmuxSidebar(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, await deriveState(s.basePath));
1201
1229
  }
1202
1230
  catch (e) {
1203
1231
  debugLog("resume-rebuild-state-failed", {
@@ -1227,7 +1255,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1227
1255
  }
1228
1256
  updateSessionLock(lockBase(), "resuming", s.currentMilestoneId ?? "unknown");
1229
1257
  writeLock(lockBase(), "resuming", s.currentMilestoneId ?? "unknown");
1230
- logCmuxEvent(loadEffectiveGSDPreferences()?.preferences, s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "progress");
1258
+ logCmuxEvent(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "progress");
1231
1259
  captureProjectRootEnv(s.originalBasePath || s.basePath);
1232
1260
  startAutoCommandPolling(s.basePath);
1233
1261
  await runAutoLoopWithUok({
@@ -1253,13 +1281,13 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1253
1281
  return;
1254
1282
  captureProjectRootEnv(s.originalBasePath || s.basePath);
1255
1283
  try {
1256
- syncCmuxSidebar(loadEffectiveGSDPreferences()?.preferences, await deriveState(s.basePath));
1284
+ syncCmuxSidebar(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, await deriveState(s.basePath));
1257
1285
  }
1258
1286
  catch (err) {
1259
1287
  // Best-effort only — sidebar sync must never block auto-mode startup
1260
1288
  logWarning("engine", `cmux sync failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1261
1289
  }
1262
- logCmuxEvent(loadEffectiveGSDPreferences()?.preferences, requestedStepMode ? "Step-mode started." : "Auto-mode started.", "progress");
1290
+ logCmuxEvent(loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, requestedStepMode ? "Step-mode started." : "Auto-mode started.", "progress");
1263
1291
  startAutoCommandPolling(s.basePath);
1264
1292
  // Dispatch the first unit
1265
1293
  await runAutoLoopWithUok({
@@ -1364,8 +1392,8 @@ export async function dispatchHookUnit(ctx, pi, hookName, triggerUnitType, trigg
1364
1392
  `Ensure the model is defined in models.json and has auth configured.`, "warning");
1365
1393
  }
1366
1394
  }
1367
- const sessionFile = ctx.sessionManager.getSessionFile();
1368
- writeLock(lockBase(), hookUnitType, triggerUnitId, sessionFile);
1395
+ const sessionFile = normalizeSessionFilePath(ctx.sessionManager.getSessionFile());
1396
+ writeLock(lockBase(), hookUnitType, triggerUnitId, sessionFile ?? undefined);
1369
1397
  clearUnitTimeout();
1370
1398
  const supervisor = resolveAutoSupervisorConfig();
1371
1399
  const hookHardTimeoutMs = (supervisor.hard_timeout_minutes ?? 30) * 60 * 1000;
@@ -1,5 +1,5 @@
1
1
  import { logWarning } from "../workflow-logger.js";
2
- import { checkAutoStartAfterDiscuss } from "../guided-flow.js";
2
+ import { checkAutoStartAfterDiscuss, maybeHandleReadyPhraseWithoutFiles, maybeHandleEmptyIntentTurn, resetEmptyTurnCounter, } from "../guided-flow.js";
3
3
  import { getAutoDashboardData, getAutoModeStartModel, isAutoActive, pauseAuto, setCurrentDispatchedModelId } from "../auto.js";
4
4
  import { getNextFallbackModel, resolveModelWithFallbacksForUnit } from "../preferences.js";
5
5
  import { pauseAutoForProviderError } from "../provider-error-pause.js";
@@ -53,6 +53,19 @@ export async function handleAgentEnd(pi, event, ctx) {
53
53
  clearDiscussionFlowState();
54
54
  return;
55
55
  }
56
+ // #4573 — When the LLM emits "Milestone X ready." but the required files
57
+ // are missing, `checkAutoStartAfterDiscuss` returns false silently. Surface
58
+ // that and nudge the LLM to complete the writes before the user hits the
59
+ // downstream "All milestones complete" warning loop.
60
+ if (maybeHandleReadyPhraseWithoutFiles(event))
61
+ return;
62
+ // #4573 — Empty-turn recovery: if the LLM announced intent in prose but
63
+ // emitted no tool calls, nudge it to execute. Fires only when auto-mode is
64
+ // active or a discussion autostart is pending (non-auto interactive discuss
65
+ // is user-driven). Runs before `isAutoActive` early return so pending
66
+ // discussions (where isAutoActive may be false) still get recovered.
67
+ if (maybeHandleEmptyIntentTurn(event, isAutoActive()))
68
+ return;
56
69
  if (!isAutoActive())
57
70
  return;
58
71
  if (isSessionSwitchInFlight())
@@ -286,6 +299,9 @@ export async function handleAgentEnd(pi, event, ctx) {
286
299
  // ── Success path ─────────────────────────────────────────────────────────
287
300
  try {
288
301
  resetRetryState(retryState);
302
+ // #4573 — Reset the empty-turn counter on any successful agent turn so
303
+ // transient stalls don't accumulate across independent units.
304
+ resetEmptyTurnCounter();
289
305
  resolveAgentEnd(event);
290
306
  }
291
307
  catch (err) {