@opengsd/gsd-pi 1.0.2-dev.fb7ddf1 → 1.1.0

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 (184) hide show
  1. package/dist/resources/.managed-resources-content-hash +1 -1
  2. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +8 -1
  3. package/dist/resources/extensions/gsd/auto/phases.js +7 -4
  4. package/dist/resources/extensions/gsd/auto.js +7 -4
  5. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  6. package/dist/web/standalone/.next/BUILD_ID +1 -1
  7. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  8. package/dist/web/standalone/.next/build-manifest.json +3 -3
  9. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  10. package/dist/web/standalone/.next/required-server-files.json +3 -3
  11. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  12. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  13. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  14. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  15. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  16. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  17. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  18. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  19. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  20. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  21. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  22. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  23. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  24. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  25. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  26. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  28. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  32. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  33. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  34. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  35. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  36. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  37. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  38. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  39. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  40. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  41. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  42. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  43. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  44. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  45. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  46. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  47. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  48. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  49. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  50. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  51. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  52. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  53. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  54. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  55. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  56. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  57. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  58. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  59. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  60. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  61. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  62. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  63. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  64. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  65. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  66. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  67. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  68. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  69. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/mcp-connections/route_client-reference-manifest.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  72. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  73. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  74. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  82. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  88. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  102. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  104. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  106. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +2 -2
  108. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/index.html +1 -1
  118. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  119. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  120. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  121. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  123. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/page.js +2 -2
  125. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  127. package/dist/web/standalone/.next/server/chunks/1834.js +3 -3
  128. package/dist/web/standalone/.next/server/chunks/7039.js +4 -4
  129. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/middleware.js +2 -2
  131. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  132. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  133. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  134. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-8867401a0497345a.js → page-9d0456bae07ee738.js} +1 -1
  135. package/dist/web/standalone/.next/static/chunks/app/layout-d5eb6f78269341a2.js +1 -0
  136. package/dist/web/standalone/.next/static/chunks/app/{page-4dbe7cf7d6733ab1.js → page-3dbf4b75cbfa8ff0.js} +1 -1
  137. package/dist/web/standalone/.next/static/chunks/main-app-91185bf25c4a9d61.js +1 -0
  138. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-687e13572ab5c769.js +1 -0
  139. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  140. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  141. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  142. package/dist/web/standalone/server.js +1 -1
  143. package/package.json +6 -6
  144. package/packages/cloud-mcp-gateway/package.json +2 -2
  145. package/packages/contracts/package.json +1 -1
  146. package/packages/daemon/package.json +4 -4
  147. package/packages/gsd-agent-core/package.json +5 -5
  148. package/packages/gsd-agent-modes/package.json +7 -7
  149. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  150. package/packages/mcp-server/dist/server.js +57 -26
  151. package/packages/mcp-server/dist/server.js.map +1 -1
  152. package/packages/mcp-server/dist/session-manager.d.ts +12 -0
  153. package/packages/mcp-server/dist/session-manager.d.ts.map +1 -1
  154. package/packages/mcp-server/dist/session-manager.js +22 -0
  155. package/packages/mcp-server/dist/session-manager.js.map +1 -1
  156. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  157. package/packages/mcp-server/dist/workflow-tools.js +124 -7
  158. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  159. package/packages/mcp-server/package.json +3 -3
  160. package/packages/native/package.json +1 -1
  161. package/packages/pi-agent-core/package.json +1 -1
  162. package/packages/pi-ai/dist/models.generated.d.ts +45 -0
  163. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  164. package/packages/pi-ai/dist/models.generated.js +38 -2
  165. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  166. package/packages/pi-ai/package.json +1 -1
  167. package/packages/pi-coding-agent/package.json +7 -7
  168. package/packages/pi-tui/package.json +1 -1
  169. package/packages/rpc-client/package.json +2 -2
  170. package/pkg/package.json +1 -1
  171. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +9 -1
  172. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +19 -5
  173. package/src/resources/extensions/gsd/auto/loop-deps.ts +5 -0
  174. package/src/resources/extensions/gsd/auto/phases.ts +7 -2
  175. package/src/resources/extensions/gsd/auto.ts +9 -4
  176. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  177. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +66 -23
  178. package/src/resources/extensions/gsd/tests/complete-milestone-prompt-rendering.test.ts +1 -0
  179. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +26 -2
  180. package/dist/web/standalone/.next/static/chunks/app/layout-7669bce99bfc10fd.js +0 -1
  181. package/dist/web/standalone/.next/static/chunks/main-app-40a5bbdf7ffc2008.js +0 -1
  182. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-7df29967b89ea48a.js +0 -1
  183. /package/dist/web/standalone/.next/static/{tH1tnDYt1E0hK9Ien73Z0 → QMnuAY25UqnNzPB_UqAWF}/_buildManifest.js +0 -0
  184. /package/dist/web/standalone/.next/static/{tH1tnDYt1E0hK9Ien73Z0 → QMnuAY25UqnNzPB_UqAWF}/_ssgManifest.js +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gsd/pi-ai",
