gsd-pi 2.67.0-dev.1cd1e0f → 2.67.0-dev.2367d7e

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 (257) hide show
  1. package/README.md +1 -1
  2. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +155 -70
  3. package/dist/resources/extensions/gsd/auto/phases.js +17 -0
  4. package/dist/resources/extensions/gsd/auto/session.js +10 -0
  5. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +12 -0
  6. package/dist/resources/extensions/gsd/auto-dispatch.js +1 -1
  7. package/dist/resources/extensions/gsd/auto-start.js +16 -30
  8. package/dist/resources/extensions/gsd/auto-worktree.js +62 -15
  9. package/dist/resources/extensions/gsd/auto.js +121 -59
  10. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +11 -435
  11. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +1 -4
  12. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +7 -64
  13. package/dist/resources/extensions/gsd/bootstrap/system-context.js +7 -2
  14. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +88 -8
  15. package/dist/resources/extensions/gsd/commands/catalog.js +2 -1
  16. package/dist/resources/extensions/gsd/commands/handlers/core.js +39 -25
  17. package/dist/resources/extensions/gsd/commands/index.js +8 -1
  18. package/dist/resources/extensions/gsd/commands-mcp-status.js +43 -7
  19. package/dist/resources/extensions/gsd/doctor-git-checks.js +4 -4
  20. package/dist/resources/extensions/gsd/doctor-proactive.js +3 -3
  21. package/dist/resources/extensions/gsd/doctor.js +8 -4
  22. package/dist/resources/extensions/gsd/gsd-db.js +11 -0
  23. package/dist/resources/extensions/gsd/guided-flow.js +56 -31
  24. package/dist/resources/extensions/gsd/init-wizard.js +37 -0
  25. package/dist/resources/extensions/gsd/interrupted-session.js +146 -0
  26. package/dist/resources/extensions/gsd/mcp-project-config.js +83 -0
  27. package/dist/resources/extensions/gsd/state.js +7 -2
  28. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +508 -0
  29. package/dist/resources/extensions/gsd/workflow-logger.js +18 -3
  30. package/dist/resources/extensions/gsd/workflow-mcp.js +261 -0
  31. package/dist/web/standalone/.next/BUILD_ID +1 -1
  32. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  33. package/dist/web/standalone/.next/build-manifest.json +3 -3
  34. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  35. package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
  36. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  37. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  38. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  46. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  47. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/index.html +1 -1
  55. package/dist/web/standalone/.next/server/app/index.rsc +2 -2
  56. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  57. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
  58. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  62. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  63. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  64. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  65. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  66. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  67. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  68. package/dist/web/standalone/.next/static/chunks/2826.821e01b07d92e948.js +9 -0
  69. package/dist/web/standalone/.next/static/chunks/app/{page-0c485498795110d6.js → page-f1e30ab6bb269149.js} +1 -1
  70. package/dist/web/standalone/.next/static/chunks/{webpack-b49b09f97429b5d0.js → webpack-6e4d7e9a4f57bed4.js} +1 -1
  71. package/package.json +4 -2
  72. package/packages/mcp-server/README.md +38 -0
  73. package/packages/mcp-server/dist/cli.d.ts +9 -0
  74. package/packages/mcp-server/dist/cli.d.ts.map +1 -0
  75. package/packages/mcp-server/dist/cli.js +58 -0
  76. package/packages/mcp-server/dist/cli.js.map +1 -0
  77. package/packages/mcp-server/dist/index.d.ts +20 -0
  78. package/packages/mcp-server/dist/index.d.ts.map +1 -0
  79. package/packages/mcp-server/dist/index.js +14 -0
  80. package/packages/mcp-server/dist/index.js.map +1 -0
  81. package/packages/mcp-server/dist/readers/captures.d.ts +25 -0
  82. package/packages/mcp-server/dist/readers/captures.d.ts.map +1 -0
  83. package/packages/mcp-server/dist/readers/captures.js +67 -0
  84. package/packages/mcp-server/dist/readers/captures.js.map +1 -0
  85. package/packages/mcp-server/dist/readers/doctor-lite.d.ts +20 -0
  86. package/packages/mcp-server/dist/readers/doctor-lite.d.ts.map +1 -0
  87. package/packages/mcp-server/dist/readers/doctor-lite.js +173 -0
  88. package/packages/mcp-server/dist/readers/doctor-lite.js.map +1 -0
  89. package/packages/mcp-server/dist/readers/index.d.ts +14 -0
  90. package/packages/mcp-server/dist/readers/index.d.ts.map +1 -0
  91. package/packages/mcp-server/dist/readers/index.js +10 -0
  92. package/packages/mcp-server/dist/readers/index.js.map +1 -0
  93. package/packages/mcp-server/dist/readers/knowledge.d.ts +18 -0
  94. package/packages/mcp-server/dist/readers/knowledge.d.ts.map +1 -0
  95. package/packages/mcp-server/dist/readers/knowledge.js +82 -0
  96. package/packages/mcp-server/dist/readers/knowledge.js.map +1 -0
  97. package/packages/mcp-server/dist/readers/metrics.d.ts +32 -0
  98. package/packages/mcp-server/dist/readers/metrics.d.ts.map +1 -0
  99. package/packages/mcp-server/dist/readers/metrics.js +74 -0
  100. package/packages/mcp-server/dist/readers/metrics.js.map +1 -0
  101. package/packages/mcp-server/dist/readers/paths.d.ts +42 -0
  102. package/packages/mcp-server/dist/readers/paths.d.ts.map +1 -0
  103. package/packages/mcp-server/dist/readers/paths.js +199 -0
  104. package/packages/mcp-server/dist/readers/paths.js.map +1 -0
  105. package/packages/mcp-server/dist/readers/roadmap.d.ts +26 -0
  106. package/packages/mcp-server/dist/readers/roadmap.d.ts.map +1 -0
  107. package/packages/mcp-server/dist/readers/roadmap.js +194 -0
  108. package/packages/mcp-server/dist/readers/roadmap.js.map +1 -0
  109. package/packages/mcp-server/dist/readers/state.d.ts +43 -0
  110. package/packages/mcp-server/dist/readers/state.d.ts.map +1 -0
  111. package/packages/mcp-server/dist/readers/state.js +184 -0
  112. package/packages/mcp-server/dist/readers/state.js.map +1 -0
  113. package/packages/mcp-server/dist/server.d.ts +28 -0
  114. package/packages/mcp-server/dist/server.d.ts.map +1 -0
  115. package/packages/mcp-server/dist/server.js +319 -0
  116. package/packages/mcp-server/dist/server.js.map +1 -0
  117. package/packages/mcp-server/dist/session-manager.d.ts +54 -0
  118. package/packages/mcp-server/dist/session-manager.d.ts.map +1 -0
  119. package/packages/mcp-server/dist/session-manager.js +284 -0
  120. package/packages/mcp-server/dist/session-manager.js.map +1 -0
  121. package/packages/mcp-server/dist/types.d.ts +61 -0
  122. package/packages/mcp-server/dist/types.d.ts.map +1 -0
  123. package/packages/mcp-server/dist/types.js +11 -0
  124. package/packages/mcp-server/dist/types.js.map +1 -0
  125. package/packages/mcp-server/dist/workflow-tools.d.ts +9 -0
  126. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -0
  127. package/packages/mcp-server/dist/workflow-tools.js +532 -0
  128. package/packages/mcp-server/dist/workflow-tools.js.map +1 -0
  129. package/packages/mcp-server/src/server.ts +6 -2
  130. package/packages/mcp-server/src/workflow-tools.test.ts +976 -0
  131. package/packages/mcp-server/src/workflow-tools.ts +997 -0
  132. package/packages/mcp-server/tsconfig.json +1 -1
  133. package/packages/pi-agent-core/dist/agent-loop.js +14 -6
  134. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  135. package/packages/pi-agent-core/src/agent-loop.test.ts +53 -0
  136. package/packages/pi-agent-core/src/agent-loop.ts +20 -6
  137. package/packages/pi-coding-agent/dist/core/contextual-tips.d.ts +43 -0
  138. package/packages/pi-coding-agent/dist/core/contextual-tips.d.ts.map +1 -0
  139. package/packages/pi-coding-agent/dist/core/contextual-tips.js +208 -0
  140. package/packages/pi-coding-agent/dist/core/contextual-tips.js.map +1 -0
  141. package/packages/pi-coding-agent/dist/core/contextual-tips.test.d.ts +2 -0
  142. package/packages/pi-coding-agent/dist/core/contextual-tips.test.d.ts.map +1 -0
  143. package/packages/pi-coding-agent/dist/core/contextual-tips.test.js +227 -0
  144. package/packages/pi-coding-agent/dist/core/contextual-tips.test.js.map +1 -0
  145. package/packages/pi-coding-agent/dist/core/index.d.ts +1 -0
  146. package/packages/pi-coding-agent/dist/core/index.d.ts.map +1 -1
  147. package/packages/pi-coding-agent/dist/core/index.js +1 -0
  148. package/packages/pi-coding-agent/dist/core/index.js.map +1 -1
  149. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.d.ts +2 -0
  150. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.d.ts.map +1 -0
  151. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +28 -0
  152. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -0
  153. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  154. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  155. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +17 -12
  156. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  157. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  158. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +19 -0
  159. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  160. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts +4 -0
  161. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  162. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +14 -0
  163. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  164. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
  165. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  166. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +15 -12
  167. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  168. package/packages/pi-coding-agent/src/core/contextual-tips.test.ts +259 -0
  169. package/packages/pi-coding-agent/src/core/contextual-tips.ts +232 -0
  170. package/packages/pi-coding-agent/src/core/index.ts +2 -0
  171. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +54 -0
  172. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +18 -12
  173. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +21 -0
  174. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +19 -0
  175. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +19 -15
  176. package/packages/rpc-client/dist/index.d.ts +10 -0
  177. package/packages/rpc-client/dist/index.d.ts.map +1 -0
  178. package/packages/rpc-client/dist/index.js +9 -0
  179. package/packages/rpc-client/dist/index.js.map +1 -0
  180. package/packages/rpc-client/dist/jsonl.d.ts +17 -0
  181. package/packages/rpc-client/dist/jsonl.d.ts.map +1 -0
  182. package/packages/rpc-client/dist/jsonl.js +54 -0
  183. package/packages/rpc-client/dist/jsonl.js.map +1 -0
  184. package/packages/rpc-client/dist/rpc-client.d.ts +259 -0
  185. package/packages/rpc-client/dist/rpc-client.d.ts.map +1 -0
  186. package/packages/rpc-client/dist/rpc-client.js +541 -0
  187. package/packages/rpc-client/dist/rpc-client.js.map +1 -0
  188. package/packages/rpc-client/dist/rpc-client.test.d.ts +2 -0
  189. package/packages/rpc-client/dist/rpc-client.test.d.ts.map +1 -0
  190. package/packages/rpc-client/dist/rpc-client.test.js +477 -0
  191. package/packages/rpc-client/dist/rpc-client.test.js.map +1 -0
  192. package/packages/rpc-client/dist/rpc-types.d.ts +566 -0
  193. package/packages/rpc-client/dist/rpc-types.d.ts.map +1 -0
  194. package/packages/rpc-client/dist/rpc-types.js +12 -0
  195. package/packages/rpc-client/dist/rpc-types.js.map +1 -0
  196. package/scripts/ensure-workspace-builds.cjs +2 -0
  197. package/scripts/link-workspace-packages.cjs +21 -14
  198. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +193 -93
  199. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +173 -79
  200. package/src/resources/extensions/gsd/auto/phases.ts +25 -0
  201. package/src/resources/extensions/gsd/auto/session.ts +10 -0
  202. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +20 -0
  203. package/src/resources/extensions/gsd/auto-dispatch.ts +1 -1
  204. package/src/resources/extensions/gsd/auto-start.ts +23 -55
  205. package/src/resources/extensions/gsd/auto-worktree.ts +59 -15
  206. package/src/resources/extensions/gsd/auto.ts +133 -64
  207. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +22 -435
  208. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +1 -5
  209. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +7 -72
  210. package/src/resources/extensions/gsd/bootstrap/system-context.ts +8 -2
  211. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +122 -6
  212. package/src/resources/extensions/gsd/commands/catalog.ts +2 -1
  213. package/src/resources/extensions/gsd/commands/handlers/core.ts +53 -26
  214. package/src/resources/extensions/gsd/commands/index.ts +7 -1
  215. package/src/resources/extensions/gsd/commands-mcp-status.ts +53 -7
  216. package/src/resources/extensions/gsd/doctor-git-checks.ts +4 -4
  217. package/src/resources/extensions/gsd/doctor-proactive.ts +3 -3
  218. package/src/resources/extensions/gsd/doctor.ts +9 -5
  219. package/src/resources/extensions/gsd/gsd-db.ts +12 -0
  220. package/src/resources/extensions/gsd/guided-flow.ts +66 -36
  221. package/src/resources/extensions/gsd/init-wizard.ts +40 -0
  222. package/src/resources/extensions/gsd/interrupted-session.ts +224 -0
  223. package/src/resources/extensions/gsd/mcp-project-config.ts +128 -0
  224. package/src/resources/extensions/gsd/state.ts +7 -1
  225. package/src/resources/extensions/gsd/tests/auto-project-root-env.test.ts +29 -0
  226. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +668 -2
  227. package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +14 -4
  228. package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +21 -0
  229. package/src/resources/extensions/gsd/tests/core-overlay-fallback.test.ts +101 -0
  230. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +380 -2
  231. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +66 -0
  232. package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +30 -0
  233. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +12 -0
  234. package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +2 -2
  235. package/src/resources/extensions/gsd/tests/integration/doctor-fixlevel.test.ts +52 -1
  236. package/src/resources/extensions/gsd/tests/integration/doctor-git.test.ts +2 -9
  237. package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +0 -33
  238. package/src/resources/extensions/gsd/tests/integration/merge-cwd-restore.test.ts +169 -0
  239. package/src/resources/extensions/gsd/tests/interrupted-session-auto.test.ts +146 -0
  240. package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +136 -0
  241. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +85 -0
  242. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +15 -0
  243. package/src/resources/extensions/gsd/tests/verification-operational-gate.test.ts +11 -0
  244. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +16 -0
  245. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +500 -0
  246. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +625 -0
  247. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +629 -0
  248. package/src/resources/extensions/gsd/workflow-logger.ts +19 -3
  249. package/src/resources/extensions/gsd/workflow-mcp.ts +320 -0
  250. package/dist/web/standalone/.next/static/chunks/6502.b804e48b7919f55e.js +0 -9
  251. package/packages/pi-coding-agent/dist/modes/interactive/provider-auth-setup.d.ts +0 -13
  252. package/packages/pi-coding-agent/dist/modes/interactive/provider-auth-setup.d.ts.map +0 -1
  253. package/packages/pi-coding-agent/dist/modes/interactive/provider-auth-setup.js +0 -27
  254. package/packages/pi-coding-agent/dist/modes/interactive/provider-auth-setup.js.map +0 -1
  255. package/packages/pi-coding-agent/src/modes/interactive/provider-auth-setup.ts +0 -40
  256. /package/dist/web/standalone/.next/static/{PHqEommYRR8CRn3i84CGM → WMDT_0C0XDkBKtsAI_AX4}/_buildManifest.js +0 -0
  257. /package/dist/web/standalone/.next/static/{PHqEommYRR8CRn3i84CGM → WMDT_0C0XDkBKtsAI_AX4}/_ssgManifest.js +0 -0
