Browse Source

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.
sam hoang 1 year ago
parent
commit
55a5a97d8b

+ 3 - 9
src/core/webview/ClineProvider.ts

@@ -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",

+ 28 - 12
src/shared/__tests__/support-prompts.test.ts

@@ -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,

+ 17 - 32
src/shared/support-prompt.ts

@@ -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

+ 88 - 160
webview-ui/src/components/prompts/PromptsView.tsx

@@ -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 && (

+ 4 - 0
webview-ui/src/components/prompts/__tests__/PromptsView.test.tsx

@@ -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,