gsd-pi 2.67.0-dev.a5b1d8f → 2.67.0-dev.fe39184

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 (191) hide show
  1. package/README.md +41 -31
  2. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +121 -8
  3. package/dist/resources/extensions/gsd/auto/phases.js +17 -0
  4. package/dist/resources/extensions/gsd/auto/session.js +6 -0
  5. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +12 -0
  6. package/dist/resources/extensions/gsd/auto-start.js +12 -0
  7. package/dist/resources/extensions/gsd/auto.js +27 -0
  8. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +11 -435
  9. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +1 -4
  10. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +7 -64
  11. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +88 -8
  12. package/dist/resources/extensions/gsd/commands/catalog.js +2 -1
  13. package/dist/resources/extensions/gsd/commands/handlers/core.js +39 -25
  14. package/dist/resources/extensions/gsd/commands/index.js +8 -1
  15. package/dist/resources/extensions/gsd/commands-mcp-status.js +43 -7
  16. package/dist/resources/extensions/gsd/guided-flow.js +16 -0
  17. package/dist/resources/extensions/gsd/init-wizard.js +37 -0
  18. package/dist/resources/extensions/gsd/mcp-project-config.js +83 -0
  19. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +508 -0
  20. package/dist/resources/extensions/gsd/workflow-logger.js +18 -3
  21. package/dist/resources/extensions/gsd/workflow-mcp.js +261 -0
  22. package/dist/web/standalone/.next/BUILD_ID +1 -1
  23. package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
  24. package/dist/web/standalone/.next/build-manifest.json +3 -3
  25. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  26. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  27. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  28. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  36. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/index.html +1 -1
  44. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
  51. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  52. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  53. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  54. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  55. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  56. package/dist/web/standalone/.next/static/chunks/6502.5dcdcf1e1432e20d.js +9 -0
  57. package/dist/web/standalone/.next/static/chunks/{webpack-b49b09f97429b5d0.js → webpack-42a66876b763aa26.js} +1 -1
  58. package/package.json +4 -2
  59. package/packages/mcp-server/README.md +38 -0
  60. package/packages/mcp-server/dist/cli.d.ts +9 -0
  61. package/packages/mcp-server/dist/cli.d.ts.map +1 -0
  62. package/packages/mcp-server/dist/cli.js +58 -0
  63. package/packages/mcp-server/dist/cli.js.map +1 -0
  64. package/packages/mcp-server/dist/index.d.ts +20 -0
  65. package/packages/mcp-server/dist/index.d.ts.map +1 -0
  66. package/packages/mcp-server/dist/index.js +14 -0
  67. package/packages/mcp-server/dist/index.js.map +1 -0
  68. package/packages/mcp-server/dist/readers/captures.d.ts +25 -0
  69. package/packages/mcp-server/dist/readers/captures.d.ts.map +1 -0
  70. package/packages/mcp-server/dist/readers/captures.js +67 -0
  71. package/packages/mcp-server/dist/readers/captures.js.map +1 -0
  72. package/packages/mcp-server/dist/readers/doctor-lite.d.ts +20 -0
  73. package/packages/mcp-server/dist/readers/doctor-lite.d.ts.map +1 -0
  74. package/packages/mcp-server/dist/readers/doctor-lite.js +173 -0
  75. package/packages/mcp-server/dist/readers/doctor-lite.js.map +1 -0
  76. package/packages/mcp-server/dist/readers/index.d.ts +14 -0
  77. package/packages/mcp-server/dist/readers/index.d.ts.map +1 -0
  78. package/packages/mcp-server/dist/readers/index.js +10 -0
  79. package/packages/mcp-server/dist/readers/index.js.map +1 -0
  80. package/packages/mcp-server/dist/readers/knowledge.d.ts +18 -0
  81. package/packages/mcp-server/dist/readers/knowledge.d.ts.map +1 -0
  82. package/packages/mcp-server/dist/readers/knowledge.js +82 -0
  83. package/packages/mcp-server/dist/readers/knowledge.js.map +1 -0
  84. package/packages/mcp-server/dist/readers/metrics.d.ts +32 -0
  85. package/packages/mcp-server/dist/readers/metrics.d.ts.map +1 -0
  86. package/packages/mcp-server/dist/readers/metrics.js +74 -0
  87. package/packages/mcp-server/dist/readers/metrics.js.map +1 -0
  88. package/packages/mcp-server/dist/readers/paths.d.ts +42 -0
  89. package/packages/mcp-server/dist/readers/paths.d.ts.map +1 -0
  90. package/packages/mcp-server/dist/readers/paths.js +199 -0
  91. package/packages/mcp-server/dist/readers/paths.js.map +1 -0
  92. package/packages/mcp-server/dist/readers/roadmap.d.ts +26 -0
  93. package/packages/mcp-server/dist/readers/roadmap.d.ts.map +1 -0
  94. package/packages/mcp-server/dist/readers/roadmap.js +194 -0
  95. package/packages/mcp-server/dist/readers/roadmap.js.map +1 -0
  96. package/packages/mcp-server/dist/readers/state.d.ts +43 -0
  97. package/packages/mcp-server/dist/readers/state.d.ts.map +1 -0
  98. package/packages/mcp-server/dist/readers/state.js +184 -0
  99. package/packages/mcp-server/dist/readers/state.js.map +1 -0
  100. package/packages/mcp-server/dist/server.d.ts +28 -0
  101. package/packages/mcp-server/dist/server.d.ts.map +1 -0
  102. package/packages/mcp-server/dist/server.js +319 -0
  103. package/packages/mcp-server/dist/server.js.map +1 -0
  104. package/packages/mcp-server/dist/session-manager.d.ts +54 -0
  105. package/packages/mcp-server/dist/session-manager.d.ts.map +1 -0
  106. package/packages/mcp-server/dist/session-manager.js +284 -0
  107. package/packages/mcp-server/dist/session-manager.js.map +1 -0
  108. package/packages/mcp-server/dist/types.d.ts +61 -0
  109. package/packages/mcp-server/dist/types.d.ts.map +1 -0
  110. package/packages/mcp-server/dist/types.js +11 -0
  111. package/packages/mcp-server/dist/types.js.map +1 -0
  112. package/packages/mcp-server/dist/workflow-tools.d.ts +9 -0
  113. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -0
  114. package/packages/mcp-server/dist/workflow-tools.js +532 -0
  115. package/packages/mcp-server/dist/workflow-tools.js.map +1 -0
  116. package/packages/mcp-server/src/server.ts +6 -2
  117. package/packages/mcp-server/src/workflow-tools.test.ts +976 -0
  118. package/packages/mcp-server/src/workflow-tools.ts +997 -0
  119. package/packages/mcp-server/tsconfig.json +1 -1
  120. package/packages/pi-agent-core/dist/agent-loop.js +14 -6
  121. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  122. package/packages/pi-agent-core/src/agent-loop.test.ts +53 -0
  123. package/packages/pi-agent-core/src/agent-loop.ts +20 -6
  124. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.d.ts +2 -0
  125. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.d.ts.map +1 -0
  126. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +28 -0
  127. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -0
  128. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  129. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  130. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +17 -12
  131. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  132. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  133. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +19 -0
  134. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  135. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +54 -0
  136. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +18 -12
  137. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +21 -0
  138. package/packages/rpc-client/dist/index.d.ts +10 -0
  139. package/packages/rpc-client/dist/index.d.ts.map +1 -0
  140. package/packages/rpc-client/dist/index.js +9 -0
  141. package/packages/rpc-client/dist/index.js.map +1 -0
  142. package/packages/rpc-client/dist/jsonl.d.ts +17 -0
  143. package/packages/rpc-client/dist/jsonl.d.ts.map +1 -0
  144. package/packages/rpc-client/dist/jsonl.js +54 -0
  145. package/packages/rpc-client/dist/jsonl.js.map +1 -0
  146. package/packages/rpc-client/dist/rpc-client.d.ts +259 -0
  147. package/packages/rpc-client/dist/rpc-client.d.ts.map +1 -0
  148. package/packages/rpc-client/dist/rpc-client.js +541 -0
  149. package/packages/rpc-client/dist/rpc-client.js.map +1 -0
  150. package/packages/rpc-client/dist/rpc-client.test.d.ts +2 -0
  151. package/packages/rpc-client/dist/rpc-client.test.d.ts.map +1 -0
  152. package/packages/rpc-client/dist/rpc-client.test.js +477 -0
  153. package/packages/rpc-client/dist/rpc-client.test.js.map +1 -0
  154. package/packages/rpc-client/dist/rpc-types.d.ts +566 -0
  155. package/packages/rpc-client/dist/rpc-types.d.ts.map +1 -0
  156. package/packages/rpc-client/dist/rpc-types.js +12 -0
  157. package/packages/rpc-client/dist/rpc-types.js.map +1 -0
  158. package/scripts/ensure-workspace-builds.cjs +2 -0
  159. package/scripts/link-workspace-packages.cjs +21 -14
  160. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +157 -8
  161. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +182 -0
  162. package/src/resources/extensions/gsd/auto/phases.ts +25 -0
  163. package/src/resources/extensions/gsd/auto/session.ts +6 -0
  164. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +20 -0
  165. package/src/resources/extensions/gsd/auto-start.ts +15 -1
  166. package/src/resources/extensions/gsd/auto.ts +29 -1
  167. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +22 -435
  168. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +1 -5
  169. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +7 -72
  170. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +122 -6
  171. package/src/resources/extensions/gsd/commands/catalog.ts +2 -1
  172. package/src/resources/extensions/gsd/commands/handlers/core.ts +53 -26
  173. package/src/resources/extensions/gsd/commands/index.ts +7 -1
  174. package/src/resources/extensions/gsd/commands-mcp-status.ts +53 -7
  175. package/src/resources/extensions/gsd/guided-flow.ts +24 -0
  176. package/src/resources/extensions/gsd/init-wizard.ts +40 -0
  177. package/src/resources/extensions/gsd/mcp-project-config.ts +128 -0
  178. package/src/resources/extensions/gsd/tests/auto-project-root-env.test.ts +29 -0
  179. package/src/resources/extensions/gsd/tests/core-overlay-fallback.test.ts +101 -0
  180. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +66 -0
  181. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +85 -0
  182. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +15 -0
  183. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +16 -0
  184. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +500 -0
  185. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +625 -0
  186. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +629 -0
  187. package/src/resources/extensions/gsd/workflow-logger.ts +19 -3
  188. package/src/resources/extensions/gsd/workflow-mcp.ts +320 -0
  189. package/dist/web/standalone/.next/static/chunks/6502.b804e48b7919f55e.js +0 -9
  190. /package/dist/web/standalone/.next/static/{NllX5BEOLdTXS9ypf1i3i → gbSATDX4Jt2ufxzUr5nYm}/_buildManifest.js +0 -0
  191. /package/dist/web/standalone/.next/static/{NllX5BEOLdTXS9ypf1i3i → gbSATDX4Jt2ufxzUr5nYm}/_ssgManifest.js +0 -0
