mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-05 08:10:14 +00:00
feat: update ask_followup_question format to use nested XML elements
- Changed from attribute-based format (<suggest mode="architect">) to nested element format (<suggest><mode>architect</mode><content>...</content></suggest>) - Updated documentation in ask-followup-question.ts with new format and examples - Modified parsing logic in askFollowupQuestionTool.ts to handle new format while maintaining backward compatibility - Added comprehensive tests for both new format and backward compatibility - All existing tests pass
This commit is contained in:
parent
613abe08fc
commit
55884030c4
3 changed files with 158 additions and 20 deletions
|
|
@ -4,14 +4,19 @@ Description: Ask the user a question to gather additional information needed to
|
|||
|
||||
Parameters:
|
||||
- question: (required) A clear, specific question addressing the information needed
|
||||
- follow_up: (required) A list of 2-4 suggested answers, each in its own <suggest> tag. Suggestions must be complete, actionable answers without placeholders. Optionally include mode attribute to switch modes (code/architect/etc.)
|
||||
- follow_up: (required) A list of 2-4 suggested answers, each in its own <suggest> tag with nested <content> element. Suggestions must be complete, actionable answers without placeholders. Optionally include <mode> element to switch modes (code/architect/etc.)
|
||||
|
||||
Usage:
|
||||
<ask_followup_question>
|
||||
<question>Your question here</question>
|
||||
<follow_up>
|
||||
<suggest>First suggestion</suggest>
|
||||
<suggest mode="code">Action with mode switch</suggest>
|
||||
<suggest>
|
||||
<content>Your suggested answer here</content>
|
||||
</suggest>
|
||||
<suggest>
|
||||
<mode>code</mode>
|
||||
<content>Implement the solution</content>
|
||||
</suggest>
|
||||
</follow_up>
|
||||
</ask_followup_question>
|
||||
|
||||
|
|
@ -19,9 +24,33 @@ Example:
|
|||
<ask_followup_question>
|
||||
<question>What is the path to the frontend-config.json file?</question>
|
||||
<follow_up>
|
||||
<suggest>./src/frontend-config.json</suggest>
|
||||
<suggest>./config/frontend-config.json</suggest>
|
||||
<suggest>./frontend-config.json</suggest>
|
||||
<suggest>
|
||||
<content>./src/frontend-config.json</content>
|
||||
</suggest>
|
||||
<suggest>
|
||||
<content>./config/frontend-config.json</content>
|
||||
</suggest>
|
||||
<suggest>
|
||||
<content>./frontend-config.json</content>
|
||||
</suggest>
|
||||
</follow_up>
|
||||
</ask_followup_question>
|
||||
|
||||
Example: Asking a question with mode switching options
|
||||
<ask_followup_question>
|
||||
<question>How would you like to proceed with this task?</question>
|
||||
<follow_up>
|
||||
<suggest>
|
||||
<mode>code</mode>
|
||||
<content>Start implementing the solution</content>
|
||||
</suggest>
|
||||
<suggest>
|
||||
<mode>architect</mode>
|
||||
<content>Plan the architecture first</content>
|
||||
</suggest>
|
||||
<suggest>
|
||||
<content>Continue with more details</content>
|
||||
</suggest>
|
||||
</follow_up>
|
||||
</ask_followup_question>`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,96 @@ describe("askFollowupQuestionTool", () => {
|
|||
})
|
||||
})
|
||||
|
||||
it("should parse suggestions without mode attributes", async () => {
|
||||
it("should parse suggestions without mode (new nested format)", async () => {
|
||||
const block: ToolUse = {
|
||||
type: "tool_use",
|
||||
name: "ask_followup_question",
|
||||
params: {
|
||||
question: "What would you like to do?",
|
||||
follow_up:
|
||||
"<suggest><content>Option 1</content></suggest><suggest><content>Option 2</content></suggest>",
|
||||
},
|
||||
partial: false,
|
||||
}
|
||||
|
||||
await askFollowupQuestionTool(
|
||||
mockCline,
|
||||
block,
|
||||
vi.fn(),
|
||||
vi.fn(),
|
||||
mockPushToolResult,
|
||||
vi.fn((tag, content) => content),
|
||||
)
|
||||
|
||||
expect(mockCline.ask).toHaveBeenCalledWith(
|
||||
"followup",
|
||||
expect.stringContaining('"suggest":[{"answer":"Option 1"},{"answer":"Option 2"}]'),
|
||||
false,
|
||||
)
|
||||
})
|
||||
|
||||
it("should parse suggestions with mode (new nested format)", async () => {
|
||||
const block: ToolUse = {
|
||||
type: "tool_use",
|
||||
name: "ask_followup_question",
|
||||
params: {
|
||||
question: "What would you like to do?",
|
||||
follow_up:
|
||||
"<suggest><mode>code</mode><content>Write code</content></suggest><suggest><mode>debug</mode><content>Debug issue</content></suggest>",
|
||||
},
|
||||
partial: false,
|
||||
}
|
||||
|
||||
await askFollowupQuestionTool(
|
||||
mockCline,
|
||||
block,
|
||||
vi.fn(),
|
||||
vi.fn(),
|
||||
mockPushToolResult,
|
||||
vi.fn((tag, content) => content),
|
||||
)
|
||||
|
||||
expect(mockCline.ask).toHaveBeenCalledWith(
|
||||
"followup",
|
||||
expect.stringContaining(
|
||||
'"suggest":[{"answer":"Write code","mode":"code"},{"answer":"Debug issue","mode":"debug"}]',
|
||||
),
|
||||
false,
|
||||
)
|
||||
})
|
||||
|
||||
it("should handle mixed suggestions with and without mode (new nested format)", async () => {
|
||||
const block: ToolUse = {
|
||||
type: "tool_use",
|
||||
name: "ask_followup_question",
|
||||
params: {
|
||||
question: "What would you like to do?",
|
||||
follow_up:
|
||||
"<suggest><content>Regular option</content></suggest><suggest><mode>architect</mode><content>Plan architecture</content></suggest>",
|
||||
},
|
||||
partial: false,
|
||||
}
|
||||
|
||||
await askFollowupQuestionTool(
|
||||
mockCline,
|
||||
block,
|
||||
vi.fn(),
|
||||
vi.fn(),
|
||||
mockPushToolResult,
|
||||
vi.fn((tag, content) => content),
|
||||
)
|
||||
|
||||
expect(mockCline.ask).toHaveBeenCalledWith(
|
||||
"followup",
|
||||
expect.stringContaining(
|
||||
'"suggest":[{"answer":"Regular option"},{"answer":"Plan architecture","mode":"architect"}]',
|
||||
),
|
||||
false,
|
||||
)
|
||||
})
|
||||
|
||||
// Backward compatibility tests for old format
|
||||
it("should parse suggestions without mode attributes (old format - backward compatibility)", async () => {
|
||||
const block: ToolUse = {
|
||||
type: "tool_use",
|
||||
name: "ask_followup_question",
|
||||
|
|
@ -48,7 +137,7 @@ describe("askFollowupQuestionTool", () => {
|
|||
)
|
||||
})
|
||||
|
||||
it("should parse suggestions with mode attributes", async () => {
|
||||
it("should parse suggestions with mode attributes (old format - backward compatibility)", async () => {
|
||||
const block: ToolUse = {
|
||||
type: "tool_use",
|
||||
name: "ask_followup_question",
|
||||
|
|
@ -77,7 +166,7 @@ describe("askFollowupQuestionTool", () => {
|
|||
)
|
||||
})
|
||||
|
||||
it("should handle mixed suggestions with and without mode attributes", async () => {
|
||||
it("should handle mixed suggestions with and without mode attributes (old format - backward compatibility)", async () => {
|
||||
const block: ToolUse = {
|
||||
type: "tool_use",
|
||||
name: "ask_followup_question",
|
||||
|
|
|
|||
|
|
@ -34,15 +34,18 @@ export async function askFollowupQuestionTool(
|
|||
}
|
||||
|
||||
if (follow_up) {
|
||||
// Define the actual structure returned by the XML parser
|
||||
type ParsedSuggestion = string | { "#text": string; "@_mode"?: string }
|
||||
// Define the actual structure returned by the XML parser for the new format
|
||||
type ParsedSuggestion =
|
||||
| string // For backward compatibility with old format or when stopNodes is used
|
||||
| { "#text": string; "@_mode"?: string } // For backward compatibility with old format
|
||||
|
||||
let parsedSuggest: {
|
||||
suggest: ParsedSuggestion[] | ParsedSuggestion
|
||||
}
|
||||
|
||||
try {
|
||||
parsedSuggest = parseXml(follow_up, ["suggest"]) as {
|
||||
// Don't use stopNodes for suggest elements to allow proper parsing of nested structure
|
||||
parsedSuggest = parseXml(follow_up) as {
|
||||
suggest: ParsedSuggestion[] | ParsedSuggestion
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -58,17 +61,34 @@ export async function askFollowupQuestionTool(
|
|||
: [parsedSuggest?.suggest].filter((sug): sug is ParsedSuggestion => sug !== undefined)
|
||||
|
||||
// Transform parsed XML to our Suggest format
|
||||
const normalizedSuggest: Suggest[] = rawSuggestions.map((sug) => {
|
||||
const normalizedSuggest: Suggest[] = rawSuggestions.map((sug: any) => {
|
||||
if (typeof sug === "string") {
|
||||
// Simple string suggestion (no mode attribute)
|
||||
// Simple string suggestion (backward compatibility)
|
||||
return { answer: sug }
|
||||
} else {
|
||||
// XML object with text content and optional mode attribute
|
||||
const result: Suggest = { answer: sug["#text"] }
|
||||
if (sug["@_mode"]) {
|
||||
result.mode = sug["@_mode"]
|
||||
} else if (sug && typeof sug === "object") {
|
||||
// Check for new nested element format
|
||||
if ("content" in sug) {
|
||||
const result: Suggest = { answer: sug.content }
|
||||
if (sug.mode) {
|
||||
result.mode = sug.mode
|
||||
}
|
||||
return result
|
||||
}
|
||||
return result
|
||||
// Old attribute format (backward compatibility)
|
||||
else if ("#text" in sug) {
|
||||
const result: Suggest = { answer: sug["#text"] }
|
||||
if (sug["@_mode"]) {
|
||||
result.mode = sug["@_mode"]
|
||||
}
|
||||
return result
|
||||
}
|
||||
// Fallback for any other object structure
|
||||
else {
|
||||
return { answer: JSON.stringify(sug) }
|
||||
}
|
||||
} else {
|
||||
// Fallback for any unexpected type
|
||||
return { answer: String(sug) }
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue