gsd-pi 2.70.0 → 2.70.1-dev.bef631a

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 (233) hide show
  1. package/dist/loader.js +4 -0
  2. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +152 -2
  3. package/dist/resources/extensions/gsd/auto-model-selection.js +33 -19
  4. package/dist/resources/extensions/gsd/auto-prompts.js +7 -3
  5. package/dist/resources/extensions/gsd/auto-start.js +28 -12
  6. package/dist/resources/extensions/gsd/auto.js +12 -8
  7. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +4 -0
  8. package/dist/resources/extensions/gsd/commands-handlers.js +22 -8
  9. package/dist/resources/extensions/gsd/doctor-engine-checks.js +12 -0
  10. package/dist/resources/extensions/gsd/doctor-format.js +2 -0
  11. package/dist/resources/extensions/gsd/guided-flow.js +33 -20
  12. package/dist/resources/extensions/gsd/init-wizard.js +3 -11
  13. package/dist/resources/extensions/gsd/pre-execution-checks.js +5 -3
  14. package/dist/resources/extensions/gsd/prompts/discuss.md +31 -13
  15. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +34 -0
  16. package/dist/resources/extensions/gsd/validate-directory.js +30 -12
  17. package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +56 -0
  18. package/dist/resources/extensions/gsd/workflow-mcp.js +12 -1
  19. package/dist/resources/extensions/slash-commands/audit.js +2 -1
  20. package/dist/resources/extensions/subagent/isolation.js +4 -2
  21. package/dist/update-check.d.ts +1 -0
  22. package/dist/update-check.js +30 -27
  23. package/dist/update-cmd.js +3 -11
  24. package/dist/web/standalone/.next/BUILD_ID +1 -1
  25. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  26. package/dist/web/standalone/.next/build-manifest.json +4 -4
  27. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  28. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  29. package/dist/web/standalone/.next/required-server-files.json +4 -4
  30. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  31. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  32. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  33. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  36. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  41. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  42. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  43. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  44. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  45. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  47. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  51. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  52. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  53. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  54. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  55. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  56. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  57. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  58. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  59. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  60. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  61. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  62. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  63. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  64. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  65. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  66. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  67. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  68. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  69. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  72. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  73. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  74. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  89. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  99. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  105. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  121. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +2 -2
  125. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/index.html +1 -1
  135. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  136. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  137. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  138. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  139. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  140. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  141. package/dist/web/standalone/.next/server/app/page.js +2 -2
  142. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  144. package/dist/web/standalone/.next/server/chunks/63.js +3 -3
  145. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  146. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/middleware.js +2 -2
  149. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  151. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  152. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  153. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  154. package/dist/web/standalone/.next/static/chunks/2826.dd3dc8bbd3025fa5.js +9 -0
  155. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  156. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  157. package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +1 -0
  158. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  159. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  160. package/dist/web/standalone/.next/static/chunks/{webpack-6e4d7e9a4f57bed4.js → webpack-b868033a5834586d.js} +1 -1
  161. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  162. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  163. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  164. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  165. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  166. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  167. package/dist/web/standalone/server.js +1 -1
  168. package/dist/web-mode.js +4 -0
  169. package/package.json +11 -11
  170. package/packages/mcp-server/dist/workflow-tools.d.ts +2 -0
  171. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  172. package/packages/mcp-server/dist/workflow-tools.js +35 -3
  173. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  174. package/packages/mcp-server/src/import-candidates.test.ts +48 -0
  175. package/packages/mcp-server/src/workflow-tools.ts +34 -1
  176. package/packages/pi-agent-core/dist/agent.d.ts +8 -0
  177. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  178. package/packages/pi-agent-core/dist/agent.js +3 -0
  179. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  180. package/packages/pi-agent-core/src/agent.test.ts +82 -0
  181. package/packages/pi-agent-core/src/agent.ts +12 -0
  182. package/packages/pi-coding-agent/dist/core/lsp/config.d.ts +1 -0
  183. package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
  184. package/packages/pi-coding-agent/dist/core/lsp/config.js +38 -15
  185. package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
  186. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  187. package/packages/pi-coding-agent/dist/core/sdk.js +10 -0
  188. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  189. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  190. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +3 -1
  191. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  192. package/packages/pi-coding-agent/package.json +1 -1
  193. package/packages/pi-coding-agent/src/core/lsp/config.ts +43 -17
  194. package/packages/pi-coding-agent/src/core/sdk.ts +8 -0
  195. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +7 -5
  196. package/pkg/package.json +1 -1
  197. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +229 -2
  198. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +205 -0
  199. package/src/resources/extensions/gsd/auto-model-selection.ts +39 -25
  200. package/src/resources/extensions/gsd/auto-prompts.ts +7 -3
  201. package/src/resources/extensions/gsd/auto-start.ts +37 -14
  202. package/src/resources/extensions/gsd/auto.ts +12 -8
  203. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +4 -0
  204. package/src/resources/extensions/gsd/commands-handlers.ts +22 -7
  205. package/src/resources/extensions/gsd/doctor-engine-checks.ts +14 -0
  206. package/src/resources/extensions/gsd/doctor-format.ts +1 -0
  207. package/src/resources/extensions/gsd/doctor-types.ts +1 -0
  208. package/src/resources/extensions/gsd/guided-flow.ts +36 -17
  209. package/src/resources/extensions/gsd/init-wizard.ts +3 -13
  210. package/src/resources/extensions/gsd/pre-execution-checks.ts +6 -3
  211. package/src/resources/extensions/gsd/prompts/discuss.md +31 -13
  212. package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +9 -0
  213. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +43 -0
  214. package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +207 -0
  215. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +48 -1
  216. package/src/resources/extensions/gsd/tests/resource-loader-import-path.test.ts +8 -7
  217. package/src/resources/extensions/gsd/tests/validate-directory.test.ts +33 -1
  218. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +87 -1
  219. package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +76 -0
  220. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +180 -1
  221. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +22 -0
  222. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +60 -25
  223. package/src/resources/extensions/gsd/validate-directory.ts +33 -11
  224. package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +76 -0
  225. package/src/resources/extensions/gsd/workflow-mcp.ts +16 -1
  226. package/src/resources/extensions/slash-commands/audit.ts +2 -1
  227. package/src/resources/extensions/subagent/isolation.ts +4 -3
  228. package/dist/web/standalone/.next/static/chunks/2826.821e01b07d92e948.js +0 -9
  229. package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +0 -1
  230. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  231. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  232. /package/dist/web/standalone/.next/static/{Nl6lg7zP5dNgNBV1107v1 → UlX0WGGZ8aBPN0uSZ5Ki4}/_buildManifest.js +0 -0
  233. /package/dist/web/standalone/.next/static/{Nl6lg7zP5dNgNBV1107v1 → UlX0WGGZ8aBPN0uSZ5Ki4}/_ssgManifest.js +0 -0
