fix claude-code discuss question fallback
This commit is contained in:
parent
7ee1fa0c46
commit
5a940856c1
4 changed files with 64 additions and 9 deletions
|
|
@ -997,7 +997,7 @@ export async function buildDiscussMilestonePrompt(mid: string, midTitle: string,
|
|||
milestoneId: mid,
|
||||
milestoneTitle: midTitle,
|
||||
inlinedTemplates: discussTemplates,
|
||||
structuredQuestionsAvailable: "true",
|
||||
structuredQuestionsAvailable: "false",
|
||||
commitInstruction: "Do not commit planning artifacts — .gsd/ is managed externally.",
|
||||
fastPathInstruction: "",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ import { DISCUSS_TOOLS_ALLOWLIST } from "./constants.js";
|
|||
import {
|
||||
getWorkflowTransportSupportError,
|
||||
getRequiredWorkflowToolsForGuidedUnit,
|
||||
supportsStructuredQuestions,
|
||||
} from "./workflow-mcp.js";
|
||||
import {
|
||||
runPreparation,
|
||||
|
|
@ -367,6 +368,20 @@ async function dispatchWorkflow(
|
|||
}
|
||||
}
|
||||
|
||||
function getStructuredQuestionsAvailability(
|
||||
pi: ExtensionAPI,
|
||||
ctx: ExtensionContext | undefined,
|
||||
): "true" | "false" {
|
||||
if (!ctx) return "false";
|
||||
|
||||
const provider = ctx.model?.provider;
|
||||
const authMode = provider ? ctx.modelRegistry.getProviderAuthMode(provider) : undefined;
|
||||
return supportsStructuredQuestions(pi.getActiveTools(), {
|
||||
authMode,
|
||||
baseUrl: ctx.model?.baseUrl,
|
||||
}) ? "true" : "false";
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a model ID string to a model object from available models.
|
||||
* Handles "provider/model" and bare ID formats.
|
||||
|
|
@ -739,7 +754,7 @@ export async function showDiscuss(
|
|||
|
||||
if (choice === "discuss_draft") {
|
||||
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
||||
const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
|
||||
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
||||
const basePrompt = loadPrompt("guided-discuss-milestone", {
|
||||
milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
||||
commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
|
||||
|
|
@ -752,7 +767,7 @@ export async function showDiscuss(
|
|||
await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone");
|
||||
} else if (choice === "discuss_fresh") {
|
||||
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
||||
const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
|
||||
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
||||
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: mid, step: false, createdAt: Date.now() });
|
||||
await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
||||
milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
||||
|
|
@ -910,7 +925,7 @@ export async function showDiscuss(
|
|||
if (confirm !== "rediscuss") continue;
|
||||
}
|
||||
|
||||
const sqAvail = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
|
||||
const sqAvail = getStructuredQuestionsAvailability(pi, ctx);
|
||||
const prompt = await buildDiscussSlicePrompt(mid, chosen.id, chosen.title, basePath, { rediscuss: isRediscuss, structuredQuestionsAvailable: sqAvail });
|
||||
await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-slice");
|
||||
|
||||
|
|
@ -1020,7 +1035,7 @@ async function dispatchDiscussForMilestone(
|
|||
].join("\n")
|
||||
: "";
|
||||
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
||||
const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
|
||||
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
||||
const basePrompt = loadPrompt("guided-discuss-milestone", {
|
||||
milestoneId: mid,
|
||||
milestoneTitle,
|
||||
|
|
@ -1461,7 +1476,7 @@ export async function showSmartEntry(
|
|||
|
||||
if (choice === "discuss_draft") {
|
||||
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
||||
const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
|
||||
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
||||
const basePrompt = loadPrompt("guided-discuss-milestone", {
|
||||
milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
||||
commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
|
||||
|
|
@ -1474,7 +1489,7 @@ export async function showSmartEntry(
|
|||
await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone");
|
||||
} else if (choice === "discuss_fresh") {
|
||||
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
||||
const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
|
||||
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
||||
pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId, step: stepMode, createdAt: Date.now() });
|
||||
await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
||||
milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
||||
|
|
@ -1572,7 +1587,7 @@ export async function showSmartEntry(
|
|||
}), "gsd-run", ctx, "plan-milestone");
|
||||
} else if (choice === "discuss") {
|
||||
const discussMilestoneTemplates = inlineTemplate("context", "Context");
|
||||
const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
|
||||
const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
|
||||
await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
|
||||
milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
|
||||
commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
|
||||
|
|
@ -1712,7 +1727,7 @@ export async function showSmartEntry(
|
|||
}),
|
||||
}), "gsd-run", ctx, "plan-slice");
|
||||
} else if (choice === "discuss") {
|
||||
const sqAvail = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
|
||||
const sqAvail = getStructuredQuestionsAvailability(pi, ctx);
|
||||
await dispatchWorkflow(pi, await buildDiscussSlicePrompt(milestoneId, sliceId, sliceTitle, basePath, { rediscuss: hasContext, structuredQuestionsAvailable: sqAvail }), "gsd-run", ctx, "discuss-slice");
|
||||
} else if (choice === "research") {
|
||||
const researchTemplates = inlineTemplate("research", "Research");
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {
|
|||
getWorkflowTransportSupportError,
|
||||
getRequiredWorkflowToolsForAutoUnit,
|
||||
getRequiredWorkflowToolsForGuidedUnit,
|
||||
supportsStructuredQuestions,
|
||||
usesWorkflowMcpTransport,
|
||||
} from "../workflow-mcp.ts";
|
||||
|
||||
|
|
@ -291,6 +292,30 @@ test("usesWorkflowMcpTransport matches local externalCli providers", () => {
|
|||
assert.equal(usesWorkflowMcpTransport("oauth", "local://custom"), false);
|
||||
});
|
||||
|
||||
test("supportsStructuredQuestions disables structured ask flow on workflow MCP transports", () => {
|
||||
assert.equal(
|
||||
supportsStructuredQuestions(["ask_user_questions"], {
|
||||
authMode: "externalCli",
|
||||
baseUrl: "local://claude-code",
|
||||
}),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
supportsStructuredQuestions(["ask_user_questions"], {
|
||||
authMode: "oauth",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
}),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
supportsStructuredQuestions([], {
|
||||
authMode: "oauth",
|
||||
baseUrl: "https://api.anthropic.com",
|
||||
}),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test("transport compatibility passes when required tools fit current MCP surface", () => {
|
||||
const error = getWorkflowTransportSupportError(
|
||||
"claude-code",
|
||||
|
|
|
|||
|
|
@ -348,6 +348,21 @@ export function usesWorkflowMcpTransport(
|
|||
return authMode === "externalCli" && typeof baseUrl === "string" && baseUrl.startsWith("local://");
|
||||
}
|
||||
|
||||
export function supportsStructuredQuestions(
|
||||
activeTools: string[],
|
||||
options: Pick<WorkflowCapabilityOptions, "authMode" | "baseUrl"> = {},
|
||||
): boolean {
|
||||
if (!activeTools.includes("ask_user_questions")) return false;
|
||||
|
||||
// Workflow MCP currently exposes ask_user_questions via MCP form elicitation.
|
||||
// Local external CLI transports such as Claude Code can invoke the tool, but
|
||||
// do not reliably complete that elicitation round-trip yet, so guided discuss
|
||||
// prompts must fall back to plain-text questioning.
|
||||
if (usesWorkflowMcpTransport(options.authMode, options.baseUrl)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function getWorkflowTransportSupportError(
|
||||
provider: string | undefined,
|
||||
requiredTools: string[],
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue