diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 1bb76734db..765c9a080d 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -1261,6 +1261,11 @@ export const webviewMessageHandler = async ( await provider.postStateToWebview() break case "updateCondensingPrompt": + // Store the condensing prompt in customSupportPrompts["CONDENSE"] instead of customCondensingPrompt + const currentSupportPrompts = getGlobalState("customSupportPrompts") ?? {} + const updatedSupportPrompts = { ...currentSupportPrompts, CONDENSE: message.text } + await updateGlobalState("customSupportPrompts", updatedSupportPrompts) + // Also update the old field for backward compatibility during migration await updateGlobalState("customCondensingPrompt", message.text) await provider.postStateToWebview() break diff --git a/src/shared/support-prompt.ts b/src/shared/support-prompt.ts index 1767a20753..51f4310fc2 100644 --- a/src/shared/support-prompt.ts +++ b/src/shared/support-prompt.ts @@ -35,6 +35,7 @@ interface SupportPromptConfig { type SupportPromptType = | "ENHANCE" + | "CONDENSE" | "EXPLAIN" | "FIX" | "IMPROVE" @@ -49,6 +50,45 @@ const supportPromptConfigs: Record = { 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): \${userInput}`, + }, + CONDENSE: { + template: `Your task is to create a detailed summary of the conversation so far, paying close attention to the user's explicit requests and your previous actions. +This summary should be thorough in capturing technical details, code patterns, and architectural decisions that would be essential for continuing with the conversation and supporting any continuing tasks. + +Your summary should be structured as follows: +Context: The context to continue the conversation with. If applicable based on the current task, this should include: + 1. Previous Conversation: High level details about what was discussed throughout the entire conversation with the user. This should be written to allow someone to be able to follow the general overarching conversation flow. + 2. Current Work: Describe in detail what was being worked on prior to this request to summarize the conversation. Pay special attention to the more recent messages in the conversation. + 3. Key Technical Concepts: List all important technical concepts, technologies, coding conventions, and frameworks discussed, which might be relevant for continuing with this work. + 4. Relevant Files and Code: If applicable, enumerate specific files and code sections examined, modified, or created for the task continuation. Pay special attention to the most recent messages and changes. + 5. Problem Solving: Document problems solved thus far and any ongoing troubleshooting efforts. + 6. Pending Tasks and Next Steps: Outline all pending tasks that you have explicitly been asked to work on, as well as list the next steps you will take for all outstanding work, if applicable. Include code snippets where they add clarity. For any next steps, include direct quotes from the most recent conversation showing exactly what task you were working on and where you left off. This should be verbatim to ensure there's no information loss in context between tasks. + +Example summary structure: +1. Previous Conversation: + [Detailed description] +2. Current Work: + [Detailed description] +3. Key Technical Concepts: + - [Concept 1] + - [Concept 2] + - [...] +4. Relevant Files and Code: + - [File Name 1] + - [Summary of why this file is important] + - [Summary of the changes made to this file, if any] + - [Important Code Snippet] + - [File Name 2] + - [Important Code Snippet] + - [...] +5. Problem Solving: + [Detailed description] +6. Pending Tasks and Next Steps: + - [Task 1 details & next steps] + - [Task 2 details & next steps] + - [...] + +Output only the summary of the conversation so far, without any additional commentary or explanation.`, }, EXPLAIN: { template: `Explain the following code from file path \${filePath}:\${startLine}-\${endLine} diff --git a/webview-ui/src/components/settings/ContextManagementSettings.tsx b/webview-ui/src/components/settings/ContextManagementSettings.tsx index 6fbd2f1dab..4afcc6f7b4 100644 --- a/webview-ui/src/components/settings/ContextManagementSettings.tsx +++ b/webview-ui/src/components/settings/ContextManagementSettings.tsx @@ -1,62 +1,20 @@ import { HTMLAttributes } from "react" import React from "react" import { useAppTranslation } from "@/i18n/TranslationContext" -import { VSCodeCheckbox, VSCodeTextArea } from "@vscode/webview-ui-toolkit/react" +import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react" import { Database, FoldVertical } from "lucide-react" import { cn } from "@/lib/utils" -import { Button, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Slider } from "@/components/ui" +import { Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Slider } from "@/components/ui" import { SetCachedStateField } from "./types" import { SectionHeader } from "./SectionHeader" import { Section } from "./Section" import { vscode } from "@/utils/vscode" -const SUMMARY_PROMPT = `\ -Your task is to create a detailed summary of the conversation so far, paying close attention to the user's explicit requests and your previous actions. -This summary should be thorough in capturing technical details, code patterns, and architectural decisions that would be essential for continuing with the conversation and supporting any continuing tasks. - -Your summary should be structured as follows: -Context: The context to continue the conversation with. If applicable based on the current task, this should include: - 1. Previous Conversation: High level details about what was discussed throughout the entire conversation with the user. This should be written to allow someone to be able to follow the general overarching conversation flow. - 2. Current Work: Describe in detail what was being worked on prior to this request to summarize the conversation. Pay special attention to the more recent messages in the conversation. - 3. Key Technical Concepts: List all important technical concepts, technologies, coding conventions, and frameworks discussed, which might be relevant for continuing with this work. - 4. Relevant Files and Code: If applicable, enumerate specific files and code sections examined, modified, or created for the task continuation. Pay special attention to the most recent messages and changes. - 5. Problem Solving: Document problems solved thus far and any ongoing troubleshooting efforts. - 6. Pending Tasks and Next Steps: Outline all pending tasks that you have explicitly been asked to work on, as well as list the next steps you will take for all outstanding work, if applicable. Include code snippets where they add clarity. For any next steps, include direct quotes from the most recent conversation showing exactly what task you were working on and where you left off. This should be verbatim to ensure there's no information loss in context between tasks. - -Example summary structure: -1. Previous Conversation: - [Detailed description] -2. Current Work: - [Detailed description] -3. Key Technical Concepts: - - [Concept 1] - - [Concept 2] - - [...] -4. Relevant Files and Code: - - [File Name 1] - - [Summary of why this file is important] - - [Summary of the changes made to this file, if any] - - [Important Code Snippet] - - [File Name 2] - - [Important Code Snippet] - - [...] -5. Problem Solving: - [Detailed description] -6. Pending Tasks and Next Steps: - - [Task 1 details & next steps] - - [Task 2 details & next steps] - - [...] - -Output only the summary of the conversation so far, without any additional commentary or explanation. -` - type ContextManagementSettingsProps = HTMLAttributes & { autoCondenseContext: boolean autoCondenseContextPercent: number - condensingApiConfigId?: string - customCondensingPrompt?: string listApiConfigMeta: any[] maxOpenTabsContext: number maxWorkspaceFiles: number @@ -67,8 +25,6 @@ type ContextManagementSettingsProps = HTMLAttributes & { setCachedStateField: SetCachedStateField< | "autoCondenseContext" | "autoCondenseContextPercent" - | "condensingApiConfigId" - | "customCondensingPrompt" | "maxOpenTabsContext" | "maxWorkspaceFiles" | "showRooIgnoredFiles" @@ -81,8 +37,6 @@ type ContextManagementSettingsProps = HTMLAttributes & { export const ContextManagementSettings = ({ autoCondenseContext, autoCondenseContextPercent, - condensingApiConfigId, - customCondensingPrompt, listApiConfigMeta, maxOpenTabsContext, maxWorkspaceFiles, @@ -321,91 +275,6 @@ export const ContextManagementSettings = ({ : t("settings:contextManagement.condensingThreshold.profileDescription")} - - {/* API Configuration Selection */} -
-
- -
{t("settings:contextManagement.condensingApiConfiguration.label")}
-
-
-
- {t("settings:contextManagement.condensingApiConfiguration.description")} -
- -
-
- - {/* Custom Prompt Section */} -
-
- -
{t("settings:contextManagement.customCondensingPrompt.label")}
-
-
-
- {t("settings:contextManagement.customCondensingPrompt.description")} -
- { - const value = (e.target as HTMLTextAreaElement).value - setCachedStateField("customCondensingPrompt", value) - vscode.postMessage({ - type: "updateCondensingPrompt", - text: value, - }) - }} - rows={8} - className="w-full font-mono text-sm" - /> -
- -
-
-
)} diff --git a/webview-ui/src/components/settings/PromptsSettings.tsx b/webview-ui/src/components/settings/PromptsSettings.tsx index 160f79dc84..a71132d62b 100644 --- a/webview-ui/src/components/settings/PromptsSettings.tsx +++ b/webview-ui/src/components/settings/PromptsSettings.tsx @@ -26,7 +26,15 @@ interface PromptsSettingsProps { const PromptsSettings = ({ customSupportPrompts, setCustomSupportPrompts }: PromptsSettingsProps) => { const { t } = useAppTranslation() - const { listApiConfigMeta, enhancementApiConfigId, setEnhancementApiConfigId } = useExtensionState() + const { + listApiConfigMeta, + enhancementApiConfigId, + setEnhancementApiConfigId, + condensingApiConfigId, + setCondensingApiConfigId, + customCondensingPrompt, + setCustomCondensingPrompt, + } = useExtensionState() const [testPrompt, setTestPrompt] = useState("") const [isEnhancing, setIsEnhancing] = useState(false) @@ -48,17 +56,36 @@ const PromptsSettings = ({ customSupportPrompts, setCustomSupportPrompts }: Prom }, []) const updateSupportPrompt = (type: SupportPromptType, value: string | undefined) => { - const updatedPrompts = { ...customSupportPrompts, [type]: value } - setCustomSupportPrompts(updatedPrompts) + if (type === "CONDENSE") { + setCustomCondensingPrompt(value || supportPrompt.default.CONDENSE) + vscode.postMessage({ + type: "updateCondensingPrompt", + text: value || supportPrompt.default.CONDENSE, + }) + } else { + const updatedPrompts = { ...customSupportPrompts, [type]: value } + setCustomSupportPrompts(updatedPrompts) + } } const handleSupportReset = (type: SupportPromptType) => { - const updatedPrompts = { ...customSupportPrompts } - delete updatedPrompts[type] - setCustomSupportPrompts(updatedPrompts) + if (type === "CONDENSE") { + setCustomCondensingPrompt(supportPrompt.default.CONDENSE) + vscode.postMessage({ + type: "updateCondensingPrompt", + text: supportPrompt.default.CONDENSE, + }) + } else { + const updatedPrompts = { ...customSupportPrompts } + delete updatedPrompts[type] + setCustomSupportPrompts(updatedPrompts) + } } const getSupportPromptValue = (type: SupportPromptType): string => { + if (type === "CONDENSE") { + return customCondensingPrompt || supportPrompt.default.CONDENSE + } return supportPrompt.get(customSupportPrompts, type) } @@ -129,29 +156,50 @@ const PromptsSettings = ({ customSupportPrompts, setCustomSupportPrompts }: Prom className="w-full" /> - {activeSupportOption === "ENHANCE" && ( + {(activeSupportOption === "ENHANCE" || activeSupportOption === "CONDENSE") && (
- {t("prompts:supportPrompts.enhance.apiConfigDescription")} + {activeSupportOption === "ENHANCE" + ? t("prompts:supportPrompts.enhance.apiConfigDescription") + : t("prompts:supportPrompts.condense.apiConfigDescription")}
-
- - setTestPrompt((e.target as HTMLTextAreaElement).value)} - placeholder={t("prompts:supportPrompts.enhance.testPromptPlaceholder")} - rows={3} - className="w-full" - data-testid="test-prompt-textarea" - /> -
- + {activeSupportOption === "ENHANCE" && ( +
+ + setTestPrompt((e.target as HTMLTextAreaElement).value)} + placeholder={t("prompts:supportPrompts.enhance.testPromptPlaceholder")} + rows={3} + className="w-full" + data-testid="test-prompt-textarea" + /> +
+ +
-
+ )}
)}
diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index fd3a8a129b..7f5f987a03 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -657,8 +657,6 @@ const SettingsView = forwardRef(({ onDone, t ({ })) // Mock vscode utilities - this is necessary since we're not in a VSCode environment -import { vscode } from "@/utils/vscode" vitest.mock("@/utils/vscode", () => ({ vscode: { @@ -169,8 +168,6 @@ describe("ContextManagementSettings", () => { ...defaultProps, autoCondenseContext: true, autoCondenseContextPercent: 75, - condensingApiConfigId: "test-config", - customCondensingPrompt: "Test prompt", } render() @@ -182,13 +179,9 @@ describe("ContextManagementSettings", () => { const slider = screen.getByTestId("condense-threshold-slider") expect(slider).toBeInTheDocument() - // Should render both select dropdowns (profile and API config) + // Should render the profile select dropdown const selects = screen.getAllByRole("combobox") - expect(selects).toHaveLength(2) - - // Should render the custom prompt textarea - const textarea = screen.getByRole("textbox") - expect(textarea).toBeInTheDocument() + expect(selects).toHaveLength(1) }) describe("Auto Condense Context functionality", () => { @@ -196,8 +189,6 @@ describe("ContextManagementSettings", () => { ...defaultProps, autoCondenseContext: true, autoCondenseContextPercent: 75, - condensingApiConfigId: "test-config", - customCondensingPrompt: "Custom test prompt", listApiConfigMeta: [ { id: "config-1", name: "Config 1" }, { id: "config-2", name: "Config 2" }, @@ -217,14 +208,13 @@ describe("ContextManagementSettings", () => { expect(mockSetCachedStateField).toHaveBeenCalledWith("autoCondenseContext", false) }) - it("shows additional settings when auto condense is enabled", () => { + it("shows threshold settings when auto condense is enabled", () => { render() - // Additional settings should be visible + // Threshold settings should be visible expect(screen.getByTestId("condense-threshold-slider")).toBeInTheDocument() - // Two comboboxes: one for profile selection, one for API config - expect(screen.getAllByRole("combobox")).toHaveLength(2) - expect(screen.getByRole("textbox")).toBeInTheDocument() + // One combobox for profile selection + expect(screen.getAllByRole("combobox")).toHaveLength(1) }) it("updates auto condense context percent", () => { @@ -246,112 +236,6 @@ describe("ContextManagementSettings", () => { render() expect(screen.getByText("75%")).toBeInTheDocument() }) - - it("updates condensing API configuration", () => { - const mockSetCachedStateField = vitest.fn() - const mockPostMessage = vitest.fn() - const postMessageSpy = vitest.spyOn(vscode, "postMessage") - postMessageSpy.mockImplementation(mockPostMessage) - - const props = { ...autoCondenseProps, setCachedStateField: mockSetCachedStateField } - render() - - // Get the second combobox (API config select) - const selects = screen.getAllByRole("combobox") - const apiSelect = selects[1] - fireEvent.click(apiSelect) - - const configOption = screen.getByText("Config 1") - fireEvent.click(configOption) - - expect(mockSetCachedStateField).toHaveBeenCalledWith("condensingApiConfigId", "config-1") - expect(mockPostMessage).toHaveBeenCalledWith({ - type: "condensingApiConfigId", - text: "config-1", - }) - }) - - it("handles selecting default config option", () => { - const mockSetCachedStateField = vitest.fn() - const mockPostMessage = vitest.fn() - const postMessageSpy = vitest.spyOn(vscode, "postMessage") - postMessageSpy.mockImplementation(mockPostMessage) - - const props = { ...autoCondenseProps, setCachedStateField: mockSetCachedStateField } - render() - - // Test selecting default config - get the second combobox (API config) - const selects = screen.getAllByRole("combobox") - const apiSelect = selects[1] - fireEvent.click(apiSelect) - const defaultOption = screen.getByText( - "settings:contextManagement.condensingApiConfiguration.useCurrentConfig", - ) - fireEvent.click(defaultOption) - - expect(mockSetCachedStateField).toHaveBeenCalledWith("condensingApiConfigId", "") - expect(mockPostMessage).toHaveBeenCalledWith({ - type: "condensingApiConfigId", - text: "", - }) - }) - - it("updates custom condensing prompt", () => { - const mockSetCachedStateField = vitest.fn() - const mockPostMessage = vitest.fn() - const postMessageSpy = vitest.spyOn(vscode, "postMessage") - postMessageSpy.mockImplementation(mockPostMessage) - - const props = { ...autoCondenseProps, setCachedStateField: mockSetCachedStateField } - render() - - const textarea = screen.getByRole("textbox") - const newPrompt = "Updated custom prompt" - fireEvent.change(textarea, { target: { value: newPrompt } }) - - expect(mockSetCachedStateField).toHaveBeenCalledWith("customCondensingPrompt", newPrompt) - expect(mockPostMessage).toHaveBeenCalledWith({ - type: "updateCondensingPrompt", - text: newPrompt, - }) - }) - - it("resets custom condensing prompt to default", () => { - const mockSetCachedStateField = vitest.fn() - const mockPostMessage = vitest.fn() - const postMessageSpy = vitest.spyOn(vscode, "postMessage") - postMessageSpy.mockImplementation(mockPostMessage) - - const props = { ...autoCondenseProps, setCachedStateField: mockSetCachedStateField } - render() - - const resetButton = screen.getByRole("button", { - name: "settings:contextManagement.customCondensingPrompt.reset", - }) - fireEvent.click(resetButton) - - // Should reset to the default SUMMARY_PROMPT - expect(mockSetCachedStateField).toHaveBeenCalledWith( - "customCondensingPrompt", - expect.stringContaining("Your task is to create a detailed summary"), - ) - expect(mockPostMessage).toHaveBeenCalledWith({ - type: "updateCondensingPrompt", - text: expect.stringContaining("Your task is to create a detailed summary"), - }) - }) - - it("uses default prompt when customCondensingPrompt is undefined", () => { - const propsWithoutCustomPrompt = { - ...autoCondenseProps, - customCondensingPrompt: undefined, - } - render() - - const textarea = screen.getByRole("textbox") as HTMLTextAreaElement - // The textarea should contain the full default SUMMARY_PROMPT - expect(textarea.value).toContain("Your task is to create a detailed summary") - }) }) describe("Edge cases and validation", () => { @@ -427,8 +311,6 @@ describe("ContextManagementSettings", () => { ...defaultProps, showRooIgnoredFiles: undefined, maxReadFileLine: undefined, - condensingApiConfigId: undefined, - customCondensingPrompt: undefined, } expect(() => { @@ -442,21 +324,15 @@ describe("ContextManagementSettings", () => { }) describe("Conditional rendering", () => { - it("does not render auto condense section when autoCondenseContext is false", () => { + it("does not render threshold settings when autoCondenseContext is false", () => { const propsWithoutAutoCondense = { ...defaultProps, autoCondenseContext: false, } render() - // When auto condense is false, all condensing-related UI should not be visible + // When auto condense is false, threshold slider should not be visible expect(screen.queryByTestId("condense-threshold-slider")).not.toBeInTheDocument() - expect( - screen.queryByText("settings:contextManagement.condensingApiConfiguration.label"), - ).not.toBeInTheDocument() - expect( - screen.queryByText("settings:contextManagement.customCondensingPrompt.label"), - ).not.toBeInTheDocument() }) it("renders max read file controls with default value when maxReadFileLine is undefined", () => { diff --git a/webview-ui/src/components/settings/__tests__/SettingsView.spec.tsx b/webview-ui/src/components/settings/__tests__/SettingsView.spec.tsx index 72d4598ea6..694ff174a7 100644 --- a/webview-ui/src/components/settings/__tests__/SettingsView.spec.tsx +++ b/webview-ui/src/components/settings/__tests__/SettingsView.spec.tsx @@ -58,6 +58,16 @@ vi.mock("@vscode/webview-ui-toolkit/react", () => ({ ), VSCodeRadioGroup: ({ children, onChange }: any) =>
{children}
, + VSCodeTextArea: ({ value, onChange, rows, className, "data-testid": dataTestId }: any) => ( +