@@ -6,6 +6,7 @@ import { tmpdir } from "node:os";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
8
8
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
9
+ import { ElicitRequestSchema } from "@modelcontextprotocol/sdk/types.js";
9
10
 
10
11
  import {
11
12
  buildWorkflowMcpServers,
@@ -13,16 +14,27 @@ import {
13
14
  getWorkflowTransportSupportError,
14
15
  getRequiredWorkflowToolsForAutoUnit,
15
16
  getRequiredWorkflowToolsForGuidedUnit,
17
+ supportsStructuredQuestions,
16
18
  usesWorkflowMcpTransport,
17
19
  } from "../workflow-mcp.ts";
18
20
 
19
21
  const __dirname = dirname(fileURLToPath(import.meta.url));
20
22
  const gsdDir = join(__dirname, "..");
21
23
 
24
+ type ElicitPayload = {
25
+ message: string;
26
+ requestedSchema: { properties: Record<string, unknown>; required?: string[] };
27
+ };
28
+
22
29
  function readSrc(file: string): string {
23
30
  return readFileSync(join(gsdDir, file), "utf-8");
24
31
  }
25
32
 
33
+ function extractElicitPayload(request: unknown): ElicitPayload {
34
+ const payload = (request as { params?: unknown }).params ?? request;
35
+ return payload as ElicitPayload;
36
+ }
37
+
26
38
  test("guided execute-task requires canonical task completion tool", () => {
27
39
  assert.deepEqual(getRequiredWorkflowToolsForGuidedUnit("execute-task"), ["gsd_task_complete"]);
28
40
  });
@@ -184,7 +196,26 @@ test("workflow MCP launch config reaches mutation tools over stdio", async () =>
184
196
  assert.match(launch.env?.NODE_OPTIONS ?? "", /resolve-ts\.mjs/);
185
197
  }
186
198
 
187
- const client = new Client({ name: "workflow-mcp-transport-test", version: "1.0.0" });
199
+ const client = new Client(
200
+ { name: "workflow-mcp-transport-test", version: "1.0.0" },
201
+ { capabilities: { elicitation: {} } },
202
+ );
203
+ client.setRequestHandler(ElicitRequestSchema, async (request) => {
204
+ const elicitation = extractElicitPayload(request as unknown);
205
+
206
+ assert.match(elicitation.message, /Please answer the following question/);
207
+ assert.ok(elicitation.requestedSchema.properties.transport_mode);
208
+ assert.ok(elicitation.requestedSchema.properties["transport_mode__note"]);
209
+ assert.ok(elicitation.requestedSchema.required?.includes("transport_mode"));
210
+
211
+ return {
212
+ action: "accept",
213
+ content: {
214
+ transport_mode: "None of the above",
215
+ transport_mode__note: "Need Windows-safe MCP elicitation.",
216
+ },
217
+ };
218
+ });
188
219
  const transport = new StdioClientTransport({
189
220
  command: launch.command,
190
221
  args: launch.args,
@@ -206,6 +237,38 @@ test("workflow MCP launch config reaches mutation tools over stdio", async () =>
206
237
  "expected workflow MCP surface to expose ask_user_questions",
207
238
  );
208
239
 
240
+ const askResult = await client.callTool(
241
+ {
242
+ name: "ask_user_questions",
243
+ arguments: {
244
+ questions: [
245
+ {
246
+ id: "transport_mode",
247
+ header: "Transport",
248
+ question: "How should the workflow prompt be delivered?",
249
+ options: [
250
+ { label: "Local UI", description: "Use the host tool UI." },
251
+ { label: "Remote UI", description: "Use a remote response channel." },
252
+ ],
253
+ },
254
+ ],
255
+ },
256
+ },
257
+ undefined,
258
+ { timeout: 30_000 },
259
+ );
260
+ assert.equal(askResult.isError, undefined);
261
+ assert.equal(
262
+ ((askResult.content as Array<{ text?: string }>)?.[0])?.text ?? "",
263
+ JSON.stringify({
264
+ answers: {
265
+ transport_mode: {
266
+ answers: ["None of the above", "user_note: Need Windows-safe MCP elicitation."],
267
+ },
268
+ },
269
+ }),
270
+ );
271
+
209
272
  const milestoneResult = await client.callTool(
210
273
  {
211
274
  name: "gsd_plan_milestone",
@@ -285,12 +348,123 @@ test("workflow MCP launch config reaches mutation tools over stdio", async () =>
285
348
  }
286
349
  });
287
350
 
351
+ test("workflow MCP ask_user_questions uses stdio elicitation round-trip", async () => {
352
+ const projectRoot = mkdtempSync(join(tmpdir(), "gsd-workflow-elicit-"));
353
+ mkdirSync(join(projectRoot, ".gsd"), { recursive: true });
354
+
355
+ const launch = detectWorkflowMcpLaunchConfig(projectRoot, {});
356
+ assert.ok(launch, "expected a workflow MCP launch config");
357
+
358
+ const client = new Client(
359
+ { name: "workflow-mcp-elicit-test", version: "1.0.0" },
360
+ { capabilities: { elicitation: {} } },
361
+ );
362
+ let requestSeen: {
363
+ message: string;
364
+ requestedSchema: { properties: Record<string, unknown>; required?: string[] };
365
+ } | null = null;
366
+
367
+ client.setRequestHandler(ElicitRequestSchema, async (request) => {
368
+ const params = extractElicitPayload(request as unknown);
369
+
370
+ requestSeen = params;
371
+
372
+ return {
373
+ action: "accept",
374
+ content: {
375
+ deployment: "None of the above",
376
+ deployment__note: "Need hybrid deployment.",
377
+ },
378
+ };
379
+ });
380
+
381
+ const transport = new StdioClientTransport({
382
+ command: launch.command,
383
+ args: launch.args,
384
+ env: { ...process.env, ...launch.env } as Record<string, string>,
385
+ cwd: launch.cwd,
386
+ stderr: "pipe",
387
+ });
388
+
389
+ try {
390
+ await client.connect(transport, { timeout: 30_000 });
391
+
392
+ const result = await client.callTool(
393
+ {
394
+ name: "ask_user_questions",
395
+ arguments: {
396
+ questions: [
397
+ {
398
+ id: "deployment",
399
+ header: "Deploy",
400
+ question: "Where will this run?",
401
+ options: [
402
+ { label: "Cloud", description: "Managed hosting." },
403
+ { label: "On-prem", description: "Runs in customer infrastructure." },
404
+ ],
405
+ },
406
+ ],
407
+ },
408
+ },
409
+ undefined,
410
+ { timeout: 30_000 },
411
+ );
412
+
413
+ assert.ok(requestSeen, "expected stdio transport to forward an elicitation request");
414
+ const seen = requestSeen as ElicitPayload;
415
+ assert.match(seen.message, /Please answer the following question/);
416
+ assert.ok(seen.requestedSchema.properties.deployment);
417
+ assert.ok(seen.requestedSchema.properties.deployment__note);
418
+ assert.ok(seen.requestedSchema.required?.includes("deployment"));
419
+
420
+ const content = (result as { content: Array<{ type: string; text?: string }> }).content;
421
+ const text = content.find((item: { type: string; text?: string }) => item.type === "text");
422
+ assert.ok(text && "text" in text);
423
+ assert.equal(
424
+ text.text,
425
+ JSON.stringify({
426
+ answers: {
427
+ deployment: {
428
+ answers: ["None of the above", "user_note: Need hybrid deployment."],
429
+ },
430
+ },
431
+ }),
432
+ );
433
+ } finally {
434
+ await client.close();
435
+ }
436
+ });
437
+
288
438
  test("usesWorkflowMcpTransport matches local externalCli providers", () => {
289
439
  assert.equal(usesWorkflowMcpTransport("externalCli", "local://claude-code"), true);
290
440
  assert.equal(usesWorkflowMcpTransport("externalCli", "https://api.example.com"), false);
291
441
  assert.equal(usesWorkflowMcpTransport("oauth", "local://custom"), false);
292
442
  });
293
443
 
444
+ test("supportsStructuredQuestions disables structured ask flow on workflow MCP transports", () => {
445
+ assert.equal(
446
+ supportsStructuredQuestions(["ask_user_questions"], {
447
+ authMode: "externalCli",
448
+ baseUrl: "local://claude-code",
449
+ }),
450
+ false,
451
+ );
452
+ assert.equal(
453
+ supportsStructuredQuestions(["ask_user_questions"], {
454
+ authMode: "oauth",
455
+ baseUrl: "https://api.anthropic.com",
456
+ }),
457
+ true,
458
+ );
459
+ assert.equal(
460
+ supportsStructuredQuestions([], {
461
+ authMode: "oauth",
462
+ baseUrl: "https://api.anthropic.com",
463
+ }),
464
+ false,
465
+ );
466
+ });
467
+
294
468
  test("transport compatibility passes when required tools fit current MCP surface", () => {
295
469
  const error = getWorkflowTransportSupportError(
296
470
  "claude-code",
@@ -514,3 +688,8 @@ test("auto phases source enforces workflow compatibility preflight", () => {
514
688
  assert.match(src, /getWorkflowTransportSupportError/);
515
689
  assert.match(src, /workflow-capability/);
516
690
  });
691
+
692
+ test("workflow transport error guidance includes /gsd mcp init hint", () => {
693
+ const src = readSrc("workflow-mcp.ts");
694
+ assert.match(src, /Please run \/gsd mcp init \./);
695
+ });
@@ -256,6 +256,28 @@ test("executePlanSlice writes task planning state and rendered plan artifacts",
256
256
  }
257
257
  });
258
258
 
259
+ test("executePlanSlice marks validation failures with isError", async () => {
260
+ const base = makeTmpBase();
261
+ try {
262
+ openTestDb(base);
263
+
264
+ const result = await inProjectDir(base, () => executePlanSlice({
265
+ milestoneId: "M001",
266
+ sliceId: "S01",
267
+ goal: "Trigger validation failure for empty tasks.",
268
+ tasks: [],
269
+ }, base));
270
+
271
+ assert.equal(result.isError, true);
272
+ assert.equal(result.details.operation, "plan_slice");
273
+ assert.match(String(result.details.error), /validation failed: tasks must be a non-empty array/);
274
+ assert.match(result.content[0].text, /Error planning slice:/);
275
+ } finally {
276
+ closeDatabase();
277
+ cleanup(base);
278
+ }
279
+ });
280
+
259
281
  test("executeSliceComplete coerces string enrichment entries and writes summary/UAT artifacts", async () => {
260
282
  const base = makeTmpBase();
261
283
  try {
@@ -38,6 +38,7 @@ export function isSupportedSummaryArtifactType(
38
38
  export interface ToolExecutionResult {
39
39
  content: Array<{ type: "text"; text: string }>;
40
40
  details: Record<string, unknown>;
41
+ isError?: boolean;
41
42
  }
42
43
 
43
44
  export interface SummarySaveParams {
@@ -57,13 +58,15 @@ export async function executeSummarySave(
57
58
  return {
58
59
  content: [{ type: "text", text: "Error: GSD database is not available. Cannot save artifact." }],
59
60
  details: { operation: "save_summary", error: "db_unavailable" },
60
- };
61
+ isError: true,
62
+ };
61
63
  }
62
64
  if (!isSupportedSummaryArtifactType(params.artifact_type)) {
63
65
  return {
64
66
  content: [{ type: "text", text: `Error: Invalid artifact_type "${params.artifact_type}". Must be one of: ${SUPPORTED_SUMMARY_ARTIFACT_TYPES.join(", ")}` }],
65
67
  details: { operation: "save_summary", error: "invalid_artifact_type" },
66
- };
68
+ isError: true,
69
+ };
67
70
  }
68
71
  const contextGuard = shouldBlockContextArtifactSaveInSnapshot(
69
72
  loadWriteGateSnapshot(basePath),
@@ -75,7 +78,8 @@ export async function executeSummarySave(
75
78
  return {
76
79
  content: [{ type: "text", text: `Error saving artifact: ${contextGuard.reason ?? "context write blocked"}` }],
77
80
  details: { operation: "save_summary", error: "context_write_blocked" },
78
- };
81
+ isError: true,
82
+ };
79
83
  }
80
84
  try {
81
85
  let relativePath: string;
@@ -108,7 +112,8 @@ export async function executeSummarySave(
108
112
  return {
109
113
  content: [{ type: "text", text: `Error saving artifact: ${msg}` }],
110
114
  details: { operation: "save_summary", error: msg },
111
- };
115
+ isError: true,
116
+ };
112
117
  }
113
118
  }
114
119
 
@@ -163,7 +168,8 @@ export async function executeTaskComplete(
163
168
  return {
164
169
  content: [{ type: "text", text: "Error: GSD database is not available. Cannot complete task." }],
165
170
  details: { operation: "complete_task", error: "db_unavailable" },
166
- };
171
+ isError: true,
172
+ };
167
173
  }
168
174
  try {
169
175
  const coerced = { ...params };
@@ -176,6 +182,7 @@ export async function executeTaskComplete(
176
182
  return {
177
183
  content: [{ type: "text", text: `Error completing task: ${result.error}` }],
178
184
  details: { operation: "complete_task", error: result.error },
185
+ isError: true,
179
186
  };
180
187
  }
181
188
  return {
@@ -194,7 +201,8 @@ export async function executeTaskComplete(
194
201
  return {
195
202
  content: [{ type: "text", text: `Error completing task: ${msg}` }],
196
203
  details: { operation: "complete_task", error: msg },
197
- };
204
+ isError: true,
205
+ };
198
206
  }
199
207
  }
200
208
 
@@ -207,7 +215,8 @@ export async function executeSliceComplete(
207
215
  return {
208
216
  content: [{ type: "text", text: "Error: GSD database is not available. Cannot complete slice." }],
209
217
  details: { operation: "complete_slice", error: "db_unavailable" },
210
- };
218
+ isError: true,
219
+ };
211
220
  }
212
221
  try {
213
222
  const splitPair = (s: string): [string, string] => {
@@ -257,6 +266,7 @@ export async function executeSliceComplete(
257
266
  return {
258
267
  content: [{ type: "text", text: `Error completing slice: ${result.error}` }],
259
268
  details: { operation: "complete_slice", error: result.error },
269
+ isError: true,
260
270
  };
261
271
  }
262
272
  return {
@@ -275,7 +285,8 @@ export async function executeSliceComplete(
275
285
  return {
276
286
  content: [{ type: "text", text: `Error completing slice: ${msg}` }],
277
287
  details: { operation: "complete_slice", error: msg },
278
- };
288
+ isError: true,
289
+ };
279
290
  }
280
291
  }
281
292
 
@@ -288,7 +299,8 @@ export async function executeCompleteMilestone(
288
299
  return {
289
300
  content: [{ type: "text", text: "Error: GSD database is not available. Cannot complete milestone." }],
290
301
  details: { operation: "complete_milestone", error: "db_unavailable" },
291
- };
302
+ isError: true,
303
+ };
292
304
  }
293
305
  try {
294
306
  const sanitized = sanitizeCompleteMilestoneParams(params);
@@ -297,6 +309,7 @@ export async function executeCompleteMilestone(
297
309
  return {
298
310
  content: [{ type: "text", text: `Error completing milestone: ${result.error}` }],
299
311
  details: { operation: "complete_milestone", error: result.error },
312
+ isError: true,
300
313
  };
301
314
  }
302
315
  return {
@@ -313,7 +326,8 @@ export async function executeCompleteMilestone(
313
326
  return {
314
327
  content: [{ type: "text", text: `Error completing milestone: ${msg}` }],
315
328
  details: { operation: "complete_milestone", error: msg },
316
- };
329
+ isError: true,
330
+ };
317
331
  }
318
332
  }
319
333
 
@@ -326,7 +340,8 @@ export async function executeValidateMilestone(
326
340
  return {
327
341
  content: [{ type: "text", text: "Error: GSD database is not available. Cannot validate milestone." }],
328
342
  details: { operation: "validate_milestone", error: "db_unavailable" },
329
- };
343
+ isError: true,
344
+ };
330
345
  }
331
346
  try {
332
347
  const result = await handleValidateMilestone(params, basePath);
@@ -334,6 +349,7 @@ export async function executeValidateMilestone(
334
349
  return {
335
350
  content: [{ type: "text", text: `Error validating milestone: ${result.error}` }],
336
351
  details: { operation: "validate_milestone", error: result.error },
352
+ isError: true,
337
353
  };
338
354
  }
339
355
  return {
@@ -351,7 +367,8 @@ export async function executeValidateMilestone(
351
367
  return {
352
368
  content: [{ type: "text", text: `Error validating milestone: ${msg}` }],
353
369
  details: { operation: "validate_milestone", error: msg },
354
- };
370
+ isError: true,
371
+ };
355
372
  }
356
373
  }
357
374
 
@@ -364,7 +381,8 @@ export async function executeReassessRoadmap(
364
381
  return {
365
382
  content: [{ type: "text", text: "Error: GSD database is not available. Cannot reassess roadmap." }],
366
383
  details: { operation: "reassess_roadmap", error: "db_unavailable" },
367
- };
384
+ isError: true,
385
+ };
368
386
  }
369
387
  try {
370
388
  const result = await handleReassessRoadmap(params, basePath);
@@ -372,6 +390,7 @@ export async function executeReassessRoadmap(
372
390
  return {
373
391
  content: [{ type: "text", text: `Error reassessing roadmap: ${result.error}` }],
374
392
  details: { operation: "reassess_roadmap", error: result.error },
393
+ isError: true,
375
394
  };
376
395
  }
377
396
  return {
@@ -390,7 +409,8 @@ export async function executeReassessRoadmap(
390
409
  return {
391
410
  content: [{ type: "text", text: `Error reassessing roadmap: ${msg}` }],
392
411
  details: { operation: "reassess_roadmap", error: msg },
393
- };
412
+ isError: true,
413
+ };
394
414
  }
395
415
  }
396
416
 
@@ -403,7 +423,8 @@ export async function executeSaveGateResult(
403
423
  return {
404
424
  content: [{ type: "text", text: "Error: GSD database is not available." }],
405
425
  details: { operation: "save_gate_result", error: "db_unavailable" },
406
- };
426
+ isError: true,
427
+ };
407
428
  }
408
429
 
409
430
  const validGates = ["Q3", "Q4", "Q5", "Q6", "Q7", "Q8"];
@@ -411,7 +432,8 @@ export async function executeSaveGateResult(
411
432
  return {
412
433
  content: [{ type: "text", text: `Error: Invalid gateId "${params.gateId}". Must be one of: ${validGates.join(", ")}` }],
413
434
  details: { operation: "save_gate_result", error: "invalid_gate_id" },
414
- };
435
+ isError: true,
436
+ };
415
437
  }
