feat: add "Always run enhance prompt" setting for automatic prompt enhancement

- Added alwaysRunEnhancePrompt boolean setting to global settings schema
- Added toggle control in PromptsSettings component under "Enhance prompt"
- Updated ExtensionStateContext to handle the new setting with getter/setter
- Modified ChatTextArea to auto-enhance prompts when setting is enabled
- Auto-enhancement triggers on Enter keypress for new tasks only
- Enhanced text is automatically sent after processing
- All existing tests passing with proper null checks for clineMessages

Fixes #9649
This commit is contained in:
Roo Code 2025-11-27 06:21:28 +00:00
parent 5b64aa95f6
commit b86f60fb08
5 changed files with 65 additions and 4 deletions

View file

@ -184,6 +184,7 @@ export const globalSettingsSchema = z.object({
customModePrompts: customModePromptsSchema.optional(),
customSupportPrompts: customSupportPromptsSchema.optional(),
enhancementApiConfigId: z.string().optional(),
alwaysRunEnhancePrompt: z.boolean().optional(),
includeTaskHistoryInEnhance: z.boolean().optional(),
historyPreviewCollapsed: z.boolean().optional(),
reasoningBlockCollapsed: z.boolean().optional(),

View file

@ -276,6 +276,7 @@ export type ExtensionState = Pick<
| "customModePrompts"
| "customSupportPrompts"
| "enhancementApiConfigId"
| "alwaysRunEnhancePrompt"
| "condensingApiConfigId"
| "customCondensingPrompt"
| "codebaseIndexConfig"

View file

@ -94,6 +94,7 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
clineMessages,
commands,
cloudUserInfo,
alwaysRunEnhancePrompt,
} = useExtensionState()
// Find the ID and display text for the currently selected API configuration.
@ -123,6 +124,8 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
return () => document.removeEventListener("mousedown", handleClickOutside)
}, [showDropdown])
const [pendingSend, setPendingSend] = useState(false)
// Handle enhanced prompt response and search results.
useEffect(() => {
const messageHandler = (event: MessageEvent) => {
@ -151,6 +154,12 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
}
setIsEnhancingPrompt(false)
// If we were waiting to send after enhancement, send now
if (pendingSend) {
setPendingSend(false)
onSend()
}
} else if (message.type === "insertTextIntoTextarea") {
if (message.text && textAreaRef.current) {
// Insert the command text at the current cursor position
@ -201,7 +210,7 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
window.addEventListener("message", messageHandler)
return () => window.removeEventListener("message", messageHandler)
}, [setInputValue, searchRequestId, inputValue])
}, [setInputValue, searchRequestId, inputValue, pendingSend, onSend])
const [isDraggingOver, setIsDraggingOver] = useState(false)
const [textAreaBaseHeight, setTextAreaBaseHeight] = useState<number | undefined>(undefined)
@ -474,10 +483,19 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
if (event.key === "Enter" && !event.shiftKey && !isComposing) {
event.preventDefault()
// Always call onSend - let ChatView handle queueing when disabled
resetHistoryNavigation()
onSend()
// Check if we should auto-enhance first
const trimmedInput = inputValue.trim()
if (alwaysRunEnhancePrompt && trimmedInput && clineMessages?.length === 0) {
// This is a new task and auto-enhance is enabled
setIsEnhancingPrompt(true)
setPendingSend(true)
vscode.postMessage({ type: "enhancePrompt" as const, text: trimmedInput })
} else {
// Normal send without enhancement
onSend()
}
}
if (event.key === "Backspace" && !isComposing) {
@ -541,6 +559,8 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
handleHistoryNavigation,
resetHistoryNavigation,
commands,
alwaysRunEnhancePrompt,
clineMessages?.length,
],
)

View file

