@opengsd/gsd-pi 1.1.1-dev.9f86580 → 1.1.1-dev.b2556262

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 (261) hide show
  1. package/dist/headless-recover.js +56 -1
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/browser-tools/index.js +39 -22
  4. package/dist/resources/extensions/browser-tools/state.js +12 -0
  5. package/dist/resources/extensions/browser-tools/tools/session.js +3 -2
  6. package/dist/resources/extensions/browser-tools/utils.js +3 -3
  7. package/dist/resources/extensions/gsd/auto/loop.js +4 -2
  8. package/dist/resources/extensions/gsd/auto/phases.js +43 -10
  9. package/dist/resources/extensions/gsd/auto/session.js +20 -1
  10. package/dist/resources/extensions/gsd/auto/workflow-kernel.js +1 -0
  11. package/dist/resources/extensions/gsd/auto-dispatch.js +72 -12
  12. package/dist/resources/extensions/gsd/auto-model-selection.js +128 -9
  13. package/dist/resources/extensions/gsd/auto-post-unit.js +19 -2
  14. package/dist/resources/extensions/gsd/auto-prompts.js +24 -19
  15. package/dist/resources/extensions/gsd/auto-recovery.js +4 -2
  16. package/dist/resources/extensions/gsd/auto-runtime-state.js +3 -0
  17. package/dist/resources/extensions/gsd/auto-start.js +1 -1
  18. package/dist/resources/extensions/gsd/auto.js +14 -11
  19. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
  20. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +172 -65
  21. package/dist/resources/extensions/gsd/closeout-wizard.js +32 -9
  22. package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -9
  23. package/dist/resources/extensions/gsd/commands-maintenance.js +93 -15
  24. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +2 -2
  25. package/dist/resources/extensions/gsd/db-writer.js +35 -0
  26. package/dist/resources/extensions/gsd/docs/preferences-reference.md +50 -1
  27. package/dist/resources/extensions/gsd/gsd-db.js +480 -172
  28. package/dist/resources/extensions/gsd/markdown-renderer.js +37 -53
  29. package/dist/resources/extensions/gsd/md-importer.js +38 -3
  30. package/dist/resources/extensions/gsd/migration-auto-check.js +126 -31
  31. package/dist/resources/extensions/gsd/parsers-legacy.js +23 -0
  32. package/dist/resources/extensions/gsd/planning-path-scope.js +22 -4
  33. package/dist/resources/extensions/gsd/pre-execution-checks.js +10 -2
  34. package/dist/resources/extensions/gsd/preferences-models.js +110 -43
  35. package/dist/resources/extensions/gsd/preferences-types.js +13 -0
  36. package/dist/resources/extensions/gsd/preferences-validation.js +68 -3
  37. package/dist/resources/extensions/gsd/preferences.js +4 -1
  38. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  39. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  40. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  41. package/dist/resources/extensions/gsd/roadmap-slices.js +5 -1
  42. package/dist/resources/extensions/gsd/safety/content-validator.js +6 -4
  43. package/dist/resources/extensions/gsd/source-observations.js +306 -0
  44. package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +15 -8
  45. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +33 -5
  46. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-worker.js +34 -13
  47. package/dist/resources/extensions/gsd/state-reconciliation/index.js +39 -14
  48. package/dist/resources/extensions/gsd/state-reconciliation/spawn-gate.js +4 -4
  49. package/dist/resources/extensions/gsd/state.js +7 -3
  50. package/dist/resources/extensions/gsd/tool-contract.js +14 -0
  51. package/dist/resources/extensions/gsd/tool-presentation-plan.js +1 -9
  52. package/dist/resources/extensions/gsd/tools/complete-slice.js +7 -6
  53. package/dist/resources/extensions/gsd/tools/plan-slice.js +42 -11
  54. package/dist/resources/extensions/gsd/tools/plan-task.js +7 -1
  55. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +57 -429
  56. package/dist/resources/extensions/gsd/uat-policy.js +130 -0
  57. package/dist/resources/extensions/gsd/uat-run.js +414 -0
  58. package/dist/resources/extensions/gsd/unit-context-manifest.js +3 -4
  59. package/dist/resources/extensions/gsd/verdict-parser.js +3 -8
  60. package/dist/resources/extensions/gsd/workflow-manifest.js +132 -5
  61. package/dist/resources/extensions/gsd/workflow-projections.js +8 -0
  62. package/dist/resources/extensions/gsd/worktree-state-projection.js +18 -17
  63. package/dist/resources/extensions/subagent/agents.js +1 -0
  64. package/dist/resources/extensions/subagent/index.js +27 -12
  65. package/dist/resources/extensions/subagent/launch.js +7 -2
  66. package/dist/web/standalone/.next/BUILD_ID +1 -1
  67. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  68. package/dist/web/standalone/.next/build-manifest.json +2 -2
  69. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  70. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/index.html +1 -1
  87. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  94. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  95. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  97. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  98. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  99. package/dist/web/standalone/node_modules/@gsd/native/dist/native.js +22 -0
  100. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  101. package/package.json +4 -4
  102. package/packages/cloud-mcp-gateway/package.json +2 -2
  103. package/packages/contracts/package.json +1 -1
  104. package/packages/daemon/package.json +4 -4
  105. package/packages/gsd-agent-core/package.json +5 -5
  106. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  107. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js +21 -23
  108. package/packages/gsd-agent-modes/dist/modes/interactive/components/assistant-message.js.map +1 -1
  109. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +3 -0
  110. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  111. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -0
  112. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  113. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts +1 -0
  114. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  115. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +66 -12
  116. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  117. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  118. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +18 -11
  119. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  120. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
  121. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +16 -0
  122. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
  123. package/packages/gsd-agent-modes/package.json +7 -7
  124. package/packages/mcp-server/dist/workflow-tools.js +1 -1
  125. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  126. package/packages/mcp-server/package.json +3 -3
  127. package/packages/native/dist/native.js +22 -0
  128. package/packages/native/package.json +1 -1
  129. package/packages/pi-agent-core/package.json +1 -1
  130. package/packages/pi-ai/dist/image-models.generated.d.ts +30 -0
  131. package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  132. package/packages/pi-ai/dist/image-models.generated.js +30 -0
  133. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  134. package/packages/pi-ai/dist/models.generated.d.ts +23 -17
  135. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  136. package/packages/pi-ai/dist/models.generated.js +25 -24
  137. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  138. package/packages/pi-ai/package.json +1 -1
  139. package/packages/pi-coding-agent/dist/core/settings-manager.js +1 -1
  140. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  141. package/packages/pi-coding-agent/dist/theme/themes.js +1 -1
  142. package/packages/pi-coding-agent/dist/theme/themes.js.map +1 -1
  143. package/packages/pi-coding-agent/package.json +7 -7
  144. package/packages/pi-tui/dist/utils.d.ts +11 -0
  145. package/packages/pi-tui/dist/utils.d.ts.map +1 -1
  146. package/packages/pi-tui/dist/utils.js +119 -6
  147. package/packages/pi-tui/dist/utils.js.map +1 -1
  148. package/packages/pi-tui/package.json +2 -1
  149. package/packages/rpc-client/package.json +2 -2
  150. package/pkg/dist/theme/themes.js +1 -1
  151. package/pkg/dist/theme/themes.js.map +1 -1
  152. package/pkg/package.json +1 -1
  153. package/src/resources/extensions/browser-tools/index.ts +39 -22
  154. package/src/resources/extensions/browser-tools/state.ts +13 -0
  155. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +57 -0
  156. package/src/resources/extensions/browser-tools/tools/session.ts +4 -2
  157. package/src/resources/extensions/browser-tools/utils.ts +3 -3
  158. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  159. package/src/resources/extensions/gsd/auto/loop.ts +4 -2
  160. package/src/resources/extensions/gsd/auto/phases.ts +42 -10
  161. package/src/resources/extensions/gsd/auto/session.ts +22 -1
  162. package/src/resources/extensions/gsd/auto/workflow-kernel.ts +1 -0
  163. package/src/resources/extensions/gsd/auto-dispatch.ts +85 -12
  164. package/src/resources/extensions/gsd/auto-model-selection.ts +164 -12
  165. package/src/resources/extensions/gsd/auto-post-unit.ts +20 -2
  166. package/src/resources/extensions/gsd/auto-prompts.ts +23 -20
  167. package/src/resources/extensions/gsd/auto-recovery.ts +22 -3
  168. package/src/resources/extensions/gsd/auto-runtime-state.ts +5 -0
  169. package/src/resources/extensions/gsd/auto-start.ts +1 -1
  170. package/src/resources/extensions/gsd/auto.ts +13 -10
  171. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
  172. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +225 -72
  173. package/src/resources/extensions/gsd/closeout-wizard.ts +47 -13
  174. package/src/resources/extensions/gsd/commands/handlers/ops.ts +2 -17
  175. package/src/resources/extensions/gsd/commands-maintenance.ts +124 -13
  176. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +2 -2
  177. package/src/resources/extensions/gsd/db-writer.ts +38 -0
  178. package/src/resources/extensions/gsd/docs/preferences-reference.md +50 -1
  179. package/src/resources/extensions/gsd/gsd-db.ts +564 -186
  180. package/src/resources/extensions/gsd/markdown-renderer.ts +44 -66
  181. package/src/resources/extensions/gsd/md-importer.ts +49 -2
  182. package/src/resources/extensions/gsd/migration-auto-check.ts +154 -34
  183. package/src/resources/extensions/gsd/parsers-legacy.ts +20 -0
  184. package/src/resources/extensions/gsd/planning-path-scope.ts +22 -4
  185. package/src/resources/extensions/gsd/pre-execution-checks.ts +9 -2
  186. package/src/resources/extensions/gsd/preferences-models.ts +112 -43
  187. package/src/resources/extensions/gsd/preferences-types.ts +39 -0
  188. package/src/resources/extensions/gsd/preferences-validation.ts +76 -2
  189. package/src/resources/extensions/gsd/preferences.ts +5 -0
  190. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +1 -1
  191. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  192. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  193. package/src/resources/extensions/gsd/roadmap-slices.ts +6 -1
  194. package/src/resources/extensions/gsd/safety/content-validator.ts +8 -5
  195. package/src/resources/extensions/gsd/source-observations.ts +402 -0
  196. package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +20 -8
  197. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +44 -5
  198. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-worker.ts +39 -11
  199. package/src/resources/extensions/gsd/state-reconciliation/index.ts +45 -15
  200. package/src/resources/extensions/gsd/state-reconciliation/spawn-gate.ts +4 -4
  201. package/src/resources/extensions/gsd/state.ts +7 -4
  202. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +15 -0
  203. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +299 -1
  204. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +32 -0
  205. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +75 -3
  206. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +22 -1
  207. package/src/resources/extensions/gsd/tests/before-provider-context-management.test.ts +145 -0
  208. package/src/resources/extensions/gsd/tests/closeout-wizard.test.ts +44 -0
  209. package/src/resources/extensions/gsd/tests/commands-dispatcher-unmerged-milestone.test.ts +26 -1
  210. package/src/resources/extensions/gsd/tests/content-validator.test.ts +74 -0
  211. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +16 -2
  212. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +1 -11
  213. package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +64 -0
  214. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +15 -0
  215. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +62 -1
  216. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +15 -0
  217. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +42 -0
  218. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +99 -0
  219. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +99 -2
  220. package/src/resources/extensions/gsd/tests/plan-task.test.ts +19 -0
  221. package/src/resources/extensions/gsd/tests/preferences.test.ts +14 -0
  222. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +1 -0
  223. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +9 -0
  224. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +101 -1
  225. package/src/resources/extensions/gsd/tests/repository-registry.test.ts +2 -2
  226. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +8 -0
  227. package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +5 -3
  228. package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +162 -18
  229. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +8 -0
  230. package/src/resources/extensions/gsd/tests/source-observations.test.ts +275 -0
  231. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +43 -0
  232. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +76 -21
  233. package/src/resources/extensions/gsd/tests/thinking-level-resolution.test.ts +203 -0
  234. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +170 -0
  235. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +7 -1
  236. package/src/resources/extensions/gsd/tests/workflow-kernel.test.ts +7 -0
  237. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +306 -1
  238. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +73 -6
  239. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +511 -1
  240. package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +44 -0
  241. package/src/resources/extensions/gsd/tool-contract.ts +28 -0
  242. package/src/resources/extensions/gsd/tool-presentation-plan.ts +1 -11
  243. package/src/resources/extensions/gsd/tools/complete-slice.ts +7 -6
  244. package/src/resources/extensions/gsd/tools/plan-slice.ts +54 -12
  245. package/src/resources/extensions/gsd/tools/plan-task.ts +8 -1
  246. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +66 -526
  247. package/src/resources/extensions/gsd/types.ts +1 -0
  248. package/src/resources/extensions/gsd/uat-policy.ts +191 -0
  249. package/src/resources/extensions/gsd/uat-run.ts +550 -0
  250. package/src/resources/extensions/gsd/unit-context-manifest.ts +3 -4
  251. package/src/resources/extensions/gsd/verdict-parser.ts +3 -10
  252. package/src/resources/extensions/gsd/workflow-manifest.ts +193 -7
  253. package/src/resources/extensions/gsd/workflow-projections.ts +9 -0
  254. package/src/resources/extensions/gsd/worktree-state-projection.ts +22 -22
  255. package/src/resources/extensions/shared/tests/format-utils.test.ts +8 -3
  256. package/src/resources/extensions/subagent/agents.ts +4 -0
  257. package/src/resources/extensions/subagent/index.ts +28 -3
  258. package/src/resources/extensions/subagent/launch.ts +8 -0
  259. package/src/resources/extensions/subagent/tests/model-override.test.ts +31 -0
  260. /package/dist/web/standalone/.next/static/{zzYMrKpPGfRQRxSFO32Jr → tJOKQbQRO-9MiFDO8DIDS}/_buildManifest.js +0 -0
  261. /package/dist/web/standalone/.next/static/{zzYMrKpPGfRQRxSFO32Jr → tJOKQbQRO-9MiFDO8DIDS}/_ssgManifest.js +0 -0
