@opengsd/gsd-pi 1.0.2-dev.867e002 → 1.0.2-dev.aed3486

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 (328) hide show
  1. package/dist/onboarding.js +22 -3
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/context7/index.js +12 -2
  4. package/dist/resources/extensions/get-secrets-from-user.js +16 -16
  5. package/dist/resources/extensions/google-cli/index.js +30 -0
  6. package/dist/resources/extensions/google-cli/models.js +55 -0
  7. package/dist/resources/extensions/google-cli/package.json +11 -0
  8. package/dist/resources/extensions/google-cli/readiness.js +12 -0
  9. package/dist/resources/extensions/google-cli/stream-adapter.js +191 -0
  10. package/dist/resources/extensions/gsd/auto/loop.js +62 -1
  11. package/dist/resources/extensions/gsd/auto/orchestrator.js +4 -2
  12. package/dist/resources/extensions/gsd/auto/phases.js +37 -0
  13. package/dist/resources/extensions/gsd/auto/run-unit.js +8 -0
  14. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  15. package/dist/resources/extensions/gsd/auto-dispatch.js +17 -7
  16. package/dist/resources/extensions/gsd/auto-post-unit.js +21 -11
  17. package/dist/resources/extensions/gsd/auto-prompts.js +5 -236
  18. package/dist/resources/extensions/gsd/auto-recovery.js +10 -5
  19. package/dist/resources/extensions/gsd/auto-start.js +232 -49
  20. package/dist/resources/extensions/gsd/auto.js +6 -1
  21. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +4 -3
  22. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +7 -2
  23. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +39 -5
  24. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +17 -7
  25. package/dist/resources/extensions/gsd/bootstrap/system-context.js +3 -27
  26. package/dist/resources/extensions/gsd/closeout-recovery.js +7 -1
  27. package/dist/resources/extensions/gsd/commands/handlers/auto.js +9 -1
  28. package/dist/resources/extensions/gsd/commands-usage.js +105 -1
  29. package/dist/resources/extensions/gsd/config-overlay.js +20 -14
  30. package/dist/resources/extensions/gsd/context-overlay.js +22 -16
  31. package/dist/resources/extensions/gsd/dashboard-overlay.js +10 -23
  32. package/dist/resources/extensions/gsd/doctor-engine-checks.js +87 -0
  33. package/dist/resources/extensions/gsd/doctor-providers.js +54 -24
  34. package/dist/resources/extensions/gsd/doctor.js +6 -1
  35. package/dist/resources/extensions/gsd/git-conflict-state.js +26 -1
  36. package/dist/resources/extensions/gsd/guided-flow.js +5 -6
  37. package/dist/resources/extensions/gsd/key-manager.js +45 -13
  38. package/dist/resources/extensions/gsd/milestone-reopen-events.js +28 -0
  39. package/dist/resources/extensions/gsd/notification-overlay.js +8 -9
  40. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +15 -13
  41. package/dist/resources/extensions/gsd/preferences-skills.js +11 -4
  42. package/dist/resources/extensions/gsd/preferences.js +14 -2
  43. package/dist/resources/extensions/gsd/prompt-loader.js +2 -0
  44. package/dist/resources/extensions/gsd/prompts/discuss.md +4 -2
  45. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  46. package/dist/resources/extensions/gsd/prompts/system.md +1 -3
  47. package/dist/resources/extensions/gsd/queue-reorder-ui.js +28 -18
  48. package/dist/resources/extensions/gsd/repository-registry.js +3 -1
  49. package/dist/resources/extensions/gsd/safety/evidence-collector.js +11 -4
  50. package/dist/resources/extensions/gsd/skill-activation.js +233 -0
  51. package/dist/resources/extensions/gsd/skill-catalog.data.js +820 -0
  52. package/dist/resources/extensions/gsd/skill-catalog.install.js +179 -0
  53. package/dist/resources/extensions/gsd/skill-catalog.js +5 -1028
  54. package/dist/resources/extensions/gsd/skill-discovery.js +121 -79
  55. package/dist/resources/extensions/gsd/skill-scope.js +52 -0
  56. package/dist/resources/extensions/gsd/skill-telemetry.js +6 -39
  57. package/dist/resources/extensions/gsd/skills.js +60 -0
  58. package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +351 -0
  59. package/dist/resources/extensions/gsd/state-reconciliation/index.js +41 -0
  60. package/dist/resources/extensions/gsd/state-reconciliation/registry.js +4 -0
  61. package/dist/resources/extensions/gsd/tools/complete-task.js +9 -0
  62. package/dist/resources/extensions/gsd/tools/exec-tool.js +42 -8
  63. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +63 -2
  64. package/dist/resources/extensions/gsd/tui/render-kit.js +51 -0
  65. package/dist/resources/extensions/gsd/unit-context-manifest.js +35 -26
  66. package/dist/resources/extensions/gsd/user-input-boundary.js +1 -1
  67. package/dist/resources/extensions/gsd/vision-ask.js +22 -0
  68. package/dist/resources/extensions/gsd/visualizer-overlay.js +8 -36
  69. package/dist/resources/extensions/gsd/worktree-lifecycle.js +24 -3
  70. package/dist/resources/extensions/search-the-web/native-search.js +57 -8
  71. package/dist/resources/extensions/shared/confirm-ui.js +9 -6
  72. package/dist/resources/extensions/shared/dialog-frame.js +42 -0
  73. package/dist/resources/extensions/shared/interview-ui.js +42 -30
  74. package/dist/resources/extensions/shared/next-action-ui.js +6 -6
  75. package/dist/resources/skills/create-skill/references/gsd-skill-ecosystem.md +1 -1
  76. package/dist/web/standalone/.next/BUILD_ID +1 -1
  77. package/dist/web/standalone/.next/app-path-routes-manifest.json +4 -4
  78. package/dist/web/standalone/.next/build-manifest.json +2 -2
  79. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  80. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/index.html +1 -1
  97. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app-paths-manifest.json +4 -4
  104. package/dist/web/standalone/.next/server/chunks/1834.js +2 -2
  105. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  106. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  107. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  108. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  109. package/package.json +1 -1
  110. package/packages/cloud-mcp-gateway/package.json +2 -2
  111. package/packages/contracts/package.json +1 -1
  112. package/packages/daemon/package.json +4 -4
  113. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts +1 -0
  114. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts.map +1 -1
  115. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js +22 -8
  116. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js.map +1 -1
  117. package/packages/gsd-agent-core/package.json +5 -5
  118. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts +12 -0
  119. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.d.ts.map +1 -0
  120. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js +45 -0
  121. package/packages/gsd-agent-modes/dist/modes/interactive/components/dialog-container.js.map +1 -0
  122. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts +3 -2
  123. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  124. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js +11 -11
  125. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-editor.js.map +1 -1
  126. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts +3 -3
  127. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  128. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js +13 -11
  129. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-input.js.map +1 -1
  130. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts +3 -3
  131. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  132. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js +12 -10
  133. package/packages/gsd-agent-modes/dist/modes/interactive/components/extension-selector.js.map +1 -1
  134. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts +1 -0
  135. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.d.ts.map +1 -1
  136. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js +1 -0
  137. package/packages/gsd-agent-modes/dist/modes/interactive/components/index.js.map +1 -1
  138. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts +1 -1
  139. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  140. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js +2 -2
  141. package/packages/gsd-agent-modes/dist/modes/interactive/components/login-dialog.js.map +1 -1
  142. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts +6 -1
  143. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  144. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js +9 -6
  145. package/packages/gsd-agent-modes/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  146. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  147. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +0 -1
  148. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  149. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts +3 -0
  150. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
  151. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +144 -2
  152. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
  153. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.d.ts.map +1 -1
  154. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js +2 -14
  155. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-session.js.map +1 -1
  156. package/packages/gsd-agent-modes/package.json +7 -7
  157. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  158. package/packages/mcp-server/dist/workflow-tools.js +10 -5
  159. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  160. package/packages/mcp-server/package.json +3 -3
  161. package/packages/native/package.json +1 -1
  162. package/packages/pi-agent-core/dist/harness/skills.d.ts.map +1 -1
  163. package/packages/pi-agent-core/dist/harness/skills.js +6 -0
  164. package/packages/pi-agent-core/dist/harness/skills.js.map +1 -1
  165. package/packages/pi-agent-core/dist/harness/system-prompt.d.ts +7 -0
  166. package/packages/pi-agent-core/dist/harness/system-prompt.d.ts.map +1 -1
  167. package/packages/pi-agent-core/dist/harness/system-prompt.js +7 -0
  168. package/packages/pi-agent-core/dist/harness/system-prompt.js.map +1 -1
  169. package/packages/pi-agent-core/package.json +1 -1
  170. package/packages/pi-ai/dist/models.generated.d.ts +8 -59
  171. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  172. package/packages/pi-ai/dist/models.generated.js +21 -72
  173. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  174. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  175. package/packages/pi-ai/dist/providers/anthropic.js +50 -0
  176. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  177. package/packages/pi-ai/dist/providers/openai-responses-shared.d.ts.map +1 -1
  178. package/packages/pi-ai/dist/providers/openai-responses-shared.js +28 -4
  179. package/packages/pi-ai/dist/providers/openai-responses-shared.js.map +1 -1
  180. package/packages/pi-ai/dist/types.d.ts +2 -0
  181. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  182. package/packages/pi-ai/dist/types.js.map +1 -1
  183. package/packages/pi-ai/package.json +1 -1
  184. package/packages/pi-coding-agent/README.md +1 -1
  185. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts +2 -2
  186. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.d.ts.map +1 -1
  187. package/packages/pi-coding-agent/dist/core/extensions/extension-upstream-types.js.map +1 -1
  188. package/packages/pi-coding-agent/dist/core/extensions/loader.js +1 -1
  189. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  190. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  191. package/packages/pi-coding-agent/dist/core/extensions/runner.js +8 -2
  192. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  193. package/packages/pi-coding-agent/dist/core/skills.d.ts +3 -0
  194. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  195. package/packages/pi-coding-agent/dist/core/skills.js +3 -0
  196. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  197. package/packages/pi-coding-agent/package.json +7 -7
  198. package/packages/pi-tui/package.json +1 -1
  199. package/packages/rpc-client/package.json +2 -2
  200. package/pkg/package.json +1 -1
  201. package/src/resources/extensions/context7/index.ts +15 -2
  202. package/src/resources/extensions/get-secrets-from-user.ts +17 -16
  203. package/src/resources/extensions/google-cli/index.ts +34 -0
  204. package/src/resources/extensions/google-cli/models.ts +57 -0
  205. package/src/resources/extensions/google-cli/package.json +11 -0
  206. package/src/resources/extensions/google-cli/readiness.ts +15 -0
  207. package/src/resources/extensions/google-cli/stream-adapter.ts +245 -0
  208. package/src/resources/extensions/gsd/auto/loop.ts +74 -1
  209. package/src/resources/extensions/gsd/auto/orchestrator.ts +4 -2
  210. package/src/resources/extensions/gsd/auto/phases.ts +46 -0
  211. package/src/resources/extensions/gsd/auto/run-unit.ts +10 -0
  212. package/src/resources/extensions/gsd/auto/session.ts +3 -0
  213. package/src/resources/extensions/gsd/auto-dispatch.ts +31 -11
  214. package/src/resources/extensions/gsd/auto-post-unit.ts +43 -14
  215. package/src/resources/extensions/gsd/auto-prompts.ts +4 -284
  216. package/src/resources/extensions/gsd/auto-recovery.ts +10 -7
  217. package/src/resources/extensions/gsd/auto-start.ts +307 -56
  218. package/src/resources/extensions/gsd/auto.ts +6 -1
  219. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +4 -3
  220. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +9 -4
  221. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +42 -5
  222. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +18 -6
  223. package/src/resources/extensions/gsd/bootstrap/system-context.ts +3 -28
  224. package/src/resources/extensions/gsd/closeout-recovery.ts +6 -1
  225. package/src/resources/extensions/gsd/commands/handlers/auto.ts +9 -1
  226. package/src/resources/extensions/gsd/commands-usage.ts +110 -5
  227. package/src/resources/extensions/gsd/config-overlay.ts +19 -16
  228. package/src/resources/extensions/gsd/context-overlay.ts +24 -19
  229. package/src/resources/extensions/gsd/dashboard-overlay.ts +14 -27
  230. package/src/resources/extensions/gsd/doctor-engine-checks.ts +99 -0
  231. package/src/resources/extensions/gsd/doctor-providers.ts +55 -27
  232. package/src/resources/extensions/gsd/doctor-types.ts +2 -0
  233. package/src/resources/extensions/gsd/doctor.ts +6 -1
  234. package/src/resources/extensions/gsd/git-conflict-state.ts +25 -1
  235. package/src/resources/extensions/gsd/guided-flow.ts +5 -6
  236. package/src/resources/extensions/gsd/key-manager.ts +57 -14
  237. package/src/resources/extensions/gsd/milestone-reopen-events.ts +28 -0
  238. package/src/resources/extensions/gsd/notification-overlay.ts +12 -11
  239. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +16 -12
  240. package/src/resources/extensions/gsd/preferences-skills.ts +11 -4
  241. package/src/resources/extensions/gsd/preferences.ts +17 -2
  242. package/src/resources/extensions/gsd/prompt-loader.ts +2 -0
  243. package/src/resources/extensions/gsd/prompts/discuss.md +4 -2
  244. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  245. package/src/resources/extensions/gsd/prompts/system.md +1 -3
  246. package/src/resources/extensions/gsd/queue-reorder-ui.ts +29 -20
  247. package/src/resources/extensions/gsd/repository-registry.ts +3 -1
  248. package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -4
  249. package/src/resources/extensions/gsd/skill-activation.ts +292 -0
  250. package/src/resources/extensions/gsd/skill-catalog.data.ts +858 -0
  251. package/src/resources/extensions/gsd/skill-catalog.install.ts +205 -0
  252. package/src/resources/extensions/gsd/skill-catalog.ts +16 -1087
  253. package/src/resources/extensions/gsd/skill-discovery.ts +134 -78
  254. package/src/resources/extensions/gsd/skill-scope.ts +63 -0
  255. package/src/resources/extensions/gsd/skill-telemetry.ts +6 -40
  256. package/src/resources/extensions/gsd/skills.ts +75 -0
  257. package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +499 -0
  258. package/src/resources/extensions/gsd/state-reconciliation/index.ts +40 -0
  259. package/src/resources/extensions/gsd/state-reconciliation/registry.ts +8 -0
  260. package/src/resources/extensions/gsd/state-reconciliation/types.ts +30 -0
  261. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +328 -2
  262. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +21 -0
  263. package/src/resources/extensions/gsd/tests/auto-post-unit-artifact-diagnostic.test.ts +28 -2
  264. package/src/resources/extensions/gsd/tests/auto-post-unit-evidence-crossref-4909.test.ts +30 -0
  265. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +41 -0
  266. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +12 -0
  267. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +436 -0
  268. package/src/resources/extensions/gsd/tests/closeout-recovery.test.ts +15 -0
  269. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +31 -0
  270. package/src/resources/extensions/gsd/tests/commands-context.test.ts +5 -3
  271. package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +15 -2
  272. package/src/resources/extensions/gsd/tests/commands-usage.test.ts +97 -0
  273. package/src/resources/extensions/gsd/tests/context-chart.test.ts +9 -0
  274. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +25 -0
  275. package/src/resources/extensions/gsd/tests/discord-invite-links.test.ts +1 -0
  276. package/src/resources/extensions/gsd/tests/discuss-prompt.test.ts +4 -2
  277. package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +1 -1
  278. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +105 -0
  279. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +101 -1
  280. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +30 -0
  281. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +6 -0
  282. package/src/resources/extensions/gsd/tests/key-manager.test.ts +23 -4
  283. package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +6 -1
  284. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +70 -10
  285. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +7 -1
  286. package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +93 -0
  287. package/src/resources/extensions/gsd/tests/queue-reorder-ui.test.ts +46 -0
  288. package/src/resources/extensions/gsd/tests/register-extension-guard.test.ts +116 -11
  289. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +30 -1
  290. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +4 -0
  291. package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +111 -0
  292. package/src/resources/extensions/gsd/tests/skill-scope-auto.test.ts +67 -0
  293. package/src/resources/extensions/gsd/tests/skills.test.ts +55 -0
  294. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +13 -2
  295. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +303 -0
  296. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +19 -0
  297. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +24 -1
  298. package/src/resources/extensions/gsd/tests/tui-border-assertions.ts +28 -0
  299. package/src/resources/extensions/gsd/tests/tui-render-kit.test.ts +14 -0
  300. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +18 -0
  301. package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +26 -0
  302. package/src/resources/extensions/gsd/tests/vision-ask.test.ts +23 -0
  303. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -1
  304. package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +74 -1
  305. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +82 -0
  306. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +16 -1
  307. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +28 -0
  308. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +45 -1
  309. package/src/resources/extensions/gsd/tools/complete-task.ts +9 -0
  310. package/src/resources/extensions/gsd/tools/exec-tool.ts +42 -10
  311. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +82 -5
  312. package/src/resources/extensions/gsd/tui/render-kit.ts +82 -0
  313. package/src/resources/extensions/gsd/unit-context-manifest.ts +37 -26
  314. package/src/resources/extensions/gsd/user-input-boundary.ts +1 -1
  315. package/src/resources/extensions/gsd/vision-ask.ts +28 -0
  316. package/src/resources/extensions/gsd/visualizer-overlay.ts +12 -40
  317. package/src/resources/extensions/gsd/worktree-lifecycle.ts +37 -2
  318. package/src/resources/extensions/search-the-web/native-search.ts +60 -8
  319. package/src/resources/extensions/shared/confirm-ui.ts +8 -12
  320. package/src/resources/extensions/shared/dialog-frame.ts +71 -0
  321. package/src/resources/extensions/shared/interview-ui.ts +43 -42
  322. package/src/resources/extensions/shared/next-action-ui.ts +6 -6
  323. package/src/resources/extensions/shared/tests/confirm-ui.test.ts +57 -0
  324. package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +163 -0
  325. package/src/resources/extensions/shared/tests/next-action-ui-hasui.test.ts +55 -0
  326. package/src/resources/skills/create-skill/references/gsd-skill-ecosystem.md +1 -1
  327. /package/dist/web/standalone/.next/static/{praHP_OATcjBkvAVejjGK → _LIEWYP8ISVAPKAEEXxFr}/_buildManifest.js +0 -0
  328. /package/dist/web/standalone/.next/static/{praHP_OATcjBkvAVejjGK → _LIEWYP8ISVAPKAEEXxFr}/_ssgManifest.js +0 -0