@@ -0,0 +1,997 @@
1
+ /**
2
+ * Workflow MCP tools — exposes the core GSD mutation/read handlers over MCP.
3
+ */
4
+
5
+ import { isAbsolute, relative, resolve } from "node:path";
6
+ import { pathToFileURL } from "node:url";
7
+ import { z } from "zod";
8
+
9
+ type WorkflowToolExecutors = {
10
+ SUPPORTED_SUMMARY_ARTIFACT_TYPES: readonly string[];
11
+ executeMilestoneStatus: (params: { milestoneId: string }, basePath?: string) => Promise<unknown>;
12
+ executePlanMilestone: (
13
+ params: {
14
+ milestoneId: string;
15
+ title: string;
16
+ vision: string;
17
+ slices: Array<{
18
+ sliceId: string;
19
+ title: string;
20
+ risk: string;
21
+ depends: string[];
22
+ demo: string;
23
+ goal: string;
24
+ successCriteria: string;
25
+ proofLevel: string;
26
+ integrationClosure: string;
27
+ observabilityImpact: string;
28
+ }>;
29
+ status?: string;
30
+ dependsOn?: string[];
31
+ successCriteria?: string[];
32
+ keyRisks?: Array<{ risk: string; whyItMatters: string }>;
33
+ proofStrategy?: Array<{ riskOrUnknown: string; retireIn: string; whatWillBeProven: string }>;
34
+ verificationContract?: string;
35
+ verificationIntegration?: string;
36
+ verificationOperational?: string;
37
+ verificationUat?: string;
38
+ definitionOfDone?: string[];
39
+ requirementCoverage?: string;
40
+ boundaryMapMarkdown?: string;
41
+ },
42
+ basePath?: string,
43
+ ) => Promise<unknown>;
44
+ executePlanSlice: (
45
+ params: {
46
+ milestoneId: string;
47
+ sliceId: string;
48
+ goal: string;
49
+ tasks: Array<{
50
+ taskId: string;
51
+ title: string;
52
+ description: string;
53
+ estimate: string;
54
+ files: string[];
55
+ verify: string;
56
+ inputs: string[];
57
+ expectedOutput: string[];
58
+ observabilityImpact?: string;
59
+ }>;
60
+ successCriteria?: string;
61
+ proofLevel?: string;
62
+ integrationClosure?: string;
63
+ observabilityImpact?: string;
64
+ },
65
+ basePath?: string,
66
+ ) => Promise<unknown>;
67
+ executeReplanSlice: (
68
+ params: {
69
+ milestoneId: string;
70
+ sliceId: string;
71
+ blockerTaskId: string;
72
+ blockerDescription: string;
73
+ whatChanged: string;
74
+ updatedTasks: Array<{
75
+ taskId: string;
76
+ title: string;
77
+ description: string;
78
+ estimate: string;
79
+ files: string[];
80
+ verify: string;
81
+ inputs: string[];
82
+ expectedOutput: string[];
83
+ fullPlanMd?: string;
84
+ }>;
85
+ removedTaskIds: string[];
86
+ },
87
+ basePath?: string,
88
+ ) => Promise<unknown>;
89
+ executeSliceComplete: (
90
+ params: {
91
+ sliceId: string;
92
+ milestoneId: string;
93
+ sliceTitle: string;
94
+ oneLiner: string;
95
+ narrative: string;
96
+ verification: string;
97
+ uatContent: string;
98
+ deviations?: string;
99
+ knownLimitations?: string;
100
+ followUps?: string;
101
+ keyFiles?: string[] | string;
102
+ keyDecisions?: string[] | string;
103
+ patternsEstablished?: string[] | string;
104
+ observabilitySurfaces?: string[] | string;
105
+ provides?: string[] | string;
106
+ requirementsSurfaced?: string[] | string;
107
+ drillDownPaths?: string[] | string;
108
+ affects?: string[] | string;
109
+ requirementsAdvanced?: Array<{ id: string; how: string } | string>;
110
+ requirementsValidated?: Array<{ id: string; proof: string } | string>;
111
+ requirementsInvalidated?: Array<{ id: string; what: string } | string>;
112
+ filesModified?: Array<{ path: string; description: string } | string>;
113
+ requires?: Array<{ slice: string; provides: string } | string>;
114
+ },
115
+ basePath?: string,
116
+ ) => Promise<unknown>;
117
+ executeCompleteMilestone: (
118
+ params: {
119
+ milestoneId: string;
120
+ title: string;
121
+ oneLiner: string;
122
+ narrative: string;
123
+ verificationPassed: boolean;
124
+ successCriteriaResults?: string;
125
+ definitionOfDoneResults?: string;
126
+ requirementOutcomes?: string;
127
+ keyDecisions?: string[];
128
+ keyFiles?: string[];
129
+ lessonsLearned?: string[];
130
+ followUps?: string;
131
+ deviations?: string;
132
+ },
133
+ basePath?: string,
134
+ ) => Promise<unknown>;
135
+ executeValidateMilestone: (
136
+ params: {
137
+ milestoneId: string;
138
+ verdict: "pass" | "needs-attention" | "needs-remediation";
139
+ remediationRound: number;
140
+ successCriteriaChecklist: string;
141
+ sliceDeliveryAudit: string;
142
+ crossSliceIntegration: string;
143
+ requirementCoverage: string;
144
+ verificationClasses?: string;
145
+ verdictRationale: string;
146
+ remediationPlan?: string;
147
+ },
148
+ basePath?: string,
149
+ ) => Promise<unknown>;
150
+ executeReassessRoadmap: (
151
+ params: {
152
+ milestoneId: string;
153
+ completedSliceId: string;
154
+ verdict: string;
155
+ assessment: string;
156
+ sliceChanges: {
157
+ modified: Array<{
158
+ sliceId: string;
159
+ title: string;
160
+ risk?: string;
161
+ depends?: string[];
162
+ demo?: string;
163
+ }>;
164
+ added: Array<{
165
+ sliceId: string;
166
+ title: string;
167
+ risk?: string;
168
+ depends?: string[];
169
+ demo?: string;
170
+ }>;
171
+ removed: string[];
172
+ };
173
+ },
174
+ basePath?: string,
175
+ ) => Promise<unknown>;
176
+ executeSaveGateResult: (
177
+ params: {
178
+ milestoneId: string;
179
+ sliceId: string;
180
+ gateId: string;
181
+ taskId?: string;
182
+ verdict: "pass" | "flag" | "omitted";
183
+ rationale: string;
184
+ findings?: string;
185
+ },
186
+ basePath?: string,
187
+ ) => Promise<unknown>;
188
+ executeSummarySave: (
189
+ params: {
190
+ milestone_id: string;
191
+ slice_id?: string;
192
+ task_id?: string;
193
+ artifact_type: string;
194
+ content: string;
195
+ },
196
+ basePath?: string,
197
+ ) => Promise<unknown>;
198
+ executeTaskComplete: (
199
+ params: {
200
+ taskId: string;
201
+ sliceId: string;
202
+ milestoneId: string;
203
+ oneLiner: string;
204
+ narrative: string;
205
+ verification: string;
206
+ deviations?: string;
207
+ knownIssues?: string;
208
+ keyFiles?: string[];
209
+ keyDecisions?: string[];
210
+ blockerDiscovered?: boolean;
211
+ verificationEvidence?: Array<
212
+ { command: string; exitCode: number; verdict: string; durationMs: number } | string
213
+ >;
214
+ },
215
+ basePath?: string,
216
+ ) => Promise<unknown>;
217
+ };
218
+
219
+ type WorkflowWriteGateModule = {
220
+ loadWriteGateSnapshot: (basePath?: string) => {
221
+ verifiedDepthMilestones: string[];
222
+ activeQueuePhase: boolean;
223
+ pendingGateId: string | null;
224
+ };
225
+ shouldBlockPendingGateInSnapshot: (
226
+ snapshot: {
227
+ verifiedDepthMilestones: string[];
228
+ activeQueuePhase: boolean;
229
+ pendingGateId: string | null;
230
+ },
231
+ toolName: string,
232
+ milestoneId: string | null,
233
+ queuePhaseActive?: boolean,
234
+ ) => { block: boolean; reason?: string };
235
+ shouldBlockQueueExecutionInSnapshot: (
236
+ snapshot: {
237
+ verifiedDepthMilestones: string[];
238
+ activeQueuePhase: boolean;
239
+ pendingGateId: string | null;
240
+ },
241
+ toolName: string,
242
+ input: string,
243
+ queuePhaseActive?: boolean,
244
+ ) => { block: boolean; reason?: string };
245
+ };
246
+
247
+ let workflowToolExecutorsPromise: Promise<WorkflowToolExecutors> | null = null;
248
+ let workflowExecutionQueue: Promise<void> = Promise.resolve();
249
+ let workflowWriteGatePromise: Promise<WorkflowWriteGateModule> | null = null;
250
+
251
+ function getAllowedProjectRoot(env: NodeJS.ProcessEnv = process.env): string | null {
252
+ const configuredRoot = env.GSD_WORKFLOW_PROJECT_ROOT?.trim();
253
+ return configuredRoot ? resolve(configuredRoot) : null;
254
+ }
255
+
256
+ function isWithinRoot(candidatePath: string, rootPath: string): boolean {
257
+ const rel = relative(rootPath, candidatePath);
258
+ return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
259
+ }
260
+
261
+ function validateProjectDir(projectDir: string, env: NodeJS.ProcessEnv = process.env): string {
262
+ if (!isAbsolute(projectDir)) {
263
+ throw new Error(`projectDir must be an absolute path. Received: ${projectDir}`);
264
+ }
265
+
266
+ const resolvedProjectDir = resolve(projectDir);
267
+ const allowedRoot = getAllowedProjectRoot(env);
268
+ if (allowedRoot && !isWithinRoot(resolvedProjectDir, allowedRoot)) {
269
+ throw new Error(
270
+ `projectDir must stay within the configured workflow project root. Received: ${resolvedProjectDir}; allowed root: ${allowedRoot}`,
271
+ );
272
+ }
273
+
274
+ return resolvedProjectDir;
275
+ }
276
+
277
+ function parseToolArgs<T>(schema: z.ZodType<T>, args: Record<string, unknown>): T {
278
+ return schema.parse(args);
279
+ }
280
+
281
+ function parseWorkflowArgs<T extends { projectDir: string }>(
282
+ schema: z.ZodType<T>,
283
+ args: Record<string, unknown>,
284
+ ): T {
285
+ const parsed = parseToolArgs(schema, args);
286
+ return {
287
+ ...parsed,
288
+ projectDir: validateProjectDir(parsed.projectDir),
289
+ };
290
+ }
291
+
292
+ function isWorkflowToolExecutors(value: unknown): value is WorkflowToolExecutors {
293
+ if (!value || typeof value !== "object") return false;
294
+ const record = value as Record<string, unknown>;
295
+ const functionExports = [
296
+ "executeMilestoneStatus",
297
+ "executePlanMilestone",
298
+ "executePlanSlice",
299
+ "executeReplanSlice",
300
+ "executeSliceComplete",
301
+ "executeCompleteMilestone",
302
+ "executeValidateMilestone",
303
+ "executeReassessRoadmap",
304
+ "executeSaveGateResult",
305
+ "executeSummarySave",
306
+ "executeTaskComplete",
307
+ ];
308
+
309
+ return Array.isArray(record.SUPPORTED_SUMMARY_ARTIFACT_TYPES) &&
310
+ functionExports.every((key) => typeof record[key] === "function");
311
+ }
312
+
313
+ function getSupportedSummaryArtifactTypes(executors: WorkflowToolExecutors): readonly string[] {
314
+ return executors.SUPPORTED_SUMMARY_ARTIFACT_TYPES;
315
+ }
316
+
317
+ function getWriteGateModuleCandidates(): string[] {
318
+ const candidates: string[] = [];
319
+ const explicitModule = process.env.GSD_WORKFLOW_WRITE_GATE_MODULE?.trim();
320
+ if (explicitModule) {
321
+ if (/^[a-z]+:/i.test(explicitModule) && !explicitModule.startsWith("file:")) {
322
+ throw new Error("GSD_WORKFLOW_WRITE_GATE_MODULE only supports file: URLs or filesystem paths.");
323
+ }
324
+ candidates.push(explicitModule.startsWith("file:") ? explicitModule : toFileUrl(explicitModule));
325
+ }
326
+
327
+ candidates.push(
328
+ new URL("../../../src/resources/extensions/gsd/bootstrap/write-gate.js", import.meta.url).href,
329
+ new URL("../../../src/resources/extensions/gsd/bootstrap/write-gate.ts", import.meta.url).href,
330
+ );
331
+
332
+ return [...new Set(candidates)];
333
+ }
334
+
335
+ function toFileUrl(modulePath: string): string {
336
+ return pathToFileURL(resolve(modulePath)).href;
337
+ }
338
+
339
+ function getWorkflowExecutorModuleCandidates(env: NodeJS.ProcessEnv = process.env): string[] {
340
+ const candidates: string[] = [];
341
+ const explicitModule = env.GSD_WORKFLOW_EXECUTORS_MODULE?.trim();
342
+ if (explicitModule) {
343
+ if (/^[a-z]+:/i.test(explicitModule) && !explicitModule.startsWith("file:")) {
344
+ throw new Error("GSD_WORKFLOW_EXECUTORS_MODULE only supports file: URLs or filesystem paths.");
345
+ }
346
+ candidates.push(explicitModule.startsWith("file:") ? explicitModule : toFileUrl(explicitModule));
347
+ }
348
+
349
+ candidates.push(
350
+ new URL("../../../src/resources/extensions/gsd/tools/workflow-tool-executors.js", import.meta.url).href,
351
+ new URL("../../../src/resources/extensions/gsd/tools/workflow-tool-executors.ts", import.meta.url).href,
352
+ );
353
+
354
+ return [...new Set(candidates)];
355
+ }
356
+
357
+ async function getWorkflowToolExecutors(): Promise<WorkflowToolExecutors> {
358
+ if (!workflowToolExecutorsPromise) {
359
+ workflowToolExecutorsPromise = (async () => {
360
+ const attempts: string[] = [];
361
+ for (const candidate of getWorkflowExecutorModuleCandidates()) {
362
+ try {
363
+ const loaded = await import(candidate);
364
+ if (isWorkflowToolExecutors(loaded)) {
365
+ return loaded;
366
+ }
367
+ attempts.push(`${candidate} (module shape mismatch)`);
368
+ } catch (err) {
369
+ attempts.push(`${candidate} (${err instanceof Error ? err.message : String(err)})`);
370
+ }
371
+ }
372
+
373
+ throw new Error(
374
+ "Unable to load GSD workflow executor bridge for MCP mutation tools. " +
375
+ "Set GSD_WORKFLOW_EXECUTORS_MODULE to an importable workflow-tool-executors module, " +
376
+ "or run the MCP server from a GSD checkout that includes src/resources/extensions/gsd/tools/workflow-tool-executors.(js|ts). " +
377
+ `Attempts: ${attempts.join("; ")}`,
378
+ );
379
+ })();
380
+ }
381
+ return workflowToolExecutorsPromise;
382
+ }
383
+
384
+ async function getWorkflowWriteGateModule(): Promise<WorkflowWriteGateModule> {
385
+ if (!workflowWriteGatePromise) {
386
+ workflowWriteGatePromise = (async () => {
387
+ const attempts: string[] = [];
388
+ for (const candidate of getWriteGateModuleCandidates()) {
389
+ try {
390
+ const loaded = await import(candidate);
391
+ if (
392
+ loaded &&
393
+ typeof loaded.loadWriteGateSnapshot === "function" &&
394
+ typeof loaded.shouldBlockPendingGateInSnapshot === "function" &&
395
+ typeof loaded.shouldBlockQueueExecutionInSnapshot === "function"
396
+ ) {
397
+ return loaded as WorkflowWriteGateModule;
398
+ }
399
+ attempts.push(`${candidate} (module shape mismatch)`);
400
+ } catch (err) {
401
+ attempts.push(`${candidate} (${err instanceof Error ? err.message : String(err)})`);
402
+ }
403
+ }
404
+
405
+ throw new Error(
406
+ "Unable to load GSD write-gate bridge for workflow MCP tools. " +
407
+ `Attempts: ${attempts.join("; ")}`,
408
+ );
409
+ })();
410
+ }
411
+ return workflowWriteGatePromise;
412
+ }
413
+
414
+ interface McpToolServer {
415
+ tool(
416
+ name: string,
417
+ description: string,
418
+ params: Record<string, unknown>,
419
+ handler: (args: Record<string, unknown>) => Promise<unknown>,
420
+ ): unknown;
421
+ }
422
+
423
+ async function runSerializedWorkflowOperation<T>(fn: () => Promise<T>): Promise<T> {
424
+ // The shared DB adapter and workflow log base path are process-global, so
425
+ // workflow MCP mutations must not overlap within a single server process.
426
+ const prior = workflowExecutionQueue;
427
+ let release!: () => void;
428
+ workflowExecutionQueue = new Promise<void>((resolve) => {
429
+ release = resolve;
430
+ });
431
+
432
+ await prior;
433
+ try {
434
+ return await fn();
435
+ } finally {
436
+ release();
437
+ }
438
+ }
439
+
440
+ async function enforceWorkflowWriteGate(
441
+ toolName: string,
442
+ projectDir: string,
443
+ milestoneId: string | null = null,
444
+ ): Promise<void> {
445
+ const writeGate = await getWorkflowWriteGateModule();
446
+ const snapshot = writeGate.loadWriteGateSnapshot(projectDir);
447
+ const pendingGate = writeGate.shouldBlockPendingGateInSnapshot(
448
+ snapshot,
449
+ toolName,
450
+ milestoneId,
451
+ snapshot.activeQueuePhase,
452
+ );
453
+ if (pendingGate.block) {
454
+ throw new Error(pendingGate.reason ?? "workflow tool blocked by pending discussion gate");
455
+ }
456
+
457
+ const queueGuard = writeGate.shouldBlockQueueExecutionInSnapshot(
458
+ snapshot,
459
+ toolName,
460
+ "",
461
+ snapshot.activeQueuePhase,
462
+ );
463
+ if (queueGuard.block) {
464
+ throw new Error(queueGuard.reason ?? "workflow tool blocked during queue mode");
465
+ }
466
+ }
467
+
468
+ async function handleTaskComplete(
469
+ projectDir: string,
470
+ args: Omit<z.infer<typeof taskCompleteSchema>, "projectDir">,
471
+ ): Promise<unknown> {
472
+ await enforceWorkflowWriteGate("gsd_task_complete", projectDir, args.milestoneId);
473
+ const {
474
+ taskId,
475
+ sliceId,
476
+ milestoneId,
477
+ oneLiner,
478
+ narrative,
479
+ verification,
480
+ deviations,
481
+ knownIssues,
482
+ keyFiles,
483
+ keyDecisions,
484
+ blockerDiscovered,
485
+ verificationEvidence,
486
+ } = args;
487
+ const { executeTaskComplete } = await getWorkflowToolExecutors();
488
+ return runSerializedWorkflowOperation(() =>
489
+ executeTaskComplete(
490
+ {
491
+ taskId,
492
+ sliceId,
493
+ milestoneId,
494
+ oneLiner,
495
+ narrative,
496
+ verification,
497
+ deviations,
498
+ knownIssues,
499
+ keyFiles,
500
+ keyDecisions,
501
+ blockerDiscovered,
502
+ verificationEvidence,
503
+ },
504
+ projectDir,
505
+ ),
506
+ );
507
+ }
508
+
509
+ async function handleSliceComplete(
510
+ projectDir: string,
511
+ args: z.infer<typeof sliceCompleteSchema>,
512
+ ): Promise<unknown> {
513
+ await enforceWorkflowWriteGate("gsd_slice_complete", projectDir, args.milestoneId);
514
+ const { executeSliceComplete } = await getWorkflowToolExecutors();
515
+ const { projectDir: _projectDir, ...params } = args;
516
+ return runSerializedWorkflowOperation(() => executeSliceComplete(params, projectDir));
517
+ }
518
+
519
+ async function handleReplanSlice(
520
+ projectDir: string,
521
+ args: z.infer<typeof replanSliceSchema>,
522
+ ): Promise<unknown> {
523
+ await enforceWorkflowWriteGate("gsd_replan_slice", projectDir, args.milestoneId);
524
+ const { executeReplanSlice } = await getWorkflowToolExecutors();
525
+ const { projectDir: _projectDir, ...params } = args;
526
+ return runSerializedWorkflowOperation(() => executeReplanSlice(params, projectDir));
527
+ }
528
+
529
+ async function handleCompleteMilestone(
530
+ projectDir: string,
531
+ args: z.infer<typeof completeMilestoneSchema>,
532
+ ): Promise<unknown> {
533
+ await enforceWorkflowWriteGate("gsd_complete_milestone", projectDir, args.milestoneId);
534
+ const { executeCompleteMilestone } = await getWorkflowToolExecutors();
535
+ const { projectDir: _projectDir, ...params } = args;
536
+ return runSerializedWorkflowOperation(() => executeCompleteMilestone(params, projectDir));
537
+ }
538
+
539
+ async function handleValidateMilestone(
540
+ projectDir: string,
541
+ args: z.infer<typeof validateMilestoneSchema>,
542
+ ): Promise<unknown> {
543
+ await enforceWorkflowWriteGate("gsd_validate_milestone", projectDir, args.milestoneId);
544
+ const { executeValidateMilestone } = await getWorkflowToolExecutors();
545
+ const { projectDir: _projectDir, ...params } = args;
546
+ return runSerializedWorkflowOperation(() => executeValidateMilestone(params, projectDir));
547
+ }
548
+
549
+ async function handleReassessRoadmap(
550
+ projectDir: string,
551
+ args: z.infer<typeof reassessRoadmapSchema>,
552
+ ): Promise<unknown> {
553
+ await enforceWorkflowWriteGate("gsd_reassess_roadmap", projectDir, args.milestoneId);
554
+ const { executeReassessRoadmap } = await getWorkflowToolExecutors();
555
+ const { projectDir: _projectDir, ...params } = args;
556
+ return runSerializedWorkflowOperation(() => executeReassessRoadmap(params, projectDir));
557
+ }
558
+
559
+ async function handleSaveGateResult(
560
+ projectDir: string,
561
+ args: z.infer<typeof saveGateResultSchema>,
562
+ ): Promise<unknown> {
563
+ await enforceWorkflowWriteGate("gsd_save_gate_result", projectDir, args.milestoneId);
564
+ const { executeSaveGateResult } = await getWorkflowToolExecutors();
565
+ const { projectDir: _projectDir, ...params } = args;
566
+ return runSerializedWorkflowOperation(() => executeSaveGateResult(params, projectDir));
567
+ }
568
+
569
+ const projectDirParam = z.string().describe("Absolute path to the project directory within the configured workflow root");
570
+
571
+ const planMilestoneParams = {
572
+ projectDir: projectDirParam,
573
+ milestoneId: z.string().describe("Milestone ID (e.g. M001)"),
574
+ title: z.string().describe("Milestone title"),
575
+ vision: z.string().describe("Milestone vision"),
576
+ slices: z.array(z.object({
577
+ sliceId: z.string(),
578
+ title: z.string(),
579
+ risk: z.string(),
580
+ depends: z.array(z.string()),
581
+ demo: z.string(),
582
+ goal: z.string(),
583
+ successCriteria: z.string(),
584
+ proofLevel: z.string(),
585
+ integrationClosure: z.string(),
586
+ observabilityImpact: z.string(),
587
+ })).describe("Planned slices for the milestone"),
588
+ status: z.string().optional().describe("Milestone status"),
589
+ dependsOn: z.array(z.string()).optional().describe("Milestone dependencies"),
590
+ successCriteria: z.array(z.string()).optional().describe("Top-level success criteria bullets"),
591
+ keyRisks: z.array(z.object({
592
+ risk: z.string(),
593
+ whyItMatters: z.string(),
594
+ })).optional().describe("Structured risk entries"),
595
+ proofStrategy: z.array(z.object({
596
+ riskOrUnknown: z.string(),
597
+ retireIn: z.string(),
598
+ whatWillBeProven: z.string(),
599
+ })).optional().describe("Structured proof strategy entries"),
600
+ verificationContract: z.string().optional(),
601
+ verificationIntegration: z.string().optional(),
602
+ verificationOperational: z.string().optional(),
603
+ verificationUat: z.string().optional(),
604
+ definitionOfDone: z.array(z.string()).optional(),
605
+ requirementCoverage: z.string().optional(),
606
+ boundaryMapMarkdown: z.string().optional(),
607
+ };
608
+ const planMilestoneSchema = z.object(planMilestoneParams);
609
+
610
+ const planSliceParams = {
611
+ projectDir: projectDirParam,
612
+ milestoneId: z.string().describe("Milestone ID (e.g. M001)"),
613
+ sliceId: z.string().describe("Slice ID (e.g. S01)"),
614
+ goal: z.string().describe("Slice goal"),
615
+ tasks: z.array(z.object({
616
+ taskId: z.string(),
617
+ title: z.string(),
618
+ description: z.string(),
619
+ estimate: z.string(),
620
+ files: z.array(z.string()),
621
+ verify: z.string(),
622
+ inputs: z.array(z.string()),
623
+ expectedOutput: z.array(z.string()),
624
+ observabilityImpact: z.string().optional(),
625
+ })).describe("Planned tasks for the slice"),
626
+ successCriteria: z.string().optional(),
627
+ proofLevel: z.string().optional(),
628
+ integrationClosure: z.string().optional(),
629
+ observabilityImpact: z.string().optional(),
630
+ };
631
+ const planSliceSchema = z.object(planSliceParams);
632
+
633
+ const completeMilestoneParams = {
634
+ projectDir: projectDirParam,
635
+ milestoneId: z.string().describe("Milestone ID (e.g. M001)"),
636
+ title: z.string().describe("Milestone title"),
637
+ oneLiner: z.string().describe("One-sentence summary of what the milestone achieved"),
638
+ narrative: z.string().describe("Detailed narrative of what happened during the milestone"),
639
+ verificationPassed: z.boolean().describe("Must be true after milestone verification succeeds"),
640
+ successCriteriaResults: z.string().optional(),
641
+ definitionOfDoneResults: z.string().optional(),
642
+ requirementOutcomes: z.string().optional(),
643
+ keyDecisions: z.array(z.string()).optional(),
644
+ keyFiles: z.array(z.string()).optional(),
645
+ lessonsLearned: z.array(z.string()).optional(),
646
+ followUps: z.string().optional(),
647
+ deviations: z.string().optional(),
648
+ };
649
+ const completeMilestoneSchema = z.object(completeMilestoneParams);
650
+
651
+ const validateMilestoneParams = {
652
+ projectDir: projectDirParam,
653
+ milestoneId: z.string().describe("Milestone ID (e.g. M001)"),
654
+ verdict: z.enum(["pass", "needs-attention", "needs-remediation"]).describe("Validation verdict"),
655
+ remediationRound: z.number().describe("Remediation round (0 for first validation)"),
656
+ successCriteriaChecklist: z.string().describe("Markdown checklist of success criteria with evidence"),
657
+ sliceDeliveryAudit: z.string().describe("Markdown auditing each slice's claimed vs delivered output"),
658
+ crossSliceIntegration: z.string().describe("Markdown describing cross-slice issues or closure"),
659
+ requirementCoverage: z.string().describe("Markdown describing requirement coverage and gaps"),
660
+ verificationClasses: z.string().optional(),
661
+ verdictRationale: z.string().describe("Why this verdict was chosen"),
662
+ remediationPlan: z.string().optional(),
663
+ };
664
+ const validateMilestoneSchema = z.object(validateMilestoneParams);
665
+
666
+ const roadmapSliceChangeSchema = z.object({
667
+ sliceId: z.string(),
668
+ title: z.string(),
669
+ risk: z.string().optional(),
670
+ depends: z.array(z.string()).optional(),
671
+ demo: z.string().optional(),
672
+ });
673
+
674
+ const reassessRoadmapParams = {
675
+ projectDir: projectDirParam,
676
+ milestoneId: z.string().describe("Milestone ID (e.g. M001)"),
677
+ completedSliceId: z.string().describe("Slice ID that just completed"),
678
+ verdict: z.string().describe("Assessment verdict such as roadmap-confirmed or roadmap-adjusted"),
679
+ assessment: z.string().describe("Assessment text explaining the roadmap decision"),
680
+ sliceChanges: z.object({
681
+ modified: z.array(roadmapSliceChangeSchema),
682
+ added: z.array(roadmapSliceChangeSchema),
683
+ removed: z.array(z.string()),
684
+ }).describe("Slice changes to apply"),
685
+ };
686
+ const reassessRoadmapSchema = z.object(reassessRoadmapParams);
687
+
688
+ const saveGateResultParams = {
689
+ projectDir: projectDirParam,
690
+ milestoneId: z.string().describe("Milestone ID (e.g. M001)"),
691
+ sliceId: z.string().describe("Slice ID (e.g. S01)"),
692
+ gateId: z.enum(["Q3", "Q4", "Q5", "Q6", "Q7", "Q8"]).describe("Gate ID"),
693
+ taskId: z.string().optional().describe("Task ID for task-scoped gates"),
694
+ verdict: z.enum(["pass", "flag", "omitted"]).describe("Gate verdict"),
695
+ rationale: z.string().describe("One-sentence justification"),
696
+ findings: z.string().optional().describe("Detailed markdown findings"),
697
+ };
698
+ const saveGateResultSchema = z.object(saveGateResultParams);
699
+
700
+ const replanSliceParams = {
701
+ projectDir: projectDirParam,
702
+ milestoneId: z.string().describe("Milestone ID (e.g. M001)"),
703
+ sliceId: z.string().describe("Slice ID (e.g. S01)"),
704
+ blockerTaskId: z.string().describe("Task ID that discovered the blocker"),
705
+ blockerDescription: z.string().describe("Description of the blocker"),
706
+ whatChanged: z.string().describe("Summary of what changed in the plan"),
707
+ updatedTasks: z.array(z.object({
708
+ taskId: z.string(),
709
+ title: z.string(),
710
+ description: z.string(),
711
+ estimate: z.string(),
712
+ files: z.array(z.string()),
713
+ verify: z.string(),
714
+ inputs: z.array(z.string()),
715
+ expectedOutput: z.array(z.string()),
716
+ fullPlanMd: z.string().optional(),
717
+ })).describe("Tasks to upsert into the replanned slice"),
718
+ removedTaskIds: z.array(z.string()).describe("Task IDs to remove from the slice"),
719
+ };
720
+ const replanSliceSchema = z.object(replanSliceParams);
721
+
722
+ const sliceCompleteParams = {
723
+ projectDir: projectDirParam,
724
+ sliceId: z.string().describe("Slice ID (e.g. S01)"),
725
+ milestoneId: z.string().describe("Milestone ID (e.g. M001)"),
726
+ sliceTitle: z.string().describe("Title of the slice"),
727
+ oneLiner: z.string().describe("One-line summary of what the slice accomplished"),
728
+ narrative: z.string().describe("Detailed narrative of what happened across all tasks"),
729
+ verification: z.string().describe("What was verified across all tasks"),
730
+ uatContent: z.string().describe("UAT test content (markdown body)"),
731
+ deviations: z.string().optional(),
732
+ knownLimitations: z.string().optional(),
733
+ followUps: z.string().optional(),
734
+ keyFiles: z.union([z.array(z.string()), z.string()]).optional(),
735
+ keyDecisions: z.union([z.array(z.string()), z.string()]).optional(),
736
+ patternsEstablished: z.union([z.array(z.string()), z.string()]).optional(),
737
+ observabilitySurfaces: z.union([z.array(z.string()), z.string()]).optional(),
738
+ provides: z.union([z.array(z.string()), z.string()]).optional(),
739
+ requirementsSurfaced: z.union([z.array(z.string()), z.string()]).optional(),
740
+ drillDownPaths: z.union([z.array(z.string()), z.string()]).optional(),
741
+ affects: z.union([z.array(z.string()), z.string()]).optional(),
742
+ requirementsAdvanced: z.array(z.union([
743
+ z.object({ id: z.string(), how: z.string() }),
744
+ z.string(),
745
+ ])).optional(),
746
+ requirementsValidated: z.array(z.union([
747
+ z.object({ id: z.string(), proof: z.string() }),
748
+ z.string(),
749
+ ])).optional(),
750
+ requirementsInvalidated: z.array(z.union([
751
+ z.object({ id: z.string(), what: z.string() }),
752
+ z.string(),
753
+ ])).optional(),
754
+ filesModified: z.array(z.union([
755
+ z.object({ path: z.string(), description: z.string() }),
756
+ z.string(),
757
+ ])).optional(),
758
+ requires: z.array(z.union([
759
+ z.object({ slice: z.string(), provides: z.string() }),
760
+ z.string(),
761
+ ])).optional(),
762
+ };
763
+ const sliceCompleteSchema = z.object(sliceCompleteParams);
764
+
765
+ const summarySaveParams = {
766
+ projectDir: projectDirParam,
767
+ milestone_id: z.string().describe("Milestone ID (e.g. M001)"),
768
+ slice_id: z.string().optional().describe("Slice ID (e.g. S01)"),
769
+ task_id: z.string().optional().describe("Task ID (e.g. T01)"),
770
+ artifact_type: z.string().describe("Artifact type to save (SUMMARY, RESEARCH, CONTEXT, ASSESSMENT, CONTEXT-DRAFT)"),
771
+ content: z.string().describe("The full markdown content of the artifact"),
772
+ };
773
+ const summarySaveSchema = z.object(summarySaveParams);
774
+
775
+ const taskCompleteParams = {
776
+ projectDir: projectDirParam,
777
+ taskId: z.string().describe("Task ID (e.g. T01)"),
778
+ sliceId: z.string().describe("Slice ID (e.g. S01)"),
779
+ milestoneId: z.string().describe("Milestone ID (e.g. M001)"),
780
+ oneLiner: z.string().describe("One-line summary of what was accomplished"),
781
+ narrative: z.string().describe("Detailed narrative of what happened during the task"),
782
+ verification: z.string().describe("What was verified and how"),
783
+ deviations: z.string().optional().describe("Deviations from the task plan"),
784
+ knownIssues: z.string().optional().describe("Known issues discovered but not fixed"),
785
+ keyFiles: z.array(z.string()).optional().describe("List of key files created or modified"),
786
+ keyDecisions: z.array(z.string()).optional().describe("List of key decisions made during this task"),
787
+ blockerDiscovered: z.boolean().optional().describe("Whether a plan-invalidating blocker was discovered"),
788
+ verificationEvidence: z.array(z.union([
789
+ z.object({
790
+ command: z.string(),
791
+ exitCode: z.number(),
792
+ verdict: z.string(),
793
+ durationMs: z.number(),
794
+ }),
795
+ z.string(),
796
+ ])).optional().describe("Verification evidence entries"),
797
+ };
798
+ const taskCompleteSchema = z.object(taskCompleteParams);
799
+
800
+ const milestoneStatusParams = {
801
+ projectDir: projectDirParam,
802
+ milestoneId: z.string().describe("Milestone ID to query (e.g. M001)"),
803
+ };
804
+ const milestoneStatusSchema = z.object(milestoneStatusParams);
805
+
806
+ export function registerWorkflowTools(server: McpToolServer): void {
807
+ server.tool(
808
+ "gsd_plan_milestone",
809
+ "Write milestone planning state to the GSD database and render ROADMAP.md from DB.",
810
+ planMilestoneParams,
811
+ async (args: Record<string, unknown>) => {
812
+ const parsed = parseWorkflowArgs(planMilestoneSchema, args);
813
+ const { projectDir, ...params } = parsed;
814
+ await enforceWorkflowWriteGate("gsd_plan_milestone", projectDir, params.milestoneId);
815
+ const { executePlanMilestone } = await getWorkflowToolExecutors();
816
+ return runSerializedWorkflowOperation(() => executePlanMilestone(params, projectDir));
817
+ },
818
+ );
819
+
820
+ server.tool(
821
+ "gsd_plan_slice",
822
+ "Write slice/task planning state to the GSD database and render plan artifacts from DB.",
823
+ planSliceParams,
824
+ async (args: Record<string, unknown>) => {
825
+ const parsed = parseWorkflowArgs(planSliceSchema, args);
826
+ const { projectDir, ...params } = parsed;
827
+ await enforceWorkflowWriteGate("gsd_plan_slice", projectDir, params.milestoneId);
828
+ const { executePlanSlice } = await getWorkflowToolExecutors();
829
+ return runSerializedWorkflowOperation(() => executePlanSlice(params, projectDir));
830
+ },
831
+ );
832
+
833
+ server.tool(
834
+ "gsd_replan_slice",
835
+ "Replan a slice after a blocker is discovered, preserving completed tasks and re-rendering PLAN.md + REPLAN.md.",
836
+ replanSliceParams,
837
+ async (args: Record<string, unknown>) => {
838
+ const parsed = parseWorkflowArgs(replanSliceSchema, args);
839
+ return handleReplanSlice(parsed.projectDir, parsed);
840
+ },
841
+ );
842
+
843
+ server.tool(
844
+ "gsd_slice_replan",
845
+ "Alias for gsd_replan_slice. Replan a slice after a blocker is discovered.",
846
+ replanSliceParams,
847
+ async (args: Record<string, unknown>) => {
848
+ const parsed = parseWorkflowArgs(replanSliceSchema, args);
849
+ return handleReplanSlice(parsed.projectDir, parsed);
850
+ },
851
+ );
852
+
853
+ server.tool(
854
+ "gsd_slice_complete",
855
+ "Record a completed slice to the GSD database, render SUMMARY.md + UAT.md, and update roadmap projection.",
856
+ sliceCompleteParams,
857
+ async (args: Record<string, unknown>) => {
858
+ const parsed = parseWorkflowArgs(sliceCompleteSchema, args);
859
+ return handleSliceComplete(parsed.projectDir, parsed);
860
+ },
861
+ );
862
+
863
+ server.tool(
864
+ "gsd_complete_slice",
865
+ "Alias for gsd_slice_complete. Record a completed slice to the GSD database and render summary/UAT artifacts.",
866
+ sliceCompleteParams,
867
+ async (args: Record<string, unknown>) => {
868
+ const parsed = parseWorkflowArgs(sliceCompleteSchema, args);
869
+ return handleSliceComplete(parsed.projectDir, parsed);
870
+ },
871
+ );
872
+
873
+ server.tool(
874
+ "gsd_complete_milestone",
875
+ "Record a completed milestone to the GSD database and render its SUMMARY.md.",
876
+ completeMilestoneParams,
877
+ async (args: Record<string, unknown>) => {
878
+ const parsed = parseWorkflowArgs(completeMilestoneSchema, args);
879
+ return handleCompleteMilestone(parsed.projectDir, parsed);
880
+ },
881
+ );
882
+
883
+ server.tool(
884
+ "gsd_milestone_complete",
885
+ "Alias for gsd_complete_milestone. Record a completed milestone to the GSD database and render its SUMMARY.md.",
886
+ completeMilestoneParams,
887
+ async (args: Record<string, unknown>) => {
888
+ const parsed = parseWorkflowArgs(completeMilestoneSchema, args);
889
+ return handleCompleteMilestone(parsed.projectDir, parsed);
890
+ },
891
+ );
892
+
893
+ server.tool(
894
+ "gsd_validate_milestone",
895
+ "Validate a milestone, persist validation results to the GSD database, and render VALIDATION.md.",
896
+ validateMilestoneParams,
897
+ async (args: Record<string, unknown>) => {
898
+ const parsed = parseWorkflowArgs(validateMilestoneSchema, args);
899
+ return handleValidateMilestone(parsed.projectDir, parsed);
900
+ },
901
+ );
902
+
903
+ server.tool(
904
+ "gsd_milestone_validate",
905
+ "Alias for gsd_validate_milestone. Validate a milestone and render VALIDATION.md.",
906
+ validateMilestoneParams,
907
+ async (args: Record<string, unknown>) => {
908
+ const parsed = parseWorkflowArgs(validateMilestoneSchema, args);
909
+ return handleValidateMilestone(parsed.projectDir, parsed);
910
+ },
911
+ );
912
+
913
+ server.tool(
914
+ "gsd_reassess_roadmap",
915
+ "Reassess a milestone roadmap after a slice completes, writing ASSESSMENT.md and re-rendering ROADMAP.md.",
916
+ reassessRoadmapParams,
917
+ async (args: Record<string, unknown>) => {
918
+ const parsed = parseWorkflowArgs(reassessRoadmapSchema, args);
919
+ return handleReassessRoadmap(parsed.projectDir, parsed);
920
+ },
921
+ );
922
+
923
+ server.tool(
924
+ "gsd_roadmap_reassess",
925
+ "Alias for gsd_reassess_roadmap. Reassess a roadmap after slice completion.",
926
+ reassessRoadmapParams,
927
+ async (args: Record<string, unknown>) => {
928
+ const parsed = parseWorkflowArgs(reassessRoadmapSchema, args);
929
+ return handleReassessRoadmap(parsed.projectDir, parsed);
930
+ },
931
+ );
932
+
933
+ server.tool(
934
+ "gsd_save_gate_result",
935
+ "Save a quality gate result to the GSD database.",
936
+ saveGateResultParams,
937
+ async (args: Record<string, unknown>) => {
938
+ const parsed = parseWorkflowArgs(saveGateResultSchema, args);
939
+ return handleSaveGateResult(parsed.projectDir, parsed);
940
+ },
941
+ );
942
+
943
+ server.tool(
944
+ "gsd_summary_save",
945
+ "Save a GSD summary/research/context/assessment artifact to the database and disk.",
946
+ summarySaveParams,
947
+ async (args: Record<string, unknown>) => {
948
+ const parsed = parseWorkflowArgs(summarySaveSchema, args);
949
+ const { projectDir, milestone_id, slice_id, task_id, artifact_type, content } = parsed;
950
+ await enforceWorkflowWriteGate("gsd_summary_save", projectDir, milestone_id);
951
+ const executors = await getWorkflowToolExecutors();
952
+ const supportedArtifactTypes = getSupportedSummaryArtifactTypes(executors);
953
+ if (!supportedArtifactTypes.includes(artifact_type)) {
954
+ throw new Error(
955
+ `artifact_type must be one of: ${supportedArtifactTypes.join(", ")}`,
956
+ );
957
+ }
958
+ return runSerializedWorkflowOperation(() =>
959
+ executors.executeSummarySave({ milestone_id, slice_id, task_id, artifact_type, content }, projectDir),
960
+ );
961
+ },
962
+ );
963
+
964
+ server.tool(
965
+ "gsd_task_complete",
966
+ "Record a completed task to the GSD database and render its SUMMARY.md.",
967
+ taskCompleteParams,
968
+ async (args: Record<string, unknown>) => {
969
+ const parsed = parseWorkflowArgs(taskCompleteSchema, args);
970
+ const { projectDir, ...taskArgs } = parsed;
971
+ return handleTaskComplete(projectDir, taskArgs);
972
+ },
973
+ );
974
+
975
+ server.tool(
976
+ "gsd_complete_task",
977
+ "Alias for gsd_task_complete. Record a completed task to the GSD database and render its SUMMARY.md.",
978
+ taskCompleteParams,
979
+ async (args: Record<string, unknown>) => {
980
+ const parsed = parseWorkflowArgs(taskCompleteSchema, args);
981
+ const { projectDir, ...taskArgs } = parsed;
982
+ return handleTaskComplete(projectDir, taskArgs);
983
+ },
984
+ );
985
+
986
+ server.tool(
987
+ "gsd_milestone_status",
988
+ "Read the current status of a milestone and all its slices from the GSD database.",
989
+ milestoneStatusParams,
990
+ async (args: Record<string, unknown>) => {
991
+ const { projectDir, milestoneId } = parseWorkflowArgs(milestoneStatusSchema, args);
992
+ await enforceWorkflowWriteGate("gsd_milestone_status", projectDir, milestoneId);
993
+ const { executeMilestoneStatus } = await getWorkflowToolExecutors();
994
+ return runSerializedWorkflowOperation(() => executeMilestoneStatus({ milestoneId }, projectDir));
995
+ },
996
+ );
997
+ }