@@ -0,0 +1,85 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { tmpdir } from "node:os";
6
+
7
+ import {
8
+ ensureProjectWorkflowMcpConfig,
9
+ GSD_WORKFLOW_MCP_SERVER_NAME,
10
+ } from "../mcp-project-config.ts";
11
+
12
+ test("ensureProjectWorkflowMcpConfig creates .mcp.json with the workflow server", () => {
13
+ const projectRoot = mkdtempSync(join(tmpdir(), "gsd-mcp-init-"));
14
+ mkdirSync(join(projectRoot, ".gsd"), { recursive: true });
15
+
16
+ try {
17
+ const result = ensureProjectWorkflowMcpConfig(projectRoot);
18
+ assert.equal(result.status, "created");
19
+ assert.equal(existsSync(result.configPath), true);
20
+
21
+ const parsed = JSON.parse(readFileSync(result.configPath, "utf-8")) as {
22
+ mcpServers?: Record<string, { command?: string; args?: string[]; env?: Record<string, string> }>;
23
+ };
24
+ const server = parsed.mcpServers?.[GSD_WORKFLOW_MCP_SERVER_NAME];
25
+ assert.ok(server, "workflow server should be written to mcpServers");
26
+ assert.equal(typeof server?.command, "string");
27
+ assert.equal(Array.isArray(server?.args), true);
28
+ assert.equal(server?.env?.GSD_WORKFLOW_PROJECT_ROOT, projectRoot);
29
+ assert.match(server?.env?.GSD_WORKFLOW_EXECUTORS_MODULE ?? "", /workflow-tool-executors\.js$/);
30
+ assert.match(server?.env?.GSD_WORKFLOW_WRITE_GATE_MODULE ?? "", /write-gate\.js$/);
31
+ } finally {
32
+ rmSync(projectRoot, { recursive: true, force: true });
33
+ }
34
+ });
35
+
36
+ test("ensureProjectWorkflowMcpConfig preserves existing mcp servers", () => {
37
+ const projectRoot = mkdtempSync(join(tmpdir(), "gsd-mcp-init-"));
38
+ mkdirSync(join(projectRoot, ".gsd"), { recursive: true });
39
+ const configPath = join(projectRoot, ".mcp.json");
40
+
41
+ writeFileSync(
42
+ configPath,
43
+ `${JSON.stringify({
44
+ mcpServers: {
45
+ railway: {
46
+ command: "npx",
47
+ args: ["railway-mcp"],
48
+ },
49
+ },
50
+ }, null, 2)}\n`,
51
+ "utf-8",
52
+ );
53
+
54
+ try {
55
+ const result = ensureProjectWorkflowMcpConfig(projectRoot);
56
+ assert.equal(result.status, "updated");
57
+
58
+ const parsed = JSON.parse(readFileSync(configPath, "utf-8")) as {
59
+ mcpServers?: Record<string, { command?: string; args?: string[] }>;
60
+ };
61
+ assert.deepEqual(parsed.mcpServers?.railway, {
62
+ command: "npx",
63
+ args: ["railway-mcp"],
64
+ });
65
+ assert.ok(parsed.mcpServers?.[GSD_WORKFLOW_MCP_SERVER_NAME]);
66
+ } finally {
67
+ rmSync(projectRoot, { recursive: true, force: true });
68
+ }
69
+ });
70
+
71
+ test("ensureProjectWorkflowMcpConfig is idempotent when config is already current", () => {
72
+ const projectRoot = mkdtempSync(join(tmpdir(), "gsd-mcp-init-"));
73
+ mkdirSync(join(projectRoot, ".gsd"), { recursive: true });
74
+
75
+ try {
76
+ const first = ensureProjectWorkflowMcpConfig(projectRoot);
77
+ const second = ensureProjectWorkflowMcpConfig(projectRoot);
78
+
79
+ assert.equal(first.status, "created");
80
+ assert.equal(second.status, "unchanged");
81
+ assert.equal(first.configPath, second.configPath);
82
+ } finally {
83
+ rmSync(projectRoot, { recursive: true, force: true });
84
+ }
85
+ });
@@ -2,6 +2,7 @@ import test, { describe } from "node:test";
2
2
  import assert from "node:assert/strict";