416
438
 
417
439
  const validVerdicts = ["pass", "flag", "omitted"];
@@ -419,7 +441,8 @@ export async function executeSaveGateResult(
419
441
  return {
420
442
  content: [{ type: "text", text: `Error: Invalid verdict "${params.verdict}". Must be one of: ${validVerdicts.join(", ")}` }],
421
443
  details: { operation: "save_gate_result", error: "invalid_verdict" },
422
- };
444
+ isError: true,
445
+ };
423
446
  }
424
447
 
425
448
  try {
@@ -443,7 +466,8 @@ export async function executeSaveGateResult(
443
466
  return {
444
467
  content: [{ type: "text", text: `Error saving gate result: ${msg}` }],
445
468
  details: { operation: "save_gate_result", error: msg },
446
- };
469
+ isError: true,
470
+ };
447
471
  }
448
472
  }
449
473
 
@@ -456,7 +480,8 @@ export async function executePlanMilestone(
456
480
  return {
457
481
  content: [{ type: "text", text: "Error: GSD database is not available. Cannot plan milestone." }],
458
482
  details: { operation: "plan_milestone", error: "db_unavailable" },
459
- };
483
+ isError: true,
484
+ };
460
485
  }
461
486
  try {
462
487
  const result = await handlePlanMilestone(params, basePath);
@@ -464,6 +489,7 @@ export async function executePlanMilestone(
464
489
  return {
465
490
  content: [{ type: "text", text: `Error planning milestone: ${result.error}` }],
466
491
  details: { operation: "plan_milestone", error: result.error },
492
+ isError: true,
467
493
  };
468
494
  }
469
495
  return {
@@ -480,7 +506,8 @@ export async function executePlanMilestone(
480
506
  return {
481
507
  content: [{ type: "text", text: `Error planning milestone: ${msg}` }],
482
508
  details: { operation: "plan_milestone", error: msg },
483
- };
509
+ isError: true,
510
+ };
484
511
  }
485
512
  }
