mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 04:11:10 -05:00
Merge remote-tracking branch 'origin/main' into vscode-lm-provider
This commit is contained in:
@@ -16,7 +16,9 @@ import { ApiConfiguration, ApiProvider, ModelInfo } from "../../shared/api"
|
||||
import { findLast } from "../../shared/array"
|
||||
import { ApiConfigMeta, ExtensionMessage } from "../../shared/ExtensionMessage"
|
||||
import { HistoryItem } from "../../shared/HistoryItem"
|
||||
import { WebviewMessage } from "../../shared/WebviewMessage"
|
||||
import { WebviewMessage, PromptMode } from "../../shared/WebviewMessage"
|
||||
import { defaultPrompts } from "../../shared/modes"
|
||||
import { SYSTEM_PROMPT, addCustomInstructions } from "../prompts/system"
|
||||
import { fileExistsAtPath } from "../../utils/fs"
|
||||
import { Cline } from "../Cline"
|
||||
import { openMention } from "../mentions"
|
||||
@@ -27,6 +29,8 @@ import { checkExistKey } from "../../shared/checkExistApiConfig"
|
||||
import { enhancePrompt } from "../../utils/enhance-prompt"
|
||||
import { getCommitInfo, searchCommits, getWorkingState } from "../../utils/git"
|
||||
import { ConfigManager } from "../config/ConfigManager"
|
||||
import { Mode } from "../prompts/types"
|
||||
import { codeMode, CustomPrompts } from "../../shared/modes"
|
||||
|
||||
/*
|
||||
https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts
|
||||
@@ -90,6 +94,10 @@ type GlobalStateKey =
|
||||
| "currentApiConfigName"
|
||||
| "listApiConfigMeta"
|
||||
| "vsCodeLmModelSelector"
|
||||
| "mode"
|
||||
| "modeApiConfigs"
|
||||
| "customPrompts"
|
||||
| "enhancementApiConfigId"
|
||||
|
||||
export const GlobalFileNames = {
|
||||
apiConversationHistory: "api_conversation_history.json",
|
||||
@@ -108,7 +116,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
private cline?: Cline
|
||||
private workspaceTracker?: WorkspaceTracker
|
||||
mcpHub?: McpHub
|
||||
private latestAnnouncementId = "dec-10-2024" // update to some unique identifier when we add a new announcement
|
||||
private latestAnnouncementId = "jan-13-2025-custom-prompt" // update to some unique identifier when we add a new announcement
|
||||
configManager: ConfigManager
|
||||
|
||||
constructor(
|
||||
@@ -235,19 +243,26 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
this.outputChannel.appendLine("Webview view resolved")
|
||||
}
|
||||
|
||||
async initClineWithTask(task?: string, images?: string[]) {
|
||||
public async initClineWithTask(task?: string, images?: string[]) {
|
||||
await this.clearTask()
|
||||
const {
|
||||
apiConfiguration,
|
||||
customInstructions,
|
||||
customPrompts,
|
||||
diffEnabled,
|
||||
fuzzyMatchThreshold
|
||||
fuzzyMatchThreshold,
|
||||
mode,
|
||||
customInstructions: globalInstructions,
|
||||
} = await this.getState()
|
||||
|
||||
const modeInstructions = customPrompts?.[mode]?.customInstructions
|
||||
const effectiveInstructions = [globalInstructions, modeInstructions]
|
||||
.filter(Boolean)
|
||||
.join('\n\n')
|
||||
|
||||
this.cline = new Cline(
|
||||
this,
|
||||
apiConfiguration,
|
||||
customInstructions,
|
||||
effectiveInstructions,
|
||||
diffEnabled,
|
||||
fuzzyMatchThreshold,
|
||||
task,
|
||||
@@ -255,19 +270,26 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
)
|
||||
}
|
||||
|
||||
async initClineWithHistoryItem(historyItem: HistoryItem) {
|
||||
public async initClineWithHistoryItem(historyItem: HistoryItem) {
|
||||
await this.clearTask()
|
||||
const {
|
||||
apiConfiguration,
|
||||
customInstructions,
|
||||
customPrompts,
|
||||
diffEnabled,
|
||||
fuzzyMatchThreshold
|
||||
fuzzyMatchThreshold,
|
||||
mode,
|
||||
customInstructions: globalInstructions,
|
||||
} = await this.getState()
|
||||
|
||||
const modeInstructions = customPrompts?.[mode]?.customInstructions
|
||||
const effectiveInstructions = [globalInstructions, modeInstructions]
|
||||
.filter(Boolean)
|
||||
.join('\n\n')
|
||||
|
||||
this.cline = new Cline(
|
||||
this,
|
||||
apiConfiguration,
|
||||
customInstructions,
|
||||
effectiveInstructions,
|
||||
diffEnabled,
|
||||
fuzzyMatchThreshold,
|
||||
undefined,
|
||||
@@ -276,8 +298,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
)
|
||||
}
|
||||
|
||||
// Send any JSON serializable data to the react app
|
||||
async postMessageToWebview(message: ExtensionMessage) {
|
||||
public async postMessageToWebview(message: ExtensionMessage) {
|
||||
await this.view?.webview.postMessage(message)
|
||||
}
|
||||
|
||||
@@ -373,6 +394,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
async (message: WebviewMessage) => {
|
||||
switch (message.type) {
|
||||
case "webviewDidLaunch":
|
||||
|
||||
this.postStateToWebview()
|
||||
this.workspaceTracker?.initializeFilePaths() // don't await
|
||||
getTheme().then((theme) =>
|
||||
@@ -570,7 +592,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
openImage(message.text!)
|
||||
break
|
||||
case "openFile":
|
||||
openFile(message.text!)
|
||||
openFile(message.text!, message.values as { create?: boolean; content?: string })
|
||||
break
|
||||
case "openMention":
|
||||
openMention(message.text)
|
||||
@@ -695,6 +717,90 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
await this.updateGlobalState("terminalOutputLineLimit", message.value)
|
||||
await this.postStateToWebview()
|
||||
break
|
||||
case "mode":
|
||||
const newMode = message.text as Mode
|
||||
await this.updateGlobalState("mode", newMode)
|
||||
|
||||
// Load the saved API config for the new mode if it exists
|
||||
const savedConfigId = await this.configManager.GetModeConfigId(newMode)
|
||||
const listApiConfig = await this.configManager.ListConfig()
|
||||
|
||||
// Update listApiConfigMeta first to ensure UI has latest data
|
||||
await this.updateGlobalState("listApiConfigMeta", listApiConfig)
|
||||
|
||||
// If this mode has a saved config, use it
|
||||
if (savedConfigId) {
|
||||
const config = listApiConfig?.find(c => c.id === savedConfigId)
|
||||
if (config?.name) {
|
||||
const apiConfig = await this.configManager.LoadConfig(config.name)
|
||||
await Promise.all([
|
||||
this.updateGlobalState("currentApiConfigName", config.name),
|
||||
this.updateApiConfiguration(apiConfig)
|
||||
])
|
||||
}
|
||||
} else {
|
||||
// If no saved config for this mode, save current config as default
|
||||
const currentApiConfigName = await this.getGlobalState("currentApiConfigName")
|
||||
if (currentApiConfigName) {
|
||||
const config = listApiConfig?.find(c => c.name === currentApiConfigName)
|
||||
if (config?.id) {
|
||||
await this.configManager.SetModeConfig(newMode, config.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await this.postStateToWebview()
|
||||
break
|
||||
case "updateEnhancedPrompt":
|
||||
const existingPrompts = await this.getGlobalState("customPrompts") || {}
|
||||
|
||||
const updatedPrompts = {
|
||||
...existingPrompts,
|
||||
enhance: message.text
|
||||
}
|
||||
|
||||
await this.updateGlobalState("customPrompts", updatedPrompts)
|
||||
|
||||
// Get current state and explicitly include customPrompts
|
||||
const currentState = await this.getState()
|
||||
|
||||
const stateWithPrompts = {
|
||||
...currentState,
|
||||
customPrompts: updatedPrompts
|
||||
}
|
||||
|
||||
// Post state with prompts
|
||||
this.view?.webview.postMessage({
|
||||
type: "state",
|
||||
state: stateWithPrompts
|
||||
})
|
||||
break
|
||||
case "updatePrompt":
|
||||
if (message.promptMode && message.customPrompt !== undefined) {
|
||||
const existingPrompts = await this.getGlobalState("customPrompts") || {}
|
||||
|
||||
const updatedPrompts = {
|
||||
...existingPrompts,
|
||||
[message.promptMode]: message.customPrompt
|
||||
}
|
||||
|
||||
await this.updateGlobalState("customPrompts", updatedPrompts)
|
||||
|
||||
// Get current state and explicitly include customPrompts
|
||||
const currentState = await this.getState()
|
||||
|
||||
const stateWithPrompts = {
|
||||
...currentState,
|
||||
customPrompts: updatedPrompts
|
||||
}
|
||||
|
||||
// Post state with prompts
|
||||
this.view?.webview.postMessage({
|
||||
type: "state",
|
||||
state: stateWithPrompts
|
||||
})
|
||||
}
|
||||
break
|
||||
case "deleteMessage": {
|
||||
const answer = await vscode.window.showInformationMessage(
|
||||
"What would you like to delete?",
|
||||
@@ -765,16 +871,28 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
await this.updateGlobalState("screenshotQuality", message.value)
|
||||
await this.postStateToWebview()
|
||||
break
|
||||
case "enhancementApiConfigId":
|
||||
await this.updateGlobalState("enhancementApiConfigId", message.text)
|
||||
await this.postStateToWebview()
|
||||
break
|
||||
case "enhancePrompt":
|
||||
if (message.text) {
|
||||
try {
|
||||
const { apiConfiguration } = await this.getState()
|
||||
const enhanceConfig = {
|
||||
...apiConfiguration,
|
||||
apiProvider: "openrouter" as const,
|
||||
openRouterModelId: "gpt-4o",
|
||||
const { apiConfiguration, customPrompts, listApiConfigMeta, enhancementApiConfigId } = await this.getState()
|
||||
|
||||
// Try to get enhancement config first, fall back to current config
|
||||
let configToUse: ApiConfiguration = apiConfiguration
|
||||
if (enhancementApiConfigId) {
|
||||
const config = listApiConfigMeta?.find(c => c.id === enhancementApiConfigId)
|
||||
if (config?.name) {
|
||||
const loadedConfig = await this.configManager.LoadConfig(config.name)
|
||||
if (loadedConfig.apiProvider) {
|
||||
configToUse = loadedConfig
|
||||
}
|
||||
}
|
||||
}
|
||||
const enhancedPrompt = await enhancePrompt(enhanceConfig, message.text)
|
||||
|
||||
const enhancedPrompt = await enhancePrompt(configToUse, message.text, customPrompts?.enhance)
|
||||
await this.postMessageToWebview({
|
||||
type: "enhancedPrompt",
|
||||
text: enhancedPrompt
|
||||
@@ -782,11 +900,45 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
} catch (error) {
|
||||
console.error("Error enhancing prompt:", error)
|
||||
vscode.window.showErrorMessage("Failed to enhance prompt")
|
||||
await this.postMessageToWebview({
|
||||
type: "enhancedPrompt"
|
||||
})
|
||||
}
|
||||
}
|
||||
break
|
||||
case "getSystemPrompt":
|
||||
try {
|
||||
const { apiConfiguration, customPrompts, customInstructions, preferredLanguage, browserViewportSize, mcpEnabled } = await this.getState()
|
||||
const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0) || ''
|
||||
|
||||
const mode = message.mode ?? codeMode
|
||||
const instructions = await addCustomInstructions(
|
||||
{ customInstructions, customPrompts, preferredLanguage },
|
||||
cwd,
|
||||
mode
|
||||
)
|
||||
|
||||
const systemPrompt = await SYSTEM_PROMPT(
|
||||
cwd,
|
||||
apiConfiguration.openRouterModelInfo?.supportsComputerUse ?? false,
|
||||
mcpEnabled ? this.mcpHub : undefined,
|
||||
undefined,
|
||||
browserViewportSize ?? "900x600",
|
||||
mode,
|
||||
customPrompts
|
||||
)
|
||||
const fullPrompt = instructions ? `${systemPrompt}${instructions}` : systemPrompt
|
||||
|
||||
await this.postMessageToWebview({
|
||||
type: "systemPrompt",
|
||||
text: fullPrompt,
|
||||
mode: message.mode
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Error getting system prompt:", error)
|
||||
vscode.window.showErrorMessage("Failed to get system prompt")
|
||||
}
|
||||
break
|
||||
case "searchCommits": {
|
||||
const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0)
|
||||
if (cwd) {
|
||||
@@ -807,16 +959,17 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
if (message.text && message.apiConfiguration) {
|
||||
try {
|
||||
await this.configManager.SaveConfig(message.text, message.apiConfiguration);
|
||||
|
||||
let listApiConfig = await this.configManager.ListConfig();
|
||||
|
||||
// Update listApiConfigMeta first to ensure UI has latest data
|
||||
await this.updateGlobalState("listApiConfigMeta", listApiConfig);
|
||||
|
||||
await Promise.all([
|
||||
this.updateApiConfiguration(message.apiConfiguration),
|
||||
this.updateGlobalState("currentApiConfigName", message.text),
|
||||
this.updateGlobalState("listApiConfigMeta", listApiConfig),
|
||||
])
|
||||
|
||||
this.postStateToWebview()
|
||||
await this.postStateToWebview()
|
||||
} catch (error) {
|
||||
console.error("Error create new api configuration:", error)
|
||||
vscode.window.showErrorMessage("Failed to create api configuration")
|
||||
@@ -826,21 +979,22 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
case "renameApiConfiguration":
|
||||
if (message.values && message.apiConfiguration) {
|
||||
try {
|
||||
|
||||
const { oldName, newName } = message.values
|
||||
|
||||
await this.configManager.SaveConfig(newName, message.apiConfiguration);
|
||||
|
||||
await this.configManager.DeleteConfig(oldName)
|
||||
|
||||
let listApiConfig = await this.configManager.ListConfig();
|
||||
const config = listApiConfig?.find(c => c.name === newName);
|
||||
|
||||
// Update listApiConfigMeta first to ensure UI has latest data
|
||||
await this.updateGlobalState("listApiConfigMeta", listApiConfig);
|
||||
|
||||
await Promise.all([
|
||||
this.updateGlobalState("currentApiConfigName", newName),
|
||||
this.updateGlobalState("listApiConfigMeta", listApiConfig),
|
||||
])
|
||||
|
||||
this.postStateToWebview()
|
||||
await this.postStateToWebview()
|
||||
} catch (error) {
|
||||
console.error("Error create new api configuration:", error)
|
||||
vscode.window.showErrorMessage("Failed to create api configuration")
|
||||
@@ -851,6 +1005,11 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
if (message.text) {
|
||||
try {
|
||||
const apiConfig = await this.configManager.LoadConfig(message.text);
|
||||
const listApiConfig = await this.configManager.ListConfig();
|
||||
const config = listApiConfig?.find(c => c.name === message.text);
|
||||
|
||||
// Update listApiConfigMeta first to ensure UI has latest data
|
||||
await this.updateGlobalState("listApiConfigMeta", listApiConfig);
|
||||
|
||||
await Promise.all([
|
||||
this.updateGlobalState("currentApiConfigName", message.text),
|
||||
@@ -866,7 +1025,6 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
break
|
||||
case "deleteApiConfiguration":
|
||||
if (message.text) {
|
||||
|
||||
const answer = await vscode.window.showInformationMessage(
|
||||
"Are you sure you want to delete this configuration profile?",
|
||||
{ modal: true },
|
||||
@@ -879,21 +1037,22 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
|
||||
try {
|
||||
await this.configManager.DeleteConfig(message.text);
|
||||
let listApiConfig = await this.configManager.ListConfig()
|
||||
const listApiConfig = await this.configManager.ListConfig();
|
||||
|
||||
// Update listApiConfigMeta first to ensure UI has latest data
|
||||
await this.updateGlobalState("listApiConfigMeta", listApiConfig);
|
||||
|
||||
// If this was the current config, switch to first available
|
||||
let currentApiConfigName = await this.getGlobalState("currentApiConfigName")
|
||||
|
||||
if (message.text === currentApiConfigName) {
|
||||
await this.updateGlobalState("currentApiConfigName", listApiConfig?.[0]?.name)
|
||||
if (listApiConfig?.[0]?.name) {
|
||||
const apiConfig = await this.configManager.LoadConfig(listApiConfig?.[0]?.name);
|
||||
|
||||
await Promise.all([
|
||||
this.updateGlobalState("listApiConfigMeta", listApiConfig),
|
||||
this.updateApiConfiguration(apiConfig),
|
||||
])
|
||||
await this.postStateToWebview()
|
||||
}
|
||||
if (message.text === currentApiConfigName && listApiConfig?.[0]?.name) {
|
||||
const apiConfig = await this.configManager.LoadConfig(listApiConfig[0].name);
|
||||
await Promise.all([
|
||||
this.updateGlobalState("currentApiConfigName", listApiConfig[0].name),
|
||||
this.updateApiConfiguration(apiConfig),
|
||||
])
|
||||
}
|
||||
|
||||
await this.postStateToWebview()
|
||||
} catch (error) {
|
||||
console.error("Error delete api configuration:", error)
|
||||
vscode.window.showErrorMessage("Failed to delete api configuration")
|
||||
@@ -918,6 +1077,17 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
}
|
||||
|
||||
private async updateApiConfiguration(apiConfiguration: ApiConfiguration) {
|
||||
// Update mode's default config
|
||||
const { mode } = await this.getState();
|
||||
if (mode) {
|
||||
const currentApiConfigName = await this.getGlobalState("currentApiConfigName");
|
||||
const listApiConfig = await this.configManager.ListConfig();
|
||||
const config = listApiConfig?.find(c => c.name === currentApiConfigName);
|
||||
if (config?.id) {
|
||||
await this.configManager.SetModeConfig(mode, config.id);
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
apiProvider,
|
||||
apiModelId,
|
||||
@@ -1444,6 +1614,9 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
requestDelaySeconds,
|
||||
currentApiConfigName,
|
||||
listApiConfigMeta,
|
||||
mode,
|
||||
customPrompts,
|
||||
enhancementApiConfigId,
|
||||
} = await this.getState()
|
||||
|
||||
const allowedCommands = vscode.workspace
|
||||
@@ -1462,8 +1635,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
uriScheme: vscode.env.uriScheme,
|
||||
clineMessages: this.cline?.clineMessages || [],
|
||||
taskHistory: (taskHistory || [])
|
||||
.filter((item) => item.ts && item.task)
|
||||
.sort((a, b) => b.ts - a.ts),
|
||||
.filter((item: HistoryItem) => item.ts && item.task)
|
||||
.sort((a: HistoryItem, b: HistoryItem) => b.ts - a.ts),
|
||||
soundEnabled: soundEnabled ?? false,
|
||||
diffEnabled: diffEnabled ?? true,
|
||||
shouldShowAnnouncement: lastShownAnnouncementId !== this.latestAnnouncementId,
|
||||
@@ -1480,6 +1653,9 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
requestDelaySeconds: requestDelaySeconds ?? 5,
|
||||
currentApiConfigName: currentApiConfigName ?? "default",
|
||||
listApiConfigMeta: listApiConfigMeta ?? [],
|
||||
mode: mode ?? codeMode,
|
||||
customPrompts: customPrompts ?? {},
|
||||
enhancementApiConfigId,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1589,7 +1765,11 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
requestDelaySeconds,
|
||||
currentApiConfigName,
|
||||
listApiConfigMeta,
|
||||
vsCodeLmModelSelector
|
||||
vsCodeLmModelSelector,
|
||||
mode,
|
||||
modeApiConfigs,
|
||||
customPrompts,
|
||||
enhancementApiConfigId,
|
||||
] = await Promise.all([
|
||||
this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
|
||||
this.getGlobalState("apiModelId") as Promise<string | undefined>,
|
||||
@@ -1645,7 +1825,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
this.getGlobalState("currentApiConfigName") as Promise<string | undefined>,
|
||||
this.getGlobalState("listApiConfigMeta") as Promise<ApiConfigMeta[] | undefined>,
|
||||
this.getGlobalState("vsCodeLmModelSelector") as Promise<vscode.LanguageModelChatSelector | undefined>,
|
||||
|
||||
this.getGlobalState("mode") as Promise<Mode | undefined>,
|
||||
this.getGlobalState("modeApiConfigs") as Promise<Record<Mode, string> | undefined>,
|
||||
this.getGlobalState("customPrompts") as Promise<CustomPrompts | undefined>,
|
||||
this.getGlobalState("enhancementApiConfigId") as Promise<string | undefined>,
|
||||
])
|
||||
|
||||
let apiProvider: ApiProvider
|
||||
@@ -1713,6 +1896,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
fuzzyMatchThreshold: fuzzyMatchThreshold ?? 1.0,
|
||||
writeDelayMs: writeDelayMs ?? 1000,
|
||||
terminalOutputLineLimit: terminalOutputLineLimit ?? 500,
|
||||
mode: mode ?? codeMode,
|
||||
preferredLanguage: preferredLanguage ?? (() => {
|
||||
// Get VSCode's locale setting
|
||||
const vscodeLang = vscode.env.language;
|
||||
@@ -1745,6 +1929,9 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
||||
requestDelaySeconds: requestDelaySeconds ?? 5,
|
||||
currentApiConfigName: currentApiConfigName ?? "default",
|
||||
listApiConfigMeta: listApiConfigMeta ?? [],
|
||||
modeApiConfigs: modeApiConfigs ?? {} as Record<Mode, string>,
|
||||
customPrompts: customPrompts ?? {},
|
||||
enhancementApiConfigId,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user