From f9025426b8e46fa1f79c110570b6d942755cf33b Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 18 Feb 2025 18:24:08 -0800 Subject: [PATCH] Toggle to act mode and enable MCP prompt if user one-click installs MCP server --- src/core/webview/ClineProvider.ts | 215 ++++++++++-------- src/shared/WebviewMessage.ts | 2 +- src/test/webview/chat-native.test.ts | 4 +- .../src/components/chat/ChatTextArea.tsx | 2 +- 4 files changed, 121 insertions(+), 102 deletions(-) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 0f2b0cdf7f..c665419218 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -31,6 +31,7 @@ import { BrowserSettings, DEFAULT_BROWSER_SETTINGS } from "../../shared/BrowserS import { ChatSettings, DEFAULT_CHAT_SETTINGS } from "../../shared/ChatSettings" import { DIFF_VIEW_URI_SCHEME } from "../../integrations/editor/DiffViewProvider" import { searchCommits } from "../../utils/git" +import { ChatContent } from "../../shared/ChatContent" /* https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts @@ -550,105 +551,9 @@ export class ClineProvider implements vscode.WebviewViewProvider { await this.postStateToWebview() } break - case "chatSettings": + case "togglePlanActMode": if (message.chatSettings) { - const didSwitchToActMode = message.chatSettings.mode === "act" - - // Get previous model info that we will revert to after saving current mode api info - const { - apiConfiguration, - previousModeApiProvider: newApiProvider, - previousModeModelId: newModelId, - previousModeModelInfo: newModelInfo, - } = await this.getState() - - // Save the last model used in this mode - await this.updateGlobalState("previousModeApiProvider", apiConfiguration.apiProvider) - switch (apiConfiguration.apiProvider) { - case "anthropic": - case "bedrock": - case "vertex": - case "gemini": - await this.updateGlobalState("previousModeModelId", apiConfiguration.apiModelId) - break - case "openrouter": - await this.updateGlobalState("previousModeModelId", apiConfiguration.openRouterModelId) - await this.updateGlobalState("previousModeModelInfo", apiConfiguration.openRouterModelInfo) - break - case "vscode-lm": - await this.updateGlobalState("previousModeModelId", apiConfiguration.vsCodeLmModelSelector) - break - case "openai": - await this.updateGlobalState("previousModeModelId", apiConfiguration.openAiModelId) - await this.updateGlobalState("previousModeModelInfo", apiConfiguration.openAiModelInfo) - break - case "ollama": - await this.updateGlobalState("previousModeModelId", apiConfiguration.ollamaModelId) - break - case "lmstudio": - await this.updateGlobalState("previousModeModelId", apiConfiguration.lmStudioModelId) - break - case "litellm": - await this.updateGlobalState("previousModeModelId", apiConfiguration.liteLlmModelId) - break - } - - // Restore the model used in previous mode - if (newApiProvider && newModelId) { - await this.updateGlobalState("apiProvider", newApiProvider) - switch (newApiProvider) { - case "anthropic": - case "bedrock": - case "vertex": - case "gemini": - await this.updateGlobalState("apiModelId", newModelId) - break - case "openrouter": - await this.updateGlobalState("openRouterModelId", newModelId) - await this.updateGlobalState("openRouterModelInfo", newModelInfo) - break - case "vscode-lm": - await this.updateGlobalState("vsCodeLmModelSelector", newModelId) - break - case "openai": - await this.updateGlobalState("openAiModelId", newModelId) - await this.updateGlobalState("openAiModelInfo", newModelInfo) - break - case "ollama": - await this.updateGlobalState("ollamaModelId", newModelId) - break - case "lmstudio": - await this.updateGlobalState("lmStudioModelId", newModelId) - break - case "litellm": - await this.updateGlobalState("liteLlmModelId", newModelId) - break - } - - if (this.cline) { - const { apiConfiguration: updatedApiConfiguration } = await this.getState() - this.cline.api = buildApiHandler(updatedApiConfiguration) - } - } - - await this.updateGlobalState("chatSettings", message.chatSettings) - await this.postStateToWebview() - // console.log("chatSettings", message.chatSettings) - if (this.cline) { - this.cline.updateChatSettings(message.chatSettings) - if (this.cline.isAwaitingPlanResponse && didSwitchToActMode) { - this.cline.didRespondToPlanAskBySwitchingMode = true - // this is necessary for the webview to update accordingly, but Cline instance will not send text back as feedback message - await this.postMessageToWebview({ - type: "invoke", - invoke: "sendMessage", - text: message.chatContent?.message || "PLAN_MODE_TOGGLE_RESPONSE", - images: message.chatContent?.images, - }) - } else { - this.cancelTask() - } - } + await this.togglePlanActModeWithChatSettings(message.chatSettings, message.chatContent) } break // case "relaunchChromeDebugMode": @@ -805,6 +710,20 @@ export class ClineProvider implements vscode.WebviewViewProvider { } case "downloadMcp": { if (message.mcpId) { + // 1. Toggle to act mode if we are in plan mode + const { chatSettings } = await this.getStateToPostToWebview() + if (chatSettings.mode === "plan") { + await this.togglePlanActModeWithChatSettings({ mode: "act" }) + } + + // 2. Enable MCP settings if disabled + // Enable MCP mode if disabled + const mcpConfig = vscode.workspace.getConfiguration("cline.mcp") + if (mcpConfig.get("mode") !== "full") { + await mcpConfig.update("mode", "full", true) + } + + // 3. download MCP await this.downloadMcp(message.mcpId) } break @@ -904,6 +823,106 @@ export class ClineProvider implements vscode.WebviewViewProvider { ) } + async togglePlanActModeWithChatSettings(chatSettings: ChatSettings, chatContent?: ChatContent) { + const didSwitchToActMode = chatSettings.mode === "act" + + // Get previous model info that we will revert to after saving current mode api info + const { + apiConfiguration, + previousModeApiProvider: newApiProvider, + previousModeModelId: newModelId, + previousModeModelInfo: newModelInfo, + } = await this.getState() + + // Save the last model used in this mode + await this.updateGlobalState("previousModeApiProvider", apiConfiguration.apiProvider) + switch (apiConfiguration.apiProvider) { + case "anthropic": + case "bedrock": + case "vertex": + case "gemini": + await this.updateGlobalState("previousModeModelId", apiConfiguration.apiModelId) + break + case "openrouter": + await this.updateGlobalState("previousModeModelId", apiConfiguration.openRouterModelId) + await this.updateGlobalState("previousModeModelInfo", apiConfiguration.openRouterModelInfo) + break + case "vscode-lm": + await this.updateGlobalState("previousModeModelId", apiConfiguration.vsCodeLmModelSelector) + break + case "openai": + await this.updateGlobalState("previousModeModelId", apiConfiguration.openAiModelId) + await this.updateGlobalState("previousModeModelInfo", apiConfiguration.openAiModelInfo) + break + case "ollama": + await this.updateGlobalState("previousModeModelId", apiConfiguration.ollamaModelId) + break + case "lmstudio": + await this.updateGlobalState("previousModeModelId", apiConfiguration.lmStudioModelId) + break + case "litellm": + await this.updateGlobalState("previousModeModelId", apiConfiguration.liteLlmModelId) + break + } + + // Restore the model used in previous mode + if (newApiProvider && newModelId) { + await this.updateGlobalState("apiProvider", newApiProvider) + switch (newApiProvider) { + case "anthropic": + case "bedrock": + case "vertex": + case "gemini": + await this.updateGlobalState("apiModelId", newModelId) + break + case "openrouter": + await this.updateGlobalState("openRouterModelId", newModelId) + await this.updateGlobalState("openRouterModelInfo", newModelInfo) + break + case "vscode-lm": + await this.updateGlobalState("vsCodeLmModelSelector", newModelId) + break + case "openai": + await this.updateGlobalState("openAiModelId", newModelId) + await this.updateGlobalState("openAiModelInfo", newModelInfo) + break + case "ollama": + await this.updateGlobalState("ollamaModelId", newModelId) + break + case "lmstudio": + await this.updateGlobalState("lmStudioModelId", newModelId) + break + case "litellm": + await this.updateGlobalState("liteLlmModelId", newModelId) + break + } + + if (this.cline) { + const { apiConfiguration: updatedApiConfiguration } = await this.getState() + this.cline.api = buildApiHandler(updatedApiConfiguration) + } + } + + await this.updateGlobalState("chatSettings", chatSettings) + await this.postStateToWebview() + // console.log("chatSettings", message.chatSettings) + if (this.cline) { + this.cline.updateChatSettings(chatSettings) + if (this.cline.isAwaitingPlanResponse && didSwitchToActMode) { + this.cline.didRespondToPlanAskBySwitchingMode = true + // this is necessary for the webview to update accordingly, but Cline instance will not send text back as feedback message + await this.postMessageToWebview({ + type: "invoke", + invoke: "sendMessage", + text: chatContent?.message || "PLAN_MODE_TOGGLE_RESPONSE", + images: chatContent?.images, + }) + } else { + this.cancelTask() + } + } + } + async subscribeEmail(email?: string) { if (!email) { return diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 123b345759..bb2a155783 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -31,7 +31,7 @@ export interface WebviewMessage { | "restartMcpServer" | "autoApprovalSettings" | "browserSettings" - | "chatSettings" + | "togglePlanActMode" | "checkpointDiff" | "checkpointRestore" | "taskCompletionViewChanges" diff --git a/src/test/webview/chat-native.test.ts b/src/test/webview/chat-native.test.ts index d3fe630a94..8e35b1528a 100644 --- a/src/test/webview/chat-native.test.ts +++ b/src/test/webview/chat-native.test.ts @@ -84,7 +84,7 @@ describe("Chat Integration Tests", () => { // Set up state change listener const stateChangePromise = new Promise((resolve) => { panel.webview.onDidReceiveMessage((message) => { - if (message.type === "chatSettings") { + if (message.type === "togglePlanActMode") { resolve(message) } }) @@ -102,7 +102,7 @@ describe("Chat Integration Tests", () => { // Set up state change listener const stateChangePromise = new Promise((resolve) => { panel.webview.onDidReceiveMessage((message) => { - if (message.type === "chatSettings") { + if (message.type === "togglePlanActMode") { resolve(message) } }) diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index c111aebe71..c45f88e256 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -655,7 +655,7 @@ const ChatTextArea = forwardRef( setTimeout(() => { const newMode = chatSettings.mode === "plan" ? "act" : "plan" vscode.postMessage({ - type: "chatSettings", + type: "togglePlanActMode", chatSettings: { mode: newMode, },