3
- "version": "1.0.2-dev.fb7ddf1",
3
+ "version": "1.1.0",
4
4
  "description": "Unified LLM API with automatic model discovery and provider configuration",
5
5
  "type": "module",
6
6
  "gsd": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gsd/pi-coding-agent",
3
- "version": "1.0.2-dev.fb7ddf1",
3
+ "version": "1.1.0",
4
4
  "description": "Coding agent CLI (vendored from earendil-works/pi)",
5
5
  "type": "module",
6
6
  "gsd": {
@@ -33,7 +33,7 @@
33
33
  "copy-assets": "node scripts/copy-assets.cjs"
34
34
  },
35
35
  "dependencies": {
36
- "@opengsd/contracts": "^1.0.2-dev.fb7ddf1",
36
+ "@opengsd/contracts": "^1.1.0",
37
37
  "@mariozechner/jiti": "^2.6.2",
38
38
  "@silvia-odwyer/photon-node": "0.3.4",
39
39
  "chalk": "5.6.2",
@@ -53,11 +53,11 @@
53
53
  "typebox": "1.1.38",
54
54
  "undici": "7.26.0",
55
55
  "yaml": "2.9.0",
56
- "@gsd/agent-core": "^1.0.2-dev.fb7ddf1",
57
- "@gsd/native": "^1.0.2-dev.fb7ddf1",
58
- "@gsd/pi-agent-core": "^1.0.2-dev.fb7ddf1",
59
- "@gsd/pi-ai": "^1.0.2-dev.fb7ddf1",
60
- "@gsd/pi-tui": "^1.0.2-dev.fb7ddf1",
56
+ "@gsd/agent-core": "^1.1.0",
57
+ "@gsd/native": "^1.1.0",
58
+ "@gsd/pi-agent-core": "^1.1.0",
59
+ "@gsd/pi-ai": "^1.1.0",
60
+ "@gsd/pi-tui": "^1.1.0",
61
61
  "@sinclair/typebox": "^0.34.41"
62
62
  },
63
63
  "devDependencies": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gsd/pi-tui",
3
- "version": "1.0.2-dev.fb7ddf1",
3
+ "version": "1.1.0",
4
4
  "description": "Terminal UI library (vendored from earendil-works/pi)",
5
5
  "type": "module",
6
6
  "gsd": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengsd/rpc-client",
3
- "version": "1.0.2-dev.fb7ddf1",
3
+ "version": "1.1.0",
4
4
  "description": "Standalone RPC client SDK for GSD — zero internal dependencies",
5
5
  "license": "MIT",
6
6
  "gsd": {
@@ -34,7 +34,7 @@
34
34
  "test": "node --test dist/rpc-client.test.js"
35
35
  },
36
36
  "dependencies": {
37
- "@opengsd/contracts": "^1.0.2-dev.fb7ddf1"
37
+ "@opengsd/contracts": "^1.1.0"
38
38
  },