486
513
 
@@ -493,7 +520,8 @@ export async function executePlanSlice(
493
520
  return {
494
521
  content: [{ type: "text", text: "Error: GSD database is not available. Cannot plan slice." }],
495
522
  details: { operation: "plan_slice", error: "db_unavailable" },
496
- };
523
+ isError: true,
524
+ };
497
525
  }
498
526
  try {
499
527
  const result = await handlePlanSlice(params, basePath);
@@ -501,6 +529,7 @@ export async function executePlanSlice(
501
529
  return {
502
530
  content: [{ type: "text", text: `Error planning slice: ${result.error}` }],
503
531
  details: { operation: "plan_slice", error: result.error },
532
+ isError: true,
504
533
  };
505
534
  }
506
535
  return {
@@ -519,7 +548,8 @@ export async function executePlanSlice(
519
548
  return {
520
549
  content: [{ type: "text", text: `Error planning slice: ${msg}` }],
521
550
  details: { operation: "plan_slice", error: msg },
522
- };
551
+ isError: true,
552
+ };
523
553
  }
524
554
  }
525
555
 
@@ -532,7 +562,8 @@ export async function executeReplanSlice(
532
562
  return {
533
563
  content: [{ type: "text", text: "Error: GSD database is not available. Cannot replan slice." }],
534
564
  details: { operation: "replan_slice", error: "db_unavailable" },
535
- };
565
+ isError: true,
566
+ };
536
567
  }