3
3
 
4
4
  import {
5
+ formatMcpInitResult,
5
6
  formatMcpStatusReport,
6
7
  formatMcpServerDetail,
7
8
  type McpServerStatus,
@@ -101,3 +102,17 @@ describe("formatMcpServerDetail", () => {
101
102
  assert.match(result, /disconnected/i);
102
103
  });
103
104
  });
105
+
106
+ describe("formatMcpInitResult", () => {
107
+ test("shows created message with config path", () => {
108
+ const result = formatMcpInitResult("created", "/tmp/project/.mcp.json", "/tmp/project");
109
+ assert.match(result, /created project mcp config/i);
110
+ assert.match(result, /\/tmp\/project\/\.mcp\.json/);
111
+ assert.match(result, /claude code/i);
112
+ });
113
+
114
+ test("shows unchanged message when config is current", () => {
115
+ const result = formatMcpInitResult("unchanged", "/tmp/project/.mcp.json", "/tmp/project");
116
+ assert.match(result, /already up to date/i);
117
+ });
118
+ });
@@ -32,6 +32,17 @@ test("isVerificationNotApplicable: 'None planned' is not applicable", () => {
32
32
  assert.equal(isVerificationNotApplicable("None planned"), true);
33
33
  });
34
34
 
35
+ test("isVerificationNotApplicable: 'None — <rationale>' is not applicable (#3897)", () => {
36
+ assert.equal(
37
+ isVerificationNotApplicable("None — no new background jobs, workers, or lifecycle changes introduced."),
38
+ true,
39
+ );
40
+ });
41
+
42
+ test("isVerificationNotApplicable: em dash without spaces is not applicable (#3897)", () => {
43
+ assert.equal(isVerificationNotApplicable("none—inline"), true);
44
+ });
45
+
35
46
  test("isVerificationNotApplicable: 'N/A' is not applicable", () => {
36
47
  assert.equal(isVerificationNotApplicable("N/A"), true);
37
48
  });
