refactor: consolidate code action and enhance prompts into unified support prompts system

- Rename codeActionPrompt to supportPrompt for better clarity
- Move enhance prompt functionality into support prompts system
- Add ENHANCE tab alongside other support prompt types
- Update UI to show enhancement configuration in ENHANCE tab
- Update tests to reflect new unified structure

This change simplifies the prompt system by treating enhancement as another type of support prompt rather than a separate system.
This commit is contained in:
sam hoang
2025-01-23 10:46:04 +07:00
parent 22907a0578
commit 55a5a97d8b
5 changed files with 140 additions and 213 deletions

View File

@@ -40,7 +40,7 @@ import { enhancePrompt } from "../../utils/enhance-prompt"
import { getCommitInfo, searchCommits, getWorkingState } from "../../utils/git" import { getCommitInfo, searchCommits, getWorkingState } from "../../utils/git"
import { ConfigManager } from "../config/ConfigManager" import { ConfigManager } from "../config/ConfigManager"
import { CustomModesManager } from "../config/CustomModesManager" import { CustomModesManager } from "../config/CustomModesManager"
import { enhance, codeActionPrompt } from "../../shared/support-prompt" import { supportPrompt } from "../../shared/support-prompt"
import { ACTION_NAMES } from "../CodeActionProvider" import { ACTION_NAMES } from "../CodeActionProvider"
@@ -205,7 +205,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
const { customPrompts } = await visibleProvider.getState() const { customPrompts } = await visibleProvider.getState()
const prompt = codeActionPrompt.create(promptType, params, customPrompts) const prompt = supportPrompt.create(promptType, params, customPrompts)
await visibleProvider.initClineWithTask(prompt) await visibleProvider.initClineWithTask(prompt)
} }
@@ -996,16 +996,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
} }
} }
const getEnhancePrompt = (value: string | PromptComponent | undefined): string => {
if (typeof value === "string") {
return value
}
return enhance.prompt // Use the constant from modes.ts which we know is a string
}
const enhancedPrompt = await enhancePrompt( const enhancedPrompt = await enhancePrompt(
configToUse, configToUse,
message.text, message.text,
getEnhancePrompt(customPrompts?.enhance), supportPrompt.get(customPrompts, "ENHANCE"),
) )
await this.postMessageToWebview({ await this.postMessageToWebview({
type: "enhancedPrompt", type: "enhancedPrompt",

View File

@@ -1,4 +1,4 @@
import { codeActionPrompt, type CodeActionType } from "../support-prompt" import { supportPrompt } from "../support-prompt"
describe("Code Action Prompts", () => { describe("Code Action Prompts", () => {
const testFilePath = "test/file.ts" const testFilePath = "test/file.ts"
@@ -6,7 +6,7 @@ describe("Code Action Prompts", () => {
describe("EXPLAIN action", () => { describe("EXPLAIN action", () => {
it("should format explain prompt correctly", () => { it("should format explain prompt correctly", () => {
const prompt = codeActionPrompt.create("EXPLAIN", { const prompt = supportPrompt.create("EXPLAIN", {
filePath: testFilePath, filePath: testFilePath,
selectedText: testCode, selectedText: testCode,
}) })
@@ -21,7 +21,7 @@ describe("Code Action Prompts", () => {
describe("FIX action", () => { describe("FIX action", () => {
it("should format fix prompt without diagnostics", () => { it("should format fix prompt without diagnostics", () => {
const prompt = codeActionPrompt.create("FIX", { const prompt = supportPrompt.create("FIX", {
filePath: testFilePath, filePath: testFilePath,
selectedText: testCode, selectedText: testCode,
}) })
@@ -45,7 +45,7 @@ describe("Code Action Prompts", () => {
}, },
] ]
const prompt = codeActionPrompt.create("FIX", { const prompt = supportPrompt.create("FIX", {
filePath: testFilePath, filePath: testFilePath,
selectedText: testCode, selectedText: testCode,
diagnostics, diagnostics,
@@ -60,7 +60,7 @@ describe("Code Action Prompts", () => {
describe("IMPROVE action", () => { describe("IMPROVE action", () => {
it("should format improve prompt correctly", () => { it("should format improve prompt correctly", () => {
const prompt = codeActionPrompt.create("IMPROVE", { const prompt = supportPrompt.create("IMPROVE", {
filePath: testFilePath, filePath: testFilePath,
selectedText: testCode, selectedText: testCode,
}) })
@@ -74,10 +74,26 @@ describe("Code Action Prompts", () => {
}) })
}) })
describe("ENHANCE action", () => {
it("should format enhance prompt correctly", () => {
const prompt = supportPrompt.create("ENHANCE", {
filePath: testFilePath,
selectedText: testCode,
})
expect(prompt).toBe(
"Generate an enhanced version of this prompt (reply with only the enhanced prompt - no conversation, explanations, lead-in, bullet points, placeholders, or surrounding quotes):",
)
// Verify it ignores parameters since ENHANCE template doesn't use any
expect(prompt).not.toContain(testFilePath)
expect(prompt).not.toContain(testCode)
})
})
describe("get template", () => { describe("get template", () => {
it("should return default template when no custom prompts provided", () => { it("should return default template when no custom prompts provided", () => {
const template = codeActionPrompt.get(undefined, "EXPLAIN") const template = supportPrompt.get(undefined, "EXPLAIN")
expect(template).toBe(codeActionPrompt.default.EXPLAIN) expect(template).toBe(supportPrompt.default.EXPLAIN)
}) })
it("should return custom template when provided", () => { it("should return custom template when provided", () => {
@@ -85,7 +101,7 @@ describe("Code Action Prompts", () => {
const customPrompts = { const customPrompts = {
EXPLAIN: customTemplate, EXPLAIN: customTemplate,
} }
const template = codeActionPrompt.get(customPrompts, "EXPLAIN") const template = supportPrompt.get(customPrompts, "EXPLAIN")
expect(template).toBe(customTemplate) expect(template).toBe(customTemplate)
}) })
@@ -93,8 +109,8 @@ describe("Code Action Prompts", () => {
const customPrompts = { const customPrompts = {
SOMETHING_ELSE: "Other template", SOMETHING_ELSE: "Other template",
} }
const template = codeActionPrompt.get(customPrompts, "EXPLAIN") const template = supportPrompt.get(customPrompts, "EXPLAIN")
expect(template).toBe(codeActionPrompt.default.EXPLAIN) expect(template).toBe(supportPrompt.default.EXPLAIN)
}) })
}) })
@@ -105,7 +121,7 @@ describe("Code Action Prompts", () => {
EXPLAIN: customTemplate, EXPLAIN: customTemplate,
} }
const prompt = codeActionPrompt.create( const prompt = supportPrompt.create(
"EXPLAIN", "EXPLAIN",
{ {
filePath: testFilePath, filePath: testFilePath,
@@ -123,7 +139,7 @@ describe("Code Action Prompts", () => {
EXPLAIN: "Other template", EXPLAIN: "Other template",
} }
const prompt = codeActionPrompt.create( const prompt = supportPrompt.create(
"EXPLAIN", "EXPLAIN",
{ {
filePath: testFilePath, filePath: testFilePath,

View File

@@ -1,21 +1,4 @@
// Separate enhance prompt type and definition // Support prompts
export type EnhanceConfig = {
prompt: string
}
export const enhance: EnhanceConfig = {
prompt: "Generate an enhanced version of this prompt (reply with only the enhanced prompt - no conversation, explanations, lead-in, bullet points, placeholders, or surrounding quotes):",
} as const
// Completely separate enhance prompt handling
export const enhancePrompt = {
default: enhance.prompt,
get: (customPrompts: Record<string, any> | undefined): string => {
return customPrompts?.enhance ?? enhance.prompt
},
} as const
// Code action prompts
type PromptParams = Record<string, string | any[]> type PromptParams = Record<string, string | any[]>
const generateDiagnosticText = (diagnostics?: any[]) => { const generateDiagnosticText = (diagnostics?: any[]) => {
@@ -41,8 +24,7 @@ export const createPrompt = (template: string, params: PromptParams): string =>
return result return result
} }
const EXPLAIN_TEMPLATE = ` const EXPLAIN_TEMPLATE = `Explain the following code from file path @/\${filePath}:
Explain the following code from file path @/\${filePath}:
\${userInput} \${userInput}
\`\`\` \`\`\`
@@ -55,8 +37,7 @@ Please provide a clear and concise explanation of what this code does, including
3. Important patterns or techniques used 3. Important patterns or techniques used
` `
const FIX_TEMPLATE = ` const FIX_TEMPLATE = `Fix any issues in the following code from file path @/\${filePath}
Fix any issues in the following code from file path @/\${filePath}
\${diagnosticText} \${diagnosticText}
\${userInput} \${userInput}
@@ -71,8 +52,7 @@ Please:
4. Explain what was fixed and why 4. Explain what was fixed and why
` `
const IMPROVE_TEMPLATE = ` const IMPROVE_TEMPLATE = `Improve the following code from file path @/\${filePath}:
Improve the following code from file path @/\${filePath}:
\${userInput} \${userInput}
\`\`\` \`\`\`
@@ -88,31 +68,36 @@ Please suggest improvements for:
Provide the improved code along with explanations for each enhancement. Provide the improved code along with explanations for each enhancement.
` `
const ENHANCE_TEMPLATE =
"Generate an enhanced version of this prompt (reply with only the enhanced prompt - no conversation, explanations, lead-in, bullet points, placeholders, or surrounding quotes):"
// Get template based on prompt type // Get template based on prompt type
const defaultTemplates = { const defaultTemplates = {
EXPLAIN: EXPLAIN_TEMPLATE, EXPLAIN: EXPLAIN_TEMPLATE,
FIX: FIX_TEMPLATE, FIX: FIX_TEMPLATE,
IMPROVE: IMPROVE_TEMPLATE, IMPROVE: IMPROVE_TEMPLATE,
ENHANCE: ENHANCE_TEMPLATE,
} as const } as const
type CodeActionType = keyof typeof defaultTemplates type SupportPromptType = keyof typeof defaultTemplates
export const codeActionPrompt = { export const supportPrompt = {
default: defaultTemplates, default: defaultTemplates,
get: (customPrompts: Record<string, any> | undefined, type: CodeActionType): string => { get: (customPrompts: Record<string, any> | undefined, type: SupportPromptType): string => {
return customPrompts?.[type] ?? defaultTemplates[type] return customPrompts?.[type] ?? defaultTemplates[type]
}, },
create: (type: CodeActionType, params: PromptParams, customPrompts?: Record<string, any>): string => { create: (type: SupportPromptType, params: PromptParams, customPrompts?: Record<string, any>): string => {
const template = codeActionPrompt.get(customPrompts, type) const template = supportPrompt.get(customPrompts, type)
return createPrompt(template, params) return createPrompt(template, params)
}, },
} as const } as const
export type { CodeActionType } export type { SupportPromptType }
// User-friendly labels for code action types // User-friendly labels for support prompt types
export const codeActionLabels: Record<CodeActionType, string> = { export const supportPromptLabels: Record<SupportPromptType, string> = {
FIX: "Fix Issues", FIX: "Fix Issues",
EXPLAIN: "Explain Code", EXPLAIN: "Explain Code",
IMPROVE: "Improve Code", IMPROVE: "Improve Code",
ENHANCE: "Enhance Prompt",
} as const } as const

View File

@@ -9,12 +9,7 @@ import {
} from "@vscode/webview-ui-toolkit/react" } from "@vscode/webview-ui-toolkit/react"
import { useExtensionState } from "../../context/ExtensionStateContext" import { useExtensionState } from "../../context/ExtensionStateContext"
import { Mode, PromptComponent, getRoleDefinition, getAllModes, ModeConfig } from "../../../../src/shared/modes" import { Mode, PromptComponent, getRoleDefinition, getAllModes, ModeConfig } from "../../../../src/shared/modes"
import { import { supportPrompt, SupportPromptType, supportPromptLabels } from "../../../../src/shared/support-prompt"
enhancePrompt,
codeActionPrompt,
CodeActionType,
codeActionLabels,
} from "../../../../src/shared/support-prompt"
import { TOOL_GROUPS, GROUP_DISPLAY_NAMES, ToolGroup } from "../../../../src/shared/tool-groups" import { TOOL_GROUPS, GROUP_DISPLAY_NAMES, ToolGroup } from "../../../../src/shared/tool-groups"
import { vscode } from "../../utils/vscode" import { vscode } from "../../utils/vscode"
@@ -50,7 +45,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
const [selectedPromptTitle, setSelectedPromptTitle] = useState("") const [selectedPromptTitle, setSelectedPromptTitle] = useState("")
const [isToolsEditMode, setIsToolsEditMode] = useState(false) const [isToolsEditMode, setIsToolsEditMode] = useState(false)
const [isCreateModeDialogOpen, setIsCreateModeDialogOpen] = useState(false) const [isCreateModeDialogOpen, setIsCreateModeDialogOpen] = useState(false)
const [activeCodeActionTab, setActiveCodeActionTab] = useState<CodeActionType>("FIX") const [activeSupportTab, setActiveSupportTab] = useState<SupportPromptType>("EXPLAIN")
// Direct update functions // Direct update functions
const updateAgentPrompt = useCallback( const updateAgentPrompt = useCallback(
@@ -255,16 +250,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
return () => window.removeEventListener("message", handler) return () => window.removeEventListener("message", handler)
}, []) }, [])
const updateEnhancePrompt = (value: string | undefined) => { const updateSupportPrompt = (type: SupportPromptType, value: string | undefined) => {
vscode.postMessage({
type: "updateSupportPrompt",
values: {
enhance: value,
},
})
}
const updateCodeActionPrompt = (type: CodeActionType, value: string | undefined) => {
vscode.postMessage({ vscode.postMessage({
type: "updateSupportPrompt", type: "updateSupportPrompt",
values: { values: {
@@ -273,14 +259,6 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
}) })
} }
const handleEnhancePromptChange = (e: Event | React.FormEvent<HTMLElement>): void => {
const value = (e as CustomEvent)?.detail?.target?.value || ((e as any).target as HTMLTextAreaElement).value
const trimmedValue = value.trim()
if (trimmedValue !== enhancePrompt.default) {
updateEnhancePrompt(trimmedValue || enhancePrompt.default)
}
}
const handleAgentReset = (modeSlug: string) => { const handleAgentReset = (modeSlug: string) => {
// Only reset role definition for built-in modes // Only reset role definition for built-in modes
const existingPrompt = customPrompts?.[modeSlug] as PromptComponent const existingPrompt = customPrompts?.[modeSlug] as PromptComponent
@@ -290,26 +268,15 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
}) })
} }
const handleEnhanceReset = () => { const handleSupportReset = (type: SupportPromptType) => {
vscode.postMessage({
type: "resetSupportPrompt",
text: "enhance",
})
}
const handleCodeActionReset = (type: CodeActionType) => {
vscode.postMessage({ vscode.postMessage({
type: "resetSupportPrompt", type: "resetSupportPrompt",
text: type, text: type,
}) })
} }
const getEnhancePromptValue = (): string => { const getSupportPromptValue = (type: SupportPromptType): string => {
return enhancePrompt.get(customPrompts) return supportPrompt.get(customPrompts, type)
}
const getCodeActionPromptValue = (type: CodeActionType): string => {
return codeActionPrompt.get(customPrompts, type)
} }
const handleTestEnhancement = () => { const handleTestEnhancement = () => {
@@ -786,7 +753,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
</div> </div>
<div style={{ marginBottom: "20px" }}> <div style={{ marginBottom: "20px" }}>
<div style={{ fontWeight: "bold", marginBottom: "12px" }}>Code Action Prompts</div> <div style={{ fontWeight: "bold", marginBottom: "12px" }}>Support Prompts</div>
<div <div
style={{ style={{
display: "flex", display: "flex",
@@ -798,33 +765,32 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
paddingBottom: "4px", paddingBottom: "4px",
paddingRight: "20px", paddingRight: "20px",
}}> }}>
{Object.keys(codeActionPrompt.default).map((type) => ( {Object.keys(supportPrompt.default).map((type) => (
<button <button
key={type} key={type}
data-testid={`${type}-tab`} data-testid={`${type}-tab`}
data-active={activeCodeActionTab === type ? "true" : "false"} data-active={activeSupportTab === type ? "true" : "false"}
onClick={() => setActiveCodeActionTab(type as CodeActionType)} onClick={() => setActiveSupportTab(type as SupportPromptType)}
style={{ style={{
padding: "4px 8px", padding: "4px 8px",
border: "none", border: "none",
background: background: activeSupportTab === type ? "var(--vscode-button-background)" : "none",
activeCodeActionTab === type ? "var(--vscode-button-background)" : "none",
color: color:
activeCodeActionTab === type activeSupportTab === type
? "var(--vscode-button-foreground)" ? "var(--vscode-button-foreground)"
: "var(--vscode-foreground)", : "var(--vscode-foreground)",
cursor: "pointer", cursor: "pointer",
opacity: activeCodeActionTab === type ? 1 : 0.8, opacity: activeSupportTab === type ? 1 : 0.8,
borderRadius: "3px", borderRadius: "3px",
fontWeight: "bold", fontWeight: "bold",
}}> }}>
{codeActionLabels[type as CodeActionType]} {supportPromptLabels[type as SupportPromptType]}
</button> </button>
))} ))}
</div> </div>
{/* Show active tab content */} {/* Show active tab content */}
<div key={activeCodeActionTab}> <div key={activeSupportTab}>
<div <div
style={{ style={{
display: "flex", display: "flex",
@@ -832,32 +798,17 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
alignItems: "center", alignItems: "center",
marginBottom: "4px", marginBottom: "4px",
}}> }}>
<div style={{ fontWeight: "bold" }}>{activeCodeActionTab} Prompt</div> <div style={{ fontWeight: "bold" }}>{activeSupportTab} Prompt</div>
<VSCodeButton <VSCodeButton
appearance="icon" appearance="icon"
onClick={() => handleCodeActionReset(activeCodeActionTab)} onClick={() => handleSupportReset(activeSupportTab)}
title={`Reset ${activeCodeActionTab} prompt to default`}> title={`Reset ${activeSupportTab} prompt to default`}>
<span className="codicon codicon-discard"></span> <span className="codicon codicon-discard"></span>
</VSCodeButton> </VSCodeButton>
</div> </div>
<VSCodeTextArea
value={getCodeActionPromptValue(activeCodeActionTab)}
onChange={(e) => {
const value =
(e as CustomEvent)?.detail?.target?.value ||
((e as any).target as HTMLTextAreaElement).value
const trimmedValue = value.trim()
updateCodeActionPrompt(activeCodeActionTab, trimmedValue || undefined)
}}
rows={4}
resize="vertical"
style={{ width: "100%" }}
/>
</div>
</div>
<h3 style={{ color: "var(--vscode-foreground)", margin: "40px 0 20px 0" }}>Prompt Enhancement</h3>
{activeSupportTab === "ENHANCE" && (
<div>
<div <div
style={{ style={{
color: "var(--vscode-foreground)", color: "var(--vscode-foreground)",
@@ -865,18 +816,15 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
marginBottom: "20px", marginBottom: "20px",
marginTop: "5px", marginTop: "5px",
}}> }}>
Use prompt enhancement to get tailored suggestions or improvements for your inputs. This ensures Roo Use prompt enhancement to get tailored suggestions or improvements for your inputs.
understands your intent and provides the best possible responses. This ensures Roo understands your intent and provides the best possible responses.
</div> </div>
<div style={{ display: "flex", flexDirection: "column", gap: "20px" }}>
<div>
<div style={{ marginBottom: "12px" }}> <div style={{ marginBottom: "12px" }}>
<div style={{ marginBottom: "8px" }}> <div style={{ marginBottom: "8px" }}>
<div style={{ fontWeight: "bold", marginBottom: "4px" }}>API Configuration</div> <div style={{ fontWeight: "bold", marginBottom: "4px" }}>API Configuration</div>
<div style={{ fontSize: "13px", color: "var(--vscode-descriptionForeground)" }}> <div style={{ fontSize: "13px", color: "var(--vscode-descriptionForeground)" }}>
You can select an API configuration to always use for enhancing prompts, or just use You can select an API configuration to always use for enhancing prompts, or
whatever is currently selected just use whatever is currently selected
</div> </div>
</div> </div>
<VSCodeDropdown <VSCodeDropdown
@@ -899,42 +847,24 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
))} ))}
</VSCodeDropdown> </VSCodeDropdown>
</div> </div>
</div>
)}
<div style={{ marginBottom: "8px" }}>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "4px",
}}>
<div style={{ fontWeight: "bold" }}>Enhancement Prompt</div>
<div style={{ display: "flex", gap: "8px" }}>
<VSCodeButton
appearance="icon"
onClick={handleEnhanceReset}
title="Revert to default">
<span className="codicon codicon-discard"></span>
</VSCodeButton>
</div>
</div>
<div
style={{
fontSize: "13px",
color: "var(--vscode-descriptionForeground)",
marginBottom: "8px",
}}>
This prompt will be used to refine your input when you hit the sparkle icon in chat.
</div>
</div>
<VSCodeTextArea <VSCodeTextArea
value={getEnhancePromptValue()} value={getSupportPromptValue(activeSupportTab)}
onChange={handleEnhancePromptChange} onChange={(e) => {
const value =
(e as CustomEvent)?.detail?.target?.value ||
((e as any).target as HTMLTextAreaElement).value
const trimmedValue = value.trim()
updateSupportPrompt(activeSupportTab, trimmedValue || undefined)
}}
rows={4} rows={4}
resize="vertical" resize="vertical"
style={{ width: "100%" }} style={{ width: "100%" }}
/> />
{activeSupportTab === "ENHANCE" && (
<div style={{ marginTop: "12px" }}> <div style={{ marginTop: "12px" }}>
<VSCodeTextArea <VSCodeTextArea
value={testPrompt} value={testPrompt}
@@ -961,11 +891,9 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
</VSCodeButton> </VSCodeButton>
</div> </div>
</div> </div>
)}
</div> </div>
</div> </div>
{/* Bottom padding */}
<div style={{ height: "20px" }} />
</div> </div>
{isCreateModeDialogOpen && ( {isCreateModeDialogOpen && (

View File

@@ -166,6 +166,10 @@ describe("PromptsView", () => {
it("handles API configuration selection", () => { it("handles API configuration selection", () => {
renderPromptsView() renderPromptsView()
// Click the ENHANCE tab first to show the API config dropdown
const enhanceTab = screen.getByTestId("ENHANCE-tab")
fireEvent.click(enhanceTab)
const dropdown = screen.getByTestId("api-config-dropdown") const dropdown = screen.getByTestId("api-config-dropdown")
fireEvent( fireEvent(
dropdown, dropdown,