537
568
  try {
538
569
  const result = await handleReplanSlice(params, basePath);
@@ -540,6 +571,7 @@ export async function executeReplanSlice(
540
571
  return {
541
572
  content: [{ type: "text", text: `Error replanning slice: ${result.error}` }],
542
573
  details: { operation: "replan_slice", error: result.error },
574
+ isError: true,
543
575
  };
544
576
  }
545
577
  return {
@@ -558,7 +590,8 @@ export async function executeReplanSlice(
558
590
  return {
559
591
  content: [{ type: "text", text: `Error replanning slice: ${msg}` }],
560
592
  details: { operation: "replan_slice", error: msg },
561
- };
593
+ isError: true,
594
+ };
562
595
  }
563
596
  }
564
597
 
@@ -576,6 +609,7 @@ export async function executeMilestoneStatus(
576
609
  return {
577
610
  content: [{ type: "text", text: "Error: GSD database is not available." }],
578
611
  details: { operation: "milestone_status", error: "db_unavailable" },
612
+ isError: true,
579
613
  };
580
614
  }
581
615
 
@@ -624,6 +658,7 @@ export async function executeMilestoneStatus(
624
658
  return {
625
659
  content: [{ type: "text", text: `Error querying milestone status: ${msg}` }],
626
660
  details: { operation: "milestone_status", error: msg },
627
- };
661
+ isError: true,
662
+ };
628
663
  }
