diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 5be3c32fb1..a41ce3c1e1 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,9 +1,5 @@ { // See http://go.microsoft.com/fwlink/?LinkId=827846 // for the documentation about the extensions.json format - "recommendations": [ - "dbaeumer.vscode-eslint", - "connor4312.esbuild-problem-matchers", - "ms-vscode.extension-test-runner" - ] + "recommendations": ["dbaeumer.vscode-eslint", "connor4312.esbuild-problem-matchers", "ms-vscode.extension-test-runner"] } diff --git a/src/api/providers/anthropic.ts b/src/api/providers/anthropic.ts index c090f17c63..76e5487a93 100644 --- a/src/api/providers/anthropic.ts +++ b/src/api/providers/anthropic.ts @@ -1,12 +1,6 @@ import { Anthropic } from "@anthropic-ai/sdk" import { Stream as AnthropicStream } from "@anthropic-ai/sdk/streaming" -import { - anthropicDefaultModelId, - AnthropicModelId, - anthropicModels, - ApiHandlerOptions, - ModelInfo, -} from "../../shared/api" +import { anthropicDefaultModelId, AnthropicModelId, anthropicModels, ApiHandlerOptions, ModelInfo } from "../../shared/api" import { ApiHandler } from "../index" import { ApiStream } from "../transform/stream" diff --git a/src/api/providers/openai.ts b/src/api/providers/openai.ts index 57cab17e68..58e4ba0250 100644 --- a/src/api/providers/openai.ts +++ b/src/api/providers/openai.ts @@ -1,11 +1,6 @@ import { Anthropic } from "@anthropic-ai/sdk" import OpenAI, { AzureOpenAI } from "openai" -import { - ApiHandlerOptions, - azureOpenAiDefaultApiVersion, - ModelInfo, - openAiModelInfoSaneDefaults, -} from "../../shared/api" +import { ApiHandlerOptions, azureOpenAiDefaultApiVersion, ModelInfo, openAiModelInfoSaneDefaults } from "../../shared/api" import { ApiHandler } from "../index" import { convertToOpenAiMessages } from "../transform/openai-format" import { ApiStream } from "../transform/stream" diff --git a/src/api/transform/gemini-format.ts b/src/api/transform/gemini-format.ts index 935e47147a..0a783dc183 100644 --- a/src/api/transform/gemini-format.ts +++ b/src/api/transform/gemini-format.ts @@ -124,17 +124,10 @@ export function convertAnthropicToolToGemini(tool: Anthropic.Messages.Tool): Fun It looks like gemini likes to double escape certain characters when writing file contents: https://discuss.ai.google.dev/t/function-call-string-property-is-double-escaped/37867 */ export function unescapeGeminiContent(content: string) { - return content - .replace(/\\n/g, "\n") - .replace(/\\'/g, "'") - .replace(/\\"/g, '"') - .replace(/\\r/g, "\r") - .replace(/\\t/g, "\t") + return content.replace(/\\n/g, "\n").replace(/\\'/g, "'").replace(/\\"/g, '"').replace(/\\r/g, "\r").replace(/\\t/g, "\t") } -export function convertGeminiResponseToAnthropic( - response: EnhancedGenerateContentResponse, -): Anthropic.Messages.Message { +export function convertGeminiResponseToAnthropic(response: EnhancedGenerateContentResponse): Anthropic.Messages.Message { const content: Anthropic.Messages.ContentBlock[] = [] // Add the main text response diff --git a/src/api/transform/openai-format.ts b/src/api/transform/openai-format.ts index fe23b9b2ff..aa995c7448 100644 --- a/src/api/transform/openai-format.ts +++ b/src/api/transform/openai-format.ts @@ -146,9 +146,7 @@ export function convertToOpenAiMessages( } // Convert OpenAI response to Anthropic format -export function convertToAnthropicMessage( - completion: OpenAI.Chat.Completions.ChatCompletion, -): Anthropic.Messages.Message { +export function convertToAnthropicMessage(completion: OpenAI.Chat.Completions.ChatCompletion): Anthropic.Messages.Message { const openAiMessage = completion.choices[0].message const anthropicMessage: Anthropic.Messages.Message = { id: completion.id, diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 60bb309038..7b2dd4d31b 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -54,8 +54,7 @@ import { removeInvalidChars } from "../utils/string" import { fixModelHtmlEscaping } from "../utils/string" import { OpenAiHandler } from "../api/providers/openai" -const cwd = - vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0) ?? path.join(os.homedir(), "Desktop") // may or may not exist but fs checking existence would immediately ask for permission which would be bad UX, need to come up with a better solution +const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0) ?? path.join(os.homedir(), "Desktop") // may or may not exist but fs checking existence would immediately ask for permission which would be bad UX, need to come up with a better solution type ToolResponse = string | Array type UserContent = Array< @@ -200,10 +199,7 @@ export class Cline { const taskMessage = this.clineMessages[0] // first message is always the task say const lastRelevantMessage = this.clineMessages[ - findLastIndex( - this.clineMessages, - (m) => !(m.ask === "resume_task" || m.ask === "resume_completed_task"), - ) + findLastIndex(this.clineMessages, (m) => !(m.ask === "resume_task" || m.ask === "resume_completed_task")) ] await this.providerRef.deref()?.updateTaskHistory({ id: this.taskId, @@ -245,9 +241,7 @@ export class Cline { // todo be more efficient about saving and posting only new data or one whole message at a time so ignore partial for saves, and only post parts of partial message instead of whole array in new listener // await this.saveClineMessages() // await this.providerRef.deref()?.postStateToWebview() - await this.providerRef - .deref() - ?.postMessageToWebview({ type: "partialMessage", partialMessage: lastMessage }) + await this.providerRef.deref()?.postMessageToWebview({ type: "partialMessage", partialMessage: lastMessage }) throw new Error("Current ask promise was ignored 1") } else { // this is a new partial message, so add it with partial state @@ -281,9 +275,7 @@ export class Cline { lastMessage.partial = false await this.saveClineMessages() // await this.providerRef.deref()?.postStateToWebview() - await this.providerRef - .deref() - ?.postMessageToWebview({ type: "partialMessage", partialMessage: lastMessage }) + await this.providerRef.deref()?.postMessageToWebview({ type: "partialMessage", partialMessage: lastMessage }) } else { // this is a new partial=false message, so add it like normal this.askResponse = undefined @@ -339,9 +331,7 @@ export class Cline { lastMessage.text = text lastMessage.images = images lastMessage.partial = partial - await this.providerRef - .deref() - ?.postMessageToWebview({ type: "partialMessage", partialMessage: lastMessage }) + await this.providerRef.deref()?.postMessageToWebview({ type: "partialMessage", partialMessage: lastMessage }) } else { // this is a new partial message, so add it with partial state const sayTs = Date.now() @@ -362,9 +352,7 @@ export class Cline { // instead of streaming partialMessage events, we do a save and post like normal to persist to disk await this.saveClineMessages() // await this.providerRef.deref()?.postStateToWebview() - await this.providerRef - .deref() - ?.postMessageToWebview({ type: "partialMessage", partialMessage: lastMessage }) // more performant than an entire postStateToWebview + await this.providerRef.deref()?.postMessageToWebview({ type: "partialMessage", partialMessage: lastMessage }) // more performant than an entire postStateToWebview } else { // this is a new partial=false message, so add it like normal const sayTs = Date.now() @@ -394,11 +382,7 @@ export class Cline { async removeLastPartialMessageIfExistsWithType(type: "ask" | "say", askOrSay: ClineAsk | ClineSay) { const lastMessage = this.clineMessages.at(-1) - if ( - lastMessage?.partial && - lastMessage.type === type && - (lastMessage.ask === askOrSay || lastMessage.say === askOrSay) - ) { + if (lastMessage?.partial && lastMessage.type === type && (lastMessage.ask === askOrSay || lastMessage.say === askOrSay)) { this.clineMessages.pop() await this.saveClineMessages() await this.providerRef.deref()?.postStateToWebview() @@ -489,8 +473,7 @@ export class Cline { // need to make sure that the api conversation history can be resumed by the api, even if it goes out of sync with cline messages - let existingApiConversationHistory: Anthropic.Messages.MessageParam[] = - await this.getSavedApiConversationHistory() + let existingApiConversationHistory: Anthropic.Messages.MessageParam[] = await this.getSavedApiConversationHistory() // v2.0 xml tags refactor caveat: since we don't use tools anymore, we need to replace all tool use blocks with a text block since the API disallows conversations with tool uses and no tool schema const conversationWithoutToolBlocks = existingApiConversationHistory.map((message) => { @@ -581,9 +564,7 @@ export class Cline { ) as Anthropic.ToolResultBlockParam[] const missingToolResponses: Anthropic.ToolResultBlockParam[] = toolUseBlocks - .filter( - (toolUse) => !existingToolResults.some((result) => result.tool_use_id === toolUse.id), - ) + .filter((toolUse) => !existingToolResults.some((result) => result.tool_use_id === toolUse.id)) .map((toolUse) => ({ type: "tool_result", tool_use_id: toolUse.id, @@ -828,9 +809,7 @@ export class Cline { if (previousApiReqIndex >= 0) { const previousRequest = this.clineMessages[previousApiReqIndex] if (previousRequest && previousRequest.text) { - const { tokensIn, tokensOut, cacheWrites, cacheReads }: ClineApiReqInfo = JSON.parse( - previousRequest.text, - ) + const { tokensIn, tokensOut, cacheWrites, cacheReads }: ClineApiReqInfo = JSON.parse(previousRequest.text) const totalTokens = (tokensIn || 0) + (tokensOut || 0) + (cacheWrites || 0) + (cacheReads || 0) let contextWindow = this.api.getModel().info.contextWindow || 128_000 // FIXME: hack to get anyone using openai compatible with deepseek to have the proper context window instead of the default 128k. We need a way for the user to specify the context window for models they input through openai compatible @@ -868,10 +847,7 @@ export class Cline { yield firstChunk.value } catch (error) { // note that this api_req_failed ask is unique in that we only present this option if the api hasn't streamed any content yet (ie it fails on the first chunk due), as it would allow them to hit a retry button. However if the api failed mid-stream, it could be in any arbitrary state where some tools may have executed, so that error is handled differently and requires cancelling the task entirely. - const { response } = await this.ask( - "api_req_failed", - error.message ?? JSON.stringify(serializeError(error), null, 2), - ) + const { response } = await this.ask("api_req_failed", error.message ?? JSON.stringify(serializeError(error), null, 2)) if (response !== "yesButtonClicked") { // this will never happen since if noButtonClicked, we will clear current task, aborting this instance throw new Error("API request failed") @@ -1049,9 +1025,7 @@ export class Cline { if (response !== "yesButtonClicked") { if (response === "messageResponse") { await this.say("user_feedback", text, images) - pushToolResult( - formatResponse.toolResult(formatResponse.toolDeniedWithFeedback(text), images), - ) + pushToolResult(formatResponse.toolResult(formatResponse.toolDeniedWithFeedback(text), images)) // this.userMessageContent.push({ // type: "text", // text: `${toolDescription()}`, @@ -1503,9 +1477,7 @@ export class Cline { } else { if (!relDirPath) { this.consecutiveMistakeCount++ - pushToolResult( - await this.sayAndCreateMissingParamError("list_code_definition_names", "path"), - ) + pushToolResult(await this.sayAndCreateMissingParamError("list_code_definition_names", "path")) break } this.consecutiveMistakeCount = 0 @@ -1654,9 +1626,7 @@ export class Cline { if (action === "launch") { if (!url) { this.consecutiveMistakeCount++ - pushToolResult( - await this.sayAndCreateMissingParamError("browser_action", "url"), - ) + pushToolResult(await this.sayAndCreateMissingParamError("browser_action", "url")) await this.browserSession.closeBrowser() break } @@ -1688,10 +1658,7 @@ export class Cline { if (!coordinate) { this.consecutiveMistakeCount++ pushToolResult( - await this.sayAndCreateMissingParamError( - "browser_action", - "coordinate", - ), + await this.sayAndCreateMissingParamError("browser_action", "coordinate"), ) await this.browserSession.closeBrowser() break // can't be within an inner switch @@ -1700,9 +1667,7 @@ export class Cline { if (action === "type") { if (!text) { this.consecutiveMistakeCount++ - pushToolResult( - await this.sayAndCreateMissingParamError("browser_action", "text"), - ) + pushToolResult(await this.sayAndCreateMissingParamError("browser_action", "text")) await this.browserSession.closeBrowser() break } @@ -1786,28 +1751,19 @@ export class Cline { // ).catch(() => {}) } else { // don't need to remove last partial since we couldn't have streamed a say - await this.ask( - "command", - removeClosingTag("command", command), - block.partial, - ).catch(() => {}) + await this.ask("command", removeClosingTag("command", command), block.partial).catch(() => {}) } break } else { if (!command) { this.consecutiveMistakeCount++ - pushToolResult( - await this.sayAndCreateMissingParamError("execute_command", "command"), - ) + pushToolResult(await this.sayAndCreateMissingParamError("execute_command", "command")) break } if (!requiresApprovalRaw) { this.consecutiveMistakeCount++ pushToolResult( - await this.sayAndCreateMissingParamError( - "execute_command", - "requires_approval", - ), + await this.sayAndCreateMissingParamError("execute_command", "requires_approval"), ) break } @@ -1887,16 +1843,12 @@ export class Cline { } else { if (!server_name) { this.consecutiveMistakeCount++ - pushToolResult( - await this.sayAndCreateMissingParamError("use_mcp_tool", "server_name"), - ) + pushToolResult(await this.sayAndCreateMissingParamError("use_mcp_tool", "server_name")) break } if (!tool_name) { this.consecutiveMistakeCount++ - pushToolResult( - await this.sayAndCreateMissingParamError("use_mcp_tool", "tool_name"), - ) + pushToolResult(await this.sayAndCreateMissingParamError("use_mcp_tool", "tool_name")) break } // arguments are optional, but if they are provided they must be valid JSON @@ -2000,16 +1952,12 @@ export class Cline { } else { if (!server_name) { this.consecutiveMistakeCount++ - pushToolResult( - await this.sayAndCreateMissingParamError("access_mcp_resource", "server_name"), - ) + pushToolResult(await this.sayAndCreateMissingParamError("access_mcp_resource", "server_name")) break } if (!uri) { this.consecutiveMistakeCount++ - pushToolResult( - await this.sayAndCreateMissingParamError("access_mcp_resource", "uri"), - ) + pushToolResult(await this.sayAndCreateMissingParamError("access_mcp_resource", "uri")) break } this.consecutiveMistakeCount = 0 @@ -2036,9 +1984,7 @@ export class Cline { // now execute the tool await this.say("mcp_server_request_started") - const resourceResult = await this.providerRef - .deref() - ?.mcpHub?.readResource(server_name, uri) + const resourceResult = await this.providerRef.deref()?.mcpHub?.readResource(server_name, uri) const resourceResultPretty = resourceResult?.contents .map((item) => { @@ -2062,24 +2008,17 @@ export class Cline { const question: string | undefined = block.params.question try { if (block.partial) { - await this.ask("followup", removeClosingTag("question", question), block.partial).catch( - () => {}, - ) + await this.ask("followup", removeClosingTag("question", question), block.partial).catch(() => {}) break } else { if (!question) { this.consecutiveMistakeCount++ - pushToolResult( - await this.sayAndCreateMissingParamError("ask_followup_question", "question"), - ) + pushToolResult(await this.sayAndCreateMissingParamError("ask_followup_question", "question")) break } this.consecutiveMistakeCount = 0 - if ( - this.autoApprovalSettings.enabled && - this.autoApprovalSettings.enableNotifications - ) { + if (this.autoApprovalSettings.enabled && this.autoApprovalSettings.enableNotifications) { showSystemNotification({ subtitle: "Cline has a question...", message: question.replace(/\n/g, " "), @@ -2130,25 +2069,16 @@ export class Cline { // NOTE: we do not want to auto approve a command run as part of the attempt_completion tool if (lastMessage && lastMessage.ask === "command") { // update command - await this.ask( - "command", - removeClosingTag("command", command), - block.partial, - ).catch(() => {}) + await this.ask("command", removeClosingTag("command", command), block.partial).catch( + () => {}, + ) } else { // last message is completion_result // we have command string, which means we have the result as well, so finish it (doesnt have to exist yet) - await this.say( - "completion_result", - removeClosingTag("result", result), - undefined, - false, + await this.say("completion_result", removeClosingTag("result", result), undefined, false) + await this.ask("command", removeClosingTag("command", command), block.partial).catch( + () => {}, ) - await this.ask( - "command", - removeClosingTag("command", command), - block.partial, - ).catch(() => {}) } } else { // no command, still outputting partial result @@ -2163,17 +2093,12 @@ export class Cline { } else { if (!result) { this.consecutiveMistakeCount++ - pushToolResult( - await this.sayAndCreateMissingParamError("attempt_completion", "result"), - ) + pushToolResult(await this.sayAndCreateMissingParamError("attempt_completion", "result")) break } this.consecutiveMistakeCount = 0 - if ( - this.autoApprovalSettings.enabled && - this.autoApprovalSettings.enableNotifications - ) { + if (this.autoApprovalSettings.enabled && this.autoApprovalSettings.enableNotifications) { showSystemNotification({ subtitle: "Task Completed", message: result.replace(/\n/g, " "), @@ -2273,10 +2198,7 @@ export class Cline { } } - async recursivelyMakeClineRequests( - userContent: UserContent, - includeFileDetails: boolean = false, - ): Promise { + async recursivelyMakeClineRequests(userContent: UserContent, includeFileDetails: boolean = false): Promise { if (this.abort) { throw new Error("Cline instance aborted") } @@ -2334,8 +2256,7 @@ export class Cline { await this.say( "api_req_started", JSON.stringify({ - request: - userContent.map((block) => formatContentBlockToMarkdown(block)).join("\n\n") + "\n\nLoading...", + request: userContent.map((block) => formatContentBlockToMarkdown(block)).join("\n\n") + "\n\nLoading...", }), ) @@ -2373,13 +2294,7 @@ export class Cline { cacheReads: cacheReadTokens, cost: totalCost ?? - calculateApiCost( - this.api.getModel().info, - inputTokens, - outputTokens, - cacheWriteTokens, - cacheReadTokens, - ), + calculateApiCost(this.api.getModel().info, inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens), cancelReason, streamingFailedMessage, } satisfies ClineApiReqInfo) @@ -2490,10 +2405,7 @@ export class Cline { // abandoned happens when extension is no longer waiting for the cline instance to finish aborting (error is thrown here when any function in the for loop throws due to this.abort) if (!this.abandoned) { this.abortTask() // if the stream failed, there's various states the task could be in (i.e. could have streamed some tools the user may have executed), so we just resort to replicating a cancel task - await abortStream( - "streaming_failed", - error.message ?? JSON.stringify(serializeError(error), null, 2), - ) + await abortStream("streaming_failed", error.message ?? JSON.stringify(serializeError(error), null, 2)) const history = await this.providerRef.deref()?.getTaskWithId(this.taskId) if (history) { await this.providerRef.deref()?.initClineWithHistoryItem(history.historyItem) diff --git a/src/core/assistant-message/diff.ts b/src/core/assistant-message/diff.ts index 6d7b68395e..4d577cd77a 100644 --- a/src/core/assistant-message/diff.ts +++ b/src/core/assistant-message/diff.ts @@ -6,11 +6,7 @@ * * Returns [matchIndexStart, matchIndexEnd] if found, or false if not found. */ -function lineTrimmedFallbackMatch( - originalContent: string, - searchContent: string, - startIndex: number, -): [number, number] | false { +function lineTrimmedFallbackMatch(originalContent: string, searchContent: string, startIndex: number): [number, number] | false { // Split both contents into lines const originalLines = originalContent.split("\n") const searchLines = searchContent.split("\n") @@ -91,11 +87,7 @@ function lineTrimmedFallbackMatch( * @param startIndex - The character index in originalContent where to start searching * @returns A tuple of [startIndex, endIndex] if a match is found, false otherwise */ -function blockAnchorFallbackMatch( - originalContent: string, - searchContent: string, - startIndex: number, -): [number, number] | false { +function blockAnchorFallbackMatch(originalContent: string, searchContent: string, startIndex: number): [number, number] | false { const originalLines = originalContent.split("\n") const searchLines = searchContent.split("\n") @@ -208,11 +200,7 @@ function blockAnchorFallbackMatch( * - If the search block cannot be matched using any of the available matching strategies, * an error is thrown. */ -export async function constructNewFileContent( - diffContent: string, - originalContent: string, - isFinal: boolean, -): Promise { +export async function constructNewFileContent(diffContent: string, originalContent: string, isFinal: boolean): Promise { let result = "" let lastProcessedIndex = 0 @@ -286,20 +274,12 @@ export async function constructNewFileContent( searchEndIndex = exactIndex + currentSearchContent.length } else { // Attempt fallback line-trimmed matching - const lineMatch = lineTrimmedFallbackMatch( - originalContent, - currentSearchContent, - lastProcessedIndex, - ) + const lineMatch = lineTrimmedFallbackMatch(originalContent, currentSearchContent, lastProcessedIndex) if (lineMatch) { ;[searchMatchIndex, searchEndIndex] = lineMatch } else { // Try block anchor fallback for larger blocks - const blockMatch = blockAnchorFallbackMatch( - originalContent, - currentSearchContent, - lastProcessedIndex, - ) + const blockMatch = blockAnchorFallbackMatch(originalContent, currentSearchContent, lastProcessedIndex) if (blockMatch) { ;[searchMatchIndex, searchEndIndex] = blockMatch } else { diff --git a/src/core/assistant-message/parse-assistant-message.ts b/src/core/assistant-message/parse-assistant-message.ts index e38e8f6458..2c37a650b6 100644 --- a/src/core/assistant-message/parse-assistant-message.ts +++ b/src/core/assistant-message/parse-assistant-message.ts @@ -1,12 +1,4 @@ -import { - AssistantMessageContent, - TextContent, - ToolUse, - ToolParamName, - toolParamNames, - toolUseNames, - ToolUseName, -} from "." +import { AssistantMessageContent, TextContent, ToolUse, ToolParamName, toolParamNames, toolUseNames, ToolUseName } from "." export function parseAssistantMessage(assistantMessage: string) { let contentBlocks: AssistantMessageContent[] = [] @@ -70,9 +62,7 @@ export function parseAssistantMessage(assistantMessage: string) { const contentStartIndex = toolContent.indexOf(contentStartTag) + contentStartTag.length const contentEndIndex = toolContent.lastIndexOf(contentEndTag) if (contentStartIndex !== -1 && contentEndIndex !== -1 && contentEndIndex > contentStartIndex) { - currentToolUse.params[contentParamName] = toolContent - .slice(contentStartIndex, contentEndIndex) - .trim() + currentToolUse.params[contentParamName] = toolContent.slice(contentStartIndex, contentEndIndex).trim() } } diff --git a/src/core/prompts/responses.ts b/src/core/prompts/responses.ts index 05f33ba71a..80b7d11e04 100644 --- a/src/core/prompts/responses.ts +++ b/src/core/prompts/responses.ts @@ -31,10 +31,7 @@ Otherwise, if you have not completed the task and do not need additional informa invalidMcpToolArgumentError: (serverName: string, toolName: string) => `Invalid JSON argument used with ${serverName} for ${toolName}. Please retry with a properly formatted JSON argument.`, - toolResult: ( - text: string, - images?: string[], - ): string | Array => { + toolResult: (text: string, images?: string[]): string | Array => { if (images && images.length > 0) { const textBlock: Anthropic.TextBlockParam = { type: "text", text } const imageBlocks: Anthropic.ImageBlockParam[] = formatImagesIntoBlocks(images) diff --git a/src/core/sliding-window/index.ts b/src/core/sliding-window/index.ts index caa604bc57..9d54dbfc64 100644 --- a/src/core/sliding-window/index.ts +++ b/src/core/sliding-window/index.ts @@ -8,9 +8,7 @@ a 200k context, we can assume that the first half is likely irrelevant to their Therefore, this function should only be called when absolutely necessary to fit within context limits, not as a continuous process. */ -export function truncateHalfConversation( - messages: Anthropic.Messages.MessageParam[], -): Anthropic.Messages.MessageParam[] { +export function truncateHalfConversation(messages: Anthropic.Messages.MessageParam[]): Anthropic.Messages.MessageParam[] { // API expects messages to be in user-assistant order, and tool use messages must be followed by tool results. We need to maintain this structure while truncating. // Always keep the first Task message (this includes the project's file structure in environment_details) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 62ec46ee2d..2b6641e1c3 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -240,13 +240,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { // then convert it to a uri we can use in the webview. // The CSS file from the React build output - const stylesUri = getUri(webview, this.context.extensionUri, [ - "webview-ui", - "build", - "static", - "css", - "main.css", - ]) + const stylesUri = getUri(webview, this.context.extensionUri, ["webview-ui", "build", "static", "css", "main.css"]) // The JS file from the React build output const scriptUri = getUri(webview, this.context.extensionUri, ["webview-ui", "build", "static", "js", "main.js"]) @@ -318,9 +312,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { case "webviewDidLaunch": this.postStateToWebview() this.workspaceTracker?.initializeFilePaths() // don't await - getTheme().then((theme) => - this.postMessageToWebview({ type: "theme", text: JSON.stringify(theme) }), - ) + getTheme().then((theme) => this.postMessageToWebview({ type: "theme", text: JSON.stringify(theme) })) // post last cached models in case the call to endpoint fails this.readOpenRouterModels().then((openRouterModels) => { if (openRouterModels) { @@ -621,10 +613,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { } async readOpenRouterModels(): Promise | undefined> { - const openRouterModelsFilePath = path.join( - await this.ensureCacheDirectoryExists(), - GlobalFileNames.openRouterModels, - ) + const openRouterModelsFilePath = path.join(await this.ensureCacheDirectoryExists(), GlobalFileNames.openRouterModels) const fileExists = await fileExistsAtPath(openRouterModelsFilePath) if (fileExists) { const fileContents = await fs.readFile(openRouterModelsFilePath, "utf8") @@ -634,10 +623,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { } async refreshOpenRouterModels() { - const openRouterModelsFilePath = path.join( - await this.ensureCacheDirectoryExists(), - GlobalFileNames.openRouterModels, - ) + const openRouterModelsFilePath = path.join(await this.ensureCacheDirectoryExists(), GlobalFileNames.openRouterModels) let models: Record = {} try { diff --git a/src/extension.ts b/src/extension.ts index 49e8bbf970..877993fe22 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -110,9 +110,7 @@ export function activate(context: vscode.ExtensionContext) { return Buffer.from(uri.query, "base64").toString("utf-8") } })() - context.subscriptions.push( - vscode.workspace.registerTextDocumentContentProvider(DIFF_VIEW_URI_SCHEME, diffContentProvider), - ) + context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider(DIFF_VIEW_URI_SCHEME, diffContentProvider)) // URI Handler const handleUri = async (uri: vscode.Uri) => { diff --git a/src/integrations/editor/DecorationController.ts b/src/integrations/editor/DecorationController.ts index 8f475408d4..6622131e13 100644 --- a/src/integrations/editor/DecorationController.ts +++ b/src/integrations/editor/DecorationController.ts @@ -63,10 +63,7 @@ export class DecorationController { // Add a new range for all lines after the current line if (line < totalLines - 1) { this.ranges.push( - new vscode.Range( - new vscode.Position(line + 1, 0), - new vscode.Position(totalLines - 1, Number.MAX_SAFE_INTEGER), - ), + new vscode.Range(new vscode.Position(line + 1, 0), new vscode.Position(totalLines - 1, Number.MAX_SAFE_INTEGER)), ) } diff --git a/src/integrations/editor/DiffViewProvider.ts b/src/integrations/editor/DiffViewProvider.ts index 4cc9f4b9d0..46387dbc82 100644 --- a/src/integrations/editor/DiffViewProvider.ts +++ b/src/integrations/editor/DiffViewProvider.ts @@ -33,9 +33,7 @@ export class DiffViewProvider { this.isEditing = true // if the file is already open, ensure it's not dirty before getting its contents if (fileExists) { - const existingDocument = vscode.workspace.textDocuments.find((doc) => - arePathsEqual(doc.uri.fsPath, absolutePath), - ) + const existingDocument = vscode.workspace.textDocuments.find((doc) => arePathsEqual(doc.uri.fsPath, absolutePath)) if (existingDocument && existingDocument.isDirty) { await existingDocument.save() } @@ -61,9 +59,7 @@ export class DiffViewProvider { const tabs = vscode.window.tabGroups.all .map((tg) => tg.tabs) .flat() - .filter( - (tab) => tab.input instanceof vscode.TabInputText && arePathsEqual(tab.input.uri.fsPath, absolutePath), - ) + .filter((tab) => tab.input instanceof vscode.TabInputText && arePathsEqual(tab.input.uri.fsPath, absolutePath)) for (const tab of tabs) { if (!tab.isDirty) { await vscode.window.tabGroups.close(tab) @@ -207,11 +203,7 @@ export class DiffViewProvider { let userEdits: string | undefined if (normalizedPreSaveContent !== normalizedNewContent) { // user made changes before approving edit. let the model know about user made changes (not including post-save auto-formatting changes) - userEdits = formatResponse.createPrettyPatch( - this.relPath.toPosix(), - normalizedNewContent, - normalizedPreSaveContent, - ) + userEdits = formatResponse.createPrettyPatch(this.relPath.toPosix(), normalizedNewContent, normalizedPreSaveContent) // return { newProblemsMessage, userEdits, finalContent: normalizedPostSaveContent } } else { // no changes to cline's edits @@ -277,11 +269,7 @@ export class DiffViewProvider { private async closeAllDiffViews() { const tabs = vscode.window.tabGroups.all .flatMap((tg) => tg.tabs) - .filter( - (tab) => - tab.input instanceof vscode.TabInputTextDiff && - tab.input?.original?.scheme === DIFF_VIEW_URI_SCHEME, - ) + .filter((tab) => tab.input instanceof vscode.TabInputTextDiff && tab.input?.original?.scheme === DIFF_VIEW_URI_SCHEME) for (const tab of tabs) { // trying to close dirty views results in save popup if (!tab.isDirty) { diff --git a/src/integrations/misc/export-markdown.ts b/src/integrations/misc/export-markdown.ts index 2aa9d7b6ed..76e95e36b7 100644 --- a/src/integrations/misc/export-markdown.ts +++ b/src/integrations/misc/export-markdown.ts @@ -42,11 +42,7 @@ export async function downloadTask(dateTs: number, conversationHistory: Anthropi } export function formatContentBlockToMarkdown( - block: - | Anthropic.TextBlockParam - | Anthropic.ImageBlockParam - | Anthropic.ToolUseBlockParam - | Anthropic.ToolResultBlockParam, + block: Anthropic.TextBlockParam | Anthropic.ImageBlockParam | Anthropic.ToolUseBlockParam | Anthropic.ToolResultBlockParam, // messages: Anthropic.MessageParam[] ): string { switch (block.type) { diff --git a/src/integrations/misc/open-file.ts b/src/integrations/misc/open-file.ts index 8dc3029947..9cd88708d3 100644 --- a/src/integrations/misc/open-file.ts +++ b/src/integrations/misc/open-file.ts @@ -28,14 +28,11 @@ export async function openFile(absolutePath: string) { try { for (const group of vscode.window.tabGroups.all) { const existingTab = group.tabs.find( - (tab) => - tab.input instanceof vscode.TabInputText && arePathsEqual(tab.input.uri.fsPath, uri.fsPath), + (tab) => tab.input instanceof vscode.TabInputText && arePathsEqual(tab.input.uri.fsPath, uri.fsPath), ) if (existingTab) { const activeColumn = vscode.window.activeTextEditor?.viewColumn - const tabColumn = vscode.window.tabGroups.all.find((group) => - group.tabs.includes(existingTab), - )?.viewColumn + const tabColumn = vscode.window.tabGroups.all.find((group) => group.tabs.includes(existingTab))?.viewColumn if (activeColumn && activeColumn !== tabColumn && !existingTab.isDirty) { await vscode.window.tabGroups.close(existingTab) } diff --git a/src/integrations/theme/default-themes/dark_vs.json b/src/integrations/theme/default-themes/dark_vs.json index e2f078182d..2abaefadfc 100644 --- a/src/integrations/theme/default-themes/dark_vs.json +++ b/src/integrations/theme/default-themes/dark_vs.json @@ -364,11 +364,7 @@ }, { "name": "coloring of the Java import and package identifiers", - "scope": [ - "storage.modifier.import.java", - "variable.language.wildcard.java", - "storage.modifier.package.java" - ], + "scope": ["storage.modifier.import.java", "variable.language.wildcard.java", "storage.modifier.package.java"], "settings": { "foreground": "#d4d4d4" } diff --git a/src/integrations/theme/default-themes/hc_black.json b/src/integrations/theme/default-themes/hc_black.json index b446ebc4c4..b88b869d47 100644 --- a/src/integrations/theme/default-themes/hc_black.json +++ b/src/integrations/theme/default-themes/hc_black.json @@ -57,12 +57,7 @@ } }, { - "scope": [ - "constant.numeric", - "constant.other.color.rgb-value", - "constant.other.rgb-value", - "support.constant.color" - ], + "scope": ["constant.numeric", "constant.other.color.rgb-value", "constant.other.rgb-value", "support.constant.color"], "settings": { "foreground": "#b5cea8" } @@ -320,11 +315,7 @@ }, { "name": "coloring of the Java import and package identifiers", - "scope": [ - "storage.modifier.import.java", - "variable.language.wildcard.java", - "storage.modifier.package.java" - ], + "scope": ["storage.modifier.import.java", "variable.language.wildcard.java", "storage.modifier.package.java"], "settings": { "foreground": "#d4d4d4" } diff --git a/src/integrations/theme/default-themes/hc_light.json b/src/integrations/theme/default-themes/hc_light.json index 1abecd39d7..5310173d9c 100644 --- a/src/integrations/theme/default-themes/hc_light.json +++ b/src/integrations/theme/default-themes/hc_light.json @@ -346,11 +346,7 @@ } }, { - "scope": [ - "storage.modifier.import.java", - "variable.language.wildcard.java", - "storage.modifier.package.java" - ], + "scope": ["storage.modifier.import.java", "variable.language.wildcard.java", "storage.modifier.package.java"], "settings": { "foreground": "#000000" } diff --git a/src/integrations/theme/default-themes/light_vs.json b/src/integrations/theme/default-themes/light_vs.json index eb098e393f..f75e0942fb 100644 --- a/src/integrations/theme/default-themes/light_vs.json +++ b/src/integrations/theme/default-themes/light_vs.json @@ -389,11 +389,7 @@ }, { "name": "coloring of the Java import and package identifiers", - "scope": [ - "storage.modifier.import.java", - "variable.language.wildcard.java", - "storage.modifier.package.java" - ], + "scope": ["storage.modifier.import.java", "variable.language.wildcard.java", "storage.modifier.package.java"], "settings": { "foreground": "#000000" } diff --git a/src/integrations/theme/getTheme.ts b/src/integrations/theme/getTheme.ts index ffed26e462..a5d4493b3d 100644 --- a/src/integrations/theme/getTheme.ts +++ b/src/integrations/theme/getTheme.ts @@ -74,11 +74,7 @@ export async function getTheme() { const converted = convertTheme(parsed) converted.base = ( - ["vs", "hc-black"].includes(converted.base) - ? converted.base - : colorTheme.includes("Light") - ? "vs" - : "vs-dark" + ["vs", "hc-black"].includes(converted.base) ? converted.base : colorTheme.includes("Light") ? "vs" : "vs-dark" ) as any return converted diff --git a/src/services/mcp/McpHub.ts b/src/services/mcp/McpHub.ts index 18a4685a9d..59dd3bf38b 100644 --- a/src/services/mcp/McpHub.ts +++ b/src/services/mcp/McpHub.ts @@ -15,14 +15,7 @@ import * as path from "path" import * as vscode from "vscode" import { z } from "zod" import { ClineProvider, GlobalFileNames } from "../../core/webview/ClineProvider" -import { - McpResource, - McpResourceResponse, - McpResourceTemplate, - McpServer, - McpTool, - McpToolCallResponse, -} from "../../shared/mcp" +import { McpResource, McpResourceResponse, McpResourceTemplate, McpServer, McpTool, McpToolCallResponse } from "../../shared/mcp" import { fileExistsAtPath } from "../../utils/fs" import { arePathsEqual } from "../../utils/path" @@ -75,10 +68,7 @@ export class McpHub { if (!provider) { throw new Error("Provider not available") } - const mcpSettingsFilePath = path.join( - await provider.ensureSettingsDirectoryExists(), - GlobalFileNames.mcpSettings, - ) + const mcpSettingsFilePath = path.join(await provider.ensureSettingsDirectoryExists(), GlobalFileNames.mcpSettings) const fileExists = await fileExistsAtPath(mcpSettingsFilePath) if (!fileExists) { await fs.writeFile( @@ -467,11 +457,7 @@ export class McpHub { ) } - async callTool( - serverName: string, - toolName: string, - toolArguments?: Record, - ): Promise { + async callTool(serverName: string, toolName: string, toolArguments?: Record): Promise { const connection = this.connections.find((conn) => conn.server.name === serverName) if (!connection) { throw new Error( diff --git a/src/services/ripgrep/index.ts b/src/services/ripgrep/index.ts index b48c60b5b2..b9ebe68c77 100644 --- a/src/services/ripgrep/index.ts +++ b/src/services/ripgrep/index.ts @@ -122,12 +122,7 @@ async function execRipgrep(bin: string, args: string[]): Promise { }) } -export async function regexSearchFiles( - cwd: string, - directoryPath: string, - regex: string, - filePattern?: string, -): Promise { +export async function regexSearchFiles(cwd: string, directoryPath: string, regex: string, filePattern?: string): Promise { const vscodeAppRoot = vscode.env.appRoot const rgPath = await getBinPath(vscodeAppRoot) diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index b3e58bc4a6..33ef75b3d1 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -20,12 +20,7 @@ export interface ExtensionMessage { | "openRouterModels" | "mcpServers" text?: string - action?: - | "chatButtonClicked" - | "mcpButtonClicked" - | "settingsButtonClicked" - | "historyButtonClicked" - | "didBecomeVisible" + action?: "chatButtonClicked" | "mcpButtonClicked" | "settingsButtonClicked" | "historyButtonClicked" | "didBecomeVisible" invoke?: "sendMessage" | "primaryButtonClick" | "secondaryButtonClick" state?: ExtensionState images?: string[] diff --git a/webview-ui/src/components/chat/Announcement.tsx b/webview-ui/src/components/chat/Announcement.tsx index 11978135de..0d47bd4be9 100644 --- a/webview-ui/src/components/chat/Announcement.tsx +++ b/webview-ui/src/components/chat/Announcement.tsx @@ -23,10 +23,7 @@ const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => { position: "relative", flexShrink: 0, }}> - +

@@ -34,17 +31,16 @@ const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => {

  • - Auto-approve menu: You can now specify which tools require approval, set a max # of - auto-approved API requests, and enable system notifications for when Cline completes a task. + Auto-approve menu: You can now specify which tools require approval, set a max # of auto-approved API + requests, and enable system notifications for when Cline completes a task.
  • - New diff editing for large files: Cline now uses an efficient search & replace approach when - modifying large files for faster, more reliable edits (no more " - {"// rest of code here"}" deletions). + New diff editing for large files: Cline now uses an efficient search & replace approach when modifying + large files for faster, more reliable edits (no more "{"// rest of code here"}" deletions).
  • - .clinerules: Add a root-level .clinerules file to specify custom instructions - for the project. + .clinerules: Add a root-level .clinerules file to specify custom instructions for the + project.

v2.2 Updates:

@@ -54,12 +50,12 @@ const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => { MCP servers - by clicking the new {" "} - icon in the menu bar. + by clicking the new icon in the + menu bar.
  • - Cline can also create custom tools–just say "add a tool that...", and watch him create the MCP - server and install it in the extension, ready to use in future tasks. + Cline can also create custom tools–just say "add a tool that...", and watch him create the MCP server and + install it in the extension, ready to use in future tasks.
  • Try it yourself by asking Cline to "add a tool that gets the latest npm docs", or diff --git a/webview-ui/src/components/chat/AutoApproveMenu.tsx b/webview-ui/src/components/chat/AutoApproveMenu.tsx index dda2a5ac3a..f958b54668 100644 --- a/webview-ui/src/components/chat/AutoApproveMenu.tsx +++ b/webview-ui/src/components/chat/AutoApproveMenu.tsx @@ -208,8 +208,8 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => { color: "var(--vscode-descriptionForeground)", fontSize: "12px", }}> - Auto-approve allows Cline to perform the following actions without asking for permission. Please - use with caution and only enable if you understand the risks. + Auto-approve allows Cline to perform the following actions without asking for permission. Please use with + caution and only enable if you understand the risks. {ACTION_METADATA.map((action) => (
    @@ -263,10 +263,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => { }} onKeyDown={(e) => { // Prevent non-numeric keys (except for backspace, delete, arrows) - if ( - !/^\d$/.test(e.key) && - !["Backspace", "Delete", "ArrowLeft", "ArrowRight"].includes(e.key) - ) { + if (!/^\d$/.test(e.key) && !["Backspace", "Delete", "ArrowLeft", "ArrowRight"].includes(e.key)) { e.preventDefault() } }} @@ -279,8 +276,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => { fontSize: "12px", marginBottom: "10px", }}> - Cline will automatically make this many API requests before asking for approval to proceed with - the task. + Cline will automatically make this many API requests before asking for approval to proceed with the task.
    { color: "var(--vscode-descriptionForeground)", fontSize: "12px", }}> - Receive system notifications when Cline requires approval to proceed or when a task is - completed. + Receive system notifications when Cline requires approval to proceed or when a task is completed.
    diff --git a/webview-ui/src/components/chat/BrowserSessionRow.tsx b/webview-ui/src/components/chat/BrowserSessionRow.tsx index 60320fd387..94aef49062 100644 --- a/webview-ui/src/components/chat/BrowserSessionRow.tsx +++ b/webview-ui/src/components/chat/BrowserSessionRow.tsx @@ -1,12 +1,7 @@ import deepEqual from "fast-deep-equal" import React, { memo, useEffect, useMemo, useRef, useState } from "react" import { useSize } from "react-use" -import { - BrowserAction, - BrowserActionResult, - ClineMessage, - ClineSayBrowserAction, -} from "../../../../src/shared/ExtensionMessage" +import { BrowserAction, BrowserActionResult, ClineMessage, ClineSayBrowserAction } from "../../../../src/shared/ExtensionMessage" import { vscode } from "../../utils/vscode" import CodeBlock, { CODE_BLOCK_BG_COLOR } from "../common/CodeBlock" import { ChatRowContent, ProgressIndicator } from "./ChatRow" @@ -98,11 +93,7 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => { // Reset for next page currentStateMessages = [] nextActionMessages = [] - } else if ( - message.say === "api_req_started" || - message.say === "text" || - message.say === "browser_action" - ) { + } else if (message.say === "api_req_started" || message.say === "text" || message.say === "browser_action") { // These messages lead to the next result, so they should always go in nextActionMessages nextActionMessages.push(message) } else { @@ -137,16 +128,12 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => { // Get initial URL from launch message const initialUrl = useMemo(() => { - const launchMessage = messages.find( - (m) => m.ask === "browser_action_launch" || m.say === "browser_action_launch", - ) + const launchMessage = messages.find((m) => m.ask === "browser_action_launch" || m.say === "browser_action_launch") return launchMessage?.text || "" }, [messages]) const isAutoApproved = useMemo(() => { - const launchMessage = messages.find( - (m) => m.ask === "browser_action_launch" || m.say === "browser_action_launch", - ) + const launchMessage = messages.find((m) => m.ask === "browser_action_launch" || m.say === "browser_action_launch") return launchMessage?.say === "browser_action_launch" }, [messages]) @@ -187,12 +174,7 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => { const [actionContent, { height: actionHeight }] = useSize(
    {currentPage?.nextAction?.messages.map((message) => ( - + ))} {!isBrowsing && messages.some((m) => m.say === "browser_action_result") && currentPageIndex === 0 && ( @@ -265,9 +247,7 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => { display: "flex", alignItems: "center", justifyContent: "center", - color: displayState.url - ? "var(--vscode-input-foreground)" - : "var(--vscode-descriptionForeground)", + color: displayState.url ? "var(--vscode-input-foreground)" : "var(--vscode-descriptionForeground)", fontSize: "12px", }}>
    { +const BrowserActionBox = ({ action, coordinate, text }: { action: BrowserAction; coordinate?: string; text?: string }) => { const getBrowserActionText = (action: BrowserAction, coordinate?: string, text?: string) => { switch (action) { case "launch": diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index fdc53de453..66c25d2f3b 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -2,12 +2,7 @@ import { VSCodeBadge, VSCodeProgressRing } from "@vscode/webview-ui-toolkit/reac import deepEqual from "fast-deep-equal" import React, { memo, useEffect, useMemo, useRef } from "react" import { useSize } from "react-use" -import { - ClineApiReqInfo, - ClineAskUseMcpServer, - ClineMessage, - ClineSayTool, -} from "../../../../src/shared/ExtensionMessage" +import { ClineApiReqInfo, ClineAskUseMcpServer, ClineMessage, ClineSayTool } from "../../../../src/shared/ExtensionMessage" import { COMMAND_OUTPUT_STRING, COMMAND_REQ_APP_STRING } from "../../../../src/shared/combineCommandSequences" import { useExtensionState } from "../../context/ExtensionStateContext" import { findMatchingResourceOrTemplate } from "../../utils/mcp" @@ -69,13 +64,7 @@ const ChatRow = memo( export default ChatRow -export const ChatRowContent = ({ - message, - isExpanded, - onToggleExpand, - lastModifiedMessage, - isLast, -}: ChatRowContentProps) => { +export const ChatRowContent = ({ message, isExpanded, onToggleExpand, lastModifiedMessage, isLast }: ChatRowContentProps) => { const { mcpServers } = useExtensionState() const [cost, apiReqCancelReason, apiReqStreamingFailedMessage] = useMemo(() => { if (message.text != null && message.say === "api_req_started") { @@ -107,23 +96,17 @@ export const ChatRowContent = ({ switch (type) { case "error": return [ - , + , Error, ] case "mistake_limit_reached": return [ - , + , Cline is having trouble..., ] case "auto_approval_max_req_reached": return [ - , + , Maximum Requests Reached, ] case "command": @@ -131,14 +114,10 @@ export const ChatRowContent = ({ isCommandExecuting ? ( ) : ( - + ), - {message.type === "ask" - ? "Cline wants to execute this command:" - : "Cline executed this command:"} + {message.type === "ask" ? "Cline wants to execute this command:" : "Cline executed this command:"} , ] case "use_mcp_server": @@ -147,30 +126,25 @@ export const ChatRowContent = ({ isMcpServerResponding ? ( ) : ( - + ), {message.type === "ask" ? ( <> - Cline wants to{" "} - {mcpServerUse.type === "use_mcp_tool" ? "use a tool" : "access a resource"} on the{" "} + Cline wants to {mcpServerUse.type === "use_mcp_tool" ? "use a tool" : "access a resource"} on the{" "} {mcpServerUse.serverName} MCP server: ) : ( <> - Cline {mcpServerUse.type === "use_mcp_tool" ? "used a tool" : "accessed a resource"} on - the {mcpServerUse.serverName} MCP server: + Cline {mcpServerUse.type === "use_mcp_tool" ? "used a tool" : "accessed a resource"} on the{" "} + {mcpServerUse.serverName} MCP server: )} , ] case "completion_result": return [ - , + , Task Completed, ] case "api_req_started": @@ -222,9 +196,7 @@ export const ChatRowContent = ({ ] case "followup": return [ - , + , Cline has a question:, ] default: @@ -276,9 +248,7 @@ export const ChatRowContent = ({
    {toolIcon("edit")} - {message.type === "ask" - ? "Cline wants to edit this file:" - : "Cline is editing this file:"} + {message.type === "ask" ? "Cline wants to edit this file:" : "Cline is editing this file:"}
    {toolIcon("new-file")} - {message.type === "ask" - ? "Cline wants to create a new file:" - : "Cline is creating a new file:"} + {message.type === "ask" ? "Cline wants to create a new file:" : "Cline is creating a new file:"}
    tool.name === useMcpServer.toolName) - ?.description || "", + server?.tools?.find((tool) => tool.name === useMcpServer.toolName)?.description || "", }} /> {useMcpServer.arguments && useMcpServer.arguments !== "{}" && ( @@ -656,9 +623,7 @@ export const ChatRowContent = ({ style={{ ...headerStyle, marginBottom: - (cost == null && apiRequestFailedMessage) || apiReqStreamingFailedMessage - ? 10 - : 0, + (cost == null && apiRequestFailedMessage) || apiReqStreamingFailedMessage ? 10 : 0, justifyContent: "space-between", cursor: "pointer", userSelect: "none", @@ -819,8 +784,8 @@ export const ChatRowContent = ({ Diff Edit Failed
    - This usually happens when the model uses search patterns that don't match anything - in the file. Retrying... + This usually happens when the model uses search patterns that don't match anything in the + file. Retrying...
    @@ -857,15 +822,13 @@ export const ChatRowContent = ({ fontSize: 18, color: "#FFA500", }}> - - Shell Integration Unavailable - + Shell Integration Unavailable
    Cline won't be able to view the command's output. Please update VSCode ( - CMD/CTRL + Shift + P → "Update") and make sure you're using a supported - shell: zsh, bash, fish, or PowerShell (CMD/CTRL + Shift + P → - "Terminal: Select Default Profile").{" "} + CMD/CTRL + Shift + P → "Update") and make sure you're using a supported shell: + zsh, bash, fish, or PowerShell (CMD/CTRL + Shift + P → "Terminal: Select Default + Profile").{" "} diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index f465942f36..38380825d6 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -72,10 +72,7 @@ const ChatTextArea = forwardRef( useEffect(() => { const handleClickOutside = (event: MouseEvent) => { - if ( - contextMenuContainerRef.current && - !contextMenuContainerRef.current.contains(event.target as Node) - ) { + if (contextMenuContainerRef.current && !contextMenuContainerRef.current.contains(event.target as Node)) { setShowContextMenu(false) } } @@ -116,11 +113,7 @@ const ChatTextArea = forwardRef( insertValue = "problems" } - const { newValue, mentionIndex } = insertMention( - textAreaRef.current.value, - cursorPosition, - insertValue, - ) + const { newValue, mentionIndex } = insertMention(textAreaRef.current.value, cursorPosition, insertValue) setInputValue(newValue) const newCursorPosition = newValue.indexOf(" ", mentionIndex + insertValue.length) + 1 @@ -162,20 +155,16 @@ const ChatTextArea = forwardRef( // Find selectable options (non-URL types) const selectableOptions = options.filter( (option) => - option.type !== ContextMenuOptionType.URL && - option.type !== ContextMenuOptionType.NoResults, + option.type !== ContextMenuOptionType.URL && option.type !== ContextMenuOptionType.NoResults, ) if (selectableOptions.length === 0) return -1 // No selectable options // Find the index of the next selectable option - const currentSelectableIndex = selectableOptions.findIndex( - (option) => option === options[prevIndex], - ) + const currentSelectableIndex = selectableOptions.findIndex((option) => option === options[prevIndex]) const newSelectableIndex = - (currentSelectableIndex + direction + selectableOptions.length) % - selectableOptions.length + (currentSelectableIndex + direction + selectableOptions.length) % selectableOptions.length // Find the index of the selected option in the original options array return options.findIndex((option) => option === selectableOptions[newSelectableIndex]) @@ -184,9 +173,7 @@ const ChatTextArea = forwardRef( } if ((event.key === "Enter" || event.key === "Tab") && selectedMenuIndex !== -1) { event.preventDefault() - const selectedOption = getContextMenuOptions(searchQuery, selectedType, queryItems)[ - selectedMenuIndex - ] + const selectedOption = getContextMenuOptions(searchQuery, selectedType, queryItems)[selectedMenuIndex] if ( selectedOption && selectedOption.type !== ContextMenuOptionType.URL && @@ -312,8 +299,7 @@ const ChatTextArea = forwardRef( if (urlRegex.test(pastedText.trim())) { e.preventDefault() const trimmedUrl = pastedText.trim() - const newValue = - inputValue.slice(0, cursorPosition) + trimmedUrl + " " + inputValue.slice(cursorPosition) + const newValue = inputValue.slice(0, cursorPosition) + trimmedUrl + " " + inputValue.slice(cursorPosition) setInputValue(newValue) const newCursorPosition = cursorPosition + trimmedUrl.length + 1 setCursorPosition(newCursorPosition) @@ -569,9 +555,7 @@ const ChatTextArea = forwardRef( }}>
    { if (!shouldDisableImages) { onSelectImages() diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index 70a8fcfef6..c81a830a2d 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -231,8 +231,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie const isStreaming = useMemo(() => { const isLastAsk = !!modifiedMessages.at(-1)?.ask // checking clineAsk isn't enough since messages effect may be called again for a tool for example, set clineAsk to its value, and if the next message is not an ask then it doesn't reset. This is likely due to how much more often we're updating messages as compared to before, and should be resolved with optimizations as it's likely a rendering bug. but as a final guard for now, the cancel button will show if the last message is not an ask - const isToolCurrentlyAsking = - isLastAsk && clineAsk !== undefined && enableButtons && primaryButtonText !== undefined + const isToolCurrentlyAsking = isLastAsk && clineAsk !== undefined && enableButtons && primaryButtonText !== undefined if (isToolCurrentlyAsking) { return false } @@ -389,9 +388,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie case "selectedImages": const newImages = message.images ?? [] if (newImages.length > 0) { - setSelectedImages((prevImages) => - [...prevImages, ...newImages].slice(0, MAX_IMAGES_PER_MESSAGE), - ) + setSelectedImages((prevImages) => [...prevImages, ...newImages].slice(0, MAX_IMAGES_PER_MESSAGE)) } break case "invoke": @@ -409,14 +406,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie } // textAreaRef.current is not explicitly required here since react gaurantees that ref will be stable across re-renders, and we're not using its value but its reference. }, - [ - isHidden, - textAreaDisabled, - enableButtons, - handleSendMessage, - handlePrimaryButtonClick, - handleSecondaryButtonClick, - ], + [isHidden, textAreaDisabled, enableButtons, handleSendMessage, handlePrimaryButtonClick, handleSecondaryButtonClick], ) useEvent("message", handleMessage) @@ -474,13 +464,9 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie return ["browser_action_launch"].includes(message.ask!) } if (message.type === "say") { - return [ - "browser_action_launch", - "api_req_started", - "text", - "browser_action", - "browser_action_result", - ].includes(message.say!) + return ["browser_action_launch", "api_req_started", "text", "browser_action", "browser_action_result"].includes( + message.say!, + ) } return false } @@ -750,10 +736,10 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie style={{ display: "inline" }}> Claude 3.5 Sonnet's agentic coding capabilities, {" "} - I can handle complex software development tasks step-by-step. With tools that let me create - & edit files, explore complex projects, use the browser, and execute terminal commands - (after you grant permission), I can assist you in ways that go beyond code completion or - tech support. I can even use MCP to create new tools and extend my own capabilities. + I can handle complex software development tasks step-by-step. With tools that let me create & edit + files, explore complex projects, use the browser, and execute terminal commands (after you grant + permission), I can assist you in ways that go beyond code completion or tech support. I can even use + MCP to create new tools and extend my own capabilities.

    {taskHistory.length > 0 && } diff --git a/webview-ui/src/components/chat/ContextMenu.tsx b/webview-ui/src/components/chat/ContextMenu.tsx index 7e56b332a5..750b789ac4 100644 --- a/webview-ui/src/components/chat/ContextMenu.tsx +++ b/webview-ui/src/components/chat/ContextMenu.tsx @@ -165,13 +165,9 @@ const ContextMenu: React.FC = ({ /> )} {(option.type === ContextMenuOptionType.Problems || - ((option.type === ContextMenuOptionType.File || - option.type === ContextMenuOptionType.Folder) && + ((option.type === ContextMenuOptionType.File || option.type === ContextMenuOptionType.Folder) && option.value)) && ( - + )}
    ))} diff --git a/webview-ui/src/components/chat/TaskHeader.tsx b/webview-ui/src/components/chat/TaskHeader.tsx index 0b3f494b66..87775b7a52 100644 --- a/webview-ui/src/components/chat/TaskHeader.tsx +++ b/webview-ui/src/components/chat/TaskHeader.tsx @@ -150,9 +150,7 @@ const TaskHeader: React.FC = ({ minWidth: 0, // This allows the div to shrink below its content size }}> Task{!isTaskExpanded && ":"} - {!isTaskExpanded && ( - {highlightMentions(task.text, false)} - )} + {!isTaskExpanded && {highlightMentions(task.text, false)}}
    {!isTaskExpanded && isCostAvailable && ( @@ -213,8 +211,7 @@ const TaskHeader: React.FC = ({ style={{ width: 30, height: "1.2em", - background: - "linear-gradient(to right, transparent, var(--vscode-badge-background))", + background: "linear-gradient(to right, transparent, var(--vscode-badge-background))", }} />
    { .filter((item) => item.ts && item.task) .slice(0, 3) .map((item) => ( -
    handleHistorySelect(item.id)}> +
    handleHistorySelect(item.id)}>
    {
    - Tokens: ↑{formatLargeNumber(item.tokensIn || 0)} ↓ - {formatLargeNumber(item.tokensOut || 0)} + Tokens: ↑{formatLargeNumber(item.tokensIn || 0)} ↓{formatLargeNumber(item.tokensOut || 0)} {!!item.cacheWrites && ( <> diff --git a/webview-ui/src/components/history/HistoryView.tsx b/webview-ui/src/components/history/HistoryView.tsx index 14b97c6d48..c8cf13c0d7 100644 --- a/webview-ui/src/components/history/HistoryView.tsx +++ b/webview-ui/src/components/history/HistoryView.tsx @@ -180,10 +180,7 @@ const HistoryView = ({ onDone }: HistoryViewProps) => { Oldest Most Expensive Most Tokens - + Most Relevant @@ -219,9 +216,7 @@ const HistoryView = ({ onDone }: HistoryViewProps) => { style={{ cursor: "pointer", borderBottom: - index < taskHistory.length - 1 - ? "1px solid var(--vscode-panel-border)" - : "none", + index < taskHistory.length - 1 ? "1px solid var(--vscode-panel-border)" : "none", }} onClick={() => handleHistorySelect(item.id)}>
    ( ) // https://gist.github.com/evenfrost/1ba123656ded32fb7a0cd4651efd4db0 -export const highlight = ( - fuseSearchResult: FuseResult[], - highlightClassName: string = "history-item-highlight", -) => { +export const highlight = (fuseSearchResult: FuseResult[], highlightClassName: string = "history-item-highlight") => { const set = (obj: Record, path: string, value: any) => { const pathValue = path.split(".") let i: number diff --git a/webview-ui/src/components/mcp/McpToolRow.tsx b/webview-ui/src/components/mcp/McpToolRow.tsx index c402d3413f..e2bc3d9b36 100644 --- a/webview-ui/src/components/mcp/McpToolRow.tsx +++ b/webview-ui/src/components/mcp/McpToolRow.tsx @@ -37,48 +37,43 @@ const McpToolRow = ({ tool }: McpToolRowProps) => { borderRadius: "3px", padding: "8px", }}> -
    +
    Parameters
    - {Object.entries(tool.inputSchema.properties as Record).map( - ([paramName, schema]) => { - const isRequired = - tool.inputSchema && - "required" in tool.inputSchema && - Array.isArray(tool.inputSchema.required) && - tool.inputSchema.required.includes(paramName) + {Object.entries(tool.inputSchema.properties as Record).map(([paramName, schema]) => { + const isRequired = + tool.inputSchema && + "required" in tool.inputSchema && + Array.isArray(tool.inputSchema.required) && + tool.inputSchema.required.includes(paramName) - return ( -
    + - - {paramName} - {isRequired && ( - * - )} - - - {schema.description || "No description"} - -
    - ) - }, - )} + {paramName} + {isRequired && *} + + + {schema.description || "No description"} + +
    + ) + })}
    )}
    diff --git a/webview-ui/src/components/mcp/McpView.tsx b/webview-ui/src/components/mcp/McpView.tsx index e8c758a333..5b6ad7cb44 100644 --- a/webview-ui/src/components/mcp/McpView.tsx +++ b/webview-ui/src/components/mcp/McpView.tsx @@ -1,10 +1,4 @@ -import { - VSCodeButton, - VSCodeLink, - VSCodePanels, - VSCodePanelTab, - VSCodePanelView, -} from "@vscode/webview-ui-toolkit/react" +import { VSCodeButton, VSCodeLink, VSCodePanels, VSCodePanelTab, VSCodePanelView } from "@vscode/webview-ui-toolkit/react" import { useState } from "react" import { vscode } from "../../utils/vscode" import { useExtensionState } from "../../context/ExtensionStateContext" @@ -113,13 +107,12 @@ const McpView = ({ onDone }: McpViewProps) => { Model Context Protocol {" "} - enables communication with locally running MCP servers that provide additional tools and resources - to extend Cline's capabilities. You can use{" "} + enables communication with locally running MCP servers that provide additional tools and resources to extend + Cline's capabilities. You can use{" "} community-made servers {" "} - or ask Cline to create new tools specific to your workflow (e.g., "add a tool that gets the latest - npm docs").{" "} + or ask Cline to create new tools specific to your workflow (e.g., "add a tool that gets the latest npm docs").{" "} See a demo here. @@ -195,10 +188,7 @@ const ServerRow = ({ server }: { server: McpServer }) => { }} onClick={handleRowClick}> {!server.error && ( - + )} {server.name}
    { Tools ({server.tools?.length || 0}) - Resources ( - {[...(server.resourceTemplates || []), ...(server.resources || [])].length || 0}) + Resources ({[...(server.resourceTemplates || []), ...(server.resources || [])].length || 0}) {server.tools && server.tools.length > 0 ? ( -
    +
    {server.tools.map((tool) => ( ))} @@ -272,16 +260,13 @@ const ServerRow = ({ server }: { server: McpServer }) => { {(server.resources && server.resources.length > 0) || (server.resourceTemplates && server.resourceTemplates.length > 0) ? ( -
    - {[...(server.resourceTemplates || []), ...(server.resources || [])].map( - (item) => ( - - ), - )} +
    + {[...(server.resourceTemplates || []), ...(server.resources || [])].map((item) => ( + + ))}
    ) : (
    diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 4419430336..330b95b729 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -33,10 +33,7 @@ import { ExtensionMessage } from "../../../../src/shared/ExtensionMessage" import { useExtensionState } from "../../context/ExtensionStateContext" import { vscode } from "../../utils/vscode" import VSCodeButtonLink from "../common/VSCodeButtonLink" -import OpenRouterModelPicker, { - ModelDescriptionMarkdown, - OPENROUTER_MODEL_PICKER_Z_INDEX, -} from "./OpenRouterModelPicker" +import OpenRouterModelPicker, { ModelDescriptionMarkdown, OPENROUTER_MODEL_PICKER_Z_INDEX } from "./OpenRouterModelPicker" interface ApiOptionsProps { showModelOptions: boolean @@ -238,9 +235,7 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage }: }}> This key is stored locally and only used to make API requests from this extension. {!apiConfiguration?.deepSeekApiKey && ( - + You can get a DeepSeek API key by signing up here. )} @@ -360,9 +355,9 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage }: marginTop: "5px", color: "var(--vscode-descriptionForeground)", }}> - Authenticate by either providing the keys above or use the default AWS credential providers, - i.e. ~/.aws/credentials or environment variables. These credentials are only used locally to - make API requests from this extension. + Authenticate by either providing the keys above or use the default AWS credential providers, i.e. + ~/.aws/credentials or environment variables. These credentials are only used locally to make API requests + from this extension.

    )} @@ -403,9 +398,7 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage }: - { - "1) create a Google Cloud account › enable the Vertex AI API › enable the desired Claude models," - } + {"1) create a Google Cloud account › enable the Vertex AI API › enable the desired Claude models,"} {" "} This key is stored locally and only used to make API requests from this extension. {!apiConfiguration?.geminiApiKey && ( - + You can get a Gemini API key by signing up here. )} @@ -495,8 +486,8 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage }: color: "var(--vscode-descriptionForeground)", }}> - (Note: Cline uses complex prompts and works best - with Claude models. Less capable models may not work as expected.) + (Note: Cline uses complex prompts and works best with Claude + models. Less capable models may not work as expected.)

    @@ -536,10 +527,7 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage }: } }}> {lmStudioModels.map((model) => ( - + {model} ))} @@ -551,8 +539,8 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage }: marginTop: "5px", color: "var(--vscode-descriptionForeground)", }}> - LM Studio allows you to run models locally on your computer. For instructions on how to get - started, see their + LM Studio allows you to run models locally on your computer. For instructions on how to get started, see + their quickstart guide. @@ -564,8 +552,8 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage }: {" "} feature to use it with this extension.{" "} - (Note: Cline uses complex prompts and works best - with Claude models. Less capable models may not work as expected.) + (Note: Cline uses complex prompts and works best with Claude + models. Less capable models may not work as expected.)

    @@ -605,10 +593,7 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage }: } }}> {ollamaModels.map((model) => ( - + {model} ))} @@ -620,16 +605,16 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage }: marginTop: "5px", color: "var(--vscode-descriptionForeground)", }}> - Ollama allows you to run models locally on your computer. For instructions on how to get - started, see their + Ollama allows you to run models locally on your computer. For instructions on how to get started, see + their quickstart guide. - (Note: Cline uses complex prompts and works best - with Claude models. Less capable models may not work as expected.) + (Note: Cline uses complex prompts and works best with Claude + models. Less capable models may not work as expected.)

    @@ -756,26 +741,25 @@ export const ModelInfoView = ({ ), modelInfo.supportsPromptCache && modelInfo.cacheWritesPrice && ( - Cache writes price:{" "} - {formatPrice(modelInfo.cacheWritesPrice || 0)}/million tokens + Cache writes price: {formatPrice(modelInfo.cacheWritesPrice || 0)} + /million tokens ), modelInfo.supportsPromptCache && modelInfo.cacheReadsPrice && ( - Cache reads price:{" "} - {formatPrice(modelInfo.cacheReadsPrice || 0)}/million tokens + Cache reads price: {formatPrice(modelInfo.cacheReadsPrice || 0)}/million + tokens ), modelInfo.outputPrice !== undefined && modelInfo.outputPrice > 0 && ( - Output price: {formatPrice(modelInfo.outputPrice)}/million - tokens + Output price: {formatPrice(modelInfo.outputPrice)}/million tokens ), isGemini && ( - * Free up to {selectedModelId && selectedModelId.includes("flash") ? "15" : "2"} requests per minute. - After that, billing depends on prompt size.{" "} + * Free up to {selectedModelId && selectedModelId.includes("flash") ? "15" : "2"} requests per minute. After that, + billing depends on prompt size.{" "} For more info, see pricing details. diff --git a/webview-ui/src/components/settings/OpenRouterModelPicker.tsx b/webview-ui/src/components/settings/OpenRouterModelPicker.tsx index bd4efd8bc1..fc588281ab 100644 --- a/webview-ui/src/components/settings/OpenRouterModelPicker.tsx +++ b/webview-ui/src/components/settings/OpenRouterModelPicker.tsx @@ -380,8 +380,7 @@ export const ModelDescriptionMarkdown = memo( style={{ width: 30, height: "1.2em", - background: - "linear-gradient(to right, transparent, var(--vscode-sideBar-background))", + background: "linear-gradient(to right, transparent, var(--vscode-sideBar-background))", }} /> { - const { apiConfiguration, version, customInstructions, setCustomInstructions, openRouterModels } = - useExtensionState() + const { apiConfiguration, version, customInstructions, setCustomInstructions, openRouterModels } = useExtensionState() const [apiErrorMessage, setApiErrorMessage] = useState(undefined) const [modelIdErrorMessage, setModelIdErrorMessage] = useState(undefined) const handleSubmit = () => { @@ -74,8 +73,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {

    Settings

    Done
    -
    +
    { value={customInstructions ?? ""} style={{ width: "100%" }} rows={4} - placeholder={ - 'e.g. "Run unit tests at the end", "Use TypeScript with async/await", "Speak in Spanish"' - } + placeholder={'e.g. "Run unit tests at the end", "Use TypeScript with async/await", "Speak in Spanish"'} onInput={(e: any) => setCustomInstructions(e.target?.value ?? "")}> Custom Instructions diff --git a/webview-ui/src/components/welcome/WelcomeView.tsx b/webview-ui/src/components/welcome/WelcomeView.tsx index ef15a4ed50..5da4d7d7a7 100644 --- a/webview-ui/src/components/welcome/WelcomeView.tsx +++ b/webview-ui/src/components/welcome/WelcomeView.tsx @@ -30,9 +30,9 @@ const WelcomeView = () => { style={{ display: "inline" }}> Claude 3.5 Sonnet's agentic coding capabilities {" "} - and access to tools that let me create & edit files, explore complex projects, use the browser, and - execute terminal commands (with your permission, of course). I can even use MCP to create new tools and - extend my own capabilities. + and access to tools that let me create & edit files, explore complex projects, use the browser, and execute + terminal commands (with your permission, of course). I can even use MCP to create new tools and extend my own + capabilities.

    To get started, this extension needs an API provider for Claude 3.5 Sonnet. diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index 251c80eed6..37c1aa4a37 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -2,12 +2,7 @@ import React, { createContext, useCallback, useContext, useEffect, useState } fr import { useEvent } from "react-use" import { DEFAULT_AUTO_APPROVAL_SETTINGS } from "../../../src/shared/AutoApprovalSettings" import { ExtensionMessage, ExtensionState } from "../../../src/shared/ExtensionMessage" -import { - ApiConfiguration, - ModelInfo, - openRouterDefaultModelId, - openRouterDefaultModelInfo, -} from "../../../src/shared/api" +import { ApiConfiguration, ModelInfo, openRouterDefaultModelId, openRouterDefaultModelInfo } from "../../../src/shared/api" import { findLastIndex } from "../../../src/shared/array" import { McpServer } from "../../../src/shared/mcp" import { convertTextMateToHljs } from "../utils/textMateToHljs" diff --git a/webview-ui/src/utils/context-mentions.ts b/webview-ui/src/utils/context-mentions.ts index a79aefb3da..0f5049cf24 100644 --- a/webview-ui/src/utils/context-mentions.ts +++ b/webview-ui/src/utils/context-mentions.ts @@ -1,10 +1,6 @@ import { mentionRegex } from "../../../src/shared/context-mentions" -export function insertMention( - text: string, - position: number, - value: string, -): { newValue: string; mentionIndex: number } { +export function insertMention(text: string, position: number, value: string): { newValue: string; mentionIndex: number } { const beforeCursor = text.slice(0, position) const afterCursor = text.slice(position) diff --git a/webview-ui/src/utils/mcp.ts b/webview-ui/src/utils/mcp.ts index 5df5df3a61..e7a17b5bcd 100644 --- a/webview-ui/src/utils/mcp.ts +++ b/webview-ui/src/utils/mcp.ts @@ -6,10 +6,7 @@ import { McpResource, McpResourceTemplate } from "../../../src/shared/mcp" * @param templates Array of URI templates to match against * @returns The matching template or undefined if no match is found */ -export function findMatchingTemplate( - uri: string, - templates: McpResourceTemplate[] = [], -): McpResourceTemplate | undefined { +export function findMatchingTemplate(uri: string, templates: McpResourceTemplate[] = []): McpResourceTemplate | undefined { return templates.find((template) => { // Convert template to regex pattern const pattern = String(template.uriTemplate) diff --git a/webview-ui/src/utils/validate.ts b/webview-ui/src/utils/validate.ts index 98920aba4f..91cf4f9136 100644 --- a/webview-ui/src/utils/validate.ts +++ b/webview-ui/src/utils/validate.ts @@ -39,11 +39,7 @@ export function validateApiConfiguration(apiConfiguration?: ApiConfiguration): s } break case "openai": - if ( - !apiConfiguration.openAiBaseUrl || - !apiConfiguration.openAiApiKey || - !apiConfiguration.openAiModelId - ) { + if (!apiConfiguration.openAiBaseUrl || !apiConfiguration.openAiApiKey || !apiConfiguration.openAiModelId) { return "You must provide a valid base URL, API key, and model ID." } break