@@ -21,7 +21,7 @@ import { invalidateAllCaches } from "./cache.js";
21
21
  import { writeLock, clearLock, readCrashLock, isLockProcessAlive } from "./crash-recovery.js";
22
22
  import { acquireSessionLock, releaseSessionLock, updateSessionLock, } from "./session-lock.js";
23
23
  import { ensureGitignore, untrackRuntimeFiles } from "./gitignore.js";
24
- import { nativeIsRepo, nativeInit, nativeAddAll, nativeCommit, nativeGetCurrentBranch, nativeDetectMainBranch, nativeBranchList, nativeBranchExists, nativeBranchListMerged, nativeBranchDelete, nativeWorktreeRemove, nativeCommitCountBetween, } from "./native-git-bridge.js";
24
+ import { nativeIsRepo, nativeInit, nativeAddAll, nativeCommit, nativeGetCurrentBranch, nativeDetectMainBranch, nativeBranchList, nativeBranchExists, nativeBranchListMerged, nativeBranchDelete, nativeWorktreeRemove, nativeCommitCountBetween, nativeHasChanges, } from "./native-git-bridge.js";
25
25
  import { GitServiceImpl } from "./git-service.js";
26
26
  import { captureIntegrationBranch, detectWorktreeName, setActiveMilestoneId, } from "./worktree.js";
