gsd-pi 2.80.0-dev.cf9433f56 → 2.80.0-dev.d4fc28e6b

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 (237) hide show
  1. package/dist/cli.js +0 -19
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +29 -0
  4. package/dist/resources/extensions/gsd/auto/loop.js +71 -8
  5. package/dist/resources/extensions/gsd/auto/phases.js +150 -94
  6. package/dist/resources/extensions/gsd/auto/resolve.js +12 -0
  7. package/dist/resources/extensions/gsd/auto/run-unit.js +10 -30
  8. package/dist/resources/extensions/gsd/auto/session.js +8 -0
  9. package/dist/resources/extensions/gsd/auto/workflow-dispatch-claim.js +33 -1
  10. package/dist/resources/extensions/gsd/auto/workflow-worker-heartbeat.js +9 -1
  11. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +5 -32
  12. package/dist/resources/extensions/gsd/auto-dispatch.js +16 -0
  13. package/dist/resources/extensions/gsd/auto-post-unit.js +17 -4
  14. package/dist/resources/extensions/gsd/auto-prompts.js +90 -15
  15. package/dist/resources/extensions/gsd/auto-start.js +197 -6
  16. package/dist/resources/extensions/gsd/auto-worktree.js +111 -1
  17. package/dist/resources/extensions/gsd/auto.js +18 -22
  18. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +86 -19
  19. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +49 -36
  20. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +15 -5
  21. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +9 -3
  22. package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +7 -1
  23. package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +9 -3
  24. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +8 -2
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +298 -54
  26. package/dist/resources/extensions/gsd/bootstrap/system-context.js +82 -23
  27. package/dist/resources/extensions/gsd/clean-root-preflight.js +24 -6
  28. package/dist/resources/extensions/gsd/commands-handlers.js +23 -9
  29. package/dist/resources/extensions/gsd/db/unit-dispatches.js +53 -0
  30. package/dist/resources/extensions/gsd/ecosystem/gsd-extension-api.js +2 -0
  31. package/dist/resources/extensions/gsd/guided-flow.js +47 -28
  32. package/dist/resources/extensions/gsd/native-git-bridge.js +32 -8
  33. package/dist/resources/extensions/gsd/orphan-stash-audit.js +101 -0
  34. package/dist/resources/extensions/gsd/parallel-orchestrator.js +13 -3
  35. package/dist/resources/extensions/gsd/pre-execution-checks.js +15 -0
  36. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
  37. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  38. package/dist/resources/extensions/gsd/prompts/execute-task.md +4 -2
  39. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  40. package/dist/resources/extensions/gsd/prompts/replan-slice.md +2 -2
  41. package/dist/resources/extensions/gsd/workflow-protocol.js +131 -0
  42. package/dist/resources/extensions/gsd/worktree-resolver.js +35 -4
  43. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  44. package/dist/web/standalone/.next/BUILD_ID +1 -1
  45. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  46. package/dist/web/standalone/.next/build-manifest.json +2 -2
  47. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  48. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  49. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  57. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/index.html +1 -1
  65. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  72. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  73. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  74. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  75. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  76. package/dist/welcome-screen.d.ts +2 -0
  77. package/dist/welcome-screen.js +9 -7
  78. package/package.json +1 -1
  79. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  80. package/packages/pi-agent-core/dist/agent-loop.js +4 -1
  81. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  82. package/packages/pi-agent-core/dist/agent.d.ts +5 -0
  83. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  84. package/packages/pi-agent-core/dist/agent.js +2 -0
  85. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  86. package/packages/pi-agent-core/dist/index.d.ts +1 -0
  87. package/packages/pi-agent-core/dist/index.d.ts.map +1 -1
  88. package/packages/pi-agent-core/dist/index.js +2 -0
  89. package/packages/pi-agent-core/dist/index.js.map +1 -1
  90. package/packages/pi-agent-core/dist/token-audit.d.ts +47 -0
  91. package/packages/pi-agent-core/dist/token-audit.d.ts.map +1 -0
  92. package/packages/pi-agent-core/dist/token-audit.js +221 -0
  93. package/packages/pi-agent-core/dist/token-audit.js.map +1 -0
  94. package/packages/pi-agent-core/dist/types.d.ts +9 -0
  95. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  96. package/packages/pi-agent-core/dist/types.js.map +1 -1
  97. package/packages/pi-agent-core/src/agent-loop.test.ts +128 -0
  98. package/packages/pi-agent-core/src/agent-loop.ts +4 -1
  99. package/packages/pi-agent-core/src/agent.ts +8 -0
  100. package/packages/pi-agent-core/src/index.ts +2 -0
  101. package/packages/pi-agent-core/src/token-audit.test.ts +189 -0
  102. package/packages/pi-agent-core/src/token-audit.ts +287 -0
  103. package/packages/pi-agent-core/src/types.ts +14 -0
  104. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  105. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +18 -0
  106. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
  107. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +12 -0
  108. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  109. package/packages/pi-coding-agent/dist/core/agent-session.js +36 -7
  110. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  111. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  112. package/packages/pi-coding-agent/dist/core/extensions/loader.js +8 -0
  113. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  114. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -0
  115. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  116. package/packages/pi-coding-agent/dist/core/extensions/runner.js +3 -6
  117. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  118. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +3 -3
  119. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  120. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +32 -1
  121. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  122. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  123. package/packages/pi-coding-agent/dist/core/hooks-runner.test.js +2 -0
  124. package/packages/pi-coding-agent/dist/core/hooks-runner.test.js.map +1 -1
  125. package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.d.ts +2 -0
  126. package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.d.ts.map +1 -0
  127. package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.js +46 -0
  128. package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.js.map +1 -0
  129. package/packages/pi-coding-agent/dist/core/sdk.d.ts +10 -2
  130. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  131. package/packages/pi-coding-agent/dist/core/sdk.js +74 -2
  132. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  133. package/packages/pi-coding-agent/dist/core/skill-tool.test.js +22 -0
  134. package/packages/pi-coding-agent/dist/core/skill-tool.test.js.map +1 -1
  135. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +6 -7
  136. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  137. package/packages/pi-coding-agent/dist/core/system-prompt.js +2 -3
  138. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  139. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +25 -0
  140. package/packages/pi-coding-agent/src/core/agent-session.ts +40 -7
  141. package/packages/pi-coding-agent/src/core/extensions/loader.ts +10 -0
  142. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +3 -3
  143. package/packages/pi-coding-agent/src/core/extensions/runner.ts +5 -5
  144. package/packages/pi-coding-agent/src/core/extensions/types.ts +35 -1
  145. package/packages/pi-coding-agent/src/core/hooks-runner.test.ts +2 -0
  146. package/packages/pi-coding-agent/src/core/sdk-tool-filter.test.ts +60 -0
  147. package/packages/pi-coding-agent/src/core/sdk.ts +85 -3
  148. package/packages/pi-coding-agent/src/core/skill-tool.test.ts +28 -0
  149. package/packages/pi-coding-agent/src/core/system-prompt.ts +8 -10
  150. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  151. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +30 -0
  152. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +26 -0
  153. package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -2
  154. package/src/resources/extensions/gsd/auto/loop.ts +84 -8
  155. package/src/resources/extensions/gsd/auto/phases.ts +218 -154
  156. package/src/resources/extensions/gsd/auto/resolve.ts +19 -0
  157. package/src/resources/extensions/gsd/auto/run-unit.ts +10 -29
  158. package/src/resources/extensions/gsd/auto/session.ts +8 -0
  159. package/src/resources/extensions/gsd/auto/workflow-dispatch-claim.ts +63 -1
  160. package/src/resources/extensions/gsd/auto/workflow-worker-heartbeat.ts +14 -1
  161. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +8 -34
  162. package/src/resources/extensions/gsd/auto-dispatch.ts +16 -0
  163. package/src/resources/extensions/gsd/auto-post-unit.ts +18 -4
  164. package/src/resources/extensions/gsd/auto-prompts.ts +95 -14
  165. package/src/resources/extensions/gsd/auto-start.ts +230 -9
  166. package/src/resources/extensions/gsd/auto-worktree.ts +123 -0
  167. package/src/resources/extensions/gsd/auto.ts +18 -18
  168. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +100 -18
  169. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +50 -36
  170. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +16 -5
  171. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +10 -3
  172. package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +8 -1
  173. package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +10 -3
  174. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +9 -2
  175. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +347 -54
  176. package/src/resources/extensions/gsd/bootstrap/system-context.ts +90 -22
  177. package/src/resources/extensions/gsd/clean-root-preflight.ts +32 -7
  178. package/src/resources/extensions/gsd/commands-handlers.ts +34 -15
  179. package/src/resources/extensions/gsd/db/unit-dispatches.ts +66 -0
  180. package/src/resources/extensions/gsd/ecosystem/gsd-extension-api.ts +3 -0
  181. package/src/resources/extensions/gsd/guided-flow.ts +52 -35
  182. package/src/resources/extensions/gsd/native-git-bridge.ts +39 -6
  183. package/src/resources/extensions/gsd/orphan-stash-audit.ts +117 -0
  184. package/src/resources/extensions/gsd/parallel-orchestrator.ts +13 -3
  185. package/src/resources/extensions/gsd/pre-execution-checks.ts +16 -0
  186. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
  187. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  188. package/src/resources/extensions/gsd/prompts/execute-task.md +4 -2
  189. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  190. package/src/resources/extensions/gsd/prompts/replan-slice.md +2 -2
  191. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +2 -2
  192. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +361 -10
  193. package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +168 -6
  194. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +15 -6
  195. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +31 -0
  196. package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +3 -2
  197. package/src/resources/extensions/gsd/tests/context-store.test.ts +7 -1
  198. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +5 -1
  199. package/src/resources/extensions/gsd/tests/execute-task-rendering.test.ts +5 -2
  200. package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +219 -0
  201. package/src/resources/extensions/gsd/tests/finalize-survivor-branch.test.ts +132 -0
  202. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +6 -3
  203. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +5 -1
  204. package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +32 -0
  205. package/src/resources/extensions/gsd/tests/knowledge.test.ts +47 -0
  206. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +1 -0
  207. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +242 -0
  208. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +34 -2
  209. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +3 -0
  210. package/src/resources/extensions/gsd/tests/orphan-merge-bootstrap.test.ts +133 -0
  211. package/src/resources/extensions/gsd/tests/orphan-stash-audit.test.ts +201 -0
  212. package/src/resources/extensions/gsd/tests/parallel-orchestrator-fast-forward.test.ts +113 -0
  213. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +7 -5
  214. package/src/resources/extensions/gsd/tests/prompt-duplication-cuts.test.ts +230 -0
  215. package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +3 -3
  216. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +38 -17
  217. package/src/resources/extensions/gsd/tests/select-resumable-milestone.test.ts +96 -0
  218. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +77 -0
  219. package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +166 -0
  220. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -0
  221. package/src/resources/extensions/gsd/tests/system-context-memory.test.ts +112 -0
  222. package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +7 -9
  223. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +291 -0
  224. package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +50 -1
  225. package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +5 -4
  226. package/src/resources/extensions/gsd/tests/workflow-dispatch-claim.test.ts +142 -0
  227. package/src/resources/extensions/gsd/tests/workflow-protocol-excerpt.test.ts +99 -0
  228. package/src/resources/extensions/gsd/tests/workflow-worker-heartbeat.test.ts +32 -1
  229. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +1 -0
  230. package/src/resources/extensions/gsd/tests/worktree-path-injection.test.ts +22 -19
  231. package/src/resources/extensions/gsd/tests/worktree-project-root-degrade.test.ts +66 -0
  232. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +104 -3
  233. package/src/resources/extensions/gsd/workflow-protocol.ts +160 -0
  234. package/src/resources/extensions/gsd/worktree-resolver.ts +49 -4
  235. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +0 -97
  236. /package/dist/web/standalone/.next/static/{-5nHJWzSdG-WkPMul_khA → cWaxzf-sdbSSbbwYu8q7a}/_buildManifest.js +0 -0
  237. /package/dist/web/standalone/.next/static/{-5nHJWzSdG-WkPMul_khA → cWaxzf-sdbSSbbwYu8q7a}/_ssgManifest.js +0 -0
