gsd-pi 2.70.1-dev.3591dcf → 2.70.1-dev.3e19108
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.
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +2 -0
- package/dist/resources/extensions/gsd/auto-start.js +3 -11
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +4 -0
- package/dist/resources/extensions/gsd/guided-flow.js +12 -10
- package/dist/resources/extensions/gsd/init-wizard.js +3 -11
- package/dist/resources/extensions/gsd/prompts/discuss.md +31 -13
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +34 -0
- package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +56 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/required-server-files.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/2826.dd3dc8bbd3025fa5.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-6e4d7e9a4f57bed4.js → webpack-b868033a5834586d.js} +1 -1
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/mcp-server/dist/env-writer.d.ts +39 -0
- package/packages/mcp-server/dist/env-writer.d.ts.map +1 -0
- package/packages/mcp-server/dist/env-writer.js +158 -0
- package/packages/mcp-server/dist/env-writer.js.map +1 -0
- package/packages/mcp-server/dist/server.d.ts +11 -2
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +102 -2
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/src/env-writer.test.ts +280 -0
- package/packages/mcp-server/src/env-writer.ts +183 -0
- package/packages/mcp-server/src/secure-env-collect.test.ts +265 -0
- package/packages/mcp-server/src/server.ts +137 -3
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +133 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +58 -21
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +152 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +83 -27
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +2 -0
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +33 -0
- package/src/resources/extensions/gsd/auto-start.ts +3 -13
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +4 -0
- package/src/resources/extensions/gsd/guided-flow.ts +12 -9
- package/src/resources/extensions/gsd/init-wizard.ts +3 -13
- package/src/resources/extensions/gsd/prompts/discuss.md +31 -13
- package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +155 -1
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +22 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +60 -25
- package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +76 -0
- package/src/resources/extensions/gsd/workflow-mcp.ts +1 -1
- package/dist/web/standalone/.next/static/chunks/2826.821e01b07d92e948.js +0 -9
- /package/dist/web/standalone/.next/static/{KdlODhIktLmeRKpLpHdKb → cHCEWiRJM5bXJa9HkP1QU}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{KdlODhIktLmeRKpLpHdKb → cHCEWiRJM5bXJa9HkP1QU}/_ssgManifest.js +0 -0
|
@@ -278,6 +278,7 @@ export function createClaudeCodeElicitationHandler(ui) {
|
|
|
278
278
|
*/
|
|
279
279
|
export function buildSdkOptions(modelId, prompt, extraOptions = {}) {
|
|
280
280
|
const mcpServers = buildWorkflowMcpServers();
|
|
281
|
+
const disallowedTools = ["AskUserQuestion"];
|
|
281
282
|
return {
|
|
282
283
|
pathToClaudeCodeExecutable: getClaudePath(),
|
|
283
284
|
model: modelId,
|
|
@@ -288,6 +289,7 @@ export function buildSdkOptions(modelId, prompt, extraOptions = {}) {
|
|
|
288
289
|
allowDangerouslySkipPermissions: true,
|
|
289
290
|
settingSources: ["project"],
|
|
290
291
|
systemPrompt: { type: "preset", preset: "claude_code" },
|
|
292
|
+
disallowedTools,
|
|
291
293
|
...(mcpServers ? { mcpServers } : {}),
|
|
292
294
|
betas: modelId.includes("sonnet") ? ["context-1m-2025-08-07"] : [],
|
|
293
295
|
...extraOptions,
|
|
@@ -248,17 +248,9 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
|
|
|
248
248
|
logWarning("engine", `mkdir failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const result = ensureProjectWorkflowMcpConfig(base);
|
|
255
|
-
if (result.status !== "unchanged") {
|
|
256
|
-
ctx.ui.notify(`Claude Code MCP prepared at ${result.configPath}`, "info");
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
catch (err) {
|
|
260
|
-
ctx.ui.notify(`Claude Code MCP prep failed: ${err instanceof Error ? err.message : String(err)}`, "warning");
|
|
261
|
-
}
|
|
251
|
+
{
|
|
252
|
+
const { prepareWorkflowMcpForProject } = await import("./workflow-mcp-auto-prep.js");
|
|
253
|
+
prepareWorkflowMcpForProject(ctx, base);
|
|
262
254
|
}
|
|
263
255
|
// Initialize GitServiceImpl
|
|
264
256
|
s.gitService = new GitServiceImpl(s.basePath, loadEffectiveGSDPreferences()?.preferences?.git ?? {});
|
|
@@ -39,6 +39,8 @@ export function registerHooks(pi) {
|
|
|
39
39
|
resetToolCallLoopGuard();
|
|
40
40
|
resetAskUserQuestionsCache();
|
|
41
41
|
await syncServiceTierStatus(ctx);
|
|
42
|
+
const { prepareWorkflowMcpForProject } = await import("../workflow-mcp-auto-prep.js");
|
|
43
|
+
prepareWorkflowMcpForProject(ctx, process.cwd());
|
|
42
44
|
// Apply show_token_cost preference (#1515)
|
|
43
45
|
try {
|
|
44
46
|
const { loadEffectiveGSDPreferences } = await import("../preferences.js");
|
|
@@ -78,6 +80,8 @@ export function registerHooks(pi) {
|
|
|
78
80
|
resetAskUserQuestionsCache();
|
|
79
81
|
clearDiscussionFlowState();
|
|
80
82
|
await syncServiceTierStatus(ctx);
|
|
83
|
+
const { prepareWorkflowMcpForProject } = await import("../workflow-mcp-auto-prep.js");
|
|
84
|
+
prepareWorkflowMcpForProject(ctx, process.cwd());
|
|
81
85
|
loadToolApiKeys();
|
|
82
86
|
});
|
|
83
87
|
pi.on("before_agent_start", async (event, ctx) => {
|
|
@@ -334,8 +334,9 @@ function resolveAvailableModel(modelId, availableModels, currentProvider) {
|
|
|
334
334
|
* Build the discuss-and-plan prompt for a new milestone.
|
|
335
335
|
* Used by all three "new milestone" paths (first ever, no active, all complete).
|
|
336
336
|
*/
|
|
337
|
-
function buildDiscussPrompt(nextId, preamble, _basePath, preparationContext) {
|
|
337
|
+
function buildDiscussPrompt(nextId, preamble, _basePath, pi, ctx, preparationContext) {
|
|
338
338
|
const milestoneRel = `.gsd/milestones/${nextId}`;
|
|
339
|
+
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
|
339
340
|
const inlinedTemplates = [
|
|
340
341
|
inlineTemplate("project", "Project"),
|
|
341
342
|
inlineTemplate("requirements", "Requirements"),
|
|
@@ -347,6 +348,7 @@ function buildDiscussPrompt(nextId, preamble, _basePath, preparationContext) {
|
|
|
347
348
|
milestoneId: nextId,
|
|
348
349
|
preamble,
|
|
349
350
|
preparationContext: preparationContext ?? "",
|
|
351
|
+
structuredQuestionsAvailable,
|
|
350
352
|
contextPath: `${milestoneRel}/${nextId}-CONTEXT.md`,
|
|
351
353
|
roadmapPath: `${milestoneRel}/${nextId}-ROADMAP.md`,
|
|
352
354
|
inlinedTemplates,
|
|
@@ -390,7 +392,7 @@ function buildHeadlessDiscussPrompt(nextId, seedContext, _basePath) {
|
|
|
390
392
|
* @param basePath - Root directory of the project
|
|
391
393
|
* @returns The discuss prompt string
|
|
392
394
|
*/
|
|
393
|
-
async function prepareAndBuildDiscussPrompt(ctx, nextId, preamble, basePath) {
|
|
395
|
+
async function prepareAndBuildDiscussPrompt(ctx, pi, nextId, preamble, basePath) {
|
|
394
396
|
const prefs = loadEffectiveGSDPreferences()?.preferences ?? {};
|
|
395
397
|
// Run preparation if enabled (default: true) — results are injected as
|
|
396
398
|
// supplementary context into the standard discuss prompt, NOT as a
|
|
@@ -421,7 +423,7 @@ async function prepareAndBuildDiscussPrompt(ctx, nextId, preamble, basePath) {
|
|
|
421
423
|
logWarning("guided", `preparation failed, proceeding without context: ${err.message}`);
|
|
422
424
|
}
|
|
423
425
|
}
|
|
424
|
-
return buildDiscussPrompt(nextId, preamble, basePath, preparationContext);
|
|
426
|
+
return buildDiscussPrompt(nextId, preamble, basePath, pi, ctx, preparationContext);
|
|
425
427
|
}
|
|
426
428
|
/**
|
|
427
429
|
* Bootstrap a .gsd/ project from scratch for headless use.
|
|
@@ -638,7 +640,7 @@ export async function showDiscuss(ctx, pi, basePath) {
|
|
|
638
640
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
639
641
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
|
|
640
642
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: false, createdAt: Date.now() });
|
|
641
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
643
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
642
644
|
}
|
|
643
645
|
return;
|
|
644
646
|
}
|
|
@@ -994,7 +996,7 @@ async function handleMilestoneActions(ctx, pi, basePath, milestoneId, milestoneT
|
|
|
994
996
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
995
997
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
|
|
996
998
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
997
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
999
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
998
1000
|
return true;
|
|
999
1001
|
}
|
|
1000
1002
|
// "back" or null
|
|
@@ -1161,7 +1163,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1161
1163
|
if (isFirst) {
|
|
1162
1164
|
// First ever — skip wizard, just ask directly
|
|
1163
1165
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
1164
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New project, milestone ${nextId}. Do NOT read or explore .gsd/ — it's empty scaffolding.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1166
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New project, milestone ${nextId}. Do NOT read or explore .gsd/ — it's empty scaffolding.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1165
1167
|
}
|
|
1166
1168
|
else {
|
|
1167
1169
|
const choice = await showNextAction(ctx, {
|
|
@@ -1179,7 +1181,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1179
1181
|
});
|
|
1180
1182
|
if (choice === "new_milestone") {
|
|
1181
1183
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
1182
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1184
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1183
1185
|
}
|
|
1184
1186
|
}
|
|
1185
1187
|
return;
|
|
@@ -1211,7 +1213,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1211
1213
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1212
1214
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
|
|
1213
1215
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
1214
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1216
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1215
1217
|
}
|
|
1216
1218
|
else if (choice === "status") {
|
|
1217
1219
|
const { fireStatusViaCommand } = await import("./commands.js");
|
|
@@ -1275,7 +1277,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1275
1277
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1276
1278
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
|
|
1277
1279
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
1278
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1280
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1279
1281
|
}
|
|
1280
1282
|
return;
|
|
1281
1283
|
}
|
|
@@ -1365,7 +1367,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
|
|
|
1365
1367
|
const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
|
|
1366
1368
|
const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
|
|
1367
1369
|
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
|
|
1368
|
-
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1370
|
+
await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, pi, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
|
|
1369
1371
|
}
|
|
1370
1372
|
else if (choice === "discard_milestone") {
|
|
1371
1373
|
const confirmed = await showConfirm(ctx, {
|
|
@@ -225,17 +225,9 @@ export async function showProjectInit(ctx, pi, basePath, detection) {
|
|
|
225
225
|
catch {
|
|
226
226
|
// Non-fatal — STATE.md will be regenerated on next /gsd invocation
|
|
227
227
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
const result = ensureProjectWorkflowMcpConfig(basePath);
|
|
232
|
-
if (result.status !== "unchanged") {
|
|
233
|
-
ctx.ui.notify(`Claude Code MCP prepared at ${result.configPath}`, "info");
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
catch (err) {
|
|
237
|
-
ctx.ui.notify(`Claude Code MCP prep failed: ${err instanceof Error ? err.message : String(err)}`, "warning");
|
|
238
|
-
}
|
|
228
|
+
{
|
|
229
|
+
const { prepareWorkflowMcpForProject } = await import("./workflow-mcp-auto-prep.js");
|
|
230
|
+
prepareWorkflowMcpForProject(ctx, basePath);
|
|
239
231
|
}
|
|
240
232
|
ctx.ui.notify("GSD initialized. Starting your first milestone...", "info");
|
|
241
233
|
return { completed: true, bootstrapped: true };
|
|
@@ -49,6 +49,26 @@ This happens ONCE, before the first round. The goal: your first questions should
|
|
|
49
49
|
|
|
50
50
|
For subsequent rounds, continue investigating between rounds — check docs, search, or scout as needed to make each round's questions smarter. But the first-round investigation is mandatory and explicit. Distribute searches across turns rather than clustering them in one turn.
|
|
51
51
|
|
|
52
|
+
## Question Rounds
|
|
53
|
+
|
|
54
|
+
Ask **1–3 questions per round**. Keep each round tightly focused on one or two of the depth checklist dimensions — do not try to cover all six in one round.
|
|
55
|
+
|
|
56
|
+
**If `{{structuredQuestionsAvailable}}` is `true`:** use `ask_user_questions` for each round. 1–3 questions per call, each as a separate question object. Keep option labels short (3–5 words). Always include a freeform "Other / let me explain" option. When the user picks that option or writes a long freeform answer, switch to plain text follow-up for that thread before resuming structured questions. **IMPORTANT: Call `ask_user_questions` exactly once per turn. Never make multiple calls with the same or overlapping questions — wait for the user's response before asking the next round.**
|
|
57
|
+
|
|
58
|
+
**If `{{structuredQuestionsAvailable}}` is `false`:** ask questions in plain text. Keep each round to 1–3 focused questions. Wait for answers before asking the next round.
|
|
59
|
+
|
|
60
|
+
After each answer set, investigate further if any answer opens a new unknown, then ask the next round.
|
|
61
|
+
|
|
62
|
+
### Round cadence
|
|
63
|
+
|
|
64
|
+
After each round of answers, decide whether you already have enough depth to write strong output.
|
|
65
|
+
|
|
66
|
+
- **Incremental persistence:** After every 2 question rounds, silently save a `{{milestoneId}}-CONTEXT-DRAFT.md` using `gsd_summary_save` with `artifact_type: "CONTEXT-DRAFT"` and `milestone_id: "{{milestoneId}}"`. This protects confirmed work against session crashes. Do NOT mention this save to the user.
|
|
67
|
+
- If not ready, continue to the next round immediately. Do **not** ask a meta "ready to wrap up?" question after every round.
|
|
68
|
+
- **Depth-matching rule:** Simple, well-defined work needs fewer rounds — maybe 1–2. Large, ambiguous visions need more — maybe 4+. Do not pad rounds to hit a number. Stop when the Depth Enforcement checklist below is fully satisfied.
|
|
69
|
+
- Do not count the reflection step as a question round. Rounds start after reflection is confirmed.
|
|
70
|
+
- When you genuinely believe the depth checklist is satisfied, move to the Depth Verification step below. Do not ask a separate "ready to wrap up?" gate — the depth verification IS the gate.
|
|
71
|
+
|
|
52
72
|
## Questioning Philosophy
|
|
53
73
|
|
|
54
74
|
You are a thinking partner, not an interviewer.
|
|
@@ -94,29 +114,27 @@ Do NOT offer to proceed until ALL of the following are satisfied. Track these in
|
|
|
94
114
|
|
|
95
115
|
Before offering to proceed, demonstrate absorption: reference specific things the user emphasized, specific terminology they used, specific nuance they sharpened — and show how those shaped your understanding. Synthesize, don't recite. "Your emphasis on X led me to prioritize Y over Z" is good. "You said X, you said Y, you said Z" is not. The user should feel heard in the specifics, not just acknowledged in the abstract.
|
|
96
116
|
|
|
97
|
-
**Questioning depth should match scope.** Simple, well-defined work needs fewer rounds — maybe 1-2. Large, ambiguous visions need more — maybe 4+. Don't pad rounds to hit a number. Stop when the depth checklist is satisfied and you genuinely understand the work.
|
|
98
|
-
|
|
99
|
-
Do not count the reflection step as a question round. Rounds start after reflection is confirmed.
|
|
100
|
-
|
|
101
117
|
## Depth Verification
|
|
102
118
|
|
|
103
119
|
Before moving to the wrap-up gate, present a structured depth summary as a checkpoint.
|
|
104
120
|
|
|
105
121
|
**Print the summary as normal chat text first** — this is where the formatting renders properly. Structure the summary across the depth checklist dimensions using the user's own terminology and framing. Cover: what you understood them to be building, what shaped your understanding most (their emphasis, constraints, concerns), and any areas where you're least confident in your understanding.
|
|
106
122
|
|
|
107
|
-
**Then
|
|
123
|
+
**Then confirm:**
|
|
108
124
|
|
|
109
|
-
**
|
|
125
|
+
**If `{{structuredQuestionsAvailable}}` is `true`:** use `ask_user_questions` with:
|
|
126
|
+
- header: "Depth Check"
|
|
127
|
+
- question: "Did I capture the depth right?"
|
|
128
|
+
- options: "Yes, you got it (Recommended)", "Not quite — let me clarify"
|
|
129
|
+
- **The question ID must contain `depth_verification`** (e.g., `depth_verification_confirm`) — this naming convention enables downstream mechanical detection and the write-gate.
|
|
110
130
|
|
|
111
|
-
|
|
112
|
-
1. Print in chat: the full depth summary with markdown formatting (headers, bold, bullets)
|
|
113
|
-
2. Call `ask_user_questions` with: header "Depth Check", question "Did I capture the depth right?", options "Yes, you got it (Recommended)" and "Not quite — let me clarify"
|
|
131
|
+
**If `{{structuredQuestionsAvailable}}` is `false`:** ask in plain text: "Did I capture that correctly? If not, tell me what I missed." Wait for explicit confirmation before proceeding. **The same non-bypassable gate applies to the plain-text path** — if the user does not respond, gives an ambiguous answer, or does not explicitly confirm, you MUST re-ask. Never rationalize past a missing confirmation.
|
|
114
132
|
|
|
115
133
|
If they clarify, absorb the correction and re-verify.
|
|
116
134
|
|
|
117
135
|
The depth verification is the required write-gate. Do **not** add another meta "ready to proceed?" checkpoint immediately after it unless there is still material ambiguity.
|
|
118
136
|
|
|
119
|
-
**CRITICAL — Non-bypassable gate:** The system mechanically blocks CONTEXT.md writes until the user selects the "(Recommended)" option. If the user declines, cancels, or the tool fails, you MUST re-ask — never rationalize past the block ("tool not responding, I'll proceed" is forbidden). The gate exists to protect the user's work; treat a block as an instruction, not an obstacle to work around.
|
|
137
|
+
**CRITICAL — Non-bypassable gate:** The system mechanically blocks CONTEXT.md writes until the user selects the "(Recommended)" option (structured path) or explicitly confirms (plain-text path). If the user declines, cancels, does not respond, or the tool fails, you MUST re-ask — never rationalize past the block ("tool not responding, I'll proceed" is forbidden). The gate exists to protect the user's work; treat a block as an instruction, not an obstacle to work around.
|
|
120
138
|
|
|
121
139
|
## Wrap-up Gate
|
|
122
140
|
|
|
@@ -244,7 +262,7 @@ If a milestone has no dependencies, omit the frontmatter. The dependency chain f
|
|
|
244
262
|
|
|
245
263
|
#### Phase 3: Sequential readiness gate for remaining milestones
|
|
246
264
|
|
|
247
|
-
For each remaining milestone **one at a time, in sequence**, decide the most likely readiness mode from the evidence you already have, then use `ask_user_questions`
|
|
265
|
+
For each remaining milestone **one at a time, in sequence**, decide the most likely readiness mode from the evidence you already have, then present the three options below to the user. **If `{{structuredQuestionsAvailable}}` is `true`:** use `ask_user_questions`. **If `{{structuredQuestionsAvailable}}` is `false`:** present the options as a plain-text numbered list and ask the user to type their choice. **Non-bypassable:** If the user does not respond, gives an ambiguous answer, or the tool fails, you MUST re-ask — never rationalize past the block or auto-select a readiness mode. Present three options:
|
|
248
266
|
|
|
249
267
|
- **"Discuss now"** — The user wants to conduct a focused discussion for this milestone in the current session, while the context from the broader discussion is still fresh. Proceed with a focused discussion for this milestone (reflection → investigation → questioning → depth verification). When the discussion concludes, write a full `CONTEXT.md`. Then move to the gate for the next milestone.
|
|
250
268
|
- **"Write draft for later"** — This milestone has seed material from the current conversation but needs its own dedicated discussion in a future session. Write a `CONTEXT-DRAFT.md` capturing the seed material (what was discussed, key ideas, provisional scope, open questions). Mark it clearly as a draft, not a finalized context. **What happens downstream:** When auto-mode reaches this milestone, it pauses and notifies the user: "M00x has draft context — needs discussion. Run /gsd." The `/gsd` wizard shows a "Discuss from draft" option that seeds the new discussion with this draft, so nothing from the current conversation is lost. After the dedicated discussion produces a full CONTEXT.md, the draft file is automatically deleted.
|
|
@@ -256,9 +274,9 @@ Before writing each milestone's CONTEXT.md (whether primary or secondary), you M
|
|
|
256
274
|
|
|
257
275
|
1. **Read the actual code** for every file or module you reference. Confirm APIs exist, check what functions actually do, identify phantom capabilities (code that exists but isn't wired up).
|
|
258
276
|
2. **Check for stale assumptions** — the codebase changes. Verify referenced modules still work as described.
|
|
259
|
-
3. **Present findings** — use `ask_user_questions` with a question ID containing BOTH `depth_verification` AND the milestone ID (e.g., `depth_verification_M002`). Present: what you're about to write, key technical findings from investigation, risks the code review surfaced.
|
|
277
|
+
3. **Present findings** — **If `{{structuredQuestionsAvailable}}` is `true`:** use `ask_user_questions` with a question ID containing BOTH `depth_verification` AND the milestone ID (e.g., `depth_verification_M002`). Present: what you're about to write, key technical findings from investigation, risks the code review surfaced. **If `{{structuredQuestionsAvailable}}` is `false`:** present the same findings in plain text and ask for explicit confirmation before proceeding.
|
|
260
278
|
|
|
261
|
-
**The system mechanically blocks CONTEXT.md writes until the per-milestone depth verification passes
|
|
279
|
+
**The system mechanically blocks CONTEXT.md writes until the per-milestone depth verification passes** (structured path: user selects "(Recommended)" option; plain-text path: user explicitly confirms). Each milestone needs its own verification — one global verification does not unlock all milestones.
|
|
262
280
|
|
|
263
281
|
**Why sequential, not batch:** After writing the primary milestone's context and roadmap, the agent still has context window capacity. Asking one milestone at a time lets the user decide per-milestone whether to invest that remaining capacity in a focused discussion now, or defer to a future session. A batch question ("Ready/Draft/Queue for M002, M003, M004?") forces the user to decide everything upfront without knowing how much session capacity remains.
|
|
264
282
|
|
|
@@ -23,12 +23,14 @@ export async function executeSummarySave(params, basePath = process.cwd()) {
|
|
|
23
23
|
return {
|
|
24
24
|
content: [{ type: "text", text: "Error: GSD database is not available. Cannot save artifact." }],
|
|
25
25
|
details: { operation: "save_summary", error: "db_unavailable" },
|
|
26
|
+
isError: true,
|
|
26
27
|
};
|
|
27
28
|
}
|
|
28
29
|
if (!isSupportedSummaryArtifactType(params.artifact_type)) {
|
|
29
30
|
return {
|
|
30
31
|
content: [{ type: "text", text: `Error: Invalid artifact_type "${params.artifact_type}". Must be one of: ${SUPPORTED_SUMMARY_ARTIFACT_TYPES.join(", ")}` }],
|
|
31
32
|
details: { operation: "save_summary", error: "invalid_artifact_type" },
|
|
33
|
+
isError: true,
|
|
32
34
|
};
|
|
33
35
|
}
|
|
34
36
|
const contextGuard = shouldBlockContextArtifactSaveInSnapshot(loadWriteGateSnapshot(basePath), params.artifact_type, params.milestone_id ?? null, params.slice_id ?? null);
|
|
@@ -36,6 +38,7 @@ export async function executeSummarySave(params, basePath = process.cwd()) {
|
|
|
36
38
|
return {
|
|
37
39
|
content: [{ type: "text", text: `Error saving artifact: ${contextGuard.reason ?? "context write blocked"}` }],
|
|
38
40
|
details: { operation: "save_summary", error: "context_write_blocked" },
|
|
41
|
+
isError: true,
|
|
39
42
|
};
|
|
40
43
|
}
|
|
41
44
|
try {
|
|
@@ -68,6 +71,7 @@ export async function executeSummarySave(params, basePath = process.cwd()) {
|
|
|
68
71
|
return {
|
|
69
72
|
content: [{ type: "text", text: `Error saving artifact: ${msg}` }],
|
|
70
73
|
details: { operation: "save_summary", error: msg },
|
|
74
|
+
isError: true,
|
|
71
75
|
};
|
|
72
76
|
}
|
|
73
77
|
}
|
|
@@ -77,6 +81,7 @@ export async function executeTaskComplete(params, basePath = process.cwd()) {
|
|
|
77
81
|
return {
|
|
78
82
|
content: [{ type: "text", text: "Error: GSD database is not available. Cannot complete task." }],
|
|
79
83
|
details: { operation: "complete_task", error: "db_unavailable" },
|
|
84
|
+
isError: true,
|
|
80
85
|
};
|
|
81
86
|
}
|
|
82
87
|
try {
|
|
@@ -87,6 +92,7 @@ export async function executeTaskComplete(params, basePath = process.cwd()) {
|
|
|
87
92
|
return {
|
|
88
93
|
content: [{ type: "text", text: `Error completing task: ${result.error}` }],
|
|
89
94
|
details: { operation: "complete_task", error: result.error },
|
|
95
|
+
isError: true,
|
|
90
96
|
};
|
|
91
97
|
}
|
|
92
98
|
return {
|
|
@@ -106,6 +112,7 @@ export async function executeTaskComplete(params, basePath = process.cwd()) {
|
|
|
106
112
|
return {
|
|
107
113
|
content: [{ type: "text", text: `Error completing task: ${msg}` }],
|
|
108
114
|
details: { operation: "complete_task", error: msg },
|
|
115
|
+
isError: true,
|
|
109
116
|
};
|
|
110
117
|
}
|
|
111
118
|
}
|
|
@@ -115,6 +122,7 @@ export async function executeSliceComplete(params, basePath = process.cwd()) {
|
|
|
115
122
|
return {
|
|
116
123
|
content: [{ type: "text", text: "Error: GSD database is not available. Cannot complete slice." }],
|
|
117
124
|
details: { operation: "complete_slice", error: "db_unavailable" },
|
|
125
|
+
isError: true,
|
|
118
126
|
};
|
|
119
127
|
}
|
|
120
128
|
try {
|
|
@@ -167,6 +175,7 @@ export async function executeSliceComplete(params, basePath = process.cwd()) {
|
|
|
167
175
|
return {
|
|
168
176
|
content: [{ type: "text", text: `Error completing slice: ${result.error}` }],
|
|
169
177
|
details: { operation: "complete_slice", error: result.error },
|
|
178
|
+
isError: true,
|
|
170
179
|
};
|
|
171
180
|
}
|
|
172
181
|
return {
|
|
@@ -186,6 +195,7 @@ export async function executeSliceComplete(params, basePath = process.cwd()) {
|
|
|
186
195
|
return {
|
|
187
196
|
content: [{ type: "text", text: `Error completing slice: ${msg}` }],
|
|
188
197
|
details: { operation: "complete_slice", error: msg },
|
|
198
|
+
isError: true,
|
|
189
199
|
};
|
|
190
200
|
}
|
|
191
201
|
}
|
|
@@ -195,6 +205,7 @@ export async function executeCompleteMilestone(params, basePath = process.cwd())
|
|
|
195
205
|
return {
|
|
196
206
|
content: [{ type: "text", text: "Error: GSD database is not available. Cannot complete milestone." }],
|
|
197
207
|
details: { operation: "complete_milestone", error: "db_unavailable" },
|
|
208
|
+
isError: true,
|
|
198
209
|
};
|
|
199
210
|
}
|
|
200
211
|
try {
|
|
@@ -204,6 +215,7 @@ export async function executeCompleteMilestone(params, basePath = process.cwd())
|
|
|
204
215
|
return {
|
|
205
216
|
content: [{ type: "text", text: `Error completing milestone: ${result.error}` }],
|
|
206
217
|
details: { operation: "complete_milestone", error: result.error },
|
|
218
|
+
isError: true,
|
|
207
219
|
};
|
|
208
220
|
}
|
|
209
221
|
return {
|
|
@@ -221,6 +233,7 @@ export async function executeCompleteMilestone(params, basePath = process.cwd())
|
|
|
221
233
|
return {
|
|
222
234
|
content: [{ type: "text", text: `Error completing milestone: ${msg}` }],
|
|
223
235
|
details: { operation: "complete_milestone", error: msg },
|
|
236
|
+
isError: true,
|
|
224
237
|
};
|
|
225
238
|
}
|
|
226
239
|
}
|
|
@@ -230,6 +243,7 @@ export async function executeValidateMilestone(params, basePath = process.cwd())
|
|
|
230
243
|
return {
|
|
231
244
|
content: [{ type: "text", text: "Error: GSD database is not available. Cannot validate milestone." }],
|
|
232
245
|
details: { operation: "validate_milestone", error: "db_unavailable" },
|
|
246
|
+
isError: true,
|
|
233
247
|
};
|
|
234
248
|
}
|
|
235
249
|
try {
|
|
@@ -238,6 +252,7 @@ export async function executeValidateMilestone(params, basePath = process.cwd())
|
|
|
238
252
|
return {
|
|
239
253
|
content: [{ type: "text", text: `Error validating milestone: ${result.error}` }],
|
|
240
254
|
details: { operation: "validate_milestone", error: result.error },
|
|
255
|
+
isError: true,
|
|
241
256
|
};
|
|
242
257
|
}
|
|
243
258
|
return {
|
|
@@ -256,6 +271,7 @@ export async function executeValidateMilestone(params, basePath = process.cwd())
|
|
|
256
271
|
return {
|
|
257
272
|
content: [{ type: "text", text: `Error validating milestone: ${msg}` }],
|
|
258
273
|
details: { operation: "validate_milestone", error: msg },
|
|
274
|
+
isError: true,
|
|
259
275
|
};
|
|
260
276
|
}
|
|
261
277
|
}
|
|
@@ -265,6 +281,7 @@ export async function executeReassessRoadmap(params, basePath = process.cwd()) {
|
|
|
265
281
|
return {
|
|
266
282
|
content: [{ type: "text", text: "Error: GSD database is not available. Cannot reassess roadmap." }],
|
|
267
283
|
details: { operation: "reassess_roadmap", error: "db_unavailable" },
|
|
284
|
+
isError: true,
|
|
268
285
|
};
|
|
269
286
|
}
|
|
270
287
|
try {
|
|
@@ -273,6 +290,7 @@ export async function executeReassessRoadmap(params, basePath = process.cwd()) {
|
|
|
273
290
|
return {
|
|
274
291
|
content: [{ type: "text", text: `Error reassessing roadmap: ${result.error}` }],
|
|
275
292
|
details: { operation: "reassess_roadmap", error: result.error },
|
|
293
|
+
isError: true,
|
|
276
294
|
};
|
|
277
295
|
}
|
|
278
296
|
return {
|
|
@@ -292,6 +310,7 @@ export async function executeReassessRoadmap(params, basePath = process.cwd()) {
|
|
|
292
310
|
return {
|
|
293
311
|
content: [{ type: "text", text: `Error reassessing roadmap: ${msg}` }],
|
|
294
312
|
details: { operation: "reassess_roadmap", error: msg },
|
|
313
|
+
isError: true,
|
|
295
314
|
};
|
|
296
315
|
}
|
|
297
316
|
}
|
|
@@ -301,6 +320,7 @@ export async function executeSaveGateResult(params, basePath = process.cwd()) {
|
|
|
301
320
|
return {
|
|
302
321
|
content: [{ type: "text", text: "Error: GSD database is not available." }],
|
|
303
322
|
details: { operation: "save_gate_result", error: "db_unavailable" },
|
|
323
|
+
isError: true,
|
|
304
324
|
};
|
|
305
325
|
}
|
|
306
326
|
const validGates = ["Q3", "Q4", "Q5", "Q6", "Q7", "Q8"];
|
|
@@ -308,6 +328,7 @@ export async function executeSaveGateResult(params, basePath = process.cwd()) {
|
|
|
308
328
|
return {
|
|
309
329
|
content: [{ type: "text", text: `Error: Invalid gateId "${params.gateId}". Must be one of: ${validGates.join(", ")}` }],
|
|
310
330
|
details: { operation: "save_gate_result", error: "invalid_gate_id" },
|
|
331
|
+
isError: true,
|
|
311
332
|
};
|
|
312
333
|
}
|
|
313
334
|
const validVerdicts = ["pass", "flag", "omitted"];
|
|
@@ -315,6 +336,7 @@ export async function executeSaveGateResult(params, basePath = process.cwd()) {
|
|
|
315
336
|
return {
|
|
316
337
|
content: [{ type: "text", text: `Error: Invalid verdict "${params.verdict}". Must be one of: ${validVerdicts.join(", ")}` }],
|
|
317
338
|
details: { operation: "save_gate_result", error: "invalid_verdict" },
|
|
339
|
+
isError: true,
|
|
318
340
|
};
|
|
319
341
|
}
|
|
320
342
|
try {
|
|
@@ -339,6 +361,7 @@ export async function executeSaveGateResult(params, basePath = process.cwd()) {
|
|
|
339
361
|
return {
|
|
340
362
|
content: [{ type: "text", text: `Error saving gate result: ${msg}` }],
|
|
341
363
|
details: { operation: "save_gate_result", error: msg },
|
|
364
|
+
isError: true,
|
|
342
365
|
};
|
|
343
366
|
}
|
|
344
367
|
}
|
|
@@ -348,6 +371,7 @@ export async function executePlanMilestone(params, basePath = process.cwd()) {
|
|
|
348
371
|
return {
|
|
349
372
|
content: [{ type: "text", text: "Error: GSD database is not available. Cannot plan milestone." }],
|
|
350
373
|
details: { operation: "plan_milestone", error: "db_unavailable" },
|
|
374
|
+
isError: true,
|
|
351
375
|
};
|
|
352
376
|
}
|
|
353
377
|
try {
|
|
@@ -356,6 +380,7 @@ export async function executePlanMilestone(params, basePath = process.cwd()) {
|
|
|
356
380
|
return {
|
|
357
381
|
content: [{ type: "text", text: `Error planning milestone: ${result.error}` }],
|
|
358
382
|
details: { operation: "plan_milestone", error: result.error },
|
|
383
|
+
isError: true,
|
|
359
384
|
};
|
|
360
385
|
}
|
|
361
386
|
return {
|
|
@@ -373,6 +398,7 @@ export async function executePlanMilestone(params, basePath = process.cwd()) {
|
|
|
373
398
|
return {
|
|
374
399
|
content: [{ type: "text", text: `Error planning milestone: ${msg}` }],
|
|
375
400
|
details: { operation: "plan_milestone", error: msg },
|
|
401
|
+
isError: true,
|
|
376
402
|
};
|
|
377
403
|
}
|
|
378
404
|
}
|
|
@@ -382,6 +408,7 @@ export async function executePlanSlice(params, basePath = process.cwd()) {
|
|
|
382
408
|
return {
|
|
383
409
|
content: [{ type: "text", text: "Error: GSD database is not available. Cannot plan slice." }],
|
|
384
410
|
details: { operation: "plan_slice", error: "db_unavailable" },
|
|
411
|
+
isError: true,
|
|
385
412
|
};
|
|
386
413
|
}
|
|
387
414
|
try {
|
|
@@ -390,6 +417,7 @@ export async function executePlanSlice(params, basePath = process.cwd()) {
|
|
|
390
417
|
return {
|
|
391
418
|
content: [{ type: "text", text: `Error planning slice: ${result.error}` }],
|
|
392
419
|
details: { operation: "plan_slice", error: result.error },
|
|
420
|
+
isError: true,
|
|
393
421
|
};
|
|
394
422
|
}
|
|
395
423
|
return {
|
|
@@ -409,6 +437,7 @@ export async function executePlanSlice(params, basePath = process.cwd()) {
|
|
|
409
437
|
return {
|
|
410
438
|
content: [{ type: "text", text: `Error planning slice: ${msg}` }],
|
|
411
439
|
details: { operation: "plan_slice", error: msg },
|
|
440
|
+
isError: true,
|
|
412
441
|
};
|
|
413
442
|
}
|
|
414
443
|
}
|
|
@@ -418,6 +447,7 @@ export async function executeReplanSlice(params, basePath = process.cwd()) {
|
|
|
418
447
|
return {
|
|
419
448
|
content: [{ type: "text", text: "Error: GSD database is not available. Cannot replan slice." }],
|
|
420
449
|
details: { operation: "replan_slice", error: "db_unavailable" },
|
|
450
|
+
isError: true,
|
|
421
451
|
};
|
|
422
452
|
}
|
|
423
453
|
try {
|
|
@@ -426,6 +456,7 @@ export async function executeReplanSlice(params, basePath = process.cwd()) {
|
|
|
426
456
|
return {
|
|
427
457
|
content: [{ type: "text", text: `Error replanning slice: ${result.error}` }],
|
|
428
458
|
details: { operation: "replan_slice", error: result.error },
|
|
459
|
+
isError: true,
|
|
429
460
|
};
|
|
430
461
|
}
|
|
431
462
|
return {
|
|
@@ -445,6 +476,7 @@ export async function executeReplanSlice(params, basePath = process.cwd()) {
|
|
|
445
476
|
return {
|
|
446
477
|
content: [{ type: "text", text: `Error replanning slice: ${msg}` }],
|
|
447
478
|
details: { operation: "replan_slice", error: msg },
|
|
479
|
+
isError: true,
|
|
448
480
|
};
|
|
449
481
|
}
|
|
450
482
|
}
|
|
@@ -455,6 +487,7 @@ export async function executeMilestoneStatus(params, basePath = process.cwd()) {
|
|
|
455
487
|
return {
|
|
456
488
|
content: [{ type: "text", text: "Error: GSD database is not available." }],
|
|
457
489
|
details: { operation: "milestone_status", error: "db_unavailable" },
|
|
490
|
+
isError: true,
|
|
458
491
|
};
|
|
459
492
|
}
|
|
460
493
|
const adapter = _getAdapter();
|
|
@@ -503,6 +536,7 @@ export async function executeMilestoneStatus(params, basePath = process.cwd()) {
|
|
|
503
536
|
return {
|
|
504
537
|
content: [{ type: "text", text: `Error querying milestone status: ${msg}` }],
|
|
505
538
|
details: { operation: "milestone_status", error: msg },
|
|
539
|
+
isError: true,
|
|
506
540
|
};
|
|
507
541
|
}
|
|
508
542
|
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ensureProjectWorkflowMcpConfig, } from "./mcp-project-config.js";
|
|
2
|
+
import { usesWorkflowMcpTransport } from "./workflow-mcp.js";
|
|
3
|
+
function getAuthModeSafe(ctx, provider) {
|
|
4
|
+
if (!provider)
|
|
5
|
+
return undefined;
|
|
6
|
+
const getAuthMode = ctx.modelRegistry?.getProviderAuthMode;
|
|
7
|
+
if (typeof getAuthMode !== "function")
|
|
8
|
+
return undefined;
|
|
9
|
+
try {
|
|
10
|
+
return getAuthMode(provider);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function hasClaudeCodeProvider(ctx) {
|
|
17
|
+
return getAuthModeSafe(ctx, "claude-code") === "externalCli";
|
|
18
|
+
}
|
|
19
|
+
function isClaudeCodeProviderReady(ctx) {
|
|
20
|
+
const readyCheck = ctx.modelRegistry?.isProviderRequestReady;
|
|
21
|
+
if (typeof readyCheck !== "function")
|
|
22
|
+
return false;
|
|
23
|
+
try {
|
|
24
|
+
return readyCheck("claude-code");
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function shouldAutoPrepareWorkflowMcp(ctx) {
|
|
31
|
+
const provider = ctx.model?.provider;
|
|
32
|
+
const baseUrl = ctx.model?.baseUrl;
|
|
33
|
+
const authMode = getAuthModeSafe(ctx, provider);
|
|
34
|
+
if (usesWorkflowMcpTransport(authMode, baseUrl))
|
|
35
|
+
return true;
|
|
36
|
+
if (provider === "claude-code")
|
|
37
|
+
return true;
|
|
38
|
+
if (hasClaudeCodeProvider(ctx))
|
|
39
|
+
return true;
|
|
40
|
+
return isClaudeCodeProviderReady(ctx);
|
|
41
|
+
}
|
|
42
|
+
export function prepareWorkflowMcpForProject(ctx, projectRoot) {
|
|
43
|
+
if (!shouldAutoPrepareWorkflowMcp(ctx))
|
|
44
|
+
return null;
|
|
45
|
+
try {
|
|
46
|
+
const result = ensureProjectWorkflowMcpConfig(projectRoot);
|
|
47
|
+
if (result.status !== "unchanged") {
|
|
48
|
+
ctx.ui?.notify?.(`Claude Code MCP prepared at ${result.configPath}`, "info");
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
ctx.ui?.notify?.(`Claude Code MCP prep failed: ${err instanceof Error ? err.message : String(err)}. Detected Claude Code model but no workflow MCP. Please run /gsd mcp init . from your project root.`, "warning");
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -310,7 +310,7 @@ export function getWorkflowTransportSupportError(provider, requiredTools, option
|
|
|
310
310
|
const unitLabel = options.unitType ? ` for ${options.unitType}` : "";
|
|
311
311
|
const providerLabel = `"${provider}"`;
|
|
312
312
|
if (!launch) {
|
|
313
|
-
return `Provider ${providerLabel} cannot run ${surface}${unitLabel}: the GSD workflow MCP server is not configured or discoverable.
|
|
313
|
+
return `Provider ${providerLabel} cannot run ${surface}${unitLabel}: the GSD workflow MCP server is not configured or discoverable. Detected Claude Code model but no workflow MCP. Please run /gsd mcp init . from your project root. You can also configure GSD_WORKFLOW_MCP_COMMAND, build packages/mcp-server/dist/cli.js, or install gsd-mcp-server on PATH.`;
|
|
314
314
|
}
|
|
315
315
|
const missing = [...new Set(requiredTools)].filter((tool) => !MCP_WORKFLOW_TOOL_SURFACE.has(tool));
|
|
316
316
|
if (missing.length === 0)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
cHCEWiRJM5bXJa9HkP1QU
|