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 { ConfigManager } from "../config/ConfigManager"
import { CustomModesManager } from "../config/CustomModesManager"
import { enhance, codeActionPrompt } from "../../shared/support-prompt"
import { supportPrompt } from "../../shared/support-prompt"
import { ACTION_NAMES } from "../CodeActionProvider"
@@ -205,7 +205,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
const { customPrompts } = await visibleProvider.getState()
const prompt = codeActionPrompt.create(promptType, params, customPrompts)
const prompt = supportPrompt.create(promptType, params, customPrompts)
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(
configToUse,
message.text,
getEnhancePrompt(customPrompts?.enhance),
supportPrompt.get(customPrompts, "ENHANCE"),
)
await this.postMessageToWebview({
type: "enhancedPrompt",

View File

@@ -1,4 +1,4 @@
import { codeActionPrompt, type CodeActionType } from "../support-prompt"
import { supportPrompt } from "../support-prompt"
describe("Code Action Prompts", () => {
const testFilePath = "test/file.ts"
@@ -6,7 +6,7 @@ describe("Code Action Prompts", () => {
describe("EXPLAIN action", () => {
it("should format explain prompt correctly", () => {
const prompt = codeActionPrompt.create("EXPLAIN", {
const prompt = supportPrompt.create("EXPLAIN", {
filePath: testFilePath,
selectedText: testCode,
})
@@ -21,7 +21,7 @@ describe("Code Action Prompts", () => {
describe("FIX action", () => {
it("should format fix prompt without diagnostics", () => {
const prompt = codeActionPrompt.create("FIX", {
const prompt = supportPrompt.create("FIX", {
filePath: testFilePath,
selectedText: testCode,
})
@@ -45,7 +45,7 @@ describe("Code Action Prompts", () => {
},
]
const prompt = codeActionPrompt.create("FIX", {
const prompt = supportPrompt.create("FIX", {
filePath: testFilePath,
selectedText: testCode,
diagnostics,
@@ -60,7 +60,7 @@ describe("Code Action Prompts", () => {
describe("IMPROVE action", () => {
it("should format improve prompt correctly", () => {
const prompt = codeActionPrompt.create("IMPROVE", {
const prompt = supportPrompt.create("IMPROVE", {
filePath: testFilePath,
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", () => {
it("should return default template when no custom prompts provided", () => {
const template = codeActionPrompt.get(undefined, "EXPLAIN")
expect(template).toBe(codeActionPrompt.default.EXPLAIN)
const template = supportPrompt.get(undefined, "EXPLAIN")
expect(template).toBe(supportPrompt.default.EXPLAIN)
})
it("should return custom template when provided", () => {
@@ -85,7 +101,7 @@ describe("Code Action Prompts", () => {
const customPrompts = {
EXPLAIN: customTemplate,
}
const template = codeActionPrompt.get(customPrompts, "EXPLAIN")
const template = supportPrompt.get(customPrompts, "EXPLAIN")
expect(template).toBe(customTemplate)
})
@@ -93,8 +109,8 @@ describe("Code Action Prompts", () => {
const customPrompts = {
SOMETHING_ELSE: "Other template",
}
const template = codeActionPrompt.get(customPrompts, "EXPLAIN")
expect(template).toBe(codeActionPrompt.default.EXPLAIN)
const template = supportPrompt.get(customPrompts, "EXPLAIN")
expect(template).toBe(supportPrompt.default.EXPLAIN)
})
})
@@ -105,7 +121,7 @@ describe("Code Action Prompts", () => {
EXPLAIN: customTemplate,
}
const prompt = codeActionPrompt.create(
const prompt = supportPrompt.create(
"EXPLAIN",
{
filePath: testFilePath,
@@ -123,7 +139,7 @@ describe("Code Action Prompts", () => {
EXPLAIN: "Other template",
}
const prompt = codeActionPrompt.create(
const prompt = supportPrompt.create(
"EXPLAIN",
{
filePath: testFilePath,

View File

@@ -1,21 +1,4 @@
// Separate enhance prompt type and definition
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
// Support prompts
type PromptParams = Record<string, string | any[]>
const generateDiagnosticText = (diagnostics?: any[]) => {
@@ -41,8 +24,7 @@ export const createPrompt = (template: string, params: PromptParams): string =>
return result
}
const EXPLAIN_TEMPLATE = `
Explain the following code from file path @/\${filePath}:
const EXPLAIN_TEMPLATE = `Explain the following code from file path @/\${filePath}:
\${userInput}
\`\`\`
@@ -55,8 +37,7 @@ Please provide a clear and concise explanation of what this code does, including
3. Important patterns or techniques used
`
const FIX_TEMPLATE = `
Fix any issues in the following code from file path @/\${filePath}
const FIX_TEMPLATE = `Fix any issues in the following code from file path @/\${filePath}
\${diagnosticText}
\${userInput}
@@ -71,8 +52,7 @@ Please:
4. Explain what was fixed and why
`
const IMPROVE_TEMPLATE = `
Improve the following code from file path @/\${filePath}:
const IMPROVE_TEMPLATE = `Improve the following code from file path @/\${filePath}:
\${userInput}
\`\`\`
@@ -88,31 +68,36 @@ Please suggest improvements for:
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
const defaultTemplates = {
EXPLAIN: EXPLAIN_TEMPLATE,
FIX: FIX_TEMPLATE,
IMPROVE: IMPROVE_TEMPLATE,
ENHANCE: ENHANCE_TEMPLATE,
} as const
type CodeActionType = keyof typeof defaultTemplates
type SupportPromptType = keyof typeof defaultTemplates
export const codeActionPrompt = {
export const supportPrompt = {
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]
},
create: (type: CodeActionType, params: PromptParams, customPrompts?: Record<string, any>): string => {
const template = codeActionPrompt.get(customPrompts, type)
create: (type: SupportPromptType, params: PromptParams, customPrompts?: Record<string, any>): string => {
const template = supportPrompt.get(customPrompts, type)
return createPrompt(template, params)
},
} as const
export type { CodeActionType }
export type { SupportPromptType }
// User-friendly labels for code action types
export const codeActionLabels: Record<CodeActionType, string> = {
// User-friendly labels for support prompt types
export const supportPromptLabels: Record<SupportPromptType, string> = {
FIX: "Fix Issues",
EXPLAIN: "Explain Code",
IMPROVE: "Improve Code",
ENHANCE: "Enhance Prompt",
} as const

View File

@@ -9,12 +9,7 @@ import {
} from "@vscode/webview-ui-toolkit/react"
import { useExtensionState } from "../../context/ExtensionStateContext"
import { Mode, PromptComponent, getRoleDefinition, getAllModes, ModeConfig } from "../../../../src/shared/modes"
import {
enhancePrompt,
codeActionPrompt,
CodeActionType,
codeActionLabels,
} from "../../../../src/shared/support-prompt"
import { supportPrompt, SupportPromptType, supportPromptLabels } from "../../../../src/shared/support-prompt"
import { TOOL_GROUPS, GROUP_DISPLAY_NAMES, ToolGroup } from "../../../../src/shared/tool-groups"
import { vscode } from "../../utils/vscode"
@@ -50,7 +45,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
const [selectedPromptTitle, setSelectedPromptTitle] = useState("")
const [isToolsEditMode, setIsToolsEditMode] = useState(false)
const [isCreateModeDialogOpen, setIsCreateModeDialogOpen] = useState(false)
const [activeCodeActionTab, setActiveCodeActionTab] = useState<CodeActionType>("FIX")
const [activeSupportTab, setActiveSupportTab] = useState<SupportPromptType>("EXPLAIN")
// Direct update functions
const updateAgentPrompt = useCallback(
@@ -255,16 +250,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
return () => window.removeEventListener("message", handler)
}, [])
const updateEnhancePrompt = (value: string | undefined) => {
vscode.postMessage({
type: "updateSupportPrompt",
values: {
enhance: value,
},
})
}
const updateCodeActionPrompt = (type: CodeActionType, value: string | undefined) => {
const updateSupportPrompt = (type: SupportPromptType, value: string | undefined) => {
vscode.postMessage({
type: "updateSupportPrompt",
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) => {
// Only reset role definition for built-in modes
const existingPrompt = customPrompts?.[modeSlug] as PromptComponent
@@ -290,26 +268,15 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
})
}
const handleEnhanceReset = () => {
vscode.postMessage({
type: "resetSupportPrompt",
text: "enhance",
})
}
const handleCodeActionReset = (type: CodeActionType) => {
const handleSupportReset = (type: SupportPromptType) => {
vscode.postMessage({
type: "resetSupportPrompt",
text: type,
})
}
const getEnhancePromptValue = (): string => {
return enhancePrompt.get(customPrompts)
}
const getCodeActionPromptValue = (type: CodeActionType): string => {
return codeActionPrompt.get(customPrompts, type)
const getSupportPromptValue = (type: SupportPromptType): string => {
return supportPrompt.get(customPrompts, type)
}
const handleTestEnhancement = () => {
@@ -786,7 +753,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
</div>
<div style={{ marginBottom: "20px" }}>
<div style={{ fontWeight: "bold", marginBottom: "12px" }}>Code Action Prompts</div>
<div style={{ fontWeight: "bold", marginBottom: "12px" }}>Support Prompts</div>
<div
style={{
display: "flex",
@@ -798,33 +765,32 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
paddingBottom: "4px",
paddingRight: "20px",
}}>
{Object.keys(codeActionPrompt.default).map((type) => (
{Object.keys(supportPrompt.default).map((type) => (
<button
key={type}
data-testid={`${type}-tab`}
data-active={activeCodeActionTab === type ? "true" : "false"}
onClick={() => setActiveCodeActionTab(type as CodeActionType)}
data-active={activeSupportTab === type ? "true" : "false"}
onClick={() => setActiveSupportTab(type as SupportPromptType)}
style={{
padding: "4px 8px",
border: "none",
background:
activeCodeActionTab === type ? "var(--vscode-button-background)" : "none",
background: activeSupportTab === type ? "var(--vscode-button-background)" : "none",
color:
activeCodeActionTab === type
activeSupportTab === type
? "var(--vscode-button-foreground)"
: "var(--vscode-foreground)",
cursor: "pointer",
opacity: activeCodeActionTab === type ? 1 : 0.8,
opacity: activeSupportTab === type ? 1 : 0.8,
borderRadius: "3px",
fontWeight: "bold",
}}>
{codeActionLabels[type as CodeActionType]}
{supportPromptLabels[type as SupportPromptType]}
</button>
))}
</div>
{/* Show active tab content */}
<div key={activeCodeActionTab}>
<div key={activeSupportTab}>
<div
style={{
display: "flex",
@@ -832,140 +798,102 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
alignItems: "center",
marginBottom: "4px",
}}>
<div style={{ fontWeight: "bold" }}>{activeCodeActionTab} Prompt</div>
<div style={{ fontWeight: "bold" }}>{activeSupportTab} Prompt</div>
<VSCodeButton
appearance="icon"
onClick={() => handleCodeActionReset(activeCodeActionTab)}
title={`Reset ${activeCodeActionTab} prompt to default`}>
onClick={() => handleSupportReset(activeSupportTab)}
title={`Reset ${activeSupportTab} prompt to default`}>
<span className="codicon codicon-discard"></span>
</VSCodeButton>
</div>
{activeSupportTab === "ENHANCE" && (
<div>
<div
style={{
color: "var(--vscode-foreground)",
fontSize: "13px",
marginBottom: "20px",
marginTop: "5px",
}}>
Use prompt enhancement to get tailored suggestions or improvements for your inputs.
This ensures Roo understands your intent and provides the best possible responses.
</div>
<div style={{ marginBottom: "12px" }}>
<div style={{ marginBottom: "8px" }}>
<div style={{ fontWeight: "bold", marginBottom: "4px" }}>API Configuration</div>
<div style={{ fontSize: "13px", color: "var(--vscode-descriptionForeground)" }}>
You can select an API configuration to always use for enhancing prompts, or
just use whatever is currently selected
</div>
</div>
<VSCodeDropdown
value={enhancementApiConfigId || ""}
data-testid="api-config-dropdown"
onChange={(e: any) => {
const value = e.detail?.target?.value || e.target?.value
setEnhancementApiConfigId(value)
vscode.postMessage({
type: "enhancementApiConfigId",
text: value,
})
}}
style={{ width: "300px" }}>
<VSCodeOption value="">Use currently selected API configuration</VSCodeOption>
{(listApiConfigMeta || []).map((config) => (
<VSCodeOption key={config.id} value={config.id}>
{config.name}
</VSCodeOption>
))}
</VSCodeDropdown>
</div>
</div>
)}
<VSCodeTextArea
value={getCodeActionPromptValue(activeCodeActionTab)}
value={getSupportPromptValue(activeSupportTab)}
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)
updateSupportPrompt(activeSupportTab, 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>
<div
style={{
color: "var(--vscode-foreground)",
fontSize: "13px",
marginBottom: "20px",
marginTop: "5px",
}}>
Use prompt enhancement to get tailored suggestions or improvements for your inputs. This ensures Roo
understands your intent and provides the best possible responses.
</div>
<div style={{ display: "flex", flexDirection: "column", gap: "20px" }}>
<div>
<div style={{ marginBottom: "12px" }}>
<div style={{ marginBottom: "8px" }}>
<div style={{ fontWeight: "bold", marginBottom: "4px" }}>API Configuration</div>
<div style={{ fontSize: "13px", color: "var(--vscode-descriptionForeground)" }}>
You can select an API configuration to always use for enhancing prompts, or just use
whatever is currently selected
</div>
</div>
<VSCodeDropdown
value={enhancementApiConfigId || ""}
data-testid="api-config-dropdown"
onChange={(e: any) => {
const value = e.detail?.target?.value || e.target?.value
setEnhancementApiConfigId(value)
vscode.postMessage({
type: "enhancementApiConfigId",
text: value,
})
}}
style={{ width: "300px" }}>
<VSCodeOption value="">Use currently selected API configuration</VSCodeOption>
{(listApiConfigMeta || []).map((config) => (
<VSCodeOption key={config.id} value={config.id}>
{config.name}
</VSCodeOption>
))}
</VSCodeDropdown>
</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" }}>
{activeSupportTab === "ENHANCE" && (
<div style={{ marginTop: "12px" }}>
<VSCodeTextArea
value={testPrompt}
onChange={(e) => setTestPrompt((e.target as HTMLTextAreaElement).value)}
placeholder="Enter a prompt to test the enhancement"
rows={3}
resize="vertical"
style={{ width: "100%" }}
data-testid="test-prompt-textarea"
/>
<div
style={{
marginTop: "8px",
display: "flex",
justifyContent: "flex-start",
alignItems: "center",
gap: 8,
}}>
<VSCodeButton
appearance="icon"
onClick={handleEnhanceReset}
title="Revert to default">
<span className="codicon codicon-discard"></span>
onClick={handleTestEnhancement}
disabled={isEnhancing}
appearance="primary">
Preview Prompt Enhancement
</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
value={getEnhancePromptValue()}
onChange={handleEnhancePromptChange}
rows={4}
resize="vertical"
style={{ width: "100%" }}
/>
<div style={{ marginTop: "12px" }}>
<VSCodeTextArea
value={testPrompt}
onChange={(e) => setTestPrompt((e.target as HTMLTextAreaElement).value)}
placeholder="Enter a prompt to test the enhancement"
rows={3}
resize="vertical"
style={{ width: "100%" }}
data-testid="test-prompt-textarea"
/>
<div
style={{
marginTop: "8px",
display: "flex",
justifyContent: "flex-start",
alignItems: "center",
gap: 8,
}}>
<VSCodeButton
onClick={handleTestEnhancement}
disabled={isEnhancing}
appearance="primary">
Preview Prompt Enhancement
</VSCodeButton>
</div>
</div>
)}
</div>
</div>
{/* Bottom padding */}
<div style={{ height: "20px" }} />
</div>
{isCreateModeDialogOpen && (

View File

@@ -166,6 +166,10 @@ describe("PromptsView", () => {
it("handles API configuration selection", () => {
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")
fireEvent(
dropdown,