@@ -1,4 +1,9 @@
1
- import { join } from "node:path";
1
+ // Project/App: GSD-2
2
+ // File Purpose: Registers GSD extension runtime hooks and token-saving tool policies.
3
+
4
+ import { existsSync } from "node:fs";
5
+ import { dirname, join } from "node:path";
6
+ import { pathToFileURL } from "node:url";
2
7
 
3
8
  import type { ExtensionAPI, ExtensionContext } from "@gsd/pi-coding-agent";
4
9
  import { isToolCallEventType } from "@gsd/pi-coding-agent";
@@ -25,12 +30,245 @@ import { initNotificationWidget } from "../notification-widget.js";
25
30
  import { resolveWorktreeProjectRoot } from "../worktree-root.js";
26
31
  import { extractSubagentAgentClasses } from "./subagent-input.js";
27
32
  import { approvalGateIdForUnit, isExplicitApprovalResponse, shouldPauseForUserApprovalQuestion } from "../user-input-boundary.js";
33
+ import { resolveSkillManifest } from "../skill-manifest.js";
28
34
 
29
- // Skip the welcome screen on the very first session_start — cli.ts already
30
- // printed it before the TUI launched. Only re-print on /clear (subsequent sessions).
31
- let isFirstSession = true;
32
35
  let approvalQuestionAbortInFlight = false;