629
664
  }
@@ -61,6 +61,33 @@ const WINDOWS_BLOCKED_PATHS = new Set([
61
61
  "C:\\Program Files (x86)",
62
62
  ]);
63
63
 
64
+ const WINDOWS_BLOCKED_SUFFIXES = new Set([
65
+ "\\",
66
+ "\\windows",
67
+ "\\windows\\system32",
68
+ "\\program files",
69
+ "\\program files (x86)",
70
+ ]);
71
+
72
+ function normalizePathForComparison(dirPath: string): string {
73
+ let normalized = dirPath.replace(/[/\\]+$/, "");
74
+ if (normalized === "") {
75
+ normalized = "/";
76
+ } else if (/^[A-Za-z]:$/.test(normalized)) {
77
+ normalized += "\\";
78
+ }
79
+ return platform() === "win32" ? normalized.toLowerCase() : normalized;
80
+ }
81
+
82
+ function isBlockedWindowsPath(normalized: string): boolean {
83
+ if (!/^[a-z]:\\/.test(normalized)) {
84
+ return false;
85
+ }
86
+
87
+ const suffix = normalized.slice(2);
88
+ return WINDOWS_BLOCKED_SUFFIXES.has(suffix);
89
+ }
90
+
64
91
  // ─── Core Validation ────────────────────────────────────────────────────────────