@@ -1246,7 +1246,7 @@ export async function cleanupAfterLoopExit(ctx: ExtensionContext): Promise<void>
1246
1246
  const preserveStepSurface = s.preserveStepSurfaceAfterLoopExit;
1247
1247
  const preserveCompletionSurface = s.completionStopInProgress;
1248
1248
  const preservePausedSurface = s.paused;
1249
- s.currentUnit = null;
1249
+ s.clearCurrentUnit();
1250
1250
  s.active = false;
1251
1251
  deactivateGSD();
1252
1252
  clearUnitTimeout();
@@ -1269,11 +1269,14 @@ export async function cleanupAfterLoopExit(ctx: ExtensionContext): Promise<void>
1269
1269
  // A transient provider-error pause intentionally leaves the paused badge
1270
1270
  // visible so the user still has a resumable auto-mode signal on screen.
1271
1271
  if (!s.paused) {
1272
- if (preserveStepSurface) {
1273
- s.preserveStepSurfaceAfterLoopExit = false;
1274
- } else if (preserveCompletionSurface) {
1272
+ if (preserveCompletionSurface) {
1275
1273
  ctx.ui.setStatus("gsd-auto", undefined);
1276
1274
  s.completionStopInProgress = false;
1275
+ if (preserveStepSurface) {
1276
+ s.preserveStepSurfaceAfterLoopExit = false;
1277
+ }
1278
+ } else if (preserveStepSurface) {
1279
+ s.preserveStepSurfaceAfterLoopExit = false;
1277
1280
  } else {
1278
1281
  ctx.ui.setStatus("gsd-auto", undefined);
1279
1282
  ctx.ui.setWidget("gsd-progress", undefined);
@@ -1987,7 +1990,7 @@ export async function pauseAuto(
1987
1990
  // Non-fatal — best-effort closeout on pause
1988
1991
  logWarning("engine", `unit closeout on pause failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1989
1992
  }
1990
- s.currentUnit = null;
1993
+ s.clearCurrentUnit();
1991
1994
  }
1992
1995
 
1993
1996
  // Keep STATE.md aligned with the DB-backed state before releasing pause state.
@@ -3308,7 +3311,7 @@ export async function dispatchHookUnit(
3308
3311
  s.stepMode = true;
3309
3312
  s.cmdCtx = ctx as ExtensionCommandContext;
3310
3313
  s.autoStartTime = Date.now();
3311
- s.currentUnit = null;
3314
+ s.clearCurrentUnit();
3312
3315
  s.pendingQuickTasks = [];
3313
3316
  }
3314
3317
 
@@ -3325,12 +3328,12 @@ export async function dispatchHookUnit(
3325
3328
  const hookUnitType = `hook/${hookName}`;
3326
3329
  const hookStartedAt = Date.now();
3327
3330
 
3328
- s.currentUnit = {
3331
+ s.setCurrentUnit({
3329
3332
  type: triggerUnitType,
3330
3333
  id: triggerUnitId,
3331
3334
  startedAt: hookStartedAt,
3332
3335
  workspaceRoot: s.basePath,
3333
- };
3336
+ });
3334
3337
 
3335
3338
  const result = await s.cmdCtx!.newSession({ workspaceRoot: s.basePath });
3336
3339
  if (result.cancelled) {
@@ -3338,12 +3341,12 @@ export async function dispatchHookUnit(
3338
3341
  return false;
3339
3342
  }
3340
3343
 
3341
- s.currentUnit = {
3344
+ s.setCurrentUnit({
3342
3345
  type: hookUnitType,
3343
3346
  id: triggerUnitId,
3344
3347
  startedAt: hookStartedAt,
3345
3348
  workspaceRoot: s.basePath,
3346
- };
3349
+ });
3347
3350
 
3348
3351
  if (hookModel) {
3349
3352
  const availableModels = ctx.modelRegistry.getAvailable();
@@ -699,7 +699,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
699
699
  files: Type.Array(Type.String(), { description: "Array<string> of files likely touched; pass [\"path\"] or [], never a single string" }),
700
700
  verify: Type.String({ description: "Verification command or block" }),
701
701
  inputs: Type.Array(Type.String(), { description: "Array<string> of input files or references; pass [\"path\"] or [], never a single string" }),
702
- expectedOutput: Type.Array(Type.String(), { description: "Array<string> of expected output files or artifacts; pass [\"path\"] or [], never a single string" }),
702
+ expectedOutput: Type.Array(Type.String(), { description: "Array<string> of files this task creates or overwrites; pass [\"path\"] or [], never prose or a single string" }),
703
703
  observabilityImpact: Type.Optional(Type.String({ description: "Task observability impact" })),
704
704
  }), { description: "Planned tasks for the slice" }),
705
705
  // ── Enrichment metadata (optional — defaults to empty) ────────────
@@ -779,7 +779,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
779
779
  files: Type.Array(Type.String(), { description: "Array<string> of files likely touched; pass [\"path\"] or [], never a single string" }),
780
780
  verify: Type.String({ description: "Verification command or block" }),
781
781
  inputs: Type.Array(Type.String(), { description: "Array<string> of input files or references; pass [\"path\"] or [], never a single string" }),
782
- expectedOutput: Type.Array(Type.String(), { description: "Array<string> of expected output files or artifacts; pass [\"path\"] or [], never a single string" }),
782
+ expectedOutput: Type.Array(Type.String(), { description: "Array<string> of files this task creates or overwrites; pass [\"path\"] or [], never prose or a single string" }),
783
783
  observabilityImpact: Type.Optional(Type.String({ description: "Task observability impact" })),
784
784
  // Single-writer v3 audit trail (Stream 2): caller-provided actor identity + causation.
785
785
  actorName: Type.Optional(Type.String({ description: "Caller-provided actor identity for the audit trail (e.g. 'executor-01', 'gsd-orchestrator')" })),
@@ -1160,7 +1160,7 @@ export function registerDbTools(pi: ExtensionAPI): void {
1160
1160
  files: Type.Array(Type.String(), { description: "Files likely touched" }),
1161
1161
  verify: Type.String({ description: "Verification command or block" }),
1162
1162
  inputs: Type.Array(Type.String(), { description: "Input files or references" }),
1163
- expectedOutput: Type.Array(Type.String(), { description: "Expected output files or artifacts" }),
1163
+ expectedOutput: Type.Array(Type.String(), { description: "Files this task creates or overwrites" }),
1164
1164
  }),
1165
1165
  { description: "Tasks to upsert (update existing or insert new)" },
1166
1166
  ),
@@ -1,7 +1,7 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Registers GSD extension runtime hooks and token-saving tool policies.
3
3
 
4
- import { existsSync } from "node:fs";
4
+ import { existsSync, mkdirSync } from "node:fs";
5
5
  import { dirname, join } from "node:path";
6
6
  import { pathToFileURL } from "node:url";
7
7
 
@@ -12,12 +12,22 @@ import { ALWAYS_PRESERVED_SHIM_TOOL_NAMES } from "@gsd/pi-ai";
12
12
  import type { GSDEcosystemBeforeAgentStartHandler } from "../ecosystem/gsd-extension-api.js";
13
13
  import { updateSnapshot } from "../ecosystem/gsd-extension-api.js";
14
14
 
15
- import { buildMilestoneFileName, resolveMilestonePath, resolveSliceFile, resolveSlicePath } from "../paths.js";
15
+ import { buildMilestoneFileName, clearPathCache, milestonesDir, resolveMilestonePath, resolveSliceFile, resolveSlicePath } from "../paths.js";
16
16
  import { canonicalToolName, clearDiscussionFlowState, isDepthConfirmationAnswer, isMilestoneDepthVerified, isQueuePhaseActive, markApprovalGateVerified, markDepthVerified, resetWriteGateState, shouldBlockContextWrite, shouldBlockPlanningUnit, shouldBlockQueueExecution, shouldBlockWorktreeWrite, isGateQuestionId, setPendingGate, clearPendingGate, getPendingGate, shouldBlockPendingGate, shouldBlockPendingGateBash, extractDepthVerificationMilestoneId } from "./write-gate.js";
17
17
  import { resolveManifest } from "../unit-context-manifest.js";
18
18
  import { isBlockedStateFile, isBashWriteToStateFile, BLOCKED_WRITE_ERROR } from "../write-intercept.js";
19
19
  import { loadFile, saveFile, formatContinue } from "../files.js";
20
- import { clearToolInvocationError, getAutoRuntimeSnapshot, isAutoActive, isAutoCompletionStopInProgress, isAutoPaused, markToolEnd, markToolStart, recordToolInvocationError } from "../auto-runtime-state.js";
20
+ import {
21
+ clearToolInvocationError,
22
+ getAutoRuntimeSnapshot,
23
+ getSourceObservationStore,
24
+ isAutoActive,
25
+ isAutoCompletionStopInProgress,
26
+ isAutoPaused,
27
+ markToolEnd,
28
+ markToolStart,
29
+ recordToolInvocationError,
30
+ } from "../auto-runtime-state.js";
21
31
 
22
32
  import { checkToolCallLoop, resetToolCallLoopGuard } from "./tool-call-loop-guard.js";
23
33
  import { maybePauseAutoForApprovalGate, resetPendingGatePauseGuard } from "./pending-gate-pause.js";
@@ -39,6 +49,7 @@ import { registerPlanMilestoneSchemaRecovery } from "./plan-milestone-schema-rec
39
49
  import { AUTO_UNIT_SCOPED_TOOLS, RUN_UAT_BROWSER_TOOL_NAMES, isWorkflowAliasTool } from "../auto-unit-tool-scope.js";
40
50
  import { filterToolsForProvider } from "../model-router.js";
41
51
  import { RUN_UAT_READ_ONLY_TOOL_NAMES, RUN_UAT_WORKFLOW_TOOL_NAMES } from "../tool-presentation-plan.js";
52
+ import { injectSourceContextBlockIntoPayload, supportsSourceObservationsForUnit } from "../source-observations.js";
42
53
 
43
54
  let approvalQuestionAbortInFlight = false;
44
55
 
@@ -474,6 +485,55 @@ function contextBasePath(ctx?: { cwd?: string }): string {
474
485
  return typeof ctx?.cwd === "string" ? ctx.cwd : process.cwd();
475
486
  }
476
487
 
488
+ function beginSourceObservationStoreForCurrentUnit(
489
+ ctx?: { cwd?: string },
490
+ ): ReturnType<typeof getSourceObservationStore> | null {
491
+ if (!isAutoActive()) return null;
492
+ const dash = getAutoRuntimeSnapshot();
493
+ if (!dash.currentUnit) return null;
494
+ if (!supportsSourceObservationsForUnit(dash.currentUnit.type)) return null;
495
+
496
+ const store = getSourceObservationStore();
497
+ store.beginUnit({
498
+ unitType: dash.currentUnit.type,
499
+ unitId: dash.currentUnit.id,
500
+ startedAt: dash.currentUnit.startedAt,
501
+ basePath: dash.currentUnit.workspaceRoot ?? (dash.basePath || contextBasePath(ctx)),
502
+ });
503
+ return store;
504
+ }
505
+
506
+ function refreshSourceObservationAfterMutation(
507
+ canonicalName: string,
508
+ input: unknown,
509
+ ctx?: { cwd?: string },
510
+ ): void {
511
+ if (canonicalName !== "edit" && canonicalName !== "write") return;
512
+ if (!input || typeof input !== "object") return;
513
+
514
+ const store = beginSourceObservationStoreForCurrentUnit(ctx);
515
+ if (!store) return;
516
+ store.observeMutation(input as { path?: unknown; file_path?: unknown });
517
+ }
518
+
519
+ function clearSourceObservationsAfterShell(
520
+ canonicalName: string,
521
+ ): void {
522
+ if (!isAutoActive()) return;
523
+ if (!isShellExecutionTool(canonicalName)) return;
524
+ const dash = getAutoRuntimeSnapshot();
525
+ if (!dash.currentUnit || !supportsSourceObservationsForUnit(dash.currentUnit.type)) return;
526
+ getSourceObservationStore().clear();
527
+ }
528
+
529
+ function isShellExecutionTool(canonicalName: string): boolean {
530
+ return canonicalName === "bash" ||
531
+ canonicalName === "bg_shell" ||
532
+ canonicalName === "async_bash" ||
533
+ canonicalName === "shell" ||
534
+ canonicalName === "powershell";
535
+ }
536
+
477
537
  function activateDeferredApprovalGate(basePath: string): void {
478
538
  if (deferredApprovalGate?.basePath !== basePath) return;
479
539
  setPendingGate(deferredApprovalGate.gateId, basePath);
@@ -497,6 +557,109 @@ function isContextDraftSummarySave(toolName: string, input: unknown): boolean {
497
557
  return (input as { artifact_type?: unknown }).artifact_type === "CONTEXT-DRAFT";
498
558
  }
499
559
 
560
+ type StructuredQuestion = {
561
+ id?: string;
562
+ header?: string;
563
+ question?: string;
564
+ options?: Array<{ label?: string; description?: string }>;
565
+ };
566
+
567
+ type StructuredAnswer = {
568
+ selected?: unknown;
569
+ notes?: unknown;
570
+ };
571
+
572
+ function selectedAnswerLabel(selected: unknown): string {
573
+ if (Array.isArray(selected)) return selected.map(String).join(", ");
574
+ if (selected == null) return "";
575
+ return String(selected);
576
+ }
577
+
578
+ function formatQuestionExchange(
579
+ questions: StructuredQuestion[],
580
+ answers: Record<string, StructuredAnswer> | undefined,
581
+ ): string {
582
+ const lines: string[] = [];
583
+ for (const question of questions) {
584
+ lines.push(`### ${question.header ?? "Question"}`, "", question.question ?? "");
585
+ if (Array.isArray(question.options)) {
586
+ lines.push("");
587
+ for (const opt of question.options) {
588
+ lines.push(`- **${opt.label ?? ""}** — ${opt.description ?? ""}`);
589
+ }
590
+ }
591
+
592
+ const answer = question.id ? answers?.[question.id] : undefined;
593
+ if (answer) {
594
+ lines.push("");
595
+ const selected = selectedAnswerLabel(answer.selected);
596
+ if (selected) lines.push(`**Selected:** ${selected}`);
597
+ if (answer.notes) lines.push(`**Notes:** ${String(answer.notes)}`);
598
+ }
599
+ lines.push("");
600
+ }
601
+ return lines.join("\n");
602
+ }
603
+
604
+ async function ensureMilestoneShell(basePath: string, milestoneId: string): Promise<string> {
605
+ const milestoneDir = resolveMilestonePath(basePath, milestoneId)
606
+ ?? join(milestonesDir(basePath), milestoneId);
607
+ mkdirSync(milestoneDir, { recursive: true });
608
+ clearPathCache();
609
+
610
+ try {
611
+ const { ensureDbOpen } = await import("./dynamic-tools.js");
612
+ if (await ensureDbOpen(basePath)) {
613
+ const { getMilestone, insertMilestone } = await import("../gsd-db.js");
614
+ if (!getMilestone(milestoneId)) {
615
+ insertMilestone({
616
+ id: milestoneId,
617
+ title: `New milestone ${milestoneId}`,
618
+ status: "queued",
619
+ });
620
+ }
621
+ }
622
+ } catch (err) {
623
+ safetyLogWarning("guided", `failed to persist milestone shell for ${milestoneId}: ${(err as Error).message}`);
624
+ }
625
+
626
+ return milestoneDir;
627
+ }
628
+
629
+ async function saveDiscussionQuestionRound(
630
+ basePath: string,
631
+ milestoneId: string,
632
+ questions: StructuredQuestion[],
633
+ details: any,
634
+ ): Promise<void> {
635
+ const milestoneDir = await ensureMilestoneShell(basePath, milestoneId);
636
+ const answers = details?.response?.answers;
637
+ const timestamp = new Date().toISOString();
638
+ const exchange = formatQuestionExchange(questions, answers);
639
+
640
+ const discussionPath = join(milestoneDir, buildMilestoneFileName(milestoneId, "DISCUSSION"));
641
+ const existingDiscussion = await loadFile(discussionPath) ?? `# ${milestoneId} Discussion Log\n\n`;
642
+ await saveFile(
643
+ discussionPath,
644
+ `${existingDiscussion}## Exchange — ${timestamp}\n\n${exchange}---\n\n`,
645
+ );
646
+
647
+ const draftPath = join(milestoneDir, buildMilestoneFileName(milestoneId, "CONTEXT-DRAFT"));
648
+ const existingDraft = await loadFile(draftPath);
649
+ const draftHeader = existingDraft
650
+ ?? [
651
+ `# ${milestoneId}: New milestone ${milestoneId}`,
652
+ "",
653
+ "This draft was captured automatically from structured question responses.",
654
+ "Use it so `/gsd` can resume the in-flight milestone discussion.",
655
+ "",
656
+ ].join("\n");
657
+ await saveFile(
658
+ draftPath,
659
+ `${draftHeader.trimEnd()}\n\n## Captured Question Round — ${timestamp}\n\n${exchange}`,
660
+ );
661
+ }
662
+
500
663
  function withDepthGateDisplayReason<T extends { block: boolean; reason?: string }>(
501
664
  result: T,
502
665
  displayReason = "Depth confirmation is waiting for your answer.",
@@ -1129,6 +1292,17 @@ export function registerHooks(
1129
1292
  if (isAutoActive() && typeof event.toolCallId === "string") {
1130
1293
  markToolEnd(event.toolCallId);
1131
1294
  }
1295
+ const toolName = canonicalToolName(event.toolName);
1296
+ if (isAutoActive() && toolName === "read" && !event.isError) {
1297
+ const store = beginSourceObservationStoreForCurrentUnit(ctx);
1298
+ if (store) {
1299
+ store.observeRead(event.input);
1300
+ }
1301
+ }
1302
+ if (!event.isError) {
1303
+ refreshSourceObservationAfterMutation(toolName, event.input, ctx);
1304
+ clearSourceObservationsAfterShell(toolName);
1305
+ }
1132
1306
  if (isAutoActive() && event.isError) {
1133
1307
  const resultPayload = ("result" in event ? event.result : undefined) as any;
1134
1308
  const errorText = typeof resultPayload === "string"
@@ -1144,11 +1318,9 @@ export function registerHooks(
1144
1318
  } else if (isAutoActive()) {
1145
1319
  clearToolInvocationError();
1146
1320
  }
1147
- const toolName = canonicalToolName(event.toolName);
1148
1321
  if (toolName !== "ask_user_questions") return;
1149
1322
  const basePath = contextBasePath(ctx);
1150
1323
  const milestoneId = await getDiscussionMilestoneIdFor(basePath);
1151
- const queueActive = isQueuePhaseActive(basePath);
1152
1324
 
1153
1325
  const details = event.details as any;
1154
1326
 
@@ -1227,36 +1399,8 @@ export function registerHooks(
1227
1399
  }
1228
1400
  }
1229
1401
 
1230
- if (!milestoneId && !queueActive) return;
1231
1402
  if (!milestoneId) return;
1232
- const milestoneDir = resolveMilestonePath(basePath, milestoneId);
1233
- if (!milestoneDir) return;
1234
-
1235
- const discussionPath = join(milestoneDir, buildMilestoneFileName(milestoneId, "DISCUSSION"));
1236
- const timestamp = new Date().toISOString();
1237
- const lines: string[] = [`## Exchange — ${timestamp}`, ""];
1238
- for (const question of questions) {
1239
- lines.push(`### ${question.header ?? "Question"}`, "", question.question ?? "");
1240
- if (Array.isArray(question.options)) {
1241
- lines.push("");
1242
- for (const opt of question.options) {
1243
- lines.push(`- **${opt.label}** — ${opt.description ?? ""}`);
1244
- }
1245
- }
1246
- const answer = details.response?.answers?.[question.id];
1247
- if (answer) {
1248
- lines.push("");
1249
- const selected = Array.isArray(answer.selected) ? answer.selected.join(", ") : answer.selected;
1250
- lines.push(`**Selected:** ${selected}`);
1251
- if (answer.notes) {
1252
- lines.push(`**Notes:** ${answer.notes}`);
1253
- }
1254
- }
1255
- lines.push("");
1256
- }
1257
- lines.push("---", "");
1258
- const existing = await loadFile(discussionPath) ?? `# ${milestoneId} Discussion Log\n\n`;
1259
- await saveFile(discussionPath, existing + lines.join("\n"));
1403
+ await saveDiscussionQuestionRound(basePath, milestoneId, questions, details);
1260
1404
  });
1261
1405
 
1262
1406
  pi.on("tool_execution_start", async (event, ctx) => {
@@ -1310,48 +1454,57 @@ export function registerHooks(
1310
1454
  const payload = event.payload as Record<string, unknown> | null;
1311
1455
  if (!payload || typeof payload !== "object") return;
1312
1456
 
1313
- // ── Observation Masking ─────────────────────────────────────────────
1314
- // Replace old tool results with placeholders to reduce context bloat.
1315
- // Only active during auto-mode when context_management.observation_masking is enabled.
1316
- if (isAutoActive()) {
1317
- try {
1318
- const { loadEffectiveGSDPreferences } = await import("../preferences.js");
1319
- const {
1320
- createObservationMask,
1321
- createResponsesInputObservationMask,
1322
- truncateContextResultMessages,
1323
- truncateResponsesInputResultItems,
1324
- } = await import("../context-masker.js");
1325
- const prefs = loadEffectiveGSDPreferences();
1326
- const cmConfig = prefs?.preferences.context_management;
1327
-
1328
- // Observation masking: replace old tool results with placeholders
1329
- if (cmConfig?.observation_masking !== false) {
1330
- const keepTurns = cmConfig?.observation_mask_turns ?? 8;
1331
- const messages = payload.messages;
1332
- if (Array.isArray(messages)) {
1333
- payload.messages = createObservationMask(keepTurns)(messages);
1334
- }
1335
- const input = payload.input;
1336
- if (Array.isArray(input)) {
1337
- payload.input = createResponsesInputObservationMask(keepTurns)(input);
1338
- }
1339
- }
1340
-
1341
- // Tool result truncation: cap individual tool result content length.
1342
- // In pi-ai format, toolResult messages have role: "toolResult" and content: TextContent[].
1343
- // Creates new objects to avoid mutating shared conversation state.
1344
- const maxChars = cmConfig?.tool_result_max_chars ?? 800;
1345
- const msgs = payload.messages;
1346
- if (Array.isArray(msgs)) {
1347
- payload.messages = truncateContextResultMessages(msgs as any, maxChars);
1457
+ // ── Context Management ──────────────────────────────────────────────
1458
+ // Load preferences once for both masking and truncation.
1459
+ try {
1460
+ const { loadEffectiveGSDPreferences } = await import("../preferences.js");
1461
+ const {
1462
+ createObservationMask,
1463
+ createResponsesInputObservationMask,
1464
+ truncateContextResultMessages,
1465
+ truncateResponsesInputResultItems,
1466
+ } = await import("../context-masker.js");
1467
+ const prefs = loadEffectiveGSDPreferences();
1468
+ const cmConfig = prefs?.preferences.context_management;
1469
+
1470
+ // Observation masking: replace old tool results with placeholders.
1471
+ // Only active during auto-mode when context_management.observation_masking is enabled.
1472
+ if (isAutoActive() && cmConfig?.observation_masking !== false) {
1473
+ const keepTurns = cmConfig?.observation_mask_turns ?? 8;
1474
+ const messages = payload.messages;
1475
+ if (Array.isArray(messages)) {
1476
+ payload.messages = createObservationMask(keepTurns)(messages);
1348
1477
  }
1349
1478
  const input = payload.input;
1350
1479
  if (Array.isArray(input)) {
1351
- payload.input = truncateResponsesInputResultItems(input as any, maxChars);
1480
+ payload.input = createResponsesInputObservationMask(keepTurns)(input);
1352
1481
  }
1353
- } catch { /* non-fatal */ }
1354
- }
1482
+ }
1483
+
1484
+ // Tool result truncation: cap individual tool result content length.
1485
+ // Applies in ALL modes (auto + interactive) to prevent context bloat.
1486
+ // In pi-ai format, toolResult messages have role: "toolResult" and content: TextContent[].
1487
+ // Creates new objects to avoid mutating shared conversation state.
1488
+ const maxChars = cmConfig?.tool_result_max_chars ?? 800;
1489
+ const msgs = payload.messages;
1490
+ if (Array.isArray(msgs)) {
1491
+ payload.messages = truncateContextResultMessages(msgs as any, maxChars);
1492
+ }
1493
+ const input = payload.input;
1494
+ if (Array.isArray(input)) {
1495
+ payload.input = truncateResponsesInputResultItems(input as any, maxChars);
1496
+ }
1497
+ } catch { /* non-fatal */ }
1498
+
1499
+ try {
1500
+ if (isAutoActive()) {
1501
+ const sourceContextBlock = getSourceObservationStore().renderActiveBlock();
1502
+ if (sourceContextBlock) {
1503
+ const nextPayload = injectSourceContextBlockIntoPayload(payload, sourceContextBlock);
1504
+ Object.assign(payload, nextPayload);
1505
+ }
1506
+ }
1507
+ } catch { /* non-fatal */ }
1355
1508
 
1356
1509
  // ── Service Tier ────────────────────────────────────────────────────
1357
1510
  const modelId = event.model?.id;
@@ -5,6 +5,7 @@ import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
5
5
 
6
6
  import type { NextAction } from "../shared/next-action-ui.js";
7
7
  import type { GSDState } from "./types.js";
8
+ import { setAutoOutcomeWidget } from "./auto-dashboard.js";
8
9
  import { invalidateAllCaches } from "./cache.js";
9
10
  import { mergeCompletedMilestone } from "./parallel-merge.js";
10
11
  import { cleanupQuickBranch, detectStrandedQuickBranch, type StrandedQuickBranch } from "./quick.js";
@@ -21,6 +22,13 @@ export interface CloseoutContext {
21
22
  unmergedMilestones: UnmergedMilestoneBlocker[];
22
23
  }
23
24
 
25
+ const MILESTONE_MERGE_CLOSEOUT_COMMANDS = [
26
+ "/gsd status for overview",
27
+ "/gsd visualize to inspect",
28
+ "/gsd notifications for history",
29
+ "/gsd start for new work",
30
+ ];
31
+
24
32
  export async function loadCloseoutContext(basePath: string): Promise<CloseoutContext> {
25
33
  const unmergedMilestones = await findUnmergedCompletedMilestones(basePath);
26
34
  return {
@@ -91,6 +99,23 @@ export function buildIdleMenuSummary(state: GSDState, closeout: CloseoutContext)
91
99
  return [state.nextAction || "No active milestone."];
92
100
  }
93
101
 
102
+ export function showMilestoneMergeCloseout(
103
+ ctx: ExtensionCommandContext,
104
+ blocker: UnmergedMilestoneBlocker,
105
+ ): void {
106
+ ctx.ui.setStatus?.("gsd-auto", undefined);
107
+ ctx.ui.setStatus?.("gsd-step", undefined);
108
+ ctx.ui.setWidget?.("gsd-progress", undefined);
109
+
110
+ setAutoOutcomeWidget(ctx, {
111
+ status: "complete",
112
+ title: `Milestone ${blocker.milestoneId} merged`,
113
+ detail: `Merged ${blocker.branch} into ${blocker.integrationBranch}. Product changes are now on ${blocker.integrationBranch}.`,
114
+ nextAction: "Review the closeout, then start the next milestone when ready.",
115
+ commands: MILESTONE_MERGE_CLOSEOUT_COMMANDS,
116
+ });
117
+ }
118
+
94
119
  export async function runMergeQuickTask(
95
120
  ctx: ExtensionCommandContext,
96
121
  basePath: string,
@@ -113,31 +138,23 @@ export async function runMergeQuickTask(
113
138
  return false;
114
139
  }
115
140
 
116
- export async function runMergeMilestone(
141
+ export async function runMergeMilestoneBlocker(
117
142
  ctx: ExtensionCommandContext,
118
143
  basePath: string,
119
- milestoneId?: string,
144
+ blocker: UnmergedMilestoneBlocker,
120
145
  ): Promise<boolean> {
121
- const blockers = await findUnmergedCompletedMilestones(basePath);
122
- const blocker = milestoneId
123
- ? blockers.find((candidate) => candidate.milestoneId === milestoneId)
124
- : blockers[0];
125
- if (!blocker) {
126
- ctx.ui.notify("No unmerged completed milestone found.", "warning");
127
- return false;
128
- }
129
-
130
146
  ctx.ui.notify(
131
147
  `Completing preserved milestone merge for ${blocker.milestoneId} from ${blocker.branch} into ${blocker.integrationBranch}.`,
132
148
  "info",
133
149
  );
134
150
  const result = await mergeCompletedMilestone(basePath, blocker.milestoneId);
135
151
  if (result.success) {
152
+ invalidateAllCaches();
153
+ showMilestoneMergeCloseout(ctx, blocker);
136
154
  ctx.ui.notify(
137
- `Milestone ${blocker.milestoneId} merged to ${blocker.integrationBranch}. Run /gsd again when ready.`,
155
+ `Milestone ${blocker.milestoneId} merged to ${blocker.integrationBranch}. Closeout is complete.`,
138
156
  "info",
139
157
  );
140
- invalidateAllCaches();
141
158
  return true;
142
159
  }
143
160
 
@@ -148,6 +165,23 @@ export async function runMergeMilestone(
148
165
  return false;
149
166
  }
150
167
 
168
+ export async function runMergeMilestone(
169
+ ctx: ExtensionCommandContext,
170
+ basePath: string,
171
+ milestoneId?: string,
172
+ ): Promise<boolean> {
173
+ const blockers = await findUnmergedCompletedMilestones(basePath);
174
+ const blocker = milestoneId
175
+ ? blockers.find((candidate) => candidate.milestoneId === milestoneId)
176
+ : blockers[0];
177
+ if (!blocker) {
178
+ ctx.ui.notify("No unmerged completed milestone found.", "warning");
179
+ return false;
180
+ }
181
+
182
+ return runMergeMilestoneBlocker(ctx, basePath, blocker);
183
+ }
184
+
151
185
  export async function handleCloseoutChoice(
152
186
  ctx: ExtensionCommandContext,
153
187
  basePath: string,
@@ -19,7 +19,7 @@ import { handleSessionReport } from "../../commands-session-report.js";
19
19
  import { handlePrBranch } from "../../commands-pr-branch.js";
20
20
  import { currentDirectoryRoot, projectRoot } from "../context.js";
21
21
  import { findUnmergedCompletedMilestones } from "../../unmerged-milestone-guard.js";
22
- import { mergeCompletedMilestone } from "../../parallel-merge.js";
22
+ import { runMergeMilestoneBlocker } from "../../closeout-wizard.js";
23
23
 
24
24
  async function handleCompletedMilestoneRecovery(
25
25
  phase: string,
@@ -37,22 +37,7 @@ async function handleCompletedMilestoneRecovery(
37
37
  : blockers[0];
38
38
  if (!blocker) return false;
39
39
 
40
- ctx.ui.notify(
41
- `Completing preserved milestone merge for ${blocker.milestoneId} from ${blocker.branch} into ${blocker.integrationBranch}.`,
42
- "info",
43
- );
44
- const result = await mergeCompletedMilestone(basePath, blocker.milestoneId);
45
- if (result.success) {
46
- ctx.ui.notify(
47
- `Milestone ${blocker.milestoneId} merged to ${blocker.integrationBranch}. Run /gsd again when ready.`,
48
- "info",
49
- );
50
- } else {
51
- ctx.ui.notify(
52
- `Milestone ${blocker.milestoneId} merge recovery failed: ${result.error}`,
53
- "error",
54
- );
55
- }
40
+ await runMergeMilestoneBlocker(ctx, basePath, blocker);
56
41
  return true;
57
42
  }
58
43