33
36
 
37
+ interface DeferredApprovalGate {
38
+ gateId: string;
39
+ basePath: string;
40
+ }
41
+
42
+ type WelcomeScreenModule = {
43
+ buildWelcomeScreenLines(opts: { version: string; remoteChannel?: string; width?: number }): string[];
44
+ };
45
+
46
+ async function loadWelcomeScreenModule(): Promise<WelcomeScreenModule | undefined> {
47
+ const candidates: string[] = [];
48
+ const gsdBinPath = process.env.GSD_BIN_PATH;
49
+ if (gsdBinPath) {
50
+ candidates.push(join(dirname(gsdBinPath), "welcome-screen.js"));
51
+ }
52
+
53
+ const packageRoot = process.env.GSD_PKG_ROOT;
54
+ if (packageRoot) {
55
+ candidates.push(join(packageRoot, "dist", "welcome-screen.js"));
56
+ candidates.push(join(packageRoot, "src", "welcome-screen.ts"));
57
+ }
58
+
59
+ for (const candidate of candidates) {
60
+ try {
61
+ if (!existsSync(candidate)) continue;
62
+ const mod = await import(pathToFileURL(candidate).href) as Partial<WelcomeScreenModule>;
63
+ if (typeof mod.buildWelcomeScreenLines === "function") {
64
+ return mod as WelcomeScreenModule;
65
+ }
66
+ } catch {
67
+ // Try the next package layout.
68
+ }
69
+ }
70
+ return undefined;
71
+ }
72
+
73
+ async function installWelcomeHeader(ctx: ExtensionContext): Promise<void> {
74
+ if (!ctx.hasUI || typeof ctx.ui?.setHeader !== "function") return;
75
+
76
+ try {
77
+ const welcome = await loadWelcomeScreenModule();
78
+ if (!welcome) return;
79
+
80
+ let remoteChannel: string | undefined;
81
+ try {
82
+ const { resolveRemoteConfig } = await import("../../remote-questions/config.js");
83
+ const rc = resolveRemoteConfig();
84
+ if (rc) remoteChannel = rc.channel;
85
+ } catch { /* non-fatal */ }
86
+
87
+ ctx.ui.setHeader(() => ({
88
+ render(width: number): string[] {
89
+ return welcome.buildWelcomeScreenLines({
90
+ version: process.env.GSD_VERSION || "0.0.0",
91
+ remoteChannel,
92
+ width,
93
+ });
94
+ },
95
+ invalidate(): void {},
96
+ }));
97
+ } catch {
98
+ /* non-fatal */
99
+ }
100
+ }
101
+
102
+ let deferredApprovalGate: DeferredApprovalGate | null = null;
103
+
104
+ export const MINIMAL_GSD_TOOL_NAMES = [
105
+ "gsd_exec",
106
+ "gsd_exec_search",
107
+ "gsd_resume",
108
+ "gsd_milestone_status",
109
+ "gsd_checkpoint_db",
110
+ "memory_query",
111
+ "capture_thought",
112
+ ] as const;
113
+
114
+ export const MINIMAL_AUTO_BASE_TOOL_NAMES = [
115
+ "ask_user_questions",
116
+ "bash",
117
+ "bg_shell",
118
+ "edit",
119
+ "glob",
120
+ "grep",
121
+ "ls",
122
+ "read",
123
+ "write",
124
+ ] as const;
125
+
126
+ const AUTO_UNIT_SCOPED_TOOLS: Record<string, readonly string[]> = {
127
+ "research-milestone": ["gsd_summary_save", "gsd_decision_save"],
128
+ "plan-milestone": ["gsd_plan_milestone", "gsd_decision_save", "gsd_requirement_update"],
129
+ "discuss-milestone": ["gsd_summary_save", "gsd_decision_save", "gsd_requirement_save"],
130
+ "validate-milestone": ["gsd_validate_milestone", "gsd_reassess_roadmap", "subagent"],
131
+ "complete-milestone": ["gsd_complete_milestone", "subagent"],
132
+ "research-slice": ["gsd_summary_save", "gsd_decision_save"],
133
+ "plan-slice": ["gsd_plan_slice", "gsd_plan_task", "gsd_decision_save"],
134
+ "refine-slice": ["gsd_plan_slice", "gsd_plan_task", "gsd_decision_save"],
135
+ "replan-slice": ["gsd_replan_slice", "gsd_plan_task", "gsd_decision_save"],
136
+ "complete-slice": ["gsd_slice_complete", "gsd_decision_save", "gsd_requirement_update", "subagent"],
137
+ "reassess-roadmap": ["gsd_reassess_roadmap"],
138
+ "execute-task": ["gsd_task_complete", "gsd_decision_save"],
139
+ "execute-task-simple": ["gsd_task_complete", "gsd_decision_save"],
140
+ "reactive-execute": ["gsd_task_complete", "gsd_decision_save"],
141
+ "run-uat": ["gsd_summary_save"],
142
+ "gate-evaluate": ["gsd_save_gate_result"],
143
+ "rewrite-docs": ["gsd_summary_save", "gsd_decision_save"],
144
+ "workflow-preferences": ["gsd_summary_save"],
145
+ "discuss-project": ["gsd_summary_save", "gsd_decision_save", "gsd_requirement_save"],
146
+ "discuss-requirements": ["gsd_requirement_save", "gsd_summary_save"],
147
+ "research-decision": ["gsd_summary_save"],
148
+ "research-project": ["gsd_summary_save", "gsd_decision_save"],
149
+ };
150
+
151
+ const WORKFLOW_GSD_TOOL_NAMES = [
152
+ ...MINIMAL_GSD_TOOL_NAMES,
153
+ ...Object.values(AUTO_UNIT_SCOPED_TOOLS).flat(),
154
+ ].filter(isGsdManagedTool);
155
+
156
+ function isGsdManagedTool(name: string): boolean {
157
+ return name.startsWith("gsd_") || name === "memory_query" || name === "capture_thought" || name === "gsd_graph";
158
+ }
159
+
160
+ export function buildMinimalGsdToolSet(activeToolNames: readonly string[]): string[] {
161
+ const active = new Set(activeToolNames);
162
+ const preserved = activeToolNames.filter((name) => !isGsdManagedTool(name));
163
+ const minimal = MINIMAL_GSD_TOOL_NAMES.filter((name) => active.has(name));
164
+ return [...new Set([...preserved, ...minimal])];
165
+ }
166
+
167
+ export function buildMinimalAutoGsdToolSet(
168
+ activeToolNames: readonly string[],
169
+ unitType: string | undefined,
170
+ ): string[] {
171
+ const active = new Set(activeToolNames);
172
+ const unitTools = unitType ? AUTO_UNIT_SCOPED_TOOLS[unitType] ?? [] : [];
173
+ const autoBaseTools = new Set<string>(MINIMAL_AUTO_BASE_TOOL_NAMES);
174
+ const preserved = activeToolNames.filter((name) => autoBaseTools.has(name));
175
+ const scoped = [...MINIMAL_GSD_TOOL_NAMES, ...unitTools].filter((name) => active.has(name));
176
+ return [...new Set([...preserved, ...scoped])];
177
+ }
178
+
179
+ export function buildMinimalGsdWorkflowToolSet(activeToolNames: readonly string[]): string[] {
180
+ const active = new Set(activeToolNames);
181
+ const autoBaseTools = new Set<string>(MINIMAL_AUTO_BASE_TOOL_NAMES);
182
+ const preserved = activeToolNames.filter((name) => autoBaseTools.has(name));
183
+ const scoped = WORKFLOW_GSD_TOOL_NAMES.filter((name) => active.has(name));
184
+ return [...new Set([...preserved, ...scoped])];
185
+ }
186
+
187
+ export function buildRequestScopedGsdToolSet(
188
+ activeToolNames: readonly string[],
189
+ requestCustomMessages: readonly { customType?: string }[] | undefined,
190
+ ): string[] | undefined {
191
+ for (let index = (requestCustomMessages?.length ?? 0) - 1; index >= 0; index--) {
192
+ const currentCustomType = requestCustomMessages?.[index]?.customType;
193
+ if (
194
+ currentCustomType === "gsd-run" ||
195
+ currentCustomType === "gsd-discuss" ||
196
+ currentCustomType === "gsd-doctor-heal" ||
197
+ currentCustomType === "gsd-triage"
198
+ ) {
199
+ return buildMinimalGsdWorkflowToolSet(activeToolNames);
200
+ }
201
+ }
202
+ return undefined;
203
+ }
204
+
205
+ export function isFullGsdToolSurfaceRequested(): boolean {
206
+ return process.env.PI_GSD_FULL_TOOLS === "1";
207
+ }
208
+
209
+ function isGeneralGsdToolScopingRequested(): boolean {
210
+ return process.env.PI_GSD_MINIMAL_TOOLS === "1";
211
+ }
212
+
213
+ export interface ScopedGsdWorkflowState {
214
+ tools: string[] | null;
215
+ visibleSkills: string[] | undefined;
216
+ restoreVisibleSkills: boolean;
217
+ }
218
+
219
+ type GsdWorkflowScopeApi = Pick<ExtensionAPI, "getActiveTools" | "setActiveTools"> & Partial<Pick<ExtensionAPI, "getVisibleSkills" | "setVisibleSkills">>;
220
+
221
+ function applyMinimalGsdToolSurface(pi: ExtensionAPI): void {
222
+ if (isFullGsdToolSurfaceRequested()) return;
223
+ const dash = getAutoRuntimeSnapshot();
224
+ if (dash.active && dash.currentUnit) {
225
+ pi.setActiveTools(buildMinimalAutoGsdToolSet(pi.getActiveTools(), dash.currentUnit.type));
226
+ return;
227
+ }
228
+ if (!isGeneralGsdToolScopingRequested()) return;
229
+ pi.setActiveTools(buildMinimalGsdToolSet(pi.getActiveTools()));
230
+ }
231
+
232
+ export function scopeGsdWorkflowToolsForDispatch(
233
+ pi: GsdWorkflowScopeApi,
234
+ unitType?: string,
235
+ ): ScopedGsdWorkflowState | null {
236
+ if (isFullGsdToolSurfaceRequested()) return null;
237
+ const current = pi.getActiveTools();
238
+ const scoped = unitType
239
+ ? buildMinimalAutoGsdToolSet(current, unitType)
240
+ : buildMinimalGsdWorkflowToolSet(current);
241
+ const toolsChanged = !(scoped.length === current.length && scoped.every((name, index) => name === current[index]));
242
+ const skillManifest = resolveSkillManifest(unitType);
243
+ const canScopeSkills = skillManifest !== null && pi.getVisibleSkills && pi.setVisibleSkills;
244
+ if (!toolsChanged && !canScopeSkills) {
245
+ return null;
246
+ }
247
+ if (toolsChanged) {
248
+ pi.setActiveTools(scoped);
249
+ }
250
+ const visibleSkills = canScopeSkills ? pi.getVisibleSkills!() : undefined;
251
+ if (canScopeSkills) {
252
+ pi.setVisibleSkills!(skillManifest);
253
+ }
254
+ return {
255
+ tools: toolsChanged ? current : null,
256
+ visibleSkills,
257
+ restoreVisibleSkills: Boolean(canScopeSkills),
258
+ };
259
+ }
260
+
261
+ export function restoreGsdWorkflowTools(
262
+ pi: Pick<ExtensionAPI, "setActiveTools"> & Partial<Pick<ExtensionAPI, "setVisibleSkills">>,
263
+ savedState: ScopedGsdWorkflowState | null,
264
+ ): void {
265
+ if (!savedState) return;
266
+ if (savedState.tools) pi.setActiveTools(savedState.tools);
267
+ if (savedState.restoreVisibleSkills && pi.setVisibleSkills) {
268
+ pi.setVisibleSkills(savedState.visibleSkills);
269
+ }
270
+ }
271
+
34
272
  async function deriveGsdState(basePath: string) {
35
273
  const { deriveState } = await import("../state.js");
36
274
  return deriveState(basePath);
@@ -85,12 +323,56 @@ async function applyCompactionThresholdOverride(ctx: ExtensionContext): Promise<
85
323
  }
