mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 04:11:10 -05:00
Merge pull request #329 from samhvw8/feat/roo-cline-code-action
New Feature code action
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { render, fireEvent, screen } from "@testing-library/react"
|
||||
import { useExtensionState } from "../../../context/ExtensionStateContext"
|
||||
import AutoApproveMenu from "../AutoApproveMenu"
|
||||
import { codeMode, defaultPrompts } from "../../../../../src/shared/modes"
|
||||
import { defaultModeSlug, defaultPrompts } from "../../../../../src/shared/modes"
|
||||
|
||||
// Mock the ExtensionStateContext hook
|
||||
jest.mock("../../../context/ExtensionStateContext")
|
||||
@@ -29,8 +29,9 @@ describe("AutoApproveMenu", () => {
|
||||
requestDelaySeconds: 5,
|
||||
currentApiConfigName: "default",
|
||||
listApiConfigMeta: [],
|
||||
mode: codeMode,
|
||||
customPrompts: defaultPrompts,
|
||||
mode: defaultModeSlug,
|
||||
customModePrompts: defaultPrompts,
|
||||
customSupportPrompts: {},
|
||||
enhancementApiConfigId: "",
|
||||
didHydrateState: true,
|
||||
showWelcome: false,
|
||||
|
||||
@@ -8,14 +8,14 @@ import {
|
||||
VSCodeCheckbox,
|
||||
} from "@vscode/webview-ui-toolkit/react"
|
||||
import { useExtensionState } from "../../context/ExtensionStateContext"
|
||||
import { Mode, PromptComponent, getRoleDefinition, getAllModes, ModeConfig } from "../../../../src/shared/modes"
|
||||
import {
|
||||
Mode,
|
||||
PromptComponent,
|
||||
getRoleDefinition,
|
||||
getAllModes,
|
||||
ModeConfig,
|
||||
enhancePrompt,
|
||||
} from "../../../../src/shared/modes"
|
||||
supportPrompt,
|
||||
SupportPromptType,
|
||||
supportPromptLabels,
|
||||
supportPromptDescriptions,
|
||||
} from "../../../../src/shared/support-prompt"
|
||||
|
||||
import { TOOL_GROUPS, GROUP_DISPLAY_NAMES, ToolGroup } from "../../../../src/shared/tool-groups"
|
||||
import { vscode } from "../../utils/vscode"
|
||||
|
||||
@@ -28,7 +28,8 @@ type PromptsViewProps = {
|
||||
|
||||
const PromptsView = ({ onDone }: PromptsViewProps) => {
|
||||
const {
|
||||
customPrompts,
|
||||
customModePrompts,
|
||||
customSupportPrompts,
|
||||
listApiConfigMeta,
|
||||
enhancementApiConfigId,
|
||||
setEnhancementApiConfigId,
|
||||
@@ -50,11 +51,12 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
||||
const [selectedPromptTitle, setSelectedPromptTitle] = useState("")
|
||||
const [isToolsEditMode, setIsToolsEditMode] = useState(false)
|
||||
const [isCreateModeDialogOpen, setIsCreateModeDialogOpen] = useState(false)
|
||||
const [activeSupportTab, setActiveSupportTab] = useState<SupportPromptType>("ENHANCE")
|
||||
|
||||
// Direct update functions
|
||||
const updateAgentPrompt = useCallback(
|
||||
(mode: Mode, promptData: PromptComponent) => {
|
||||
const existingPrompt = customPrompts?.[mode]
|
||||
const existingPrompt = customModePrompts?.[mode] as PromptComponent
|
||||
const updatedPrompt = { ...existingPrompt, ...promptData }
|
||||
|
||||
// Only include properties that differ from defaults
|
||||
@@ -68,7 +70,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
||||
customPrompt: updatedPrompt,
|
||||
})
|
||||
},
|
||||
[customPrompts],
|
||||
[customModePrompts],
|
||||
)
|
||||
|
||||
const updateCustomMode = useCallback((slug: string, modeConfig: ModeConfig) => {
|
||||
@@ -254,36 +256,33 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
||||
return () => window.removeEventListener("message", handler)
|
||||
}, [])
|
||||
|
||||
const updateEnhancePrompt = (value: string | undefined) => {
|
||||
const updateSupportPrompt = (type: SupportPromptType, value: string | undefined) => {
|
||||
vscode.postMessage({
|
||||
type: "updateEnhancedPrompt",
|
||||
text: value,
|
||||
type: "updateSupportPrompt",
|
||||
values: {
|
||||
[type]: value,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
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]
|
||||
const existingPrompt = customModePrompts?.[modeSlug] as PromptComponent
|
||||
updateAgentPrompt(modeSlug, {
|
||||
...existingPrompt,
|
||||
roleDefinition: undefined,
|
||||
})
|
||||
}
|
||||
|
||||
const handleEnhanceReset = () => {
|
||||
updateEnhancePrompt(undefined)
|
||||
const handleSupportReset = (type: SupportPromptType) => {
|
||||
vscode.postMessage({
|
||||
type: "resetSupportPrompt",
|
||||
text: type,
|
||||
})
|
||||
}
|
||||
|
||||
const getEnhancePromptValue = (): string => {
|
||||
return enhancePrompt.get(customPrompts)
|
||||
const getSupportPromptValue = (type: SupportPromptType): string => {
|
||||
return supportPrompt.get(customSupportPrompts, type)
|
||||
}
|
||||
|
||||
const handleTestEnhancement = () => {
|
||||
@@ -319,7 +318,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
||||
</div>
|
||||
|
||||
<div style={{ flex: 1, overflow: "auto", padding: "0 20px" }}>
|
||||
<div style={{ marginBottom: "20px" }}>
|
||||
<div style={{ paddingBottom: "20px", borderBottom: "1px solid var(--vscode-input-border)" }}>
|
||||
<div style={{ marginBottom: "20px" }}>
|
||||
<div style={{ fontWeight: "bold", marginBottom: "4px" }}>Preferred Language</div>
|
||||
<select
|
||||
@@ -392,7 +391,13 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
||||
style={{ width: "100%" }}
|
||||
data-testid="global-custom-instructions-textarea"
|
||||
/>
|
||||
<div style={{ fontSize: "12px", color: "var(--vscode-descriptionForeground)", marginTop: "5px" }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
marginTop: "5px",
|
||||
marginBottom: "40px",
|
||||
}}>
|
||||
Instructions can also be loaded from{" "}
|
||||
<span
|
||||
style={{
|
||||
@@ -416,7 +421,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: "20px" }}>
|
||||
<div style={{ marginTop: "20px" }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
@@ -563,7 +568,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
||||
<VSCodeTextArea
|
||||
value={(() => {
|
||||
const customMode = findModeBySlug(mode, customModes)
|
||||
const prompt = customPrompts?.[mode]
|
||||
const prompt = customModePrompts?.[mode] as PromptComponent
|
||||
return customMode?.roleDefinition ?? prompt?.roleDefinition ?? getRoleDefinition(mode)
|
||||
})()}
|
||||
onChange={(e) => {
|
||||
@@ -680,7 +685,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
||||
<VSCodeTextArea
|
||||
value={(() => {
|
||||
const customMode = findModeBySlug(mode, customModes)
|
||||
const prompt = customPrompts?.[mode]
|
||||
const prompt = customModePrompts?.[mode] as PromptComponent
|
||||
return customMode?.customInstructions ?? prompt?.customInstructions ?? ""
|
||||
})()}
|
||||
onChange={(e) => {
|
||||
@@ -696,7 +701,7 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
||||
})
|
||||
} else {
|
||||
// For built-in modes, update the prompts
|
||||
const existingPrompt = customPrompts?.[mode]
|
||||
const existingPrompt = customModePrompts?.[mode] as PromptComponent
|
||||
updateAgentPrompt(mode, {
|
||||
...existingPrompt,
|
||||
customInstructions: value.trim() || undefined,
|
||||
@@ -742,7 +747,14 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ marginBottom: "20px", display: "flex", justifyContent: "flex-start" }}>
|
||||
<div
|
||||
style={{
|
||||
paddingBottom: "40px",
|
||||
marginBottom: "20px",
|
||||
borderBottom: "1px solid var(--vscode-input-border)",
|
||||
display: "flex",
|
||||
justifyContent: "flex-start",
|
||||
}}>
|
||||
<VSCodeButton
|
||||
appearance="primary"
|
||||
onClick={() => {
|
||||
@@ -759,116 +771,168 @@ const PromptsView = ({ onDone }: PromptsViewProps) => {
|
||||
</VSCodeButton>
|
||||
</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",
|
||||
marginTop: "20px",
|
||||
paddingBottom: "60px",
|
||||
borderBottom: "1px solid var(--vscode-input-border)",
|
||||
}}>
|
||||
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>
|
||||
<h3 style={{ color: "var(--vscode-foreground)", marginBottom: "12px" }}>Support Prompts</h3>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
gap: "16px",
|
||||
alignItems: "center",
|
||||
marginBottom: "12px",
|
||||
overflowX: "auto",
|
||||
flexWrap: "nowrap",
|
||||
paddingBottom: "4px",
|
||||
paddingRight: "20px",
|
||||
}}>
|
||||
{Object.keys(supportPrompt.default).map((type) => (
|
||||
<button
|
||||
key={type}
|
||||
data-testid={`${type}-tab`}
|
||||
data-active={activeSupportTab === type ? "true" : "false"}
|
||||
onClick={() => setActiveSupportTab(type as SupportPromptType)}
|
||||
style={{
|
||||
padding: "4px 8px",
|
||||
border: "none",
|
||||
background: activeSupportTab === type ? "var(--vscode-button-background)" : "none",
|
||||
color:
|
||||
activeSupportTab === type
|
||||
? "var(--vscode-button-foreground)"
|
||||
: "var(--vscode-foreground)",
|
||||
cursor: "pointer",
|
||||
opacity: activeSupportTab === type ? 1 : 0.8,
|
||||
borderRadius: "3px",
|
||||
fontWeight: "bold",
|
||||
}}>
|
||||
{supportPromptLabels[type as SupportPromptType]}
|
||||
</button>
|
||||
))}
|
||||
</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>
|
||||
{/* Support prompt description */}
|
||||
<div
|
||||
style={{
|
||||
fontSize: "13px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
margin: "8px 0 16px",
|
||||
}}>
|
||||
{supportPromptDescriptions[activeSupportTab]}
|
||||
</div>
|
||||
|
||||
{/* Show active tab content */}
|
||||
<div key={activeSupportTab}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
marginBottom: "4px",
|
||||
}}>
|
||||
<div style={{ fontWeight: "bold" }}>Prompt</div>
|
||||
<VSCodeButton
|
||||
appearance="icon"
|
||||
onClick={() => handleSupportReset(activeSupportTab)}
|
||||
title={`Reset ${activeSupportTab} prompt to default`}>
|
||||
<span className="codicon codicon-discard"></span>
|
||||
</VSCodeButton>
|
||||
</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
|
||||
value={getEnhancePromptValue()}
|
||||
onChange={handleEnhancePromptChange}
|
||||
rows={4}
|
||||
value={getSupportPromptValue(activeSupportTab)}
|
||||
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={6}
|
||||
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>
|
||||
{activeSupportTab === "ENHANCE" && (
|
||||
<>
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
color: "var(--vscode-foreground)",
|
||||
fontSize: "13px",
|
||||
marginBottom: "20px",
|
||||
marginTop: "5px",
|
||||
}}></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>
|
||||
|
||||
<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 && (
|
||||
|
||||
@@ -12,7 +12,7 @@ jest.mock("../../../utils/vscode", () => ({
|
||||
}))
|
||||
|
||||
const mockExtensionState = {
|
||||
customPrompts: {},
|
||||
customModePrompts: {},
|
||||
listApiConfigMeta: [
|
||||
{ id: "config1", name: "Config 1" },
|
||||
{ id: "config2", name: "Config 2" },
|
||||
@@ -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,
|
||||
|
||||
@@ -14,7 +14,8 @@ import { convertTextMateToHljs } from "../utils/textMateToHljs"
|
||||
import { findLastIndex } from "../../../src/shared/array"
|
||||
import { McpServer } from "../../../src/shared/mcp"
|
||||
import { checkExistKey } from "../../../src/shared/checkExistApiConfig"
|
||||
import { Mode, CustomPrompts, defaultModeSlug, defaultPrompts, ModeConfig } from "../../../src/shared/modes"
|
||||
import { Mode, CustomModePrompts, defaultModeSlug, defaultPrompts, ModeConfig } from "../../../src/shared/modes"
|
||||
import { CustomSupportPrompts } from "../../../src/shared/support-prompt"
|
||||
|
||||
export interface ExtensionStateContextType extends ExtensionState {
|
||||
didHydrateState: boolean
|
||||
@@ -57,7 +58,8 @@ export interface ExtensionStateContextType extends ExtensionState {
|
||||
onUpdateApiConfig: (apiConfig: ApiConfiguration) => void
|
||||
mode: Mode
|
||||
setMode: (value: Mode) => void
|
||||
setCustomPrompts: (value: CustomPrompts) => void
|
||||
setCustomModePrompts: (value: CustomModePrompts) => void
|
||||
setCustomSupportPrompts: (value: CustomSupportPrompts) => void
|
||||
enhancementApiConfigId?: string
|
||||
setEnhancementApiConfigId: (value: string) => void
|
||||
experimentalDiffStrategy: boolean
|
||||
@@ -93,7 +95,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
||||
currentApiConfigName: "default",
|
||||
listApiConfigMeta: [],
|
||||
mode: defaultModeSlug,
|
||||
customPrompts: defaultPrompts,
|
||||
customModePrompts: defaultPrompts,
|
||||
customSupportPrompts: {},
|
||||
enhancementApiConfigId: "",
|
||||
experimentalDiffStrategy: false,
|
||||
autoApprovalEnabled: false,
|
||||
@@ -270,7 +273,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
||||
setListApiConfigMeta,
|
||||
onUpdateApiConfig,
|
||||
setMode: (value: Mode) => setState((prevState) => ({ ...prevState, mode: value })),
|
||||
setCustomPrompts: (value) => setState((prevState) => ({ ...prevState, customPrompts: value })),
|
||||
setCustomModePrompts: (value) => setState((prevState) => ({ ...prevState, customModePrompts: value })),
|
||||
setCustomSupportPrompts: (value) => setState((prevState) => ({ ...prevState, customSupportPrompts: value })),
|
||||
setEnhancementApiConfigId: (value) =>
|
||||
setState((prevState) => ({ ...prevState, enhancementApiConfigId: value })),
|
||||
setExperimentalDiffStrategy: (value) =>
|
||||
|
||||
Reference in New Issue
Block a user