39
39
  "engines": {
40
40
  "node": ">=22.0.0"
package/pkg/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glittercowboy/gsd",
3
- "version": "1.0.2-dev.fb7ddf1",
3
+ "version": "1.1.0",
4
4
  "piConfig": {
5
5
  "name": "gsd",
6
6
  "configDir": ".gsd"
@@ -351,6 +351,9 @@ export function buildPromptFromContext(context: Context, toolContext: PromptTool
351
351
  const toolSearchLine = toolContext.workflowMcpServerName
352
352
  ? "- ToolSearch is NOT available — never use it to discover tools; invoke the MCP tool directly\n"
353
353
  : "- ToolSearch is NOT available — never use it to discover tools\n";
354
+ const browserToolLine =
355
+ "- Pi/GSD browser_* tools from prior context (for example browser_navigate, browser_find, browser_evaluate, browser_close) are not Claude Code tools. " +
356
+ "Never use ToolSearch to select browser_* tools; for browser verification, use Bash to run a local Playwright/Node check unless an explicit browser MCP tool is already listed.\n";
354
357
 
355
358
  // The prior system context lists pi-native tool names (lowercase: bash, read, gsd_exec, etc.)
356
359
  // but this process runs inside Claude Code where tool names differ. Inject a remapping note
@@ -362,6 +365,7 @@ export function buildPromptFromContext(context: Context, toolContext: PromptTool
362
365
  "- Shell commands: 'Bash' (not 'bash')\n" +
363
366
  "- File operations: 'Read', 'Write', 'Edit', 'Glob', 'Grep' (PascalCase, not lowercase)\n" +
364
367
  workflowToolLine +
368
+ browserToolLine +
365
369
  toolSearchLine +
366
370
  "</tool_context>",
367
371
  );
@@ -1523,7 +1527,11 @@ export function buildSdkOptions(
1523
1527
  const workflowMcpTools = filteredMcpServers
1524
1528
  ? Object.keys(filteredMcpServers).map((serverName) => `mcp__${serverName}__*`)
1525
1529
  : (!workflowExplicitlyBlocked && workflowServerName ? [`mcp__${workflowServerName}__*`] : []);
1526
- const disallowedTools: string[] = [...(workflowMcpTools.length > 0 ? ["AskUserQuestion"] : []), ...extraDisallowedTools];
1530
+ const disallowedTools: string[] = [...new Set([
1531
+ "ToolSearch",
1532
+ ...(workflowMcpTools.length > 0 ? ["AskUserQuestion"] : []),
1533
+ ...extraDisallowedTools,
1534
+ ])];
1527
1535
  const allowedTools = [
1528
1536
  "Read",
1529
1537
  "Write",
@@ -431,6 +431,20 @@ describe("stream-adapter — no transcript fabrication (#4102)", () => {
431
431
  assert.ok(!prompt.includes("mcp__gsd-workflow__<tool_name>"));
432
432
  assert.ok(!prompt.includes("mcp__gsd-workflow__gsd_exec"));
433
433
  });
434
+
435
+ test("buildPromptFromContext remaps pi-native browser tools for Claude Code", () => {
436
+ const context: Context = {
437
+ systemPrompt: "Browser verification: use browser_find and browser_navigate.",
438
+ messages: [{ role: "user", content: "Verify the app" } as Message],
439
+ };
440
+
441
+ const prompt = buildPromptFromContext(context, { workflowMcpServerName: "gsd-workflow" });
442
+
443
+ assert.ok(prompt.includes("browser_navigate"), "remap should name stale browser tool examples");
444
+ assert.ok(prompt.includes("not Claude Code tools"), "remap should explain browser_* is unavailable in Claude Code");
445
+ assert.ok(prompt.includes("Never use ToolSearch to select browser_* tools"));
446
+ assert.ok(prompt.includes("Bash to run a local Playwright/Node check"));
447
+ });
434
448
  });
435
449
 
436
450
  describe("stream-adapter — Claude Code external tool results", () => {
@@ -925,7 +939,7 @@ describe("stream-adapter — session persistence (#2859)", () => {
925
939
  assert.equal(srv.env.GSD_CLI_PATH, "/tmp/gsd");
926
940
  assert.equal(srv.env.GSD_PERSIST_WRITE_GATE_STATE, "1");
927
941
  assert.equal(srv.env.GSD_WORKFLOW_PROJECT_ROOT, "/tmp/project");
928
- assert.deepEqual(options.disallowedTools, ["AskUserQuestion"]);
942
+ assert.deepEqual(options.disallowedTools, ["ToolSearch", "AskUserQuestion"]);
929
943
  assert.deepEqual(options.allowedTools, [
930
944
  "Read",
931
945
  "Write",
@@ -961,7 +975,7 @@ describe("stream-adapter — session persistence (#2859)", () => {
961
975
  const options = buildSdkOptions("claude-sonnet-4-20250514", "test");
962
976
  const mcpServers = options.mcpServers as Record<string, any>;
963
977
  assert.ok(mcpServers?.["custom-workflow"], "expected custom workflow server config");
964
- assert.deepEqual(options.disallowedTools, ["AskUserQuestion"]);
978
+ assert.deepEqual(options.disallowedTools, ["ToolSearch", "AskUserQuestion"]);
965
979
  assert.deepEqual(options.allowedTools, [
966
980
  "Read",
967
981
  "Write",
@@ -1005,9 +1019,9 @@ describe("stream-adapter — session persistence (#2859)", () => {
1005
1019
  const mcpServers = (options as any).mcpServers;
1006
1020
  if (mcpServers) {
1007
1021
  assert.ok(mcpServers["gsd-workflow"], "if present, must be gsd-workflow");
1008
- assert.deepEqual((options as any).disallowedTools, ["AskUserQuestion"]);
1022
+ assert.deepEqual((options as any).disallowedTools, ["ToolSearch", "AskUserQuestion"]);
1009
1023
  } else {
1010
- assert.deepEqual((options as any).disallowedTools, []);
1024
+ assert.deepEqual((options as any).disallowedTools, ["ToolSearch"]);
1011
1025
  }
1012
1026
  rmSync(emptyDir, { recursive: true, force: true });
1013
1027
  } finally {
@@ -1046,7 +1060,7 @@ describe("stream-adapter — session persistence (#2859)", () => {
1046
1060
  assert.equal(srv.env.GSD_CLI_PATH, "/tmp/gsd");
1047
1061
  assert.equal(srv.env.GSD_PERSIST_WRITE_GATE_STATE, "1");
1048
1062
  assert.equal(srv.env.GSD_WORKFLOW_PROJECT_ROOT, resolvedRepoDir);
1049
- assert.deepEqual(options.disallowedTools, ["AskUserQuestion"]);
1063
+ assert.deepEqual(options.disallowedTools, ["ToolSearch", "AskUserQuestion"]);
1050
1064
  } finally {
1051
1065
  process.chdir(originalCwd);
1052
1066
  rmSync(repoDir, { recursive: true, force: true });
@@ -33,6 +33,11 @@ export interface StopAutoOptions {
33
33
  milestoneTitle?: string | null;
34
34
  allMilestonesComplete?: boolean;
35
35
  };
36
+ /**
37
+ * Foreground closeout-boundary stops already have the useful final surface in
38
+ * the transcript. Preserve it instead of installing a replacement widget.
39
+ */
40
+ preserveCloseoutTranscript?: boolean;
36
41
  /** Preserve, rather than merge, a completed milestone during stop cleanup. */
37
42
  preserveCompletedMilestoneBranch?: boolean;
38
43
  }
@@ -583,6 +583,7 @@ async function restorePreflightStashOrStop(
583
583
  export async function _runMilestoneMergeWithStashRestore(
584
584
  ic: IterationContext,
585
585
  milestoneId: string,
586
+ options: { preserveCloseoutTranscript?: boolean } = {},
586
587
  ): Promise<{ action: "break"; reason: string } | null> {
587
588
  const { ctx, pi, s, deps } = ic;
588
589
 
@@ -597,6 +598,7 @@ export async function _runMilestoneMergeWithStashRestore(
597
598
  : `Pre-merge dirty working tree overlaps milestone ${milestoneId}`;
598
599
  await deps.stopAuto(ctx, pi, reason, {
599
600
  preserveCompletedMilestoneBranch: true,
601
+ preserveCloseoutTranscript: options.preserveCloseoutTranscript,
600
602
  });
601
603
  return {
602
604
  action: "break",
@@ -677,6 +679,7 @@ export async function _runMilestoneMergeWithStashRestore(
677
679
  export async function _runMilestoneMergeOnceWithStashRestore(
678
680
  ic: IterationContext,
679
681
  milestoneId: string,
682
+ options: { preserveCloseoutTranscript?: boolean } = {},
680
683
  ): Promise<{ action: "break"; reason: string } | null> {
681
684
  if (ic.s.milestoneMergedInPhases) {
682
685
  debugLog("autoLoop", {
@@ -686,7 +689,7 @@ export async function _runMilestoneMergeOnceWithStashRestore(
686
689
  });
687
690
  return null;
688
691
  }
689
- return _runMilestoneMergeWithStashRestore(ic, milestoneId);
692
+ return _runMilestoneMergeWithStashRestore(ic, milestoneId, options);
690
693
  }
691
694
 
692
695
  async function emitCancelledUnitEnd(
@@ -3063,7 +3066,9 @@ export async function runFinalize(
3063
3066
  }
3064
3067
 
3065
3068
  if (preUnitSnapshot?.type === "complete-milestone" && s.currentMilestoneId) {
3066
- const stop = await _runMilestoneMergeOnceWithStashRestore(ic, s.currentMilestoneId);
3069
+ const stop = await _runMilestoneMergeOnceWithStashRestore(ic, s.currentMilestoneId, {
3070
+ preserveCloseoutTranscript: true,
3071
+ });
3067
3072
  if (stop) {
3068
3073
  clearFinalizingUnit();
3069
3074
  return stop;
@@ -1381,9 +1381,13 @@ export async function stopAuto(
1381
1381
  const loadedPreferences = loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences;
1382
1382
  const stopNotificationPrefix = formatAutoStopNotificationPrefix(reason);
1383
1383
  const displayReason = formatAutoStopDisplayReason(reason);
1384
+ const isHeadlessStop = process.env.GSD_HEADLESS === "1";
1384
1385
  const completionStopRequested = Boolean(options.completionWidget);
1385
- const installCompletionWidget = completionStopRequested;
1386
- const preserveCompletionSurface = completionStopRequested;
1386
+ const preserveCloseoutTranscript = !isHeadlessStop && (
1387
+ options.preserveCloseoutTranscript ?? completionStopRequested
1388
+ );
1389
+ const installCompletionWidget = completionStopRequested && !preserveCloseoutTranscript;
1390
+ const preserveCompletionSurface = completionStopRequested || preserveCloseoutTranscript;
1387
1391
  s.completionStopInProgress = preserveCompletionSurface;
1388
1392
 
1389
1393
  // #4764 — telemetry: record the exit reason, isolation mode, whether an auto
@@ -1808,8 +1812,9 @@ export async function stopAuto(
1808
1812
  if (installCompletionWidget) {
1809
1813
  // Completion stops keep the durable final closeout surface visible.
1810
1814
  } else if (preserveCompletionSurface) {
1811
- ctx?.ui.setWidget("gsd-progress", undefined);
1812
- ctx?.ui.setWidget("gsd-outcome", undefined);
1815
+ // Foreground closeout-boundary stops preserve the transcript that the
1816
+ // completing unit already printed. Avoid replacing it with a widget or
1817
+ // clearing the progress slot, which can push the closeout into scrollback.
1813
1818
  } else {
1814
1819
  ctx?.ui.setWidget("gsd-progress", undefined);
1815
1820
  const status = isBlockedStopReason(reason) ? "blocked" : reason?.toLowerCase().includes("fail") ? "failed" : "stopped";
@@ -84,7 +84,7 @@ Subagents report only; they do not write user source. Fold any findings into Dec
84
84
  - `deviations` (string) — Deviations from the original plan
85
85
 
86
86
  14. Do not commit manually — the system auto-commits your changes after this unit completes.
87
- - Say: "Milestone {{milestoneId}} complete."
87
+ - After `gsd_complete_milestone` succeeds, emit only one closeout line: "Milestone {{milestoneId}} complete." Do not add a second final-status block, repeat the tool result, or restate the closeout summary.
88
88
 
89
89
  **Important:** Do NOT skip code-change, success-criteria, or definition-of-done verification (steps 4-6). The summary must reflect verified outcomes. Verification failures block completion; there is no override. If a verification tool fails, errors, or returns unexpected output, treat it as failure.
90
90
 
@@ -486,7 +486,7 @@ test("rerootCommandSession refreshes command workspace to project root", async (
486
486
  assert.deepEqual(calls, ["/project/root"]);
487
487
  });
488
488
 
489
- test("stopAuto foreground completion closeout reroots session and installs the durable roll-up surface", async (t) => {
489
+ test("stopAuto foreground completion closeout reroots session and preserves the transcript surface", async (t) => {
490
490
  const base = mkdtempSync(join(tmpdir(), "gsd-completion-stop-"));
491
491
  const previousCwd = process.cwd();
492
492
  const widgetCalls: Array<[string, unknown]> = [];
@@ -604,19 +604,11 @@ test("stopAuto foreground completion closeout reroots session and installs the d
604
604
  assert.deepEqual(newSessionWorkspaces, [base], "completion stop must reroot command session to original project root");
605
605
  assert.equal(restoreCalls, 1, "completion stop must restore project root through lifecycle");
606
606
  assert.equal(realpathSync(process.cwd()), realpathSync(base), "completion stop must chdir back to project root");
607
- const lastProgressWidget = widgetCalls.filter(([key]) => key === "gsd-progress").at(-1);
608
607
  assert.equal(
609
- typeof lastProgressWidget?.[1],
610
- "function",
611
- "foreground completion stop must leave the durable roll-up widget visible",
608
+ widgetCalls.some(([key, value]) => key === "gsd-progress" && typeof value === "function"),
609
+ false,
610
+ "foreground completion stop must not install a replacement roll-up widget over the transcript",
612
611
  );
613
- const rollup = (lastProgressWidget?.[1] as any)(
614
- { requestRender() {} },
615
- { fg: (_color: string, text: string) => text, bold: (text: string) => text },
616
- ).render(140).join("\n");
617
- assert.match(rollup, /Milestone M003 roll-up/);
618
- assert.match(rollup, /Budget tracking/);
619
- assert.match(rollup, /Users can see what shipped without opening a fresh session/);
620
612
  assert.ok(
621
613
  notifications.every(message => !message.includes("/gsd auto to resume")),
622
614
  "completion stop notification must not tell users to resume a finished auto run",
@@ -739,7 +731,66 @@ test("stopAuto completion closeout emits a headless terminal notification withou
739
731
  }
740
732
  });
741
733
 
742
- test("stopAuto foreground all-complete closeout leaves a durable roll-up as the final surface", async () => {
734
+ test("stopAuto closeout-transcript preservation suppresses generic stop widgets", async () => {
735
+ const base = mkdtempSync(join(tmpdir(), "gsd-closeout-transcript-stop-"));
736
+ const previousCwd = process.cwd();
737
+ const widgetCalls: Array<[string, unknown]> = [];
738
+ const notifications: string[] = [];
739
+
740
+ autoSession.reset();
741
+ autoSession.active = true;
742
+ autoSession.paused = false;
743
+ autoSession.basePath = base;
744
+ autoSession.originalBasePath = base;
745
+
746
+ try {
747
+ await stopAuto(
748
+ {
749
+ hasUI: true,
750
+ ui: {
751
+ setStatus: () => {},
752
+ setWidget: (key: string, value: unknown) => {
753
+ widgetCalls.push([key, value]);
754
+ },
755
+ setHeader: () => {},
756
+ notify: (message: string) => {
757
+ notifications.push(message);
758
+ },
759
+ },
760
+ modelRegistry: { find: () => null },
761
+ } as any,
762
+ { events: { emit: () => {} } } as any,
763
+ "Pre-merge dirty working tree overlaps milestone M003",
764
+ {
765
+ preserveCloseoutTranscript: true,
766
+ preserveCompletedMilestoneBranch: true,
767
+ },
768
+ );
769
+
770
+ assert.equal(
771
+ widgetCalls.some(([key]) => key === "gsd-outcome"),
772
+ false,
773
+ "closeout-preserving stop must not install a generic auto-stopped outcome",
774
+ );
775
+ assert.equal(
776
+ widgetCalls.some(([key, value]) => key === "gsd-progress" && value === undefined),
777
+ false,
778
+ "closeout-preserving stop must not clear the transcript/progress surface",
779
+ );
780
+ assert.equal(
781
+ notifications.some(message => message.includes("Auto-mode stopped")),
782
+ false,
783
+ "closeout-preserving stop must not append a duplicate terminal stop notification",
784
+ );
785
+ } finally {
786
+ try { closeDatabase(); } catch { /* noop */ }
787
+ autoSession.reset();
788
+ process.chdir(previousCwd);
789
+ rmSync(base, { recursive: true, force: true });
790
+ }
791
+ });
792
+
793
+ test("stopAuto foreground all-complete closeout preserves the transcript surface", async () => {
743
794
  const base = mkdtempSync(join(tmpdir(), "gsd-all-complete-closeout-"));
744
795
  const previousCwd = process.cwd();
745
796
  const widgetCalls: Array<[string, unknown]> = [];
@@ -789,17 +840,9 @@ test("stopAuto foreground all-complete closeout leaves a durable roll-up as the
789
840
 
790
841
  assert.equal(
791
842
  widgetCalls.some(([key, value]) => key === "gsd-progress" && typeof value === "function"),
792
- true,
793
- "foreground all-complete closeout must install a durable roll-up widget",
843
+ false,
844
+ "foreground all-complete closeout must not replace the visible transcript with a roll-up widget",
794
845
  );
795
- const finalProgress = widgetCalls.filter(([key]) => key === "gsd-progress").at(-1);
796
- assert.equal(typeof finalProgress?.[1], "function", "foreground all-complete closeout keeps the roll-up visible");
797
- const rollup = (finalProgress?.[1] as any)(
798
- { requestRender() {} },
799
- { fg: (_color: string, text: string) => text, bold: (text: string) => text },
800
- ).render(140).join("\n");
801
- assert.match(rollup, /All milestones complete/);
802
- assert.match(rollup, /Review the roll-up/);
803
846
  const finalOutcome = widgetCalls.filter(([key]) => key === "gsd-outcome").at(-1);
804
847
  assert.equal(finalOutcome?.[1], undefined, "foreground all-complete closeout must not add an outcome replacement");
805
848
  } finally {
@@ -43,5 +43,6 @@ test("complete milestone prompt renders compact verification and completion guid
43
43
  assert.match(prompt, /self-diff/i);
44
44
  assert.match(prompt, /GSD-(?:Task|Unit)/);
45
45
  assert.match(prompt, /Milestone M001 complete/);
46
+ assert.match(prompt, /Do not add a second final-status block/);
46
47
  assert.doesNotMatch(prompt, /\{\{[a-zA-Z][a-zA-Z0-9_]*\}\}/);
47
48
  });
@@ -17,7 +17,7 @@ interface CallLog {
17
17
  mergeCalls: number;
18
18
  postflightCalls: number;
19
19
  stopAutoCalls: Array<string | undefined>;
20
- stopAutoOptions: Array<{ preserveCompletedMilestoneBranch?: boolean } | undefined>;
20
+ stopAutoOptions: Array<{ preserveCompletedMilestoneBranch?: boolean; preserveCloseoutTranscript?: boolean } | undefined>;
21
21
  pauseAutoCalls: Array<string | undefined>;
22
22
  notifyCalls: Array<{ message: string; level: string }>;
23
23
  milestoneMergedInPhases: boolean;
@@ -104,7 +104,7 @@ function buildIc(opts: {
104
104
  _c?: unknown,
105
105
  _p?: unknown,
106
106
  reason?: string,
107
- options?: { preserveCompletedMilestoneBranch?: boolean },
107
+ options?: { preserveCompletedMilestoneBranch?: boolean; preserveCloseoutTranscript?: boolean },
108
108
  ) => {
109
109
  log.stopAutoCalls.push(reason);
110
110
  log.stopAutoOptions.push(options);
@@ -293,6 +293,30 @@ test("dirty overlap: preflight stops before merge and postflight restore", async
293
293
  );
294
294
  });
295
295
 
296
+ test("dirty overlap after closeout preserves the visible closeout transcript", async () => {
297
+ const { ic, log } = buildIc({
298
+ preflightResult: PREFLIGHT_BLOCKED,
299
+ mergeBehavior: "succeed",
300
+ postflightResult: POP_OK,
301
+ });
302
+
303
+ const result = await _runMilestoneMergeWithStashRestore(ic, "M002", {
304
+ preserveCloseoutTranscript: true,
305
+ });
306
+
307
+ assert.deepEqual(result, {
308
+ action: "break",
309
+ reason: "preflight-dirty-overlap",
310
+ });
311
+ assert.equal(log.mergeCalls, 0, "blocked preflight must not start milestone merge");
312
+ assert.equal(log.stopAutoOptions[0]?.preserveCompletedMilestoneBranch, true);
313
+ assert.equal(
314
+ log.stopAutoOptions[0]?.preserveCloseoutTranscript,
315
+ true,
316
+ "post-closeout merge blocks must not replace the closeout transcript with a stop widget",
317
+ );
318
+ });
319
+
296
320
  test("unmerged conflicts: preflight stops before merge and postflight restore", async () => {
297
321
  const { ic, log } = buildIc({
298
322
  preflightResult: PREFLIGHT_UNMERGED,
@@ -1 +0,0 @@
1
- (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[7177],{2022:()=>{},40275:(e,r,s)=>{Promise.resolve().then(s.t.bind(s,2022,23)),Promise.resolve().then(s.bind(s,66998)),Promise.resolve().then(s.bind(s,48333))},48333:(e,r,s)=>{"use strict";s.d(r,{Toaster:()=>a});var o=s(27552),t=s(52919),n=s(80413);let a=({...e})=>{let{theme:r="system"}=(0,t.D)();return(0,o.jsx)(n.l$,{theme:r,className:"toaster group",style:{"--normal-bg":"var(--popover)","--normal-text":"var(--popover-foreground)","--normal-border":"var(--border)"},...e})}},66998:(e,r,s)=>{"use strict";s.d(r,{ThemeProvider:()=>n});var o=s(27552);s(32140);var t=s(52919);function n({children:e,...r}){return(0,o.jsx)(t.N,{...r,children:e})}}},e=>{e.O(0,[789,963,806,4630,7358],()=>e(e.s=40275)),_N_E=e.O()}]);
@@ -1 +0,0 @@
1
- (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[7358],{6415:(e,s,n)=>{Promise.resolve().then(n.t.bind(n,1278,23)),Promise.resolve().then(n.t.bind(n,3591,23)),Promise.resolve().then(n.t.bind(n,99333,23)),Promise.resolve().then(n.t.bind(n,14382,23)),Promise.resolve().then(n.t.bind(n,15398,23)),Promise.resolve().then(n.t.bind(n,11948,23)),Promise.resolve().then(n.t.bind(n,61903,23)),Promise.resolve().then(n.bind(n,85742))},98156:()=>{}},e=>{var s=s=>e(e.s=s);e.O(0,[806,4630],()=>(s(74274),s(6415))),_N_E=e.O()}]);
@@ -1 +0,0 @@
1
- (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[9337],{88935:(e,s,_)=>{Promise.resolve().then(_.t.bind(_,1278,23))}},e=>{e.O(0,[806,4630,7358],()=>e(e.s=88935)),_N_E=e.O()}]);