86
324
  }
87
325
 
88
- export function resolveNotificationStoreBasePath(cwd: string = process.cwd()): string {
89
- return resolveWorktreeProjectRoot(cwd);
326
+ function clearDeferredApprovalGate(basePath?: string): void {
327
+ if (!basePath || deferredApprovalGate?.basePath === basePath) {
328
+ deferredApprovalGate = null;
329
+ }
330
+ }
331
+
332
+ function deferApprovalGate(gateId: string, basePath: string): void {
333
+ deferredApprovalGate = { gateId, basePath };
334
+ }
335
+
336
+ function contextBasePath(ctx?: { cwd?: string }): string {
337
+ return typeof ctx?.cwd === "string" ? ctx.cwd : process.cwd();
338
+ }
339
+
340
+ function activateDeferredApprovalGate(basePath: string): void {
341
+ if (deferredApprovalGate?.basePath !== basePath) return;
342
+ setPendingGate(deferredApprovalGate.gateId, basePath);
343
+ deferredApprovalGate = null;
344
+ }
345
+
346
+ function isContextDraftSummarySave(toolName: string, input: unknown): boolean {
347
+ if (toolName !== "gsd_summary_save" && toolName !== "summary_save") return false;
348
+ if (!input || typeof input !== "object") return false;
349
+ return (input as { artifact_type?: unknown }).artifact_type === "CONTEXT-DRAFT";
350
+ }
351
+
352
+ function shouldBlockDeferredApprovalTool(
353
+ toolName: string,
354
+ input: unknown,
355
+ basePath: string,
356
+ ): { block: boolean; reason?: string } {
357
+ if (deferredApprovalGate?.basePath !== basePath) return { block: false };
358
+ if (toolName === "ask_user_questions") return { block: false };
359
+ if (isContextDraftSummarySave(toolName, input)) return { block: false };
360
+ return {
361
+ block: true,
362
+ reason: [
363
+ `HARD BLOCK: Approval question "${deferredApprovalGate.gateId}" has been shown to the user.`,
364
+ `Only CONTEXT-DRAFT persistence may finish in this same assistant turn.`,
365
+ `Wait for the user's answer before calling additional tools.`,
366
+ ].join(" "),
367
+ };
368
+ }
369
+
370
+ export function resolveNotificationStoreBasePath(basePath: string): string {
371
+ return resolveWorktreeProjectRoot(basePath);
90
372
  }
91
373
 
92
374
  function initSessionNotifications(ctx: ExtensionContext): void {
93
- initNotificationStore(resolveNotificationStoreBasePath());
375
+ initNotificationStore(resolveNotificationStoreBasePath(contextBasePath(ctx)));
94
376
  installNotifyInterceptor(ctx);
95
377
  initNotificationWidget(ctx);
96
378
  }
@@ -132,53 +414,34 @@ export function registerHooks(
132
414
  ecosystemHandlers: GSDEcosystemBeforeAgentStartHandler[],
133
415
  ): void {
134
416
  pi.on("session_start", async (_event, ctx) => {
417
+ const basePath = contextBasePath(ctx);
135
418
  initSessionNotifications(ctx);
136
419
  if (!isAutoActive()) {
137
420
  const { initHealthWidget } = await import("../health-widget.js");
138
421
  initHealthWidget(ctx);
139
422
  }
140
- resetWriteGateState(process.cwd());
423
+ resetWriteGateState(basePath);
141
424
  resetToolCallLoopGuard();
142
425
  approvalQuestionAbortInFlight = false;
426
+ clearDeferredApprovalGate();
143
427
  await resetAskUserQuestionsTurnCache();
144
428
  await syncServiceTierStatus(ctx);
145
429
  await applyDisabledModelProviderPolicy(ctx);
146
430
  await applyCompactionThresholdOverride(ctx);
147
431
  // Skip MCP auto-prep when running inside an auto-worktree (see session_switch below).
148
432
  const { isInAutoWorktree } = await import("../auto-worktree.js");
149
- if (!isInAutoWorktree(process.cwd())) {
433
+ if (!isInAutoWorktree(basePath)) {
150
434
  const { prepareWorkflowMcpForProject } = await import("../workflow-mcp-auto-prep.js");
151
- prepareWorkflowMcpForProject(ctx, process.cwd());
435
+ prepareWorkflowMcpForProject(ctx, basePath);
152
436
  }
153
437
 
154
438
  // Apply show_token_cost preference (#1515)
155
439
  try {
156
440
  const { loadEffectiveGSDPreferences } = await import("../preferences.js");
157
- const prefs = loadEffectiveGSDPreferences();
441
+ const prefs = loadEffectiveGSDPreferences(basePath);
158
442
  process.env.GSD_SHOW_TOKEN_COST = prefs?.preferences.show_token_cost ? "1" : "";
159
443
  } catch { /* non-fatal */ }
160
- if (isFirstSession) {
161
- isFirstSession = false;
162
- } else {
163
- try {
164
- const gsdBinPath = process.env.GSD_BIN_PATH;
165
- if (gsdBinPath) {
166
- const { dirname } = await import("node:path");
167
- const { printWelcomeScreen } = await import(
168
- join(dirname(gsdBinPath), "welcome-screen.js")
169
- ) as { printWelcomeScreen: (opts: { version: string; modelName?: string; provider?: string; remoteChannel?: string }) => void };
170
-
171
- let remoteChannel: string | undefined;
172
- try {
173
- const { resolveRemoteConfig } = await import("../../remote-questions/config.js");
174
- const rc = resolveRemoteConfig();
175
- if (rc) remoteChannel = rc.channel;
176
- } catch { /* non-fatal */ }
177
-
178
- printWelcomeScreen({ version: process.env.GSD_VERSION || "0.0.0", remoteChannel });
179
- }
180
- } catch { /* non-fatal */ }
181
- }
444
+ await installWelcomeHeader(ctx);
182
445
  await loadToolApiKeysForSession();
183
446
  if (isAutoActive()) {
184
447
  ctx.ui.setWidget("gsd-health", undefined);
@@ -186,11 +449,13 @@ export function registerHooks(
186
449
  });
187
450
 
188
451
  pi.on("session_switch", async (_event, ctx) => {
452
+ const basePath = contextBasePath(ctx);
189
453
  initSessionNotifications(ctx);
190
- resetWriteGateState(process.cwd());
454
+ resetWriteGateState(basePath);
191
455
  resetToolCallLoopGuard();
456
+ clearDeferredApprovalGate();
192
457
  await resetAskUserQuestionsTurnCache();
193
- clearDiscussionFlowState(process.cwd());
458
+ clearDiscussionFlowState(basePath);
194
459
  await syncServiceTierStatus(ctx);
195
460
  await applyDisabledModelProviderPolicy(ctx);
196
461
  await applyCompactionThresholdOverride(ctx);
@@ -199,9 +464,9 @@ export function registerHooks(
199
464
  // post-chdir rewrites the file mid-run (non-idempotent due to cwd-relative
200
465
  // CLI path resolution), dirtying the tree and breaking the milestone merge.
201
466
  const { isInAutoWorktree } = await import("../auto-worktree.js");
202
- if (!isInAutoWorktree(process.cwd())) {
467
+ if (!isInAutoWorktree(basePath)) {
203
468
  const { prepareWorkflowMcpForProject } = await import("../workflow-mcp-auto-prep.js");
204
- prepareWorkflowMcpForProject(ctx, process.cwd());
469
+ prepareWorkflowMcpForProject(ctx, basePath);
205
470
  }
206
471
  await loadToolApiKeysForSession();
207
472
  if (!isAutoActive()) {
@@ -213,11 +478,13 @@ export function registerHooks(
213
478
  });
214
479
 
215
480
  pi.on("before_agent_start", async (event, ctx: ExtensionContext) => {
481
+ applyMinimalGsdToolSurface(pi);
482
+
216
483
  // Wait for ecosystem loader to finish (no-op after first turn).
217
484
  const { getEcosystemReadyPromise } = await import("../ecosystem/loader.js");
218
485
  await getEcosystemReadyPromise();
219
486
 
220
- const beforeAgentBasePath = process.cwd();
487
+ const beforeAgentBasePath = contextBasePath(ctx);
221
488
  const pendingApprovalGate = getPendingGate(beforeAgentBasePath);
222
489
  if (pendingApprovalGate && isExplicitApprovalResponse(event.prompt, pendingApprovalGate)) {
223
490
  markApprovalGateVerified(pendingApprovalGate, beforeAgentBasePath);
@@ -225,6 +492,7 @@ export function registerHooks(
225
492
  if (milestoneId) markDepthVerified(milestoneId, beforeAgentBasePath);
226
493
  clearPendingGate(beforeAgentBasePath);
227
494
  }
495
+ clearDeferredApprovalGate(beforeAgentBasePath);
228
496
 
229
497
  // GSD's own context injection (existing behavior — unchanged).
230
498
  const { buildBeforeAgentStartResult } = await import("./system-context.js");
@@ -233,7 +501,7 @@ export function registerHooks(
233
501
  // Refresh the snapshot used by ecosystem getPhase()/getActiveUnit().
234
502
  // deriveState has its own ~100ms cache so this is cheap on repeat calls.
235
503
  try {
236
- const state = await deriveGsdState(process.cwd());
504
+ const state = await deriveGsdState(beforeAgentBasePath);
237
505
  updateSnapshot(state);
238
506
  } catch {
239
507
  updateSnapshot(null);
@@ -275,7 +543,11 @@ export function registerHooks(
275
543
  resetToolCallLoopGuard();
276
544
  await resetAskUserQuestionsTurnCache();
277
545
  const { handleAgentEnd } = await import("./agent-end-recovery.js");
278
- await handleAgentEnd(pi, event, ctx);
546
+ try {
547
+ await handleAgentEnd(pi, event, ctx);
548
+ } finally {
549
+ activateDeferredApprovalGate(contextBasePath(ctx));
550
+ }
279
551
  });
280
552
 
281
553
  // Squash-merge quick-task branch back to the original branch after the
@@ -290,8 +562,8 @@ export function registerHooks(
290
562
  }
291
563
  });
292
564
 
293
- pi.on("session_before_compact", async () => {
294
- const basePath = process.cwd();
565
+ pi.on("session_before_compact", async (_event, ctx) => {
566
+ const basePath = contextBasePath(ctx);
295
567
  // Context Mode is default-on. Write the resumable snapshot before any
296
568
  // active-auto cancel return so auto sessions still leave re-entry context.
297
569
  await writeContextModeCompactionSnapshot(basePath);
@@ -358,7 +630,7 @@ export function registerHooks(
358
630
  if (!unitType) {
359
631
  try {
360
632
  const { getPendingDeepProjectSetupUnitForContext } = await import("../guided-flow.js");
361
- const pending = getPendingDeepProjectSetupUnitForContext(ctx, process.cwd());
633
+ const pending = getPendingDeepProjectSetupUnitForContext(ctx, contextBasePath(ctx));
362
634
  unitType = pending?.unitType;
363
635
  unitId = pending?.unitId;
364
636
  } catch {
@@ -367,7 +639,7 @@ export function registerHooks(
367
639
  }
368
640
 
369
641
  if (!unitType) {
370
- const milestoneId = await getDiscussionMilestoneIdFor(process.cwd());
642
+ const milestoneId = await getDiscussionMilestoneIdFor(contextBasePath(ctx));
371
643
  if (milestoneId) {
372
644
  unitType = "discuss-milestone";
373
645
  unitId = milestoneId;
@@ -377,15 +649,16 @@ export function registerHooks(
377
649
  if (!shouldPauseForUserApprovalQuestion(unitType, [event.message])) return;
378
650
 
379
651
  const gateId = approvalGateIdForUnit(unitType, unitId);
380
- if (gateId) setPendingGate(gateId, process.cwd());
652
+ if (gateId) deferApprovalGate(gateId, contextBasePath(ctx));
381
653
 
382
654
  approvalQuestionAbortInFlight = true;
383
655
  ctx.ui.notify(
384
656
  `${unitType}${unitId ? ` ${unitId}` : ""} is waiting for your approval - pausing before more tool calls run.`,
385
657
  "info",
386
658
  );
387
- // The pending gate set above blocks subsequent non-read-only tool calls
388
- // via the tool_call hook below, so we do not abort the in-flight stream.
659
+ // The durable pending gate is activated at agent_end so same-turn
660
+ // CONTEXT-DRAFT persistence can finish after the text boundary streams.
661
+ // The tool_call hook below still blocks non-draft tools in this turn.
389
662
  // Aborting mid-stream eats the model's question text on external CLI
390
663
  // providers (Claude Code SDK) because lastTextContent isn't populated
391
664
  // from in-flight builder state — the user only ever sees "Claude Code
@@ -396,7 +669,7 @@ export function registerHooks(
396
669
  const { isParallelActive, shutdownParallel } = await import("../parallel-orchestrator.js");
397
670
  if (isParallelActive()) {
398
671
  try {
399
- await shutdownParallel(process.cwd());
672
+ await shutdownParallel(contextBasePath(ctx));
400
673
  } catch {
401
674
  // best-effort
402
675
  }
@@ -408,8 +681,8 @@ export function registerHooks(
408
681
  }
409
682
  });
410
683
 
411
- pi.on("tool_call", async (event) => {
412
- const discussionBasePath = process.cwd();
684
+ pi.on("tool_call", async (event, ctx) => {
685
+ const discussionBasePath = contextBasePath(ctx);
413
686
  const toolName = canonicalToolName(event.toolName);
414
687
  // ── Loop guard: block repeated identical tool calls ──
415
688
  const loopCheck = checkToolCallLoop(toolName, event.input as Record<string, unknown>);
@@ -417,6 +690,13 @@ export function registerHooks(
417
690
  return { block: true, reason: loopCheck.reason };
418
691
  }
419
692
 
693
+ const deferredGateGuard = shouldBlockDeferredApprovalTool(
694
+ toolName,
695
+ event.input,
696
+ discussionBasePath,
697
+ );
698
+ if (deferredGateGuard.block) return deferredGateGuard;
699
+
420
700
  // ── Discussion gate enforcement: track pending gate questions ─────────
421
701
  // Only gate-shaped ask_user_questions calls should block execution.
422
702
  // The gate stays pending until the user selects the approval option.
@@ -586,7 +866,7 @@ export function registerHooks(
586
866
  }
587
867
  });
588
868
 
589
- pi.on("tool_result", async (event) => {
869
+ pi.on("tool_result", async (event, ctx) => {
590
870
  if (isAutoActive() && typeof event.toolCallId === "string") {
591
871
  markToolEnd(event.toolCallId);
592
872
  }
@@ -605,7 +885,7 @@ export function registerHooks(
605
885
  }
606
886
  const toolName = canonicalToolName(event.toolName);
607
887
  if (toolName !== "ask_user_questions") return;
608
- const basePath = process.cwd();
888
+ const basePath = contextBasePath(ctx);
609
889
  const milestoneId = await getDiscussionMilestoneIdFor(basePath);
610
890
  const queueActive = isQueuePhaseActive(basePath);
611
891
 
@@ -811,8 +1091,21 @@ export function registerHooks(
811
1091
  // Tool set adaptation hook (ADR-005 Phase 4)
812
1092
  // Extensions can override tool set after model selection by returning { toolNames: [...] }
813
1093
  // Return undefined to let the built-in provider compatibility filtering proceed.
814
- pi.on("adjust_tool_set", async (_event) => {
815
- // Default: no override — let provider capability filtering handle tool set
1094
+ pi.on("adjust_tool_set", async (event) => {
1095
+ if (isFullGsdToolSurfaceRequested()) return undefined;
1096
+ const removed = new Set(event.filteredTools);
1097
+ const providerCompatible = event.activeToolNames.filter((name) => !removed.has(name));
1098
+ const requestScoped = buildRequestScopedGsdToolSet(providerCompatible, event.requestCustomMessages);
1099
+ if (requestScoped) {
1100
+ return { toolNames: requestScoped };
1101
+ }
1102
+ const dash = getAutoRuntimeSnapshot();
1103
+ if (dash.active && dash.currentUnit) {
1104
+ return { toolNames: buildMinimalAutoGsdToolSet(providerCompatible, dash.currentUnit.type) };
1105
+ }
1106
+ if (isGeneralGsdToolScopingRequested()) {
1107
+ return { toolNames: buildMinimalGsdToolSet(providerCompatible) };
1108
+ }
816
1109
  return undefined;
817
1110
  });
818
1111
  }