@ -38,6 +38,8 @@ const PromptsSettings = ({
listApiConfigMeta,
enhancementApiConfigId,
setEnhancementApiConfigId,
alwaysRunEnhancePrompt,
setAlwaysRunEnhancePrompt,
condensingApiConfigId,
setCondensingApiConfigId,
customCondensingPrompt,
@ -283,6 +285,33 @@ const PromptsSettings = ({
</div>
</div>
<div>
<VSCodeCheckbox
checked={alwaysRunEnhancePrompt}
onChange={(e: Event | FormEvent<HTMLElement>) => {
const target = (
"target" in e ? e.target : null
) as HTMLInputElement | null
if (!target) {
return
}
setAlwaysRunEnhancePrompt(target.checked)
vscode.postMessage({
type: "updateSettings",
updatedSettings: { alwaysRunEnhancePrompt: target.checked },
})
}}>
<span className="font-medium">Always run enhance prompt</span>
</VSCodeCheckbox>
<div className="text-vscode-descriptionForeground text-sm mt-1 mb-3">
Automatically enhance every message before sending it to improve quality and
clarity
</div>
</div>
<div>
<label className="block font-medium mb-1">
{t("prompts:supportPrompts.enhance.testEnhancement")}

View file

@ -122,6 +122,8 @@ export interface ExtensionStateContextType extends ExtensionState {
setCustomSupportPrompts: (value: CustomSupportPrompts) => void
enhancementApiConfigId?: string
setEnhancementApiConfigId: (value: string) => void
alwaysRunEnhancePrompt?: boolean
setAlwaysRunEnhancePrompt: (value: boolean) => void
setExperimentEnabled: (id: ExperimentId, enabled: boolean) => void
setAutoApprovalEnabled: (value: boolean) => void
customModes: ModeConfig[]
@ -228,6 +230,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
customSupportPrompts: {},
experiments: experimentDefault,
enhancementApiConfigId: "",
alwaysRunEnhancePrompt: false, // Default to false for backward compatibility
condensingApiConfigId: "", // Default empty string for condensing API config ID
customCondensingPrompt: "", // Default empty string for custom condensing prompt
hasOpenedModeSelector: false, // Default to false (not opened yet)
@ -295,6 +298,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
project: {},
global: {},
})
const [alwaysRunEnhancePrompt, setAlwaysRunEnhancePrompt] = useState(false)
const [includeTaskHistoryInEnhance, setIncludeTaskHistoryInEnhance] = useState(true)
const [prevCloudIsAuthenticated, setPrevCloudIsAuthenticated] = useState(false)
const [includeCurrentTime, setIncludeCurrentTime] = useState(true)
@ -332,6 +336,10 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
if ((newState as any).followupAutoApproveTimeoutMs !== undefined) {
setFollowupAutoApproveTimeoutMs((newState as any).followupAutoApproveTimeoutMs)
}
// Update alwaysRunEnhancePrompt if present in state message
if ((newState as any).alwaysRunEnhancePrompt !== undefined) {
setAlwaysRunEnhancePrompt((newState as any).alwaysRunEnhancePrompt)
}
// Update includeTaskHistoryInEnhance if present in state message
if ((newState as any).includeTaskHistoryInEnhance !== undefined) {
setIncludeTaskHistoryInEnhance((newState as any).includeTaskHistoryInEnhance)
@ -474,6 +482,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
profileThresholds: state.profileThresholds ?? {},
alwaysAllowFollowupQuestions,
followupAutoApproveTimeoutMs,
alwaysRunEnhancePrompt,
remoteControlEnabled: state.remoteControlEnabled ?? false,
taskSyncEnabled: state.taskSyncEnabled,
featureRoomoteControlEnabled: state.featureRoomoteControlEnabled ?? false,
@ -493,6 +502,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
setAlwaysAllowModeSwitch: (value) => setState((prevState) => ({ ...prevState, alwaysAllowModeSwitch: value })),
setAlwaysAllowSubtasks: (value) => setState((prevState) => ({ ...prevState, alwaysAllowSubtasks: value })),
setAlwaysAllowFollowupQuestions,
setAlwaysRunEnhancePrompt,
setFollowupAutoApproveTimeoutMs: (value) =>
setState((prevState) => ({ ...prevState, followupAutoApproveTimeoutMs: value })),
setShowAnnouncement: (value) => setState((prevState) => ({ ...prevState, shouldShowAnnouncement: value })),