@@ -18,6 +18,7 @@ import {
18
18
  summarizeLogs,
19
19
  formatForNotification,
20
20
  setLogBasePath,
21
+ setStderrLoggingEnabled,
21
22
  _resetLogs,
22
23
  } from "../workflow-logger.ts";
23
24
 
@@ -375,5 +376,20 @@ describe("workflow-logger", () => {
375
376
  logError("tool", "failed", { cmd: "complete_task" });
376
377
  assert.ok(written[0].includes('"cmd":"complete_task"'));
377
378
  });
379
+
380
+ test("suppresses stderr when disabled", (t) => {
381
+ const written: string[] = [];
382
+ const orig = process.stderr.write.bind(process.stderr);
383
+ const previous = setStderrLoggingEnabled(false);
384
+ // @ts-ignore — patching for test
385
+ process.stderr.write = (chunk: string) => { written.push(chunk); return true; };
386
+ t.after(() => {
387
+ process.stderr.write = orig;
388
+ setStderrLoggingEnabled(previous);
389
+ });
390
+
391
+ logWarning("engine", "hidden warning");
392
+ assert.deepEqual(written, []);
393
+ });
378
394
  });
379
395
  });
@@ -0,0 +1,500 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
+ import { dirname, join } from "node:path";
5
+ import { tmpdir } from "node:os";
6
+ import { fileURLToPath } from "node:url";
7
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
8
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
9
+
10
+ import {
11
+ buildWorkflowMcpServers,
12
+ detectWorkflowMcpLaunchConfig,
13
+ getWorkflowTransportSupportError,
14
+ getRequiredWorkflowToolsForAutoUnit,
15
+ getRequiredWorkflowToolsForGuidedUnit,
16
+ usesWorkflowMcpTransport,
17
+ } from "../workflow-mcp.ts";
18
+
19
+ const __dirname = dirname(fileURLToPath(import.meta.url));
20
+ const gsdDir = join(__dirname, "..");
21
+
22
+ function readSrc(file: string): string {
23
+ return readFileSync(join(gsdDir, file), "utf-8");
24
+ }
25
+
26
+ test("guided execute-task requires canonical task completion tool", () => {
27
+ assert.deepEqual(getRequiredWorkflowToolsForGuidedUnit("execute-task"), ["gsd_task_complete"]);
28
+ });
29
+
30
+ test("auto execute-task requires legacy completion alias until prompt contract is aligned", () => {
31
+ assert.deepEqual(getRequiredWorkflowToolsForAutoUnit("execute-task"), ["gsd_complete_task"]);
32
+ });
33
+
34
+ test("detectWorkflowMcpLaunchConfig prefers explicit env override", () => {
35
+ const launch = detectWorkflowMcpLaunchConfig("/tmp/project", {
36
+ GSD_WORKFLOW_MCP_NAME: "workflow-tools",
37
+ GSD_WORKFLOW_MCP_COMMAND: "node",
38
+ GSD_WORKFLOW_MCP_ARGS: JSON.stringify(["dist/cli.js"]),
39
+ GSD_WORKFLOW_MCP_ENV: JSON.stringify({ FOO: "bar" }),
40
+ GSD_WORKFLOW_MCP_CWD: "/tmp/project",
41
+ GSD_CLI_PATH: "/tmp/gsd",
42
+ });
43
+
44
+ assert.deepEqual(launch, {
45
+ name: "workflow-tools",
46
+ command: "node",
47
+ args: ["dist/cli.js"],
48
+ cwd: "/tmp/project",
49
+ env: launch?.env,
50
+ });
51
+ assert.equal(launch?.env?.FOO, "bar");
52
+ assert.equal(launch?.env?.GSD_CLI_PATH, "/tmp/gsd");
53
+ assert.equal(launch?.env?.GSD_PERSIST_WRITE_GATE_STATE, "1");
54
+ assert.equal(launch?.env?.GSD_WORKFLOW_PROJECT_ROOT, "/tmp/project");
55
+ assert.match(launch?.env?.GSD_WORKFLOW_EXECUTORS_MODULE ?? "", /workflow-tool-executors\.(js|ts)$/);
56
+ assert.match(launch?.env?.GSD_WORKFLOW_WRITE_GATE_MODULE ?? "", /write-gate\.(js|ts)$/);
57
+ });
58
+
59
+ test("buildWorkflowMcpServers mirrors explicit launch config", () => {
60
+ const servers = buildWorkflowMcpServers("/tmp/project", {
61
+ GSD_WORKFLOW_MCP_COMMAND: "node",
62
+ GSD_WORKFLOW_MCP_ARGS: JSON.stringify(["dist/cli.js"]),
63
+ });
64
+
65
+ assert.deepEqual(servers, {
66
+ "gsd-workflow": {
67
+ command: "node",
68
+ args: ["dist/cli.js"],
69
+ env: servers?.["gsd-workflow"]?.env,
70
+ },
71
+ });
72
+ assert.equal((servers?.["gsd-workflow"]?.env as Record<string, string> | undefined)?.GSD_PERSIST_WRITE_GATE_STATE, "1");
73
+ assert.equal((servers?.["gsd-workflow"]?.env as Record<string, string> | undefined)?.GSD_WORKFLOW_PROJECT_ROOT, "/tmp/project");
74
+ assert.match((servers?.["gsd-workflow"]?.env as Record<string, string> | undefined)?.GSD_WORKFLOW_EXECUTORS_MODULE ?? "", /workflow-tool-executors\.(js|ts)$/);
75
+ assert.match((servers?.["gsd-workflow"]?.env as Record<string, string> | undefined)?.GSD_WORKFLOW_WRITE_GATE_MODULE ?? "", /write-gate\.(js|ts)$/);
76
+ });
77
+
78
+ test("detectWorkflowMcpLaunchConfig resolves the bundled server from GSD_PROJECT_ROOT", () => {
79
+ const repoRoot = mkdtempSync(join(tmpdir(), "gsd-workflow-root-"));
80
+ const worktreeRoot = mkdtempSync(join(tmpdir(), "gsd-workflow-worktree-"));
81
+ const cliPath = join(repoRoot, "packages", "mcp-server", "dist", "cli.js");
82
+
83
+ mkdirSync(join(repoRoot, "packages", "mcp-server", "dist"), { recursive: true });
84
+ writeFileSync(cliPath, "#!/usr/bin/env node\n", "utf-8");
85
+
86
+ const launch = detectWorkflowMcpLaunchConfig(worktreeRoot, {
87
+ GSD_PROJECT_ROOT: repoRoot,
88
+ });
89
+
90
+ assert.deepEqual(launch, {
91
+ name: "gsd-workflow",
92
+ command: process.execPath,
93
+ args: [cliPath],
94
+ cwd: repoRoot,
95
+ env: launch?.env,
96
+ });
97
+ assert.equal(launch?.env?.GSD_PERSIST_WRITE_GATE_STATE, "1");
98
+ assert.equal(launch?.env?.GSD_WORKFLOW_PROJECT_ROOT, repoRoot);
99
+ assert.match(launch?.env?.GSD_WORKFLOW_EXECUTORS_MODULE ?? "", /workflow-tool-executors\.(js|ts)$/);
100
+ assert.match(launch?.env?.GSD_WORKFLOW_WRITE_GATE_MODULE ?? "", /write-gate\.(js|ts)$/);
101
+ });
102
+
103
+ test("detectWorkflowMcpLaunchConfig resolves the bundled server from GSD_BIN_PATH ancestry", () => {
104
+ const repoRoot = mkdtempSync(join(tmpdir(), "gsd-workflow-root-"));
105
+ const worktreeRoot = mkdtempSync(join(tmpdir(), "gsd-workflow-worktree-"));
106
+ const cliPath = join(repoRoot, "packages", "mcp-server", "dist", "cli.js");
107
+ const devCliPath = join(repoRoot, "scripts", "dev-cli.js");
108
+
109
+ mkdirSync(join(repoRoot, "packages", "mcp-server", "dist"), { recursive: true });
110
+ mkdirSync(join(repoRoot, "scripts"), { recursive: true });
111
+ writeFileSync(cliPath, "#!/usr/bin/env node\n", "utf-8");
112
+ writeFileSync(devCliPath, "#!/usr/bin/env node\n", "utf-8");
113
+
114
+ const launch = detectWorkflowMcpLaunchConfig(worktreeRoot, {
115
+ GSD_BIN_PATH: devCliPath,
116
+ });
117
+
118
+ assert.deepEqual(launch, {
119
+ name: "gsd-workflow",
120
+ command: process.execPath,
121
+ args: [cliPath],
122
+ cwd: worktreeRoot,
123
+ env: launch?.env,
124
+ });
125
+ assert.equal(launch?.env?.GSD_CLI_PATH, devCliPath);
126
+ assert.equal(launch?.env?.GSD_PERSIST_WRITE_GATE_STATE, "1");
127
+ assert.equal(launch?.env?.GSD_WORKFLOW_PROJECT_ROOT, worktreeRoot);
128
+ assert.match(launch?.env?.GSD_WORKFLOW_EXECUTORS_MODULE ?? "", /workflow-tool-executors\.(js|ts)$/);
129
+ assert.match(launch?.env?.GSD_WORKFLOW_WRITE_GATE_MODULE ?? "", /write-gate\.(js|ts)$/);
130
+ });
131
+
132
+ test("detectWorkflowMcpLaunchConfig resolves the bundled server relative to the installed GSD package", () => {
133
+ const launch = detectWorkflowMcpLaunchConfig("/tmp/project", {
134
+ GSD_BIN_PATH: "/tmp/gsd-loader.js",
135
+ });
136
+
137
+ assert.equal(launch?.command, process.execPath);
138
+ assert.equal(launch?.cwd, "/tmp/project");
139
+ assert.equal(launch?.env?.GSD_CLI_PATH, "/tmp/gsd-loader.js");
140
+ assert.equal(launch?.env?.GSD_WORKFLOW_PROJECT_ROOT, "/tmp/project");
141
+ assert.match(launch?.env?.GSD_WORKFLOW_EXECUTORS_MODULE ?? "", /workflow-tool-executors\.(js|ts)$/);
142
+ assert.match(launch?.env?.GSD_WORKFLOW_WRITE_GATE_MODULE ?? "", /write-gate\.(js|ts)$/);
143
+ assert.equal(typeof launch?.args?.[0], "string");
144
+ assert.match(launch?.args?.[0] ?? "", /packages[\/\\]mcp-server[\/\\]dist[\/\\]cli\.js$/);
145
+ });
146
+
147
+ test("detectWorkflowMcpLaunchConfig resolves the bundled server relative to the package without env hints", () => {
148
+ const launch = detectWorkflowMcpLaunchConfig("/tmp/project", {});
149
+
150
+ assert.equal(launch?.command, process.execPath);
151
+ assert.equal(launch?.cwd, "/tmp/project");
152
+ assert.equal(launch?.env?.GSD_CLI_PATH, undefined);
153
+ assert.equal(launch?.env?.GSD_WORKFLOW_PROJECT_ROOT, "/tmp/project");
154
+ assert.match(launch?.env?.GSD_WORKFLOW_EXECUTORS_MODULE ?? "", /workflow-tool-executors\.(js|ts)$/);
155
+ assert.match(launch?.env?.GSD_WORKFLOW_WRITE_GATE_MODULE ?? "", /write-gate\.(js|ts)$/);
156
+ assert.equal(typeof launch?.args?.[0], "string");
157
+ assert.match(launch?.args?.[0] ?? "", /packages[\/\\]mcp-server[\/\\]dist[\/\\]cli\.js$/);
158
+ });
159
+
160
+ test("workflow MCP launch config reaches mutation tools over stdio", async () => {
161
+ const projectRoot = mkdtempSync(join(tmpdir(), "gsd-workflow-transport-"));
162
+ mkdirSync(join(projectRoot, ".gsd"), { recursive: true });
163
+
164
+ const launch = detectWorkflowMcpLaunchConfig(projectRoot, {});
165
+ assert.ok(launch, "expected a workflow MCP launch config");
166
+ assert.match(
167
+ launch.env?.GSD_WORKFLOW_EXECUTORS_MODULE ?? "",
168
+ /dist[\/\\]resources[\/\\]extensions[\/\\]gsd[\/\\]tools[\/\\]workflow-tool-executors\.js$/,
169
+ );
170
+ assert.match(
171
+ launch.env?.GSD_WORKFLOW_WRITE_GATE_MODULE ?? "",
172
+ /dist[\/\\]resources[\/\\]extensions[\/\\]gsd[\/\\]bootstrap[\/\\]write-gate\.js$/,
173
+ );
174
+
175
+ const client = new Client({ name: "workflow-mcp-transport-test", version: "1.0.0" });
176
+ const transport = new StdioClientTransport({
177
+ command: launch.command,
178
+ args: launch.args,
179
+ env: { ...process.env, ...launch.env } as Record<string, string>,
180
+ cwd: launch.cwd,
181
+ stderr: "pipe",
182
+ });
183
+
184
+ try {
185
+ await client.connect(transport, { timeout: 30_000 });
186
+
187
+ const tools = await client.listTools(undefined, { timeout: 30_000 });
188
+ assert.ok(
189
+ (tools.tools ?? []).some((tool) => tool.name === "gsd_plan_slice"),
190
+ "expected workflow MCP surface to expose gsd_plan_slice",
191
+ );
192
+
193
+ const milestoneResult = await client.callTool(
194
+ {
195
+ name: "gsd_plan_milestone",
196
+ arguments: {
197
+ projectDir: projectRoot,
198
+ milestoneId: "M001",
199
+ title: "Transport planning",
200
+ vision: "Verify stdio workflow MCP uses the executor bridge.",
201
+ slices: [
202
+ {
203
+ sliceId: "S01",
204
+ title: "Bridge path",
205
+ risk: "low",
206
+ depends: [],
207
+ demo: "Milestone planning succeeds over stdio MCP.",
208
+ goal: "Prove the executor bridge works in the spawned server.",
209
+ successCriteria: "gsd_plan_slice can write plan artifacts.",
210
+ proofLevel: "integration",
211
+ integrationClosure: "Stdio MCP client reaches the workflow executor bridge.",
212
+ observabilityImpact: "Regression test covers the spawned-server path.",
213
+ },
214
+ ],
215
+ },
216
+ },
217
+ undefined,
218
+ { timeout: 30_000 },
219
+ );
220
+ assert.equal(milestoneResult.isError, undefined);
221
+ assert.match(
222
+ ((milestoneResult.content as Array<{ text?: string }>)?.[0])?.text ?? "",
223
+ /Planned milestone M001/,
224
+ );
225
+
226
+ const sliceResult = await client.callTool(
227
+ {
228
+ name: "gsd_plan_slice",
229
+ arguments: {
230
+ projectDir: projectRoot,
231
+ milestoneId: "M001",
232
+ sliceId: "S01",
233
+ goal: "Persist slice planning over the spawned MCP transport.",
234
+ tasks: [
235
+ {
236
+ taskId: "T01",
237
+ title: "Connect the bridge",
238
+ description: "Ensure the workflow executor bridge resolves in the child process.",
239
+ estimate: "10m",
240
+ files: ["src/resources/extensions/gsd/workflow-mcp.ts"],
241
+ verify: "node --test",
242
+ inputs: ["M001-ROADMAP.md"],
243
+ expectedOutput: ["S01-PLAN.md", "T01-PLAN.md"],
244
+ },
245
+ ],
246
+ },
247
+ },
248
+ undefined,
249
+ { timeout: 30_000 },
250
+ );
251
+ assert.equal(sliceResult.isError, undefined);
252
+ assert.match(
253
+ ((sliceResult.content as Array<{ text?: string }>)?.[0])?.text ?? "",
254
+ /Planned slice S01/,
255
+ );
256
+ assert.ok(
257
+ existsSync(join(projectRoot, ".gsd", "milestones", "M001", "slices", "S01", "S01-PLAN.md")),
258
+ "expected slice plan artifact to be written through stdio MCP",
259
+ );
260
+ assert.ok(
261
+ existsSync(
262
+ join(projectRoot, ".gsd", "milestones", "M001", "slices", "S01", "tasks", "T01-PLAN.md"),
263
+ ),
264
+ "expected task plan artifact to be written through stdio MCP",
265
+ );
266
+ } finally {
267
+ await client.close().catch(() => {});
268
+ rmSync(projectRoot, { recursive: true, force: true });
269
+ }
270
+ });
271
+
272
+ test("usesWorkflowMcpTransport matches local externalCli providers", () => {
273
+ assert.equal(usesWorkflowMcpTransport("externalCli", "local://claude-code"), true);
274
+ assert.equal(usesWorkflowMcpTransport("externalCli", "https://api.example.com"), false);
275
+ assert.equal(usesWorkflowMcpTransport("oauth", "local://custom"), false);
276
+ });
277
+
278
+ test("transport compatibility passes when required tools fit current MCP surface", () => {
279
+ const error = getWorkflowTransportSupportError(
280
+ "claude-code",
281
+ ["gsd_task_complete"],
282
+ {
283
+ projectRoot: "/tmp/project",
284
+ env: { GSD_WORKFLOW_MCP_COMMAND: "node" },
285
+ surface: "guided flow",
286
+ unitType: "execute-task",
287
+ authMode: "externalCli",
288
+ baseUrl: "local://claude-code",
289
+ },
290
+ );
291
+
292
+ assert.equal(error, null);
293
+ });
294
+
295
+ test("transport compatibility discovers the bundled MCP server without env overrides", () => {
296
+ const error = getWorkflowTransportSupportError(
297
+ "claude-code",
298
+ ["gsd_task_complete"],
299
+ {
300
+ projectRoot: "/tmp/project",
301
+ env: {},
302
+ surface: "auto-mode",
303
+ unitType: "execute-task",
304
+ authMode: "externalCli",
305
+ baseUrl: "local://claude-code",
306
+ },
307
+ );
308
+
309
+ assert.equal(error, null);
310
+ });
311
+
312
+ test("transport compatibility now allows auto execute-task over workflow MCP surface", () => {
313
+ const error = getWorkflowTransportSupportError(
314
+ "claude-code",
315
+ ["gsd_complete_task"],
316
+ {
317
+ projectRoot: "/tmp/project",
318
+ env: { GSD_WORKFLOW_MCP_COMMAND: "node" },
319
+ surface: "auto-mode",
320
+ unitType: "execute-task",
321
+ authMode: "externalCli",
322
+ baseUrl: "local://claude-code",
323
+ },
324
+ );
325
+
326
+ assert.equal(error, null);
327
+ });
328
+
329
+ test("transport compatibility ignores API-backed providers", () => {
330
+ const error = getWorkflowTransportSupportError(
331
+ "openai-codex",
332
+ ["gsd_plan_slice"],
333
+ {
334
+ projectRoot: "/tmp/project",
335
+ env: {},
336
+ surface: "auto-mode",
337
+ unitType: "plan-slice",
338
+ authMode: "oauth",
339
+ baseUrl: "https://api.openai.com",
340
+ },
341
+ );
342
+
343
+ assert.equal(error, null);
344
+ });
345
+
346
+ test("transport compatibility now allows plan-slice over workflow MCP surface", () => {
347
+ const error = getWorkflowTransportSupportError(
348
+ "claude-code",
349
+ ["gsd_plan_slice"],
350
+ {
351
+ projectRoot: "/tmp/project",
352
+ env: { GSD_WORKFLOW_MCP_COMMAND: "node" },
353
+ surface: "auto-mode",
354
+ unitType: "plan-slice",
355
+ authMode: "externalCli",
356
+ baseUrl: "local://claude-code",
357
+ },
358
+ );
359
+
360
+ assert.equal(error, null);
361
+ });
362
+
363
+ test("transport compatibility now allows complete-slice over workflow MCP surface", () => {
364
+ const error = getWorkflowTransportSupportError(
365
+ "claude-code",
366
+ ["gsd_complete_slice"],
367
+ {
368
+ projectRoot: "/tmp/project",
369
+ env: { GSD_WORKFLOW_MCP_COMMAND: "node" },
370
+ surface: "auto-mode",
371
+ unitType: "complete-slice",
372
+ authMode: "externalCli",
373
+ baseUrl: "local://claude-code",
374
+ },
375
+ );
376
+
377
+ assert.equal(error, null);
378
+ });
379
+
380
+ test("transport compatibility now allows reassess-roadmap over workflow MCP surface", () => {
381
+ const error = getWorkflowTransportSupportError(
382
+ "claude-code",
383
+ ["gsd_milestone_status", "gsd_reassess_roadmap"],
384
+ {
385
+ projectRoot: "/tmp/project",
386
+ env: { GSD_WORKFLOW_MCP_COMMAND: "node" },
387
+ surface: "auto-mode",
388
+ unitType: "reassess-roadmap",
389
+ authMode: "externalCli",
390
+ baseUrl: "local://claude-code",
391
+ },
392
+ );
393
+
394
+ assert.equal(error, null);
395
+ });
396
+
397
+ test("transport compatibility now allows gate-evaluate over workflow MCP surface", () => {
398
+ const error = getWorkflowTransportSupportError(
399
+ "claude-code",
400
+ ["gsd_save_gate_result"],
401
+ {
402
+ projectRoot: "/tmp/project",
403
+ env: { GSD_WORKFLOW_MCP_COMMAND: "node" },
404
+ surface: "auto-mode",
405
+ unitType: "gate-evaluate",
406
+ authMode: "externalCli",
407
+ baseUrl: "local://claude-code",
408
+ },
409
+ );
410
+
411
+ assert.equal(error, null);
412
+ });
413
+
414
+ test("transport compatibility now allows validate-milestone over workflow MCP surface", () => {
415
+ const error = getWorkflowTransportSupportError(
416
+ "claude-code",
417
+ ["gsd_milestone_status", "gsd_validate_milestone"],
418
+ {
419
+ projectRoot: "/tmp/project",
420
+ env: { GSD_WORKFLOW_MCP_COMMAND: "node" },
421
+ surface: "auto-mode",
422
+ unitType: "validate-milestone",
423
+ authMode: "externalCli",
424
+ baseUrl: "local://claude-code",
425
+ },
426
+ );
427
+
428
+ assert.equal(error, null);
429
+ });
430
+
431
+ test("transport compatibility now allows complete-milestone over workflow MCP surface", () => {
432
+ const error = getWorkflowTransportSupportError(
433
+ "claude-code",
434
+ ["gsd_milestone_status", "gsd_complete_milestone"],
435
+ {
436
+ projectRoot: "/tmp/project",
437
+ env: { GSD_WORKFLOW_MCP_COMMAND: "node" },
438
+ surface: "auto-mode",
439
+ unitType: "complete-milestone",
440
+ authMode: "externalCli",
441
+ baseUrl: "local://claude-code",
442
+ },
443
+ );
444
+
445
+ assert.equal(error, null);
446
+ });
447
+
448
+ test("transport compatibility now allows replan-slice over workflow MCP surface", () => {
449
+ const error = getWorkflowTransportSupportError(
450
+ "claude-code",
451
+ ["gsd_replan_slice"],
452
+ {
453
+ projectRoot: "/tmp/project",
454
+ env: { GSD_WORKFLOW_MCP_COMMAND: "node" },
455
+ surface: "auto-mode",
456
+ unitType: "replan-slice",
457
+ authMode: "externalCli",
458
+ baseUrl: "local://claude-code",
459
+ },
460
+ );
461
+
462
+ assert.equal(error, null);
463
+ });
464
+
465
+ test("transport compatibility still blocks units whose MCP tools are not exposed", () => {
466
+ const error = getWorkflowTransportSupportError(
467
+ "claude-code",
468
+ ["gsd_skip_slice"],
469
+ {
470
+ projectRoot: "/tmp/project",
471
+ env: { GSD_WORKFLOW_MCP_COMMAND: "node" },
472
+ surface: "auto-mode",
473
+ unitType: "skip-slice",
474
+ authMode: "externalCli",
475
+ baseUrl: "local://claude-code",
476
+ },
477
+ );
478
+
479
+ assert.match(error ?? "", /requires gsd_skip_slice/);
480
+ assert.match(error ?? "", /currently exposes only/);
481
+ });
482
+
483
+ test("guided-flow source enforces workflow compatibility preflight", () => {
484
+ const src = readSrc("guided-flow.ts");
485
+ assert.match(src, /getRequiredWorkflowToolsForGuidedUnit/);
486
+ assert.match(src, /getWorkflowTransportSupportError/);
487
+ });
488
+
489
+ test("auto direct dispatch source enforces workflow compatibility preflight", () => {
490
+ const src = readSrc("auto-direct-dispatch.ts");
491
+ assert.match(src, /getRequiredWorkflowToolsForAutoUnit/);
492
+ assert.match(src, /getWorkflowTransportSupportError/);
493
+ });
494
+
495
+ test("auto phases source enforces workflow compatibility preflight", () => {
496
+ const src = readSrc(join("auto", "phases.ts"));
497
+ assert.match(src, /getRequiredWorkflowToolsForAutoUnit/);
498
+ assert.match(src, /getWorkflowTransportSupportError/);
499
+ assert.match(src, /workflow-capability/);
500
+ });