27
27
  import { getAutoWorktreePath, checkoutBranchWithStashGuard } from "./auto-worktree.js";
@@ -126,17 +126,68 @@ export function resolveSurvivorRecoveryIsolationMode(isolationMode, phase) {
126
126
  return "branch";
127
127
  return isolationMode;
128
128
  }
129
- export function auditOrphanedMilestoneBranches(basePath, isolationMode, gitDeps = {}) {
129
+ function isBlockingStrandedWorkAction(action) {
130
+ return action.kind === "in-progress-stranded-work" && action.blocksAuto;
131
+ }
132
+ function detectWorktreeEvidence(basePath, milestoneId, hasChanges) {
133
+ const wtDir = getWorktreeDir(basePath, milestoneId);
134
+ const wtPath = getAutoWorktreePath(basePath, milestoneId);
135
+ let dirty = false;
136
+ if (wtPath) {
137
+ try {
138
+ dirty = hasChanges(wtPath);
139
+ }
140
+ catch {
141
+ dirty = false;
142
+ }
143
+ }
144
+ return {
145
+ path: wtPath,
146
+ dirExists: existsSync(wtDir),
147
+ dirty,
148
+ };
149
+ }
150
+ function strandedWorkMessage(args) {
151
+ const evidence = [];
152
+ if (args.branch && args.commitsAhead > 0) {
153
+ evidence.push(`branch ${args.branch} has ${args.commitsAhead} commit(s) ahead of ${args.mainBranch}`);
154
+ }
155
+ if (args.dirtyWorktree) {
156
+ evidence.push("the worktree has uncommitted changes");
157
+ }
158
+ if (evidence.length === 0) {
159
+ evidence.push("physical git evidence exists");
160
+ }
161
+ const wtSuffix = args.worktreeDirExists
162
+ ? ` Worktree directory at .gsd/worktrees/${args.milestoneId}/ holds live work.`
163
+ : "";
164
+ const recovery = args.recoveryMode === "worktree"
165
+ ? "Recovering will adopt the existing worktree."
166
+ : "Recovering will adopt the milestone branch.";
167
+ return (`Stranded work for in-progress milestone ${args.milestoneId}: ${evidence.join("; ")}.` +
168
+ wtSuffix +
169
+ ` ${recovery} Park or discard explicitly if abandoning.`);
170
+ }
171
+ export function auditOrphanedMilestoneBranches(basePath, _isolationMode, gitDeps = {}) {
130
172
  const recovered = [];
131
173
  const warnings = [];
174
+ const actions = [];
132
175
  const branchList = gitDeps.branchList ?? nativeBranchList;
133
176
  const branchExists = gitDeps.branchExists ?? nativeBranchExists;
134
- // Skip in none mode — no milestone branches are created
135
- if (isolationMode === "none")
136
- return { recovered, warnings };
177
+ const hasChanges = gitDeps.hasChanges ?? nativeHasChanges;
178
+ const pushAction = (action) => {
179
+ actions.push(action);
180
+ if (action.severity === "info") {
181
+ recovered.push(action.message);
182
+ }
183
+ else {
184
+ warnings.push(action.message);
185
+ }
186
+ };
137
187
  // Skip if DB not available — can't determine completion status
138
- if (!isDbAvailable())
139
- return { recovered, warnings };
188
+ if (!isDbAvailable()) {
189
+ return { recovered, warnings, actions, blockingStrandedWork: null };
190
+ }
140
191
  let milestoneBranches;
141
192
  let milestoneBranchListAvailable = true;
142
193
  try {
@@ -170,6 +221,7 @@ export function auditOrphanedMilestoneBranches(basePath, isolationMode, gitDeps
170
221
  if (!milestone)
171
222
  continue;
172
223
  const isMerged = mergedBranches.has(branch);
224
+ const worktreeEvidence = detectWorktreeEvidence(basePath, milestoneId, hasChanges);
173
225
  // #4762 — in-progress milestone branch with unmerged commits ahead of
174
226
  // main. This is the pre-completion orphan case: auto-mode exited without
175
227
  // completing the milestone (pause, stop, crash, merge error, blocker) and
@@ -181,32 +233,45 @@ export function auditOrphanedMilestoneBranches(basePath, isolationMode, gitDeps
181
233
  // Parked/other closed statuses go through the legacy complete/unmerged
182
234
  // path below where appropriate.
183
235
  if (!isClosedStatus(milestone.status)) {
184
- if (isMerged)
185
- continue; // nothing to recover
186
236
  let commitsAhead = 0;
187
237
  try {
188
238
  commitsAhead = nativeCommitCountBetween(basePath, mainBranch, branch);
189
239
  }
190
240
  catch {
191
- // Rev-walk failure — skip rather than noise
192
- continue;
241
+ commitsAhead = 0;
193
242
  }
194
- if (commitsAhead === 0)
243
+ if ((isMerged || commitsAhead === 0) && !worktreeEvidence.dirty)
195
244
  continue;
196
- const wtDir = getWorktreeDir(basePath, milestoneId);
197
- const wtDirExists = existsSync(wtDir);
198
- const wtSuffix = wtDirExists
199
- ? ` Worktree directory at .gsd/worktrees/${milestoneId}/ holds the live work.`
200
- : "";
201
- warnings.push(`Branch ${branch} has ${commitsAhead} commit(s) ahead of ${mainBranch} for in-progress milestone ${milestoneId}.` +
202
- wtSuffix +
203
- ` Run \`/gsd auto\` to resume, or merge manually if abandoning.`);
245
+ const recoveryMode = worktreeEvidence.path
246
+ ? "worktree"
247
+ : "branch";
248
+ const message = strandedWorkMessage({
249
+ milestoneId,
250
+ branch,
251
+ commitsAhead,
252
+ mainBranch,
253
+ dirtyWorktree: worktreeEvidence.dirty,
254
+ worktreeDirExists: worktreeEvidence.dirExists,
255
+ recoveryMode,
256
+ });
257
+ pushAction({
258
+ kind: "in-progress-stranded-work",
259
+ milestoneId,
260
+ branch,
261
+ commitsAhead,
262
+ dirtyWorktree: worktreeEvidence.dirty,
263
+ worktreeDirExists: worktreeEvidence.dirExists,
264
+ recoveryMode,
265
+ message,
266
+ severity: "warning",
267
+ blocksAuto: true,
268
+ });
204
269
  // #4764 telemetry
205
270
  try {
206
271
  emitWorktreeOrphaned(basePath, milestoneId, {
207
272
  reason: "in-progress-unmerged",
208
273
  commitsAhead,
209
- worktreeDirExists: wtDirExists,
274
+ worktreeDirExists: worktreeEvidence.dirExists,
210
275
  });
211
276
  }
212
277
  catch (err) {
@@ -223,7 +288,14 @@ export function auditOrphanedMilestoneBranches(basePath, isolationMode, gitDeps
223
288
  // Branch is merged — safe to delete branch and clean up worktree dir
224
289
  try {
225
290
  nativeBranchDelete(basePath, branch, true);
226
- recovered.push(`Deleted merged branch ${branch} for completed milestone ${milestoneId}.`);
291
+ pushAction({
292
+ kind: "complete-merged-branch",
293
+ milestoneId,
294
+ branch,
295
+ message: `Deleted merged branch ${branch} for completed milestone ${milestoneId}.`,
296
+ severity: "info",
297
+ blocksAuto: false,
298
+ });
227
299
  }
228
300
  catch (err) {
229
301
  warnings.push(`Failed to delete merged branch ${branch}: ${err instanceof Error ? err.message : String(err)}`);
@@ -246,7 +318,15 @@ export function auditOrphanedMilestoneBranches(basePath, isolationMode, gitDeps
246
318
  if (isInsideWorktreesDir(basePath, wtDir)) {
247
319
  try {
248
320
  rmSync(wtDir, { recursive: true, force: true });
249
- recovered.push(`Removed orphaned worktree directory for ${milestoneId}.`);
321
+ pushAction({
322
+ kind: "complete-merged-worktree",
323
+ milestoneId,
324
+ branch,
325
+ worktreeDirExists: true,
326
+ message: `Removed orphaned worktree directory for ${milestoneId}.`,
327
+ severity: "info",
328
+ blocksAuto: false,
329
+ });
250
330
  }
251
331
  catch (err2) {
252
332
  warnings.push(`Failed to remove worktree directory for ${milestoneId}: ${err2 instanceof Error ? err2.message : String(err2)}`);
@@ -257,14 +337,30 @@ export function auditOrphanedMilestoneBranches(basePath, isolationMode, gitDeps
257
337
  }
258
338
  }
259
339
  else {
260
- recovered.push(`Removed orphaned worktree directory for ${milestoneId}.`);
340
+ pushAction({
341
+ kind: "complete-merged-worktree",
342
+ milestoneId,
343
+ branch,
344
+ worktreeDirExists: true,
345
+ message: `Removed orphaned worktree directory for ${milestoneId}.`,
346
+ severity: "info",
347
+ blocksAuto: false,
348
+ });
261
349
  }
262
350
  }
263
351
  }
264
352
  else {
265
353
  // Branch is NOT merged — preserve for safety, warn the user
266
- warnings.push(`Branch ${branch} exists for completed milestone ${milestoneId} but is NOT merged into ${mainBranch}. ` +
267
- `This may contain unmerged work. Merge manually or run \`/gsd doctor fix\` to resolve.`);
354
+ pushAction({
355
+ kind: "complete-unmerged-branch",
356
+ milestoneId,
357
+ branch,
358
+ worktreeDirExists: worktreeEvidence.dirExists,
359
+ message: `Branch ${branch} exists for completed milestone ${milestoneId} but is NOT merged into ${mainBranch}. ` +
360
+ `This may contain unmerged work. Merge manually or run \`/gsd doctor fix\` to resolve.`,
361
+ severity: "warning",
362
+ blocksAuto: false,
363
+ });
268
364
  // #4764 telemetry
269
365
  try {
270
366
  emitWorktreeOrphaned(basePath, milestoneId, {
@@ -300,6 +396,43 @@ export function auditOrphanedMilestoneBranches(basePath, isolationMode, gitDeps
300
396
  completedMilestones = [];
301
397
  }
302
398
  for (const m of completedMilestones) {
399
+ if (!isClosedStatus(m.status)) {
400
+ if (seenMilestoneIds.has(m.id))
401
+ continue;
402
+ const worktreeEvidence = detectWorktreeEvidence(basePath, m.id, hasChanges);
403
+ if (!worktreeEvidence.dirty)
404
+ continue;
405
+ const message = strandedWorkMessage({
406
+ milestoneId: m.id,
407
+ commitsAhead: 0,
408
+ mainBranch,
409
+ dirtyWorktree: true,
410
+ worktreeDirExists: worktreeEvidence.dirExists,
411
+ recoveryMode: "worktree",
412
+ });
413
+ pushAction({
414
+ kind: "in-progress-stranded-work",
415
+ milestoneId: m.id,
416
+ commitsAhead: 0,
417
+ dirtyWorktree: true,
418
+ worktreeDirExists: worktreeEvidence.dirExists,
419
+ recoveryMode: "worktree",
420
+ message,
421
+ severity: "warning",
422
+ blocksAuto: true,
423
+ });
424
+ try {
425
+ emitWorktreeOrphaned(basePath, m.id, {
426
+ reason: "in-progress-unmerged",
427
+ commitsAhead: 0,
428
+ worktreeDirExists: worktreeEvidence.dirExists,
429
+ });
430
+ }
431
+ catch (err) {
432
+ logWarning("engine", `worktree-orphaned telemetry failed for ${m.id}: ${err instanceof Error ? err.message : String(err)}`);
433
+ }
434
+ continue;
435
+ }
303
436
  if (m.status !== "complete")
304
437
  continue;
305
438
  if (seenMilestoneIds.has(m.id))
@@ -332,17 +465,36 @@ export function auditOrphanedMilestoneBranches(basePath, isolationMode, gitDeps
332
465
  if (existsSync(wtDir)) {
333
466
  try {
334
467
  rmSync(wtDir, { recursive: true, force: true });
335
- recovered.push(`Removed orphaned worktree directory for ${m.id} (branch already deleted).`);
468
+ pushAction({
469
+ kind: "complete-branchless-worktree",
470
+ milestoneId: m.id,
471
+ worktreeDirExists: true,
472
+ message: `Removed orphaned worktree directory for ${m.id} (branch already deleted).`,
473
+ severity: "info",
474
+ blocksAuto: false,
475
+ });
336
476
  }
337
477
  catch (err) {
338
478
  warnings.push(`Failed to remove orphaned worktree directory for ${m.id}: ${err instanceof Error ? err.message : String(err)}`);
339
479
  }
340
480
  }
341
481
  else {
342
- recovered.push(`Removed orphaned worktree directory for ${m.id} (branch already deleted).`);
482
+ pushAction({
483
+ kind: "complete-branchless-worktree",
484
+ milestoneId: m.id,
485
+ worktreeDirExists: true,
486
+ message: `Removed orphaned worktree directory for ${m.id} (branch already deleted).`,
487
+ severity: "info",
488
+ blocksAuto: false,
489
+ });
343
490
  }
344
491
  }
345
- return { recovered, warnings };
492
+ return {
493
+ recovered,
494
+ warnings,
495
+ actions,
496
+ blockingStrandedWork: actions.find(isBlockingStrandedWorkAction) ?? null,
497
+ };
346
498
  }
347
499
  /**
348
500
  * Pure decision function for picking which orphan milestone the auto-loop
@@ -679,17 +831,27 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
679
831
  // was lost due to session ending between completion and teardown.
680
832
  // Must run after DB open and before worktree entry.
681
833
  let orphanAuditRecovered = false;
834
+ let strandedRecoveryActions = [];
835
+ let strandedRecoveryAction = null;
682
836
  try {
683
837
  const auditResult = auditOrphanedMilestoneBranches(base, getIsolationMode(base));
838
+ strandedRecoveryActions = auditResult.actions.filter(isBlockingStrandedWorkAction);
839
+ strandedRecoveryAction = strandedRecoveryActions[0] ?? null;
684
840
  for (const msg of auditResult.recovered) {
685
841
  ctx.ui.notify(`Orphan audit: ${msg}`, "info");
686
842
  }
687
843
  for (const msg of auditResult.warnings) {
688
- ctx.ui.notify(`Orphan audit: ${msg}`, "warning");
844
+ const prefix = msg.startsWith("Stranded work") ? "" : "Orphan audit: ";
845
+ ctx.ui.notify(`${prefix}${msg}`, "warning");
689
846
  }
690
847
  if (auditResult.recovered.length > 0) {
691
848
  orphanAuditRecovered = true;
692
- debugLog("orphan-audit", { recovered: auditResult.recovered, warnings: auditResult.warnings });
849
+ debugLog("orphan-audit", {
850
+ recovered: auditResult.recovered,
851
+ warnings: auditResult.warnings,
852
+ strandedRecoveryAction,
853
+ strandedRecoveryActions,
854
+ });
693
855
  }
694
856
  }
695
857
  catch (err) {
@@ -724,13 +886,6 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
724
886
  logWarning("bootstrap", `orphaned preflight-stash audit failed: ${err instanceof Error ? err.message : String(err)}`);
725
887
  }
726
888
  let state = await deriveState(base);
727
- if (process.env.GSD_HEADLESS === "1" &&
728
- orphanAuditRecovered &&
729
- !state.activeMilestone &&
730
- state.phase === "complete") {
731
- ctx.ui.notify("Auto-mode stopped (Recovered completed milestone cleanup; all milestones complete).", "info");
732
- return releaseLockAndReturn();
733
- }
734
889
  // Stale worktree state recovery (#654)
735
890
  if (state.activeMilestone &&
736
891
  shouldUseWorktreeIsolation(base) &&
@@ -740,6 +895,28 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
740
895
  state = await deriveState(wtPath);
741
896
  }
742
897
  }
898
+ const blockingStrandedRecoveryAction = state.activeMilestone
899
+ ? strandedRecoveryActions.find((action) => action.milestoneId !== state.activeMilestone?.id) ?? strandedRecoveryAction
900
+ : strandedRecoveryAction;
901
+ if (blockingStrandedRecoveryAction) {
902
+ if (!state.activeMilestone) {
903
+ ctx.ui.notify(`Stranded work for ${blockingStrandedRecoveryAction.milestoneId} blocks auto-mode, but that milestone is not active in project state. Park or discard it explicitly before continuing.`, "error");
904
+ return releaseLockAndReturn();
905
+ }
906
+ if (state.activeMilestone.id !== blockingStrandedRecoveryAction.milestoneId) {
907
+ ctx.ui.notify(`Stranded work for ${blockingStrandedRecoveryAction.milestoneId} blocks auto-mode before ${state.activeMilestone.id}. Recover, park, or discard ${blockingStrandedRecoveryAction.milestoneId} explicitly before continuing.`, "error");
908
+ return releaseLockAndReturn();
909
+ }
910
+ strandedRecoveryAction = blockingStrandedRecoveryAction;
911
+ ctx.ui.notify(`Recovering stranded work for ${strandedRecoveryAction.milestoneId} before dispatching new units.`, "info");
912
+ }
913
+ if (process.env.GSD_HEADLESS === "1" &&
914
+ orphanAuditRecovered &&
915
+ !state.activeMilestone &&
916
+ state.phase === "complete") {
917
+ ctx.ui.notify("Auto-mode stopped (Recovered completed milestone cleanup; all milestones complete).", "info");
918
+ return releaseLockAndReturn();
919
+ }
743
920
  // Milestone branch recovery (#601, #2358)
744
921
  // Detect survivor milestone branches in both pre-planning and complete phases.
745
922
  // In phase=complete, the milestone artifacts exist but finalization (merge,
@@ -768,7 +945,8 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
768
945
  // The worktree/branch was created but the milestone only has CONTEXT-DRAFT.md.
769
946
  // Route to the interactive discussion handler instead of falling through to
770
947
  // auto-mode, which would immediately stop with "needs discussion".
771
- if (decideSurvivorAction(hasSurvivorBranch, state.phase) === "discuss") {
948
+ if (!strandedRecoveryAction &&
949
+ decideSurvivorAction(hasSurvivorBranch, state.phase) === "discuss") {
772
950
  const { showSmartEntry } = await import("./guided-flow.js");
773
951
  await showSmartEntry(ctx, pi, base, { step: requestedStepMode });
774
952
  invalidateAllCaches();
@@ -848,14 +1026,14 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
848
1026
  const effectivePrefs = loadEffectiveGSDPreferences(base)?.preferences;
849
1027
  const { shouldRunDeepProjectSetup } = await import("./auto-dispatch.js");
850
1028
  const deepProjectStagePending = shouldRunDeepProjectSetup(state, effectivePrefs, base, { hasSurvivorBranch });
851
- if (deepProjectStagePending) {
1029
+ if (deepProjectStagePending && !strandedRecoveryAction) {
852
1030
  // Deep project-level setup runs before the first milestone exists. Let
853
1031
  // the auto loop dispatch workflow-preferences / project / requirements
854
1032
  // units instead of recursing back through showSmartEntry while this
855
1033
  // bootstrap still holds the session lock.
856
1034
  s.currentMilestoneId = null;
857
1035
  }
858
- if (!hasSurvivorBranch && !deepProjectStagePending) {
1036
+ if (!hasSurvivorBranch && !deepProjectStagePending && !strandedRecoveryAction) {
859
1037
  // No active work — start a new milestone via discuss flow
860
1038
  if (!state.activeMilestone || state.phase === "complete") {
861
1039
  // Guard against recursive dialog loop (#1348):
@@ -917,7 +1095,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
917
1095
  }
918
1096
  }
919
1097
  // Unreachable safety check
920
- if (!state.activeMilestone && !deepProjectStagePending) {
1098
+ if (!state.activeMilestone && !deepProjectStagePending && !strandedRecoveryAction) {
921
1099
  const { showSmartEntry } = await import("./guided-flow.js");
922
1100
  await showSmartEntry(ctx, pi, base, { step: requestedStepMode });
923
1101
  return releaseLockAndReturn();
@@ -952,7 +1130,9 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
952
1130
  s.resourceVersionOnStart = readResourceVersion();
953
1131
  s.pendingQuickTasks = [];
954
1132
  s.currentUnit = null;
955
- s.currentMilestoneId ??= deepProjectStagePending ? null : state.activeMilestone?.id ?? null;
1133
+ s.currentMilestoneId ??=
1134
+ strandedRecoveryAction?.milestoneId ??
1135
+ (deepProjectStagePending ? null : state.activeMilestone?.id ?? null);
956
1136
  s.originalModelId = startModelSnapshot?.id ?? ctx.model?.id ?? null;
957
1137
  s.originalModelProvider = startModelSnapshot?.provider ?? ctx.model?.provider ?? null;
958
1138
  s.originalThinkingLevel = startThinkingSnapshot ?? null;
@@ -960,7 +1140,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
960
1140
  registerSigtermHandler(base);
961
1141
  // Capture integration branch
962
1142
  if (s.currentMilestoneId) {
963
- if (getIsolationMode(base) !== "none") {
1143
+ if (getIsolationMode(base) !== "none" || strandedRecoveryAction) {
964
1144
  captureIntegrationBranch(base, s.currentMilestoneId);
965
1145
  }
966
1146
  setActiveMilestoneId(base, s.currentMilestoneId);
@@ -970,7 +1150,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
970
1150
  // milestone/<MID>. Auto-checkout back to the integration branch.
971
1151
  const isolationMode = getIsolationMode(base);
972
1152
  const isRepo = nativeIsRepo(base);
973
- if (isolationMode === "none" && isRepo) {
1153
+ if (isolationMode === "none" && isRepo && !strandedRecoveryAction) {
974
1154
  try {
975
1155
  const currentBranch = nativeGetCurrentBranch(base);
976
1156
  const integrationBranch = nativeDetectMainBranch(base);
@@ -1001,12 +1181,15 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
1001
1181
  return symlinkRe.test(p);
1002
1182
  };
1003
1183
  if (s.currentMilestoneId &&
1004
- getIsolationMode(base) !== "none" &&
1184
+ (getIsolationMode(base) !== "none" || strandedRecoveryAction?.recoveryMode) &&
1005
1185
  !detectWorktreeName(base) &&
1006
1186
  !isUnderGsdWorktrees(base)) {
1007
- const enterResult = buildLifecycle().enterMilestone(s.currentMilestoneId, {
1008
- notify: ctx.ui.notify.bind(ctx.ui),
1009
- });
1187
+ const lifecycle = buildLifecycle();
1188
+ const enterResult = strandedRecoveryAction?.recoveryMode
1189
+ ? lifecycle.adoptStrandedMilestone(s.currentMilestoneId, base, { notify: ctx.ui.notify.bind(ctx.ui) }, { mode: strandedRecoveryAction.recoveryMode })
1190
+ : lifecycle.enterMilestone(s.currentMilestoneId, {
1191
+ notify: ctx.ui.notify.bind(ctx.ui),
1192
+ });
1010
1193
  if (!enterResult.ok) {
1011
1194
  s.active = false;
1012
1195
  if (enterResult.reason === "lease-conflict") {
@@ -37,6 +37,8 @@ import { runGSDDoctor, rebuildState } from "./doctor.js";
37
37
  import { preDispatchHealthGate, recordHealthSnapshot, resetProactiveHealing, setLevelChangeCallback, } from "./doctor-proactive.js";
38
38
  import { clearSkillSnapshot } from "./skill-discovery.js";
39
39
  import { captureAvailableSkills, resetSkillTelemetry, } from "./skill-telemetry.js";
40
+ import { getInstalledSkillNames } from "./skills.js";
41
+ import { effectiveSkillNamesForUnit } from "./skill-scope.js";
40
42
  import { getRtkSessionSavings } from "../shared/rtk-session-stats.js";
41
43
  import { deactivateGSD } from "../shared/gsd-phase-state.js";
42
44
  import { initMetrics, resetMetrics, getLedger, getProjectTotals, filterUnitsForMilestone, formatCost, formatTokenCount, } from "./metrics.js";
@@ -2040,7 +2042,10 @@ function buildLoopDeps(pi) {
2040
2042
  autoCommitUnit,
2041
2043
  recordOutcome,
2042
2044
  writeLock,
2043
- captureAvailableSkills,
2045
+ captureAvailableSkills: () => {
2046
+ const unitType = s.currentUnit?.type;
2047
+ captureAvailableSkills(effectiveSkillNamesForUnit(unitType, getInstalledSkillNames()));
2048
+ },
2044
2049
  ensurePreconditions,
2045
2050
  updateSliceProgressCache,
2046
2051
  // Model selection + supervision
@@ -678,8 +678,9 @@ export function registerDbTools(pi) {
678
678
  promptSnippet: "Complete a GSD task (DB write + summary render + checkbox toggle)",
679
679
  promptGuidelines: [
680
680
  "Use gsd_task_complete (or gsd_complete_task) when a task is finished and needs to be recorded.",
681
- "All string fields are required. verificationEvidence is an array of objects with command, exitCode, verdict, durationMs.",
682
- "The tool validates required fields and returns an error message if any are missing.",
681
+ "Include verification whenever possible. If verification is omitted, the executor derives it from verificationEvidence when possible.",
682
+ "verificationEvidence is an array of objects with command, exitCode, verdict, durationMs.",
683
+ "The tool validates required fields and returns an error message if verification cannot be derived.",
683
684
  "On success, returns the summaryPath where the SUMMARY.md was written.",
684
685
  "Idempotent — calling with the same params twice will upsert (INSERT OR REPLACE) without error.",
685
686
  ],
@@ -690,7 +691,7 @@ export function registerDbTools(pi) {
690
691
  milestoneId: Type.String({ description: "Milestone ID (e.g. M001)" }),
691
692
  oneLiner: Type.String({ description: "One-line summary of what was accomplished" }),
692
693
  narrative: Type.String({ description: "Detailed narrative of what happened during the task" }),
693
- verification: Type.String({ description: "What was verified and how — commands run, tests passed, behavior confirmed" }),
694
+ verification: Type.Optional(Type.String({ description: "What was verified and how — commands run, tests passed, behavior confirmed. If omitted, derived from verificationEvidence when possible." })),
694
695
  // ── Enrichment metadata (optional — defaults to empty) ────────────
695
696
  deviations: Type.Optional(Type.String({ description: "Deviations from the task plan, or 'None.'" })),
696
697
  knownIssues: Type.Optional(Type.String({ description: "Known issues discovered but not fixed, or 'None.'" })),
@@ -36,8 +36,13 @@ export function registerExecTools(pi) {
36
36
  "Need persisted output? Read the stdout_path returned in details (file on local disk).",
37
37
  ],
38
38
  parameters: Type.Object({
39
- runtime: Type.Union([Type.Literal("bash"), Type.Literal("node"), Type.Literal("python")], { description: "Interpreter: bash (-c), node (-e), or python3 (-c)." }),
40
- script: Type.String({ description: "Script body. Keep output small (log the finding, not the data)." }),
39
+ runtime: Type.Optional(Type.String({
40
+ description: "Optional interpreter. Defaults to bash. Supported: bash, node, python; sh/shell, js/nodejs, and py/python3 aliases are accepted.",
41
+ })),
42
+ script: Type.Optional(Type.String({ description: "Script body. Keep output small (log the finding, not the data)." })),
43
+ command: Type.Optional(Type.String({ description: "Alias for script; defaults to bash when runtime is omitted." })),
44
+ cmd: Type.Optional(Type.String({ description: "Short alias for script." })),
45
+ code: Type.Optional(Type.String({ description: "Alias for script, useful for node/python snippets." })),
41
46
  purpose: Type.Optional(Type.String({ description: "Short label recorded in meta.json for later review." })),
42
47
  timeout_ms: Type.Optional(Type.Number({
43
48
  description: "Per-invocation timeout (ms). Capped at 600000. Default from preferences.",
@@ -21,6 +21,31 @@ import { logWarning } from "../workflow-logger.js";
21
21
  // EventEmitter does not buffer events for late subscribers.
22
22
  import { initCmuxEventListeners } from "../../cmux/index.js";
23
23
  export { writeCrashLog } from "./crash-log.js";
24
+ // Pipe-closed storm guard. #99/#101 stopped EPIPE from flooding ~/.gsd/crash,
25
+ // but a persistently-broken output pipe whose `destroyed`/`writableEnded` flags
26
+ // never flip is still swallowed on every write — a tight, progress-free CPU
27
+ // spin. If the pipe-closed error fires in a tight loop the pipe is gone for
28
+ // good; exit cleanly instead.
29
+ const EPIPE_STORM_THRESHOLD = 100;
30
+ const EPIPE_STORM_WINDOW_MS = 10_000;
31
+ let epipeCount = 0;
32
+ let epipeWindowStart = 0;
33
+ /** Write to stderr without ever re-throwing — stderr can EPIPE too, which would
34
+ * re-enter this handler and re-loop. */
35
+ function safeStderr(msg) {
36
+ try {
37
+ process.stderr.write(msg);
38
+ }
39
+ catch { /* stderr is also broken; nothing we can do */ }
40
+ }
41
+ /** A peer closing the read end of a pipe mid-write surfaces differently per
42
+ * platform: POSIX throws `EPIPE`; Windows throws `Error: write EOF` (or
43
+ * `read EOF`) with no `code` set, from node:internal/stream_base_commons.
44
+ * Both are the same logical condition and must be treated as recoverable —
45
+ * otherwise the Windows EOF variant escapes to the uncaught-exception path
46
+ * and crashes auto-mode workers mid-iteration (#181). ECONNRESET is NOT
47
+ * included here: it commonly comes from network sockets (#182 follow-up) and
48
+ * is a real error that should surface rather than be silently swallowed. */
24
49
  function isPipeClosedError(err) {
25
50
  const errno = err.code;
26
51
  if (errno === "EPIPE")
@@ -30,7 +55,7 @@ function isPipeClosedError(err) {
30
55
  }
31
56
  export function handleRecoverableExtensionProcessError(err) {
32
57
  if (err.message.includes("ProcessTransport is not ready for writing")) {
33
- process.stderr.write(`[gsd] swallowed dead transport control write: ${err.message}\n`);
58
+ safeStderr(`[gsd] swallowed dead transport control write: ${err.message}\n`);
34
59
  return true;
35
60
  }
36
61
  if (isPipeClosedError(err)) {
@@ -40,24 +65,33 @@ export function handleRecoverableExtensionProcessError(err) {
40
65
  if (stdoutGone) {
41
66
  process.exit(0);
42
67
  }
43
- process.stderr.write(`[gsd] swallowed ${tag} (syscall=${err.syscall ?? "?"})\n`);
68
+ const now = Date.now();
69
+ if (now - epipeWindowStart > EPIPE_STORM_WINDOW_MS) {
70
+ epipeWindowStart = now;
71
+ epipeCount = 0;
72
+ }
73
+ if (++epipeCount > EPIPE_STORM_THRESHOLD) {
74
+ safeStderr(`[gsd] ${tag} storm (${epipeCount} within ${EPIPE_STORM_WINDOW_MS}ms) — output pipe is gone; exiting.\n`);
75
+ process.exit(0);
76
+ }
77
+ safeStderr(`[gsd] swallowed ${tag} (syscall=${err.syscall ?? "?"})\n`);
44
78
  return true;
45
79
  }
46
80
  if (err.code === "EIO") {
47
81
  const syscall = err.syscall;
48
82
  if (syscall === "read") {
49
- process.stderr.write(`[gsd] EIO: ${err.message}\n`);
83
+ safeStderr(`[gsd] EIO: ${err.message}\n`);
50
84
  return true;
51
85
  }
52
86
  }
53
87
  if (err.code === "ENOENT") {
54
88
  const syscall = err.syscall;
55
89
  if (syscall?.startsWith("spawn")) {
56
- process.stderr.write(`[gsd] spawn ENOENT: ${err.path ?? "unknown"} — command not found\n`);
90
+ safeStderr(`[gsd] spawn ENOENT: ${err.path ?? "unknown"} — command not found\n`);
57
91
  return true;
58
92
  }
59
93
  if (syscall === "uv_cwd") {
60
- process.stderr.write(`[gsd] ENOENT (${syscall}): ${err.message}\n`);
94
+ safeStderr(`[gsd] ENOENT (${syscall}): ${err.message}\n`);
61
95
  return true;
62
96
  }
63
97
  }