65
92
 
66
93
  /**
@@ -84,16 +111,11 @@ export function validateDirectory(dirPath: string): DirectoryValidationResult {
84
111
 
85
112
  // Normalize trailing slashes for consistent comparison.
86
113
  // Special cases: "/" → "/" (not ""), "C:\" → "C:\" (not "C:")
87
- let normalized = resolved.replace(/[/\\]+$/, "");
88
- if (normalized === "") {
89
- normalized = "/";
90
- } else if (/^[A-Za-z]:$/.test(normalized)) {
91
- normalized = normalized + "\\";
92
- }
114
+ const normalized = normalizePathForComparison(resolved);
93
115
 
94
116
  // ── Check 1: Blocked system paths ──────────────────────────────────────
95
117
  const blockedPaths = platform() === "win32" ? WINDOWS_BLOCKED_PATHS : UNIX_BLOCKED_PATHS;
96
- if (blockedPaths.has(normalized)) {
118
+ if (platform() === "win32" ? isBlockedWindowsPath(normalized) : blockedPaths.has(normalized)) {
97
119
  return {
98
120
  safe: false,
99
121
  severity: "blocked",
@@ -104,9 +126,9 @@ export function validateDirectory(dirPath: string): DirectoryValidationResult {
104
126
  // ── Check 2: Home directory itself (not subdirs) ───────────────────────
105
127
  let resolvedHome: string;
106
128
  try {
107
- resolvedHome = realpathSync(resolve(homedir())).replace(/[/\\]+$/, "");
129
+ resolvedHome = normalizePathForComparison(realpathSync(resolve(homedir())));
108
130
  } catch {
109
- resolvedHome = resolve(homedir()).replace(/[/\\]+$/, "");
131
+ resolvedHome = normalizePathForComparison(resolve(homedir()));
110
132
  }
111
133
 
112
134
  if (normalized === resolvedHome) {
@@ -120,9 +142,9 @@ export function validateDirectory(dirPath: string): DirectoryValidationResult {
120
142
  // ── Check 3: Temp directory root ───────────────────────────────────────
121
143
  let resolvedTmp: string;
122
144
  try {
123
- resolvedTmp = realpathSync(resolve(tmpdir())).replace(/[/\\]+$/, "");
145
+ resolvedTmp = normalizePathForComparison(realpathSync(resolve(tmpdir())));
124
146
  } catch {
125
- resolvedTmp = resolve(tmpdir()).replace(/[/\\]+$/, "");
147
+ resolvedTmp = normalizePathForComparison(resolve(tmpdir()));
126
148
  }
127
149
 
128
150
  if (normalized === resolvedTmp) {