Skip to main content
Glama
4b648882430d8aad657e9165c33c42a605e7b408d335749f5f401b1eacb7e484.cache1.95 MB
{"key":"D:\\Projects\\cline\\src","entry":{"lastAccessed":"2025-01-16T17:01:47.356Z","lastModified":1737046907356,"content":{"content":"[{\"key\":\"last_read_result\",\"content\":{\"content\":\"{\\\"files\\\":{\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\api\\\\\\\\index.ts\\\":{\\\"content\\\":\\\"import { Anthropic } from \\\\\\\"@anthropic-ai/sdk\\\\\\\"\\\\r\\\\nimport { ApiConfiguration, ModelInfo } from \\\\\\\"../shared/api\\\\\\\"\\\\r\\\\nimport { AnthropicHandler } from \\\\\\\"./providers/anthropic\\\\\\\"\\\\r\\\\nimport { AwsBedrockHandler } from \\\\\\\"./providers/bedrock\\\\\\\"\\\\r\\\\nimport { OpenRouterHandler } from \\\\\\\"./providers/openrouter\\\\\\\"\\\\r\\\\nimport { VertexHandler } from \\\\\\\"./providers/vertex\\\\\\\"\\\\r\\\\nimport { OpenAiHandler } from \\\\\\\"./providers/openai\\\\\\\"\\\\r\\\\nimport { OllamaHandler } from \\\\\\\"./providers/ollama\\\\\\\"\\\\r\\\\nimport { LmStudioHandler } from \\\\\\\"./providers/lmstudio\\\\\\\"\\\\r\\\\nimport { GeminiHandler } from \\\\\\\"./providers/gemini\\\\\\\"\\\\r\\\\nimport { OpenAiNativeHandler } from \\\\\\\"./providers/openai-native\\\\\\\"\\\\r\\\\nimport { ApiStream } from \\\\\\\"./transform/stream\\\\\\\"\\\\r\\\\nimport { DeepSeekHandler } from \\\\\\\"./providers/deepseek\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport interface ApiHandler {\\\\r\\\\n\\\\tcreateMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream\\\\r\\\\n\\\\tgetModel(): { id: string; info: ModelInfo }\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport function buildApiHandler(configuration: ApiConfiguration): ApiHandler {\\\\r\\\\n\\\\tconst { apiProvider, ...options } = configuration\\\\r\\\\n\\\\tswitch (apiProvider) {\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"anthropic\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn new AnthropicHandler(options)\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"openrouter\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn new OpenRouterHandler(options)\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"bedrock\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn new AwsBedrockHandler(options)\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"vertex\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn new VertexHandler(options)\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"openai\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn new OpenAiHandler(options)\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"ollama\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn new OllamaHandler(options)\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"lmstudio\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn new LmStudioHandler(options)\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"gemini\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn new GeminiHandler(options)\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"openai-native\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn new OpenAiNativeHandler(options)\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"deepseek\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn new DeepSeekHandler(options)\\\\r\\\\n\\\\t\\\\tdefault:\\\\r\\\\n\\\\t\\\\t\\\\treturn new AnthropicHandler(options)\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":1711,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.927Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.927Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":47,\\\"nonEmptyLines\\\":44,\\\"commentLines\\\":0,\\\"complexity\\\":11},\\\"dependencies\\\":[\\\"@anthropic-ai/sdk\\\",\\\"../shared/api\\\",\\\"./providers/anthropic\\\",\\\"./providers/bedrock\\\",\\\"./providers/openrouter\\\",\\\"./providers/vertex\\\",\\\"./providers/openai\\\",\\\"./providers/ollama\\\",\\\"./providers/lmstudio\\\",\\\"./providers/gemini\\\",\\\"./providers/openai-native\\\",\\\"./transform/stream\\\",\\\"./providers/deepseek\\\"],\\\"quality\\\":{\\\"score\\\":85,\\\"issues\\\":[],\\\"duplicateLines\\\":3,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.290Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":47},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\api\\\\\\\\providers\\\\\\\\anthropic.ts\\\":{\\\"content\\\":\\\"import { Anthropic } from \\\\\\\"@anthropic-ai/sdk\\\\\\\"\\\\r\\\\nimport { Stream as AnthropicStream } from \\\\\\\"@anthropic-ai/sdk/streaming\\\\\\\"\\\\r\\\\nimport { anthropicDefaultModelId, AnthropicModelId, anthropicModels, ApiHandlerOptions, ModelInfo } from \\\\\\\"../../shared/api\\\\\\\"\\\\r\\\\nimport { ApiHandler } from \\\\\\\"../index\\\\\\\"\\\\r\\\\nimport { ApiStream } from \\\\\\\"../transform/stream\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport class AnthropicHandler implements ApiHandler {\\\\r\\\\n\\\\tprivate options: ApiHandlerOptions\\\\r\\\\n\\\\tprivate client: Anthropic\\\\r\\\\n\\\\r\\\\n\\\\tconstructor(options: ApiHandlerOptions) {\\\\r\\\\n\\\\t\\\\tthis.options = options\\\\r\\\\n\\\\t\\\\tthis.client = new Anthropic({\\\\r\\\\n\\\\t\\\\t\\\\tapiKey: this.options.apiKey,\\\\r\\\\n\\\\t\\\\t\\\\tbaseURL: this.options.anthropicBaseUrl || undefined,\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {\\\\r\\\\n\\\\t\\\\tlet stream: AnthropicStream<Anthropic.Beta.PromptCaching.Messages.RawPromptCachingBetaMessageStreamEvent>\\\\r\\\\n\\\\t\\\\tconst modelId = this.getModel().id\\\\r\\\\n\\\\t\\\\tswitch (modelId) {\\\\r\\\\n\\\\t\\\\t\\\\t// 'latest' alias does not support cache_control\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"claude-3-5-sonnet-20241022\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"claude-3-5-haiku-20241022\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"claude-3-opus-20240229\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"claude-3-haiku-20240307\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t/*\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tThe latest message will be the new user message, one before will be the assistant message from a previous request, and the user message before that will be a previously cached user message. So we need to mark the latest user message as ephemeral to cache it for the next request, and mark the second to last user message as ephemeral to let the server know the last message to retrieve from the cache for the current request..\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t*/\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst userMsgIndices = messages.reduce(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t(acc, msg, index) => (msg.role === \\\\\\\"user\\\\\\\" ? [...acc, index] : acc),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t[] as number[],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst lastUserMsgIndex = userMsgIndices[userMsgIndices.length - 1] ?? -1\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst secondLastMsgUserIndex = userMsgIndices[userMsgIndices.length - 2] ?? -1\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tstream = await this.client.beta.promptCaching.messages.create(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodel: modelId,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmax_tokens: this.getModel().info.maxTokens || 8192,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttemperature: 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tsystem: [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: systemPrompt,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcache_control: { type: \\\\\\\"ephemeral\\\\\\\" },\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t], // setting cache breakpoint for system prompt so new tasks can reuse it\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmessages: messages.map((message, index) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (index === lastUserMsgIndex || index === secondLastMsgUserIndex) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t...message,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontent:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttypeof message.content === \\\\\\\"string\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t? [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: message.content,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcache_control: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"ephemeral\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t: message.content.map((content, contentIndex) =>\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontentIndex === message.content.length - 1\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t? {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t...content,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcache_control: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"ephemeral\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t: content,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn message\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// tools, // cache breakpoints go from tools > system > messages, and since tools dont change, we can just set the breakpoint at the end of system (this avoids having to set a breakpoint at the end of tools which by itself does not meet min requirements for haiku caching)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// tool_choice: { type: \\\\\\\"auto\\\\\\\" },\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// tools: tools,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tstream: true,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t(() => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// prompt caching: https://x.com/alexalbert__/status/1823751995901272068\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// https://github.com/anthropics/anthropic-sdk-typescript?tab=readme-ov-file#default-headers\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// https://github.com/anthropics/anthropic-sdk-typescript/commit/c920b77fc67bd839bfeb6716ceab9d7c9bbe7393\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tswitch (modelId) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"claude-3-5-sonnet-20241022\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"claude-3-5-haiku-20241022\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"claude-3-opus-20240229\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"claude-3-haiku-20240307\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\theaders: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"anthropic-beta\\\\\\\": \\\\\\\"prompt-caching-2024-07-31\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tdefault:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn undefined\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t})(),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tdefault: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tstream = (await this.client.messages.create({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmodel: modelId,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmax_tokens: this.getModel().info.maxTokens || 8192,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttemperature: 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tsystem: [{ text: systemPrompt, type: \\\\\\\"text\\\\\\\" }],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmessages,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// tools,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// tool_choice: { type: \\\\\\\"auto\\\\\\\" },\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tstream: true,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t})) as any\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tfor await (const chunk of stream) {\\\\r\\\\n\\\\t\\\\t\\\\tswitch (chunk.type) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"message_start\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// tells us cache reads/writes/input/output\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst usage = chunk.message.usage\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"usage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tinputTokens: usage.input_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\toutputTokens: usage.output_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcacheWriteTokens: usage.cache_creation_input_tokens || undefined,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcacheReadTokens: usage.cache_read_input_tokens || undefined,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"message_delta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// tells us stop_reason, stop_sequence, and output tokens along the way and at the end of the message\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"usage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tinputTokens: 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\toutputTokens: chunk.usage.output_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"message_stop\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// no usage data, just an indicator that the message is done\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"content_block_start\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tswitch (chunk.content_block.type) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"text\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// we may receive multiple text blocks, in which case just insert a line break between them\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (chunk.index > 0) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: \\\\\\\"\\\\\\\\n\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: chunk.content_block.text,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"content_block_delta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tswitch (chunk.delta.type) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"text_delta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: chunk.delta.text,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"content_block_stop\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tgetModel(): { id: AnthropicModelId; info: ModelInfo } {\\\\r\\\\n\\\\t\\\\tconst modelId = this.options.apiModelId\\\\r\\\\n\\\\t\\\\tif (modelId && modelId in anthropicModels) {\\\\r\\\\n\\\\t\\\\t\\\\tconst id = modelId as AnthropicModelId\\\\r\\\\n\\\\t\\\\t\\\\treturn { id, info: anthropicModels[id] }\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\tid: anthropicDefaultModelId,\\\\r\\\\n\\\\t\\\\t\\\\tinfo: anthropicModels[anthropicDefaultModelId],\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":6456,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.928Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.928Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":189,\\\"nonEmptyLines\\\":182,\\\"commentLines\\\":14,\\\"complexity\\\":39},\\\"dependencies\\\":[\\\"@anthropic-ai/sdk\\\",\\\"@anthropic-ai/sdk/streaming\\\",\\\"../../shared/api\\\",\\\"../index\\\",\\\"../transform/stream\\\"],\\\"quality\\\":{\\\"score\\\":-224,\\\"issues\\\":[],\\\"duplicateLines\\\":62,\\\"longLines\\\":7,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.292Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":189},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\api\\\\\\\\providers\\\\\\\\bedrock.ts\\\":{\\\"content\\\":\\\"import AnthropicBedrock from \\\\\\\"@anthropic-ai/bedrock-sdk\\\\\\\"\\\\r\\\\nimport { Anthropic } from \\\\\\\"@anthropic-ai/sdk\\\\\\\"\\\\r\\\\nimport { ApiHandler } from \\\\\\\"../\\\\\\\"\\\\r\\\\nimport { ApiHandlerOptions, bedrockDefaultModelId, BedrockModelId, bedrockModels, ModelInfo } from \\\\\\\"../../shared/api\\\\\\\"\\\\r\\\\nimport { ApiStream } from \\\\\\\"../transform/stream\\\\\\\"\\\\r\\\\n\\\\r\\\\n// https://docs.anthropic.com/en/api/claude-on-amazon-bedrock\\\\r\\\\nexport class AwsBedrockHandler implements ApiHandler {\\\\r\\\\n\\\\tprivate options: ApiHandlerOptions\\\\r\\\\n\\\\tprivate client: AnthropicBedrock\\\\r\\\\n\\\\r\\\\n\\\\tconstructor(options: ApiHandlerOptions) {\\\\r\\\\n\\\\t\\\\tthis.options = options\\\\r\\\\n\\\\t\\\\tthis.client = new AnthropicBedrock({\\\\r\\\\n\\\\t\\\\t\\\\t// Authenticate by either providing the keys below or use the default AWS credential providers, such as\\\\r\\\\n\\\\t\\\\t\\\\t// using ~/.aws/credentials or the \\\\\\\"AWS_SECRET_ACCESS_KEY\\\\\\\" and \\\\\\\"AWS_ACCESS_KEY_ID\\\\\\\" environment variables.\\\\r\\\\n\\\\t\\\\t\\\\t...(this.options.awsAccessKey ? { awsAccessKey: this.options.awsAccessKey } : {}),\\\\r\\\\n\\\\t\\\\t\\\\t...(this.options.awsSecretKey ? { awsSecretKey: this.options.awsSecretKey } : {}),\\\\r\\\\n\\\\t\\\\t\\\\t...(this.options.awsSessionToken ? { awsSessionToken: this.options.awsSessionToken } : {}),\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// awsRegion changes the aws region to which the request is made. By default, we read AWS_REGION,\\\\r\\\\n\\\\t\\\\t\\\\t// and if that's not present, we default to us-east-1. Note that we do not read ~/.aws/config for the region.\\\\r\\\\n\\\\t\\\\t\\\\tawsRegion: this.options.awsRegion,\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {\\\\r\\\\n\\\\t\\\\t// cross region inference requires prefixing the model id with the region\\\\r\\\\n\\\\t\\\\tlet modelId: string\\\\r\\\\n\\\\t\\\\tif (this.options.awsUseCrossRegionInference) {\\\\r\\\\n\\\\t\\\\t\\\\tlet regionPrefix = (this.options.awsRegion || \\\\\\\"\\\\\\\").slice(0, 3)\\\\r\\\\n\\\\t\\\\t\\\\tswitch (regionPrefix) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"us-\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmodelId = `us.${this.getModel().id}`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"eu-\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmodelId = `eu.${this.getModel().id}`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tdefault:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// cross region inference is not supported in this region, falling back to default model\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmodelId = this.getModel().id\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\tmodelId = this.getModel().id\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst stream = await this.client.messages.create({\\\\r\\\\n\\\\t\\\\t\\\\tmodel: modelId,\\\\r\\\\n\\\\t\\\\t\\\\tmax_tokens: this.getModel().info.maxTokens || 8192,\\\\r\\\\n\\\\t\\\\t\\\\ttemperature: 0,\\\\r\\\\n\\\\t\\\\t\\\\tsystem: systemPrompt,\\\\r\\\\n\\\\t\\\\t\\\\tmessages,\\\\r\\\\n\\\\t\\\\t\\\\tstream: true,\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\tfor await (const chunk of stream) {\\\\r\\\\n\\\\t\\\\t\\\\tswitch (chunk.type) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"message_start\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst usage = chunk.message.usage\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"usage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tinputTokens: usage.input_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\toutputTokens: usage.output_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"message_delta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"usage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tinputTokens: 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\toutputTokens: chunk.usage.output_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"content_block_start\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tswitch (chunk.content_block.type) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"text\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (chunk.index > 0) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: \\\\\\\"\\\\\\\\n\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: chunk.content_block.text,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"content_block_delta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tswitch (chunk.delta.type) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"text_delta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: chunk.delta.text,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tgetModel(): { id: BedrockModelId; info: ModelInfo } {\\\\r\\\\n\\\\t\\\\tconst modelId = this.options.apiModelId\\\\r\\\\n\\\\t\\\\tif (modelId && modelId in bedrockModels) {\\\\r\\\\n\\\\t\\\\t\\\\tconst id = modelId as BedrockModelId\\\\r\\\\n\\\\t\\\\t\\\\treturn { id, info: bedrockModels[id] }\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\tid: bedrockDefaultModelId,\\\\r\\\\n\\\\t\\\\t\\\\tinfo: bedrockModels[bedrockDefaultModelId],\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":3626,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.928Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.928Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":116,\\\"nonEmptyLines\\\":108,\\\"commentLines\\\":7,\\\"complexity\\\":22},\\\"dependencies\\\":[\\\"@anthropic-ai/bedrock-sdk\\\",\\\"@anthropic-ai/sdk\\\",\\\"../\\\",\\\"../../shared/api\\\",\\\"../transform/stream\\\"],\\\"quality\\\":{\\\"score\\\":-82,\\\"issues\\\":[],\\\"duplicateLines\\\":34,\\\"longLines\\\":6,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.295Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":116},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\api\\\\\\\\providers\\\\\\\\deepseek.ts\\\":{\\\"content\\\":\\\"import { Anthropic } from \\\\\\\"@anthropic-ai/sdk\\\\\\\"\\\\r\\\\nimport OpenAI from \\\\\\\"openai\\\\\\\"\\\\r\\\\nimport { ApiHandler } from \\\\\\\"../\\\\\\\"\\\\r\\\\nimport { ApiHandlerOptions, DeepSeekModelId, ModelInfo, deepSeekDefaultModelId, deepSeekModels } from \\\\\\\"../../shared/api\\\\\\\"\\\\r\\\\nimport { convertToOpenAiMessages } from \\\\\\\"../transform/openai-format\\\\\\\"\\\\r\\\\nimport { ApiStream } from \\\\\\\"../transform/stream\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport class DeepSeekHandler implements ApiHandler {\\\\r\\\\n\\\\tprivate options: ApiHandlerOptions\\\\r\\\\n\\\\tprivate client: OpenAI\\\\r\\\\n\\\\r\\\\n\\\\tconstructor(options: ApiHandlerOptions) {\\\\r\\\\n\\\\t\\\\tthis.options = options\\\\r\\\\n\\\\t\\\\tthis.client = new OpenAI({\\\\r\\\\n\\\\t\\\\t\\\\tbaseURL: \\\\\\\"https://api.deepseek.com/v1\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tapiKey: this.options.deepSeekApiKey,\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {\\\\r\\\\n\\\\t\\\\tconst stream = await this.client.chat.completions.create({\\\\r\\\\n\\\\t\\\\t\\\\tmodel: this.getModel().id,\\\\r\\\\n\\\\t\\\\t\\\\tmax_completion_tokens: this.getModel().info.maxTokens,\\\\r\\\\n\\\\t\\\\t\\\\ttemperature: 0,\\\\r\\\\n\\\\t\\\\t\\\\tmessages: [{ role: \\\\\\\"system\\\\\\\", content: systemPrompt }, ...convertToOpenAiMessages(messages)],\\\\r\\\\n\\\\t\\\\t\\\\tstream: true,\\\\r\\\\n\\\\t\\\\t\\\\tstream_options: { include_usage: true },\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tfor await (const chunk of stream) {\\\\r\\\\n\\\\t\\\\t\\\\tconst delta = chunk.choices[0]?.delta\\\\r\\\\n\\\\t\\\\t\\\\tif (delta?.content) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttext: delta.content,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tif (chunk.usage) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"usage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tinputTokens: chunk.usage.prompt_tokens || 0, // (deepseek reports total input AND cache reads/writes, see context caching: https://api-docs.deepseek.com/guides/kv_cache) where the input tokens is the sum of the cache hits/misses, while anthropic reports them as separate tokens. This is important to know for 1) context management truncation algorithm, and 2) cost calculation (NOTE: we report both input and cache stats but for now set input price to 0 since all the cost calculation will be done using cache hits/misses)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\toutputTokens: chunk.usage.completion_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// @ts-ignore-next-line\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcacheReadTokens: chunk.usage.prompt_cache_hit_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// @ts-ignore-next-line\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcacheWriteTokens: chunk.usage.prompt_cache_miss_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tgetModel(): { id: DeepSeekModelId; info: ModelInfo } {\\\\r\\\\n\\\\t\\\\tconst modelId = this.options.apiModelId\\\\r\\\\n\\\\t\\\\tif (modelId && modelId in deepSeekModels) {\\\\r\\\\n\\\\t\\\\t\\\\tconst id = modelId as DeepSeekModelId\\\\r\\\\n\\\\t\\\\t\\\\treturn { id, info: deepSeekModels[id] }\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\tid: deepSeekDefaultModelId,\\\\r\\\\n\\\\t\\\\t\\\\tinfo: deepSeekModels[deepSeekDefaultModelId],\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":2495,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.929Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.928Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":65,\\\"nonEmptyLines\\\":58,\\\"commentLines\\\":2,\\\"complexity\\\":11},\\\"dependencies\\\":[\\\"@anthropic-ai/sdk\\\",\\\"openai\\\",\\\"../\\\",\\\"../../shared/api\\\",\\\"../transform/openai-format\\\",\\\"../transform/stream\\\"],\\\"quality\\\":{\\\"score\\\":29,\\\"issues\\\":[],\\\"duplicateLines\\\":13,\\\"longLines\\\":3,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.297Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":65},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\api\\\\\\\\providers\\\\\\\\gemini.ts\\\":{\\\"content\\\":\\\"import { Anthropic } from \\\\\\\"@anthropic-ai/sdk\\\\\\\"\\\\r\\\\nimport { GoogleGenerativeAI } from \\\\\\\"@google/generative-ai\\\\\\\"\\\\r\\\\nimport { ApiHandler } from \\\\\\\"../\\\\\\\"\\\\r\\\\nimport { ApiHandlerOptions, geminiDefaultModelId, GeminiModelId, geminiModels, ModelInfo } from \\\\\\\"../../shared/api\\\\\\\"\\\\r\\\\nimport { convertAnthropicMessageToGemini } from \\\\\\\"../transform/gemini-format\\\\\\\"\\\\r\\\\nimport { ApiStream } from \\\\\\\"../transform/stream\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport class GeminiHandler implements ApiHandler {\\\\r\\\\n\\\\tprivate options: ApiHandlerOptions\\\\r\\\\n\\\\tprivate client: GoogleGenerativeAI\\\\r\\\\n\\\\r\\\\n\\\\tconstructor(options: ApiHandlerOptions) {\\\\r\\\\n\\\\t\\\\tif (!options.geminiApiKey) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"API key is required for Google Gemini\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tthis.options = options\\\\r\\\\n\\\\t\\\\tthis.client = new GoogleGenerativeAI(options.geminiApiKey)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {\\\\r\\\\n\\\\t\\\\tconst model = this.client.getGenerativeModel({\\\\r\\\\n\\\\t\\\\t\\\\tmodel: this.getModel().id,\\\\r\\\\n\\\\t\\\\t\\\\tsystemInstruction: systemPrompt,\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\tconst result = await model.generateContentStream({\\\\r\\\\n\\\\t\\\\t\\\\tcontents: messages.map(convertAnthropicMessageToGemini),\\\\r\\\\n\\\\t\\\\t\\\\tgenerationConfig: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// maxOutputTokens: this.getModel().info.maxTokens,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttemperature: 0,\\\\r\\\\n\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tfor await (const chunk of result.stream) {\\\\r\\\\n\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttext: chunk.text(),\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst response = await result.response\\\\r\\\\n\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\ttype: \\\\\\\"usage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tinputTokens: response.usageMetadata?.promptTokenCount ?? 0,\\\\r\\\\n\\\\t\\\\t\\\\toutputTokens: response.usageMetadata?.candidatesTokenCount ?? 0,\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tgetModel(): { id: GeminiModelId; info: ModelInfo } {\\\\r\\\\n\\\\t\\\\tconst modelId = this.options.apiModelId\\\\r\\\\n\\\\t\\\\tif (modelId && modelId in geminiModels) {\\\\r\\\\n\\\\t\\\\t\\\\tconst id = modelId as GeminiModelId\\\\r\\\\n\\\\t\\\\t\\\\treturn { id, info: geminiModels[id] }\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\tid: geminiDefaultModelId,\\\\r\\\\n\\\\t\\\\t\\\\tinfo: geminiModels[geminiDefaultModelId],\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":1870,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.929Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.929Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":60,\\\"nonEmptyLines\\\":53,\\\"commentLines\\\":1,\\\"complexity\\\":10},\\\"dependencies\\\":[\\\"@anthropic-ai/sdk\\\",\\\"@google/generative-ai\\\",\\\"../\\\",\\\"../../shared/api\\\",\\\"../transform/gemini-format\\\",\\\"../transform/stream\\\"],\\\"quality\\\":{\\\"score\\\":41,\\\"issues\\\":[],\\\"duplicateLines\\\":11,\\\"longLines\\\":2,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.298Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":60},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\api\\\\\\\\providers\\\\\\\\lmstudio.ts\\\":{\\\"content\\\":\\\"import { Anthropic } from \\\\\\\"@anthropic-ai/sdk\\\\\\\"\\\\r\\\\nimport OpenAI from \\\\\\\"openai\\\\\\\"\\\\r\\\\nimport { ApiHandler } from \\\\\\\"../\\\\\\\"\\\\r\\\\nimport { ApiHandlerOptions, ModelInfo, openAiModelInfoSaneDefaults } from \\\\\\\"../../shared/api\\\\\\\"\\\\r\\\\nimport { convertToOpenAiMessages } from \\\\\\\"../transform/openai-format\\\\\\\"\\\\r\\\\nimport { ApiStream } from \\\\\\\"../transform/stream\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport class LmStudioHandler implements ApiHandler {\\\\r\\\\n\\\\tprivate options: ApiHandlerOptions\\\\r\\\\n\\\\tprivate client: OpenAI\\\\r\\\\n\\\\r\\\\n\\\\tconstructor(options: ApiHandlerOptions) {\\\\r\\\\n\\\\t\\\\tthis.options = options\\\\r\\\\n\\\\t\\\\tthis.client = new OpenAI({\\\\r\\\\n\\\\t\\\\t\\\\tbaseURL: (this.options.lmStudioBaseUrl || \\\\\\\"http://localhost:1234\\\\\\\") + \\\\\\\"/v1\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tapiKey: \\\\\\\"noop\\\\\\\",\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {\\\\r\\\\n\\\\t\\\\tconst openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [\\\\r\\\\n\\\\t\\\\t\\\\t{ role: \\\\\\\"system\\\\\\\", content: systemPrompt },\\\\r\\\\n\\\\t\\\\t\\\\t...convertToOpenAiMessages(messages),\\\\r\\\\n\\\\t\\\\t]\\\\r\\\\n\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tconst stream = await this.client.chat.completions.create({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tmodel: this.getModel().id,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tmessages: openAiMessages,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttemperature: 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tstream: true,\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\tfor await (const chunk of stream) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst delta = chunk.choices[0]?.delta\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (delta?.content) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: delta.content,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t// LM Studio doesn't return an error code/body for now\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"Please check the LM Studio developer logs to debug what went wrong. You may need to load the model with a larger context length to work with Cline's prompts.\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tgetModel(): { id: string; info: ModelInfo } {\\\\r\\\\n\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\tid: this.options.lmStudioModelId || \\\\\\\"\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tinfo: openAiModelInfoSaneDefaults,\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":1739,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.834Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.834Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":57,\\\"nonEmptyLines\\\":51,\\\"commentLines\\\":1,\\\"complexity\\\":7},\\\"dependencies\\\":[\\\"@anthropic-ai/sdk\\\",\\\"openai\\\",\\\"../\\\",\\\"../../shared/api\\\",\\\"../transform/openai-format\\\",\\\"../transform/stream\\\"],\\\"quality\\\":{\\\"score\\\":51,\\\"issues\\\":[],\\\"duplicateLines\\\":9,\\\"longLines\\\":2,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.299Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":57},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\api\\\\\\\\providers\\\\\\\\ollama.ts\\\":{\\\"content\\\":\\\"import { Anthropic } from \\\\\\\"@anthropic-ai/sdk\\\\\\\"\\\\r\\\\nimport OpenAI from \\\\\\\"openai\\\\\\\"\\\\r\\\\nimport { ApiHandler } from \\\\\\\"../\\\\\\\"\\\\r\\\\nimport { ApiHandlerOptions, ModelInfo, openAiModelInfoSaneDefaults } from \\\\\\\"../../shared/api\\\\\\\"\\\\r\\\\nimport { convertToOpenAiMessages } from \\\\\\\"../transform/openai-format\\\\\\\"\\\\r\\\\nimport { ApiStream } from \\\\\\\"../transform/stream\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport class OllamaHandler implements ApiHandler {\\\\r\\\\n\\\\tprivate options: ApiHandlerOptions\\\\r\\\\n\\\\tprivate client: OpenAI\\\\r\\\\n\\\\r\\\\n\\\\tconstructor(options: ApiHandlerOptions) {\\\\r\\\\n\\\\t\\\\tthis.options = options\\\\r\\\\n\\\\t\\\\tthis.client = new OpenAI({\\\\r\\\\n\\\\t\\\\t\\\\tbaseURL: (this.options.ollamaBaseUrl || \\\\\\\"http://localhost:11434\\\\\\\") + \\\\\\\"/v1\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tapiKey: \\\\\\\"ollama\\\\\\\",\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {\\\\r\\\\n\\\\t\\\\tconst openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [\\\\r\\\\n\\\\t\\\\t\\\\t{ role: \\\\\\\"system\\\\\\\", content: systemPrompt },\\\\r\\\\n\\\\t\\\\t\\\\t...convertToOpenAiMessages(messages),\\\\r\\\\n\\\\t\\\\t]\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst stream = await this.client.chat.completions.create({\\\\r\\\\n\\\\t\\\\t\\\\tmodel: this.getModel().id,\\\\r\\\\n\\\\t\\\\t\\\\tmessages: openAiMessages,\\\\r\\\\n\\\\t\\\\t\\\\ttemperature: 0,\\\\r\\\\n\\\\t\\\\t\\\\tstream: true,\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\tfor await (const chunk of stream) {\\\\r\\\\n\\\\t\\\\t\\\\tconst delta = chunk.choices[0]?.delta\\\\r\\\\n\\\\t\\\\t\\\\tif (delta?.content) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttext: delta.content,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tgetModel(): { id: string; info: ModelInfo } {\\\\r\\\\n\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\tid: this.options.ollamaModelId || \\\\\\\"\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tinfo: openAiModelInfoSaneDefaults,\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":1434,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.834Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.834Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":50,\\\"nonEmptyLines\\\":44,\\\"commentLines\\\":0,\\\"complexity\\\":6},\\\"dependencies\\\":[\\\"@anthropic-ai/sdk\\\",\\\"openai\\\",\\\"../\\\",\\\"../../shared/api\\\",\\\"../transform/openai-format\\\",\\\"../transform/stream\\\"],\\\"quality\\\":{\\\"score\\\":58,\\\"issues\\\":[],\\\"duplicateLines\\\":8,\\\"longLines\\\":1,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.301Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":50},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\api\\\\\\\\providers\\\\\\\\openai-native.ts\\\":{\\\"content\\\":\\\"import { Anthropic } from \\\\\\\"@anthropic-ai/sdk\\\\\\\"\\\\r\\\\nimport OpenAI from \\\\\\\"openai\\\\\\\"\\\\r\\\\nimport { ApiHandler } from \\\\\\\"../\\\\\\\"\\\\r\\\\nimport {\\\\r\\\\n\\\\tApiHandlerOptions,\\\\r\\\\n\\\\tModelInfo,\\\\r\\\\n\\\\topenAiNativeDefaultModelId,\\\\r\\\\n\\\\tOpenAiNativeModelId,\\\\r\\\\n\\\\topenAiNativeModels,\\\\r\\\\n} from \\\\\\\"../../shared/api\\\\\\\"\\\\r\\\\nimport { convertToOpenAiMessages } from \\\\\\\"../transform/openai-format\\\\\\\"\\\\r\\\\nimport { ApiStream } from \\\\\\\"../transform/stream\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport class OpenAiNativeHandler implements ApiHandler {\\\\r\\\\n\\\\tprivate options: ApiHandlerOptions\\\\r\\\\n\\\\tprivate client: OpenAI\\\\r\\\\n\\\\r\\\\n\\\\tconstructor(options: ApiHandlerOptions) {\\\\r\\\\n\\\\t\\\\tthis.options = options\\\\r\\\\n\\\\t\\\\tthis.client = new OpenAI({\\\\r\\\\n\\\\t\\\\t\\\\tapiKey: this.options.openAiNativeApiKey,\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {\\\\r\\\\n\\\\t\\\\tswitch (this.getModel().id) {\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"o1\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"o1-preview\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"o1-mini\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// o1 doesnt support streaming, non-1 temp, or system prompt\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst response = await this.client.chat.completions.create({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmodel: this.getModel().id,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmessages: [{ role: \\\\\\\"user\\\\\\\", content: systemPrompt }, ...convertToOpenAiMessages(messages)],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttext: response.choices[0]?.message.content || \\\\\\\"\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"usage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tinputTokens: response.usage?.prompt_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\toutputTokens: response.usage?.completion_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tdefault: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst stream = await this.client.chat.completions.create({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmodel: this.getModel().id,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// max_completion_tokens: this.getModel().info.maxTokens,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttemperature: 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmessages: [{ role: \\\\\\\"system\\\\\\\", content: systemPrompt }, ...convertToOpenAiMessages(messages)],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tstream: true,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tstream_options: { include_usage: true },\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tfor await (const chunk of stream) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst delta = chunk.choices[0]?.delta\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (delta?.content) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: delta.content,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// contains a null value except for the last chunk which contains the token usage statistics for the entire request\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (chunk.usage) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"usage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tinputTokens: chunk.usage.prompt_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\toutputTokens: chunk.usage.completion_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tgetModel(): { id: OpenAiNativeModelId; info: ModelInfo } {\\\\r\\\\n\\\\t\\\\tconst modelId = this.options.apiModelId\\\\r\\\\n\\\\t\\\\tif (modelId && modelId in openAiNativeModels) {\\\\r\\\\n\\\\t\\\\t\\\\tconst id = modelId as OpenAiNativeModelId\\\\r\\\\n\\\\t\\\\t\\\\treturn { id, info: openAiNativeModels[id] }\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\tid: openAiNativeDefaultModelId,\\\\r\\\\n\\\\t\\\\t\\\\tinfo: openAiNativeModels[openAiNativeDefaultModelId],\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":2672,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.934Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.934Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":90,\\\"nonEmptyLines\\\":83,\\\"commentLines\\\":3,\\\"complexity\\\":18},\\\"dependencies\\\":[\\\"@anthropic-ai/sdk\\\",\\\"openai\\\",\\\"../\\\",\\\"../transform/openai-format\\\",\\\"../transform/stream\\\"],\\\"quality\\\":{\\\"score\\\":-19,\\\"issues\\\":[],\\\"duplicateLines\\\":23,\\\"longLines\\\":2,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.303Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":90},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\api\\\\\\\\providers\\\\\\\\openai.ts\\\":{\\\"content\\\":\\\"import { Anthropic } from \\\\\\\"@anthropic-ai/sdk\\\\\\\"\\\\r\\\\nimport OpenAI, { AzureOpenAI } from \\\\\\\"openai\\\\\\\"\\\\r\\\\nimport { ApiHandlerOptions, azureOpenAiDefaultApiVersion, ModelInfo, openAiModelInfoSaneDefaults } from \\\\\\\"../../shared/api\\\\\\\"\\\\r\\\\nimport { ApiHandler } from \\\\\\\"../index\\\\\\\"\\\\r\\\\nimport { convertToOpenAiMessages } from \\\\\\\"../transform/openai-format\\\\\\\"\\\\r\\\\nimport { ApiStream } from \\\\\\\"../transform/stream\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport class OpenAiHandler implements ApiHandler {\\\\r\\\\n\\\\tprivate options: ApiHandlerOptions\\\\r\\\\n\\\\tprivate client: OpenAI\\\\r\\\\n\\\\r\\\\n\\\\tconstructor(options: ApiHandlerOptions) {\\\\r\\\\n\\\\t\\\\tthis.options = options\\\\r\\\\n\\\\t\\\\t// Azure API shape slightly differs from the core API shape: https://github.com/openai/openai-node?tab=readme-ov-file#microsoft-azure-openai\\\\r\\\\n\\\\t\\\\tif (this.options.openAiBaseUrl?.toLowerCase().includes(\\\\\\\"azure.com\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\tthis.client = new AzureOpenAI({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbaseURL: this.options.openAiBaseUrl,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tapiKey: this.options.openAiApiKey,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tapiVersion: this.options.azureApiVersion || azureOpenAiDefaultApiVersion,\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\tthis.client = new OpenAI({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbaseURL: this.options.openAiBaseUrl,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tapiKey: this.options.openAiApiKey,\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {\\\\r\\\\n\\\\t\\\\tconst openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [\\\\r\\\\n\\\\t\\\\t\\\\t{ role: \\\\\\\"system\\\\\\\", content: systemPrompt },\\\\r\\\\n\\\\t\\\\t\\\\t...convertToOpenAiMessages(messages),\\\\r\\\\n\\\\t\\\\t]\\\\r\\\\n\\\\t\\\\tconst stream = await this.client.chat.completions.create({\\\\r\\\\n\\\\t\\\\t\\\\tmodel: this.options.openAiModelId ?? \\\\\\\"\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tmessages: openAiMessages,\\\\r\\\\n\\\\t\\\\t\\\\ttemperature: 0,\\\\r\\\\n\\\\t\\\\t\\\\tstream: true,\\\\r\\\\n\\\\t\\\\t\\\\tstream_options: { include_usage: true },\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\tfor await (const chunk of stream) {\\\\r\\\\n\\\\t\\\\t\\\\tconst delta = chunk.choices[0]?.delta\\\\r\\\\n\\\\t\\\\t\\\\tif (delta?.content) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttext: delta.content,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tif (chunk.usage) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"usage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tinputTokens: chunk.usage.prompt_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\toutputTokens: chunk.usage.completion_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tgetModel(): { id: string; info: ModelInfo } {\\\\r\\\\n\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\tid: this.options.openAiModelId ?? \\\\\\\"\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tinfo: openAiModelInfoSaneDefaults,\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":2142,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.935Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.935Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":66,\\\"nonEmptyLines\\\":61,\\\"commentLines\\\":1,\\\"complexity\\\":16},\\\"dependencies\\\":[\\\"@anthropic-ai/sdk\\\",\\\"openai\\\",\\\"../../shared/api\\\",\\\"../index\\\",\\\"../transform/openai-format\\\",\\\"../transform/stream\\\"],\\\"quality\\\":{\\\"score\\\":19,\\\"issues\\\":[],\\\"duplicateLines\\\":15,\\\"longLines\\\":3,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.304Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":66},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\api\\\\\\\\providers\\\\\\\\openrouter.ts\\\":{\\\"content\\\":\\\"import { Anthropic } from \\\\\\\"@anthropic-ai/sdk\\\\\\\"\\\\r\\\\nimport axios from \\\\\\\"axios\\\\\\\"\\\\r\\\\nimport OpenAI from \\\\\\\"openai\\\\\\\"\\\\r\\\\nimport { ApiHandler } from \\\\\\\"../\\\\\\\"\\\\r\\\\nimport { ApiHandlerOptions, ModelInfo, openRouterDefaultModelId, openRouterDefaultModelInfo } from \\\\\\\"../../shared/api\\\\\\\"\\\\r\\\\nimport { convertToOpenAiMessages } from \\\\\\\"../transform/openai-format\\\\\\\"\\\\r\\\\nimport { ApiStream } from \\\\\\\"../transform/stream\\\\\\\"\\\\r\\\\nimport delay from \\\\\\\"delay\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport class OpenRouterHandler implements ApiHandler {\\\\r\\\\n\\\\tprivate options: ApiHandlerOptions\\\\r\\\\n\\\\tprivate client: OpenAI\\\\r\\\\n\\\\r\\\\n\\\\tconstructor(options: ApiHandlerOptions) {\\\\r\\\\n\\\\t\\\\tthis.options = options\\\\r\\\\n\\\\t\\\\tthis.client = new OpenAI({\\\\r\\\\n\\\\t\\\\t\\\\tbaseURL: \\\\\\\"https://openrouter.ai/api/v1\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tapiKey: this.options.openRouterApiKey,\\\\r\\\\n\\\\t\\\\t\\\\tdefaultHeaders: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"HTTP-Referer\\\\\\\": \\\\\\\"https://cline.bot\\\\\\\", // Optional, for including your app on openrouter.ai rankings.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"X-Title\\\\\\\": \\\\\\\"Cline\\\\\\\", // Optional. Shows in rankings on openrouter.ai.\\\\r\\\\n\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {\\\\r\\\\n\\\\t\\\\t// Convert Anthropic messages to OpenAI format\\\\r\\\\n\\\\t\\\\tconst openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [\\\\r\\\\n\\\\t\\\\t\\\\t{ role: \\\\\\\"system\\\\\\\", content: systemPrompt },\\\\r\\\\n\\\\t\\\\t\\\\t...convertToOpenAiMessages(messages),\\\\r\\\\n\\\\t\\\\t]\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// prompt caching: https://openrouter.ai/docs/prompt-caching\\\\r\\\\n\\\\t\\\\t// this is specifically for claude models (some models may 'support prompt caching' automatically without this)\\\\r\\\\n\\\\t\\\\tswitch (this.getModel().id) {\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3.5-sonnet\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3.5-sonnet:beta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3.5-sonnet-20240620\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3.5-sonnet-20240620:beta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3-5-haiku\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3-5-haiku:beta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3-5-haiku-20241022\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3-5-haiku-20241022:beta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3-haiku\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3-haiku:beta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3-opus\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3-opus:beta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\topenAiMessages[0] = {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\trole: \\\\\\\"system\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcontent: [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: systemPrompt,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// @ts-ignore-next-line\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcache_control: { type: \\\\\\\"ephemeral\\\\\\\" },\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Add cache_control to the last two user messages\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// (note: this works because we only ever add one user message at a time, but if we added multiple we'd need to mark the user message before the last assistant message)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst lastTwoUserMessages = openAiMessages.filter((msg) => msg.role === \\\\\\\"user\\\\\\\").slice(-2)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlastTwoUserMessages.forEach((msg) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (typeof msg.content === \\\\\\\"string\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmsg.content = [{ type: \\\\\\\"text\\\\\\\", text: msg.content }]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (Array.isArray(msg.content)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// NOTE: this is fine since env details will always be added at the end. but if it weren't there, and the user added a image_url type message, it would pop a text part before it and then move it after to the end.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlet lastTextPart = msg.content.filter((part) => part.type === \\\\\\\"text\\\\\\\").pop()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!lastTextPart) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlastTextPart = { type: \\\\\\\"text\\\\\\\", text: \\\\\\\"...\\\\\\\" }\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmsg.content.push(lastTextPart)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// @ts-ignore-next-line\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlastTextPart[\\\\\\\"cache_control\\\\\\\"] = { type: \\\\\\\"ephemeral\\\\\\\" }\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\tdefault:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Not sure how openrouter defaults max tokens when no value is provided, but the anthropic api requires this value and since they offer both 4096 and 8192 variants, we should ensure 8192.\\\\r\\\\n\\\\t\\\\t// (models usually default to max tokens allowed)\\\\r\\\\n\\\\t\\\\tlet maxTokens: number | undefined\\\\r\\\\n\\\\t\\\\tswitch (this.getModel().id) {\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3.5-sonnet\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3.5-sonnet:beta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3.5-sonnet-20240620\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3.5-sonnet-20240620:beta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3-5-haiku\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3-5-haiku:beta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3-5-haiku-20241022\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3-5-haiku-20241022:beta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tmaxTokens = 8_192\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Removes messages in the middle when close to context window limit. Should not be applied to models that support prompt caching since it would continuously break the cache.\\\\r\\\\n\\\\t\\\\tlet shouldApplyMiddleOutTransform = !this.getModel().info.supportsPromptCache\\\\r\\\\n\\\\t\\\\t// except for deepseek (which we set supportsPromptCache to true for), where because the context window is so small our truncation algo might miss and we should use openrouter's middle-out transform as a fallback to ensure we don't exceed the context window (FIXME: once we have a more robust token estimator we should not rely on this)\\\\r\\\\n\\\\t\\\\tif (this.getModel().id === \\\\\\\"deepseek/deepseek-chat\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\tshouldApplyMiddleOutTransform = true\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// @ts-ignore-next-line\\\\r\\\\n\\\\t\\\\tconst stream = await this.client.chat.completions.create({\\\\r\\\\n\\\\t\\\\t\\\\tmodel: this.getModel().id,\\\\r\\\\n\\\\t\\\\t\\\\tmax_tokens: maxTokens,\\\\r\\\\n\\\\t\\\\t\\\\ttemperature: 0,\\\\r\\\\n\\\\t\\\\t\\\\tmessages: openAiMessages,\\\\r\\\\n\\\\t\\\\t\\\\tstream: true,\\\\r\\\\n\\\\t\\\\t\\\\ttransforms: shouldApplyMiddleOutTransform ? [\\\\\\\"middle-out\\\\\\\"] : undefined,\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet genId: string | undefined\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tfor await (const chunk of stream) {\\\\r\\\\n\\\\t\\\\t\\\\t// openrouter returns an error object instead of the openai sdk throwing an error\\\\r\\\\n\\\\t\\\\t\\\\tif (\\\\\\\"error\\\\\\\" in chunk) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst error = chunk.error as { message?: string; code?: number }\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.error(`OpenRouter API Error: ${error?.code} - ${error?.message}`)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthrow new Error(`OpenRouter API Error ${error?.code}: ${error?.message}`)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tif (!genId && chunk.id) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tgenId = chunk.id\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tconst delta = chunk.choices[0]?.delta\\\\r\\\\n\\\\t\\\\t\\\\tif (delta?.content) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttext: delta.content,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t// if (chunk.usage) {\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\t\\\\ttype: \\\\\\\"usage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\t\\\\tinputTokens: chunk.usage.prompt_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\t\\\\toutputTokens: chunk.usage.completion_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t// }\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tawait delay(500) // FIXME: necessary delay to ensure generation endpoint is ready\\\\r\\\\n\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tconst response = await axios.get(`https://openrouter.ai/api/v1/generation?id=${genId}`, {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\theaders: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tAuthorization: `Bearer ${this.options.openRouterApiKey}`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttimeout: 5_000, // this request hangs sometimes\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tconst generation = response.data?.data\\\\r\\\\n\\\\t\\\\t\\\\tconsole.log(\\\\\\\"OpenRouter generation details:\\\\\\\", response.data)\\\\r\\\\n\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"usage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// cacheWriteTokens: 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// cacheReadTokens: 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// openrouter generation endpoint fails often\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tinputTokens: generation?.native_tokens_prompt || 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\toutputTokens: generation?.native_tokens_completion || 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttotalCost: generation?.total_cost || 0,\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t// ignore if fails\\\\r\\\\n\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Error fetching OpenRouter generation details:\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tgetModel(): { id: string; info: ModelInfo } {\\\\r\\\\n\\\\t\\\\tconst modelId = this.options.openRouterModelId\\\\r\\\\n\\\\t\\\\tconst modelInfo = this.options.openRouterModelInfo\\\\r\\\\n\\\\t\\\\tif (modelId && modelInfo) {\\\\r\\\\n\\\\t\\\\t\\\\treturn { id: modelId, info: modelInfo }\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\tid: openRouterDefaultModelId,\\\\r\\\\n\\\\t\\\\t\\\\tinfo: openRouterDefaultModelInfo,\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":7102,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.935Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.935Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":185,\\\"nonEmptyLines\\\":168,\\\"commentLines\\\":25,\\\"complexity\\\":52},\\\"dependencies\\\":[\\\"@anthropic-ai/sdk\\\",\\\"axios\\\",\\\"openai\\\",\\\"../\\\",\\\"../../shared/api\\\",\\\"../transform/openai-format\\\",\\\"../transform/stream\\\",\\\"delay\\\"],\\\"quality\\\":{\\\"score\\\":-113,\\\"issues\\\":[],\\\"duplicateLines\\\":39,\\\"longLines\\\":9,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.309Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":185},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\api\\\\\\\\providers\\\\\\\\vertex.ts\\\":{\\\"content\\\":\\\"import { Anthropic } from \\\\\\\"@anthropic-ai/sdk\\\\\\\"\\\\r\\\\nimport { AnthropicVertex } from \\\\\\\"@anthropic-ai/vertex-sdk\\\\\\\"\\\\r\\\\nimport { ApiHandler } from \\\\\\\"../\\\\\\\"\\\\r\\\\nimport { ApiHandlerOptions, ModelInfo, vertexDefaultModelId, VertexModelId, vertexModels } from \\\\\\\"../../shared/api\\\\\\\"\\\\r\\\\nimport { ApiStream } from \\\\\\\"../transform/stream\\\\\\\"\\\\r\\\\n\\\\r\\\\n// https://docs.anthropic.com/en/api/claude-on-vertex-ai\\\\r\\\\nexport class VertexHandler implements ApiHandler {\\\\r\\\\n\\\\tprivate options: ApiHandlerOptions\\\\r\\\\n\\\\tprivate client: AnthropicVertex\\\\r\\\\n\\\\r\\\\n\\\\tconstructor(options: ApiHandlerOptions) {\\\\r\\\\n\\\\t\\\\tthis.options = options\\\\r\\\\n\\\\t\\\\tthis.client = new AnthropicVertex({\\\\r\\\\n\\\\t\\\\t\\\\tprojectId: this.options.vertexProjectId,\\\\r\\\\n\\\\t\\\\t\\\\t// https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-claude#regions\\\\r\\\\n\\\\t\\\\t\\\\tregion: this.options.vertexRegion,\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {\\\\r\\\\n\\\\t\\\\tconst stream = await this.client.messages.create({\\\\r\\\\n\\\\t\\\\t\\\\tmodel: this.getModel().id,\\\\r\\\\n\\\\t\\\\t\\\\tmax_tokens: this.getModel().info.maxTokens || 8192,\\\\r\\\\n\\\\t\\\\t\\\\ttemperature: 0,\\\\r\\\\n\\\\t\\\\t\\\\tsystem: systemPrompt,\\\\r\\\\n\\\\t\\\\t\\\\tmessages,\\\\r\\\\n\\\\t\\\\t\\\\tstream: true,\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\tfor await (const chunk of stream) {\\\\r\\\\n\\\\t\\\\t\\\\tswitch (chunk.type) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"message_start\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst usage = chunk.message.usage\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"usage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tinputTokens: usage.input_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\toutputTokens: usage.output_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"message_delta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"usage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tinputTokens: 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\toutputTokens: chunk.usage.output_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"content_block_start\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tswitch (chunk.content_block.type) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"text\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (chunk.index > 0) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: \\\\\\\"\\\\\\\\n\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: chunk.content_block.text,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"content_block_delta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tswitch (chunk.delta.type) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"text_delta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tyield {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: chunk.delta.text,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tgetModel(): { id: VertexModelId; info: ModelInfo } {\\\\r\\\\n\\\\t\\\\tconst modelId = this.options.apiModelId\\\\r\\\\n\\\\t\\\\tif (modelId && modelId in vertexModels) {\\\\r\\\\n\\\\t\\\\t\\\\tconst id = modelId as VertexModelId\\\\r\\\\n\\\\t\\\\t\\\\treturn { id, info: vertexModels[id] }\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\tid: vertexDefaultModelId,\\\\r\\\\n\\\\t\\\\t\\\\tinfo: vertexModels[vertexDefaultModelId],\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":2450,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.935Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.935Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":90,\\\"nonEmptyLines\\\":84,\\\"commentLines\\\":2,\\\"complexity\\\":14},\\\"dependencies\\\":[\\\"@anthropic-ai/sdk\\\",\\\"@anthropic-ai/vertex-sdk\\\",\\\"../\\\",\\\"../../shared/api\\\",\\\"../transform/stream\\\"],\\\"quality\\\":{\\\"score\\\":-44,\\\"issues\\\":[],\\\"duplicateLines\\\":28,\\\"longLines\\\":2,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.311Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":90},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\api\\\\\\\\transform\\\\\\\\gemini-format.ts\\\":{\\\"content\\\":\\\"import { Anthropic } from \\\\\\\"@anthropic-ai/sdk\\\\\\\"\\\\r\\\\nimport {\\\\r\\\\n\\\\tContent,\\\\r\\\\n\\\\tEnhancedGenerateContentResponse,\\\\r\\\\n\\\\tFunctionCallPart,\\\\r\\\\n\\\\tFunctionDeclaration,\\\\r\\\\n\\\\tFunctionResponsePart,\\\\r\\\\n\\\\tInlineDataPart,\\\\r\\\\n\\\\tPart,\\\\r\\\\n\\\\tSchemaType,\\\\r\\\\n\\\\tTextPart,\\\\r\\\\n} from \\\\\\\"@google/generative-ai\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport function convertAnthropicContentToGemini(\\\\r\\\\n\\\\tcontent:\\\\r\\\\n\\\\t\\\\t| string\\\\r\\\\n\\\\t\\\\t| Array<\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t| Anthropic.Messages.TextBlockParam\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t| Anthropic.Messages.ImageBlockParam\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t| Anthropic.Messages.ToolUseBlockParam\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t| Anthropic.Messages.ToolResultBlockParam\\\\r\\\\n\\\\t\\\\t >,\\\\r\\\\n): Part[] {\\\\r\\\\n\\\\tif (typeof content === \\\\\\\"string\\\\\\\") {\\\\r\\\\n\\\\t\\\\treturn [{ text: content } as TextPart]\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\treturn content.flatMap((block) => {\\\\r\\\\n\\\\t\\\\tswitch (block.type) {\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"text\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn { text: block.text } as TextPart\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"image\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (block.source.type !== \\\\\\\"base64\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Unsupported image source type\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tinlineData: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tdata: block.source.data,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmimeType: block.source.media_type,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} as InlineDataPart\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"tool_use\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tfunctionCall: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tname: block.name,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\targs: block.input,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} as FunctionCallPart\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"tool_result\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst name = block.tool_use_id.split(\\\\\\\"-\\\\\\\")[0]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (!block.content) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn []\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (typeof block.content === \\\\\\\"string\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tfunctionResponse: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tname,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tresponse: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tname,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontent: block.content,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t} as FunctionResponsePart\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// The only case when tool_result could be array is when the tool failed and we're providing ie user feedback potentially with images\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst textParts = block.content.filter((part) => part.type === \\\\\\\"text\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst imageParts = block.content.filter((part) => part.type === \\\\\\\"image\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst text = textParts.length > 0 ? textParts.map((part) => part.text).join(\\\\\\\"\\\\\\\\n\\\\\\\\n\\\\\\\") : \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst imageText = imageParts.length > 0 ? \\\\\\\"\\\\\\\\n\\\\\\\\n(See next part for image)\\\\\\\" : \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tfunctionResponse: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tname,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tresponse: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tname,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontent: text + imageText,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} as FunctionResponsePart,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t...imageParts.map(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t(part) =>\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tinlineData: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tdata: part.source.data,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmimeType: part.source.media_type,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}) as InlineDataPart,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tdefault:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthrow new Error(`Unsupported content block type: ${(block as any).type}`)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t})\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport function convertAnthropicMessageToGemini(message: Anthropic.Messages.MessageParam): Content {\\\\r\\\\n\\\\treturn {\\\\r\\\\n\\\\t\\\\trole: message.role === \\\\\\\"assistant\\\\\\\" ? \\\\\\\"model\\\\\\\" : \\\\\\\"user\\\\\\\",\\\\r\\\\n\\\\t\\\\tparts: convertAnthropicContentToGemini(message.content),\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport function convertAnthropicToolToGemini(tool: Anthropic.Messages.Tool): FunctionDeclaration {\\\\r\\\\n\\\\treturn {\\\\r\\\\n\\\\t\\\\tname: tool.name,\\\\r\\\\n\\\\t\\\\tdescription: tool.description || \\\\\\\"\\\\\\\",\\\\r\\\\n\\\\t\\\\tparameters: {\\\\r\\\\n\\\\t\\\\t\\\\ttype: SchemaType.OBJECT,\\\\r\\\\n\\\\t\\\\t\\\\tproperties: Object.fromEntries(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tObject.entries(tool.input_schema.properties || {}).map(([key, value]) => [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tkey,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: (value as any).type.toUpperCase(),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tdescription: (value as any).description || \\\\\\\"\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t]),\\\\r\\\\n\\\\t\\\\t\\\\t),\\\\r\\\\n\\\\t\\\\t\\\\trequired: (tool.input_schema.required as string[]) || [],\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n/*\\\\r\\\\nIt 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\\\\r\\\\n*/\\\\r\\\\nexport function unescapeGeminiContent(content: string) {\\\\r\\\\n\\\\treturn content.replace(/\\\\\\\\\\\\\\\\n/g, \\\\\\\"\\\\\\\\n\\\\\\\").replace(/\\\\\\\\\\\\\\\\'/g, \\\\\\\"'\\\\\\\").replace(/\\\\\\\\\\\\\\\\\\\\\\\"/g, '\\\\\\\"').replace(/\\\\\\\\\\\\\\\\r/g, \\\\\\\"\\\\\\\\r\\\\\\\").replace(/\\\\\\\\\\\\\\\\t/g, \\\\\\\"\\\\\\\\t\\\\\\\")\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport function convertGeminiResponseToAnthropic(response: EnhancedGenerateContentResponse): Anthropic.Messages.Message {\\\\r\\\\n\\\\tconst content: Anthropic.Messages.ContentBlock[] = []\\\\r\\\\n\\\\r\\\\n\\\\t// Add the main text response\\\\r\\\\n\\\\tconst text = response.text()\\\\r\\\\n\\\\tif (text) {\\\\r\\\\n\\\\t\\\\tcontent.push({ type: \\\\\\\"text\\\\\\\", text })\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Add function calls as tool_use blocks\\\\r\\\\n\\\\tconst functionCalls = response.functionCalls()\\\\r\\\\n\\\\tif (functionCalls) {\\\\r\\\\n\\\\t\\\\tfunctionCalls.forEach((call, index) => {\\\\r\\\\n\\\\t\\\\t\\\\tif (\\\\\\\"content\\\\\\\" in call.args && typeof call.args.content === \\\\\\\"string\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcall.args.content = unescapeGeminiContent(call.args.content)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tcontent.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"tool_use\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tid: `${call.name}-${index}-${Date.now()}`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tname: call.name,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tinput: call.args,\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Determine stop reason\\\\r\\\\n\\\\tlet stop_reason: Anthropic.Messages.Message[\\\\\\\"stop_reason\\\\\\\"] = null\\\\r\\\\n\\\\tconst finishReason = response.candidates?.[0]?.finishReason\\\\r\\\\n\\\\tif (finishReason) {\\\\r\\\\n\\\\t\\\\tswitch (finishReason) {\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"STOP\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tstop_reason = \\\\\\\"end_turn\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"MAX_TOKENS\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tstop_reason = \\\\\\\"max_tokens\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"SAFETY\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"RECITATION\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"OTHER\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tstop_reason = \\\\\\\"stop_sequence\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t// Add more cases if needed\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\treturn {\\\\r\\\\n\\\\t\\\\tid: `msg_${Date.now()}`, // Generate a unique ID\\\\r\\\\n\\\\t\\\\ttype: \\\\\\\"message\\\\\\\",\\\\r\\\\n\\\\t\\\\trole: \\\\\\\"assistant\\\\\\\",\\\\r\\\\n\\\\t\\\\tcontent,\\\\r\\\\n\\\\t\\\\tmodel: \\\\\\\"\\\\\\\",\\\\r\\\\n\\\\t\\\\tstop_reason,\\\\r\\\\n\\\\t\\\\tstop_sequence: null, // Gemini doesn't provide this information\\\\r\\\\n\\\\t\\\\tusage: {\\\\r\\\\n\\\\t\\\\t\\\\tinput_tokens: response.usageMetadata?.promptTokenCount ?? 0,\\\\r\\\\n\\\\t\\\\t\\\\toutput_tokens: response.usageMetadata?.candidatesTokenCount ?? 0,\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":5311,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.936Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.936Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":189,\\\"nonEmptyLines\\\":179,\\\"commentLines\\\":6,\\\"complexity\\\":36},\\\"dependencies\\\":[\\\"@anthropic-ai/sdk\\\"],\\\"quality\\\":{\\\"score\\\":-125,\\\"issues\\\":[],\\\"duplicateLines\\\":43,\\\"longLines\\\":5,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.312Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":189},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\api\\\\\\\\transform\\\\\\\\o1-format.ts\\\":{\\\"content\\\":\\\"import { Anthropic } from \\\\\\\"@anthropic-ai/sdk\\\\\\\"\\\\r\\\\nimport OpenAI from \\\\\\\"openai\\\\\\\"\\\\r\\\\n\\\\r\\\\nconst o1SystemPrompt = (systemPrompt: string) => `\\\\r\\\\n# System Prompt\\\\r\\\\n\\\\r\\\\n${systemPrompt}\\\\r\\\\n\\\\r\\\\n# Instructions for Formulating Your Response\\\\r\\\\n\\\\r\\\\nYou must respond to the user's request by using at least one tool call. When formulating your response, follow these guidelines:\\\\r\\\\n\\\\r\\\\n1. Begin your response with normal text, explaining your thoughts, analysis, or plan of action.\\\\r\\\\n2. If you need to use any tools, place ALL tool calls at the END of your message, after your normal text explanation.\\\\r\\\\n3. You can use multiple tool calls if needed, but they should all be grouped together at the end of your message.\\\\r\\\\n4. After placing the tool calls, do not add any additional normal text. The tool calls should be the final content in your message.\\\\r\\\\n\\\\r\\\\nHere's the general structure your responses should follow:\\\\r\\\\n\\\\r\\\\n\\\\\\\\`\\\\\\\\`\\\\\\\\`\\\\r\\\\n[Your normal text response explaining your thoughts and actions]\\\\r\\\\n\\\\r\\\\n[Tool Call 1]\\\\r\\\\n[Tool Call 2 if needed]\\\\r\\\\n[Tool Call 3 if needed]\\\\r\\\\n...\\\\r\\\\n\\\\\\\\`\\\\\\\\`\\\\\\\\`\\\\r\\\\n\\\\r\\\\nRemember:\\\\r\\\\n- Choose the most appropriate tool(s) based on the task and the tool descriptions provided.\\\\r\\\\n- Formulate your tool calls using the XML format specified for each tool.\\\\r\\\\n- Provide clear explanations in your normal text about what actions you're taking and why you're using particular tools.\\\\r\\\\n- Act as if the tool calls will be executed immediately after your message, and your next response will have access to their results.\\\\r\\\\n\\\\r\\\\n# Tool Descriptions and XML Formats\\\\r\\\\n\\\\r\\\\n1. execute_command:\\\\r\\\\n<execute_command>\\\\r\\\\n<command>Your command here</command>\\\\r\\\\n</execute_command>\\\\r\\\\nDescription: Execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory.\\\\r\\\\n\\\\r\\\\n2. list_files:\\\\r\\\\n<list_files>\\\\r\\\\n<path>Directory path here</path>\\\\r\\\\n<recursive>true or false (optional)</recursive>\\\\r\\\\n</list_files>\\\\r\\\\nDescription: List files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents.\\\\r\\\\n\\\\r\\\\n3. list_code_definition_names:\\\\r\\\\n<list_code_definition_names>\\\\r\\\\n<path>Directory path here</path>\\\\r\\\\n</list_code_definition_names>\\\\r\\\\nDescription: Lists definition names (classes, functions, methods, etc.) used in source code files at the top level of the specified directory. This tool provides insights into the codebase structure and important constructs, encapsulating high-level concepts and relationships that are crucial for understanding the overall architecture.\\\\r\\\\n\\\\r\\\\n4. search_files:\\\\r\\\\n<search_files>\\\\r\\\\n<path>Directory path here</path>\\\\r\\\\n<regex>Your regex pattern here</regex>\\\\r\\\\n<filePattern>Optional file pattern here</filePattern>\\\\r\\\\n</search_files>\\\\r\\\\nDescription: Perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.\\\\r\\\\n\\\\r\\\\n5. read_file:\\\\r\\\\n<read_file>\\\\r\\\\n<path>File path here</path>\\\\r\\\\n</read_file>\\\\r\\\\nDescription: Read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string.\\\\r\\\\n\\\\r\\\\n6. write_to_file:\\\\r\\\\n<write_to_file>\\\\r\\\\n<path>File path here</path>\\\\r\\\\n<content>\\\\r\\\\nYour file content here\\\\r\\\\n</content>\\\\r\\\\n</write_to_file>\\\\r\\\\nDescription: Write content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. Always provide the full intended content of the file, without any truncation. This tool will automatically create any directories needed to write the file.\\\\r\\\\n\\\\r\\\\n7. ask_followup_question:\\\\r\\\\n<ask_followup_question>\\\\r\\\\n<question>Your question here</question>\\\\r\\\\n</ask_followup_question>\\\\r\\\\nDescription: Ask the user a question to gather additional information needed to complete the task. This tool should be used when you encounter ambiguities, need clarification, or require more details to proceed effectively. It allows for interactive problem-solving by enabling direct communication with the user. Use this tool judiciously to maintain a balance between gathering necessary information and avoiding excessive back-and-forth.\\\\r\\\\n\\\\r\\\\n8. attempt_completion:\\\\r\\\\n<attempt_completion>\\\\r\\\\n<command>Optional command to demonstrate result</command>\\\\r\\\\n<result>\\\\r\\\\nYour final result description here\\\\r\\\\n</result>\\\\r\\\\n</attempt_completion>\\\\r\\\\nDescription: Once you've completed the task, use this tool to present the result to the user. They may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.\\\\r\\\\n\\\\r\\\\n# Examples\\\\r\\\\n\\\\r\\\\nHere are some examples of how to structure your responses with tool calls:\\\\r\\\\n\\\\r\\\\nExample 1: Using a single tool\\\\r\\\\n\\\\r\\\\nLet's run the test suite for our project. This will help us ensure that all our components are functioning correctly.\\\\r\\\\n\\\\r\\\\n<execute_command>\\\\r\\\\n<command>npm test</command>\\\\r\\\\n</execute_command>\\\\r\\\\n\\\\r\\\\nExample 2: Using multiple tools\\\\r\\\\n\\\\r\\\\nLet's create two new configuration files for the web application: one for the frontend and one for the backend.\\\\r\\\\n\\\\r\\\\n<write_to_file>\\\\r\\\\n<path>./frontend-config.json</path>\\\\r\\\\n<content>\\\\r\\\\n{\\\\r\\\\n \\\\\\\"apiEndpoint\\\\\\\": \\\\\\\"https://api.example.com\\\\\\\",\\\\r\\\\n \\\\\\\"theme\\\\\\\": {\\\\r\\\\n \\\\\\\"primaryColor\\\\\\\": \\\\\\\"#007bff\\\\\\\",\\\\r\\\\n \\\\\\\"secondaryColor\\\\\\\": \\\\\\\"#6c757d\\\\\\\",\\\\r\\\\n \\\\\\\"fontFamily\\\\\\\": \\\\\\\"Arial, sans-serif\\\\\\\"\\\\r\\\\n },\\\\r\\\\n \\\\\\\"features\\\\\\\": {\\\\r\\\\n \\\\\\\"darkMode\\\\\\\": true,\\\\r\\\\n \\\\\\\"notifications\\\\\\\": true,\\\\r\\\\n \\\\\\\"analytics\\\\\\\": false\\\\r\\\\n },\\\\r\\\\n \\\\\\\"version\\\\\\\": \\\\\\\"1.0.0\\\\\\\"\\\\r\\\\n}\\\\r\\\\n</content>\\\\r\\\\n</write_to_file>\\\\r\\\\n\\\\r\\\\n<write_to_file>\\\\r\\\\n<path>./backend-config.yaml</path>\\\\r\\\\n<content>\\\\r\\\\ndatabase:\\\\r\\\\n host: localhost\\\\r\\\\n port: 5432\\\\r\\\\n name: myapp_db\\\\r\\\\n user: admin\\\\r\\\\n\\\\r\\\\nserver:\\\\r\\\\n port: 3000\\\\r\\\\n environment: development\\\\r\\\\n logLevel: debug\\\\r\\\\n\\\\r\\\\nsecurity:\\\\r\\\\n jwtSecret: your-secret-key-here\\\\r\\\\n passwordSaltRounds: 10\\\\r\\\\n\\\\r\\\\ncaching:\\\\r\\\\n enabled: true\\\\r\\\\n provider: redis\\\\r\\\\n ttl: 3600\\\\r\\\\n\\\\r\\\\nexternalServices:\\\\r\\\\n emailProvider: sendgrid\\\\r\\\\n storageProvider: aws-s3\\\\r\\\\n</content>\\\\r\\\\n</write_to_file>\\\\r\\\\n\\\\r\\\\nExample 3: Asking a follow-up question\\\\r\\\\n\\\\r\\\\nI've analyzed the project structure, but I need more information to proceed. Let me ask the user for clarification.\\\\r\\\\n\\\\r\\\\n<ask_followup_question>\\\\r\\\\n<question>Which specific feature would you like me to implement in the example.py file?</question>\\\\r\\\\n</ask_followup_question>\\\\r\\\\n`\\\\r\\\\n\\\\r\\\\nexport function convertToO1Messages(\\\\r\\\\n\\\\topenAiMessages: OpenAI.Chat.ChatCompletionMessageParam[],\\\\r\\\\n\\\\tsystemPrompt: string,\\\\r\\\\n): OpenAI.Chat.ChatCompletionMessageParam[] {\\\\r\\\\n\\\\tconst toolsReplaced = openAiMessages.reduce((acc, message) => {\\\\r\\\\n\\\\t\\\\tif (message.role === \\\\\\\"tool\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t// Convert tool messages to user messages\\\\r\\\\n\\\\t\\\\t\\\\tacc.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\trole: \\\\\\\"user\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcontent: message.content || \\\\\\\"\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t} else if (message.role === \\\\\\\"assistant\\\\\\\" && message.tool_calls) {\\\\r\\\\n\\\\t\\\\t\\\\t// Convert tool calls to content and remove tool_calls\\\\r\\\\n\\\\t\\\\t\\\\tlet content = message.content || \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tmessage.tool_calls.forEach((toolCall) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (toolCall.type === \\\\\\\"function\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcontent += `\\\\\\\\nTool Call: ${toolCall.function.name}\\\\\\\\nArguments: ${toolCall.function.arguments}`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\tacc.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\trole: \\\\\\\"assistant\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcontent: content,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttool_calls: undefined,\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t// Keep other messages as they are\\\\r\\\\n\\\\t\\\\t\\\\tacc.push(message)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn acc\\\\r\\\\n\\\\t}, [] as OpenAI.Chat.ChatCompletionMessageParam[])\\\\r\\\\n\\\\r\\\\n\\\\t// Find the index of the last assistant message\\\\r\\\\n\\\\t// const lastAssistantIndex = findLastIndex(toolsReplaced, (message) => message.role === \\\\\\\"assistant\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\t// Create a new array to hold the modified messages\\\\r\\\\n\\\\tconst messagesWithSystemPrompt = [\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\trole: \\\\\\\"user\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tcontent: o1SystemPrompt(systemPrompt),\\\\r\\\\n\\\\t\\\\t} as OpenAI.Chat.ChatCompletionUserMessageParam,\\\\r\\\\n\\\\t\\\\t...toolsReplaced,\\\\r\\\\n\\\\t]\\\\r\\\\n\\\\r\\\\n\\\\t// If there's an assistant message, insert the system prompt after it\\\\r\\\\n\\\\t// if (lastAssistantIndex !== -1) {\\\\r\\\\n\\\\t// \\\\tconst insertIndex = lastAssistantIndex + 1\\\\r\\\\n\\\\t// \\\\tif (insertIndex < messagesWithSystemPrompt.length && messagesWithSystemPrompt[insertIndex].role === \\\\\\\"user\\\\\\\") {\\\\r\\\\n\\\\t// \\\\t\\\\tmessagesWithSystemPrompt.splice(insertIndex, 0, {\\\\r\\\\n\\\\t// \\\\t\\\\t\\\\trole: \\\\\\\"user\\\\\\\",\\\\r\\\\n\\\\t// \\\\t\\\\t\\\\tcontent: o1SystemPrompt(systemPrompt),\\\\r\\\\n\\\\t// \\\\t\\\\t})\\\\r\\\\n\\\\t// \\\\t}\\\\r\\\\n\\\\t// } else {\\\\r\\\\n\\\\t// \\\\t// If there were no assistant messages, prepend the system prompt\\\\r\\\\n\\\\t// \\\\tmessagesWithSystemPrompt.unshift({\\\\r\\\\n\\\\t// \\\\t\\\\trole: \\\\\\\"user\\\\\\\",\\\\r\\\\n\\\\t// \\\\t\\\\tcontent: o1SystemPrompt(systemPrompt),\\\\r\\\\n\\\\t// \\\\t})\\\\r\\\\n\\\\t// }\\\\r\\\\n\\\\r\\\\n\\\\treturn messagesWithSystemPrompt\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\ninterface ToolCall {\\\\r\\\\n\\\\ttool: string\\\\r\\\\n\\\\ttool_input: Record<string, string>\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nconst toolNames = [\\\\r\\\\n\\\\t\\\\\\\"execute_command\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"list_files\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"list_code_definition_names\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"search_files\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"read_file\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"write_to_file\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"ask_followup_question\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"attempt_completion\\\\\\\",\\\\r\\\\n]\\\\r\\\\n\\\\r\\\\nfunction parseAIResponse(response: string): {\\\\r\\\\n\\\\tnormalText: string\\\\r\\\\n\\\\ttoolCalls: ToolCall[]\\\\r\\\\n} {\\\\r\\\\n\\\\t// Create a regex pattern to match any tool call opening tag\\\\r\\\\n\\\\tconst toolCallPattern = new RegExp(`<(${toolNames.join(\\\\\\\"|\\\\\\\")})`, \\\\\\\"i\\\\\\\")\\\\r\\\\n\\\\tconst match = response.match(toolCallPattern)\\\\r\\\\n\\\\r\\\\n\\\\tif (!match) {\\\\r\\\\n\\\\t\\\\t// No tool calls found\\\\r\\\\n\\\\t\\\\treturn { normalText: response.trim(), toolCalls: [] }\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tconst toolCallStart = match.index!\\\\r\\\\n\\\\tconst normalText = response.slice(0, toolCallStart).trim()\\\\r\\\\n\\\\tconst toolCallsText = response.slice(toolCallStart)\\\\r\\\\n\\\\r\\\\n\\\\tconst toolCalls = parseToolCalls(toolCallsText)\\\\r\\\\n\\\\r\\\\n\\\\treturn { normalText, toolCalls }\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nfunction parseToolCalls(toolCallsText: string): ToolCall[] {\\\\r\\\\n\\\\tconst toolCalls: ToolCall[] = []\\\\r\\\\n\\\\r\\\\n\\\\tlet remainingText = toolCallsText\\\\r\\\\n\\\\r\\\\n\\\\twhile (remainingText.length > 0) {\\\\r\\\\n\\\\t\\\\tconst toolMatch = toolNames.find((tool) => new RegExp(`<${tool}`, \\\\\\\"i\\\\\\\").test(remainingText))\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (!toolMatch) {\\\\r\\\\n\\\\t\\\\t\\\\tbreak // No more tool calls found\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst startTag = `<${toolMatch}`\\\\r\\\\n\\\\t\\\\tconst endTag = `</${toolMatch}>`\\\\r\\\\n\\\\t\\\\tconst startIndex = remainingText.indexOf(startTag)\\\\r\\\\n\\\\t\\\\tconst endIndex = remainingText.indexOf(endTag, startIndex)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (endIndex === -1) {\\\\r\\\\n\\\\t\\\\t\\\\tbreak // Malformed XML, no closing tag found\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst toolCallContent = remainingText.slice(startIndex, endIndex + endTag.length)\\\\r\\\\n\\\\t\\\\tremainingText = remainingText.slice(endIndex + endTag.length).trim()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst toolCall = parseToolCall(toolMatch, toolCallContent)\\\\r\\\\n\\\\t\\\\tif (toolCall) {\\\\r\\\\n\\\\t\\\\t\\\\ttoolCalls.push(toolCall)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\treturn toolCalls\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nfunction parseToolCall(toolName: string, content: string): ToolCall | null {\\\\r\\\\n\\\\tconst tool_input: Record<string, string> = {}\\\\r\\\\n\\\\r\\\\n\\\\t// Remove the outer tool tags\\\\r\\\\n\\\\tconst innerContent = content.replace(new RegExp(`^<${toolName}>|</${toolName}>$`, \\\\\\\"g\\\\\\\"), \\\\\\\"\\\\\\\").trim()\\\\r\\\\n\\\\r\\\\n\\\\t// Parse nested XML elements\\\\r\\\\n\\\\tconst paramRegex = /<(\\\\\\\\w+)>([\\\\\\\\s\\\\\\\\S]*?)<\\\\\\\\/\\\\\\\\1>/gs\\\\r\\\\n\\\\tlet match\\\\r\\\\n\\\\r\\\\n\\\\twhile ((match = paramRegex.exec(innerContent)) !== null) {\\\\r\\\\n\\\\t\\\\tconst [, paramName, paramValue] = match\\\\r\\\\n\\\\t\\\\t// Preserve newlines and trim only leading/trailing whitespace\\\\r\\\\n\\\\t\\\\ttool_input[paramName] = paramValue.replace(/^\\\\\\\\s+|\\\\\\\\s+$/g, \\\\\\\"\\\\\\\")\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Validate required parameters\\\\r\\\\n\\\\tif (!validateToolInput(toolName, tool_input)) {\\\\r\\\\n\\\\t\\\\tconsole.error(`Invalid tool call for ${toolName}:`, content)\\\\r\\\\n\\\\t\\\\treturn null\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\treturn { tool: toolName, tool_input }\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nfunction validateToolInput(toolName: string, tool_input: Record<string, string>): boolean {\\\\r\\\\n\\\\tswitch (toolName) {\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"execute_command\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn \\\\\\\"command\\\\\\\" in tool_input\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"read_file\\\\\\\":\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"list_code_definition_names\\\\\\\":\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"list_files\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn \\\\\\\"path\\\\\\\" in tool_input\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"search_files\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn \\\\\\\"path\\\\\\\" in tool_input && \\\\\\\"regex\\\\\\\" in tool_input\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"write_to_file\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn \\\\\\\"path\\\\\\\" in tool_input && \\\\\\\"content\\\\\\\" in tool_input\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"ask_followup_question\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn \\\\\\\"question\\\\\\\" in tool_input\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"attempt_completion\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn \\\\\\\"result\\\\\\\" in tool_input\\\\r\\\\n\\\\t\\\\tdefault:\\\\r\\\\n\\\\t\\\\t\\\\treturn false\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n// Example usage:\\\\r\\\\n// const aiResponse = `Here's my analysis of the situation...\\\\r\\\\n\\\\r\\\\n// <execute_command>\\\\r\\\\n// <command>ls -la</command>\\\\r\\\\n// </execute_command>\\\\r\\\\n\\\\r\\\\n// <write_to_file>\\\\r\\\\n// <path>./example.txt</path>\\\\r\\\\n// <content>Hello, World!</content>\\\\r\\\\n// </write_to_file>`;\\\\r\\\\n//\\\\r\\\\n// const { normalText, toolCalls } = parseAIResponse(aiResponse);\\\\r\\\\n// console.log(normalText);\\\\r\\\\n// console.log(toolCalls);\\\\r\\\\n\\\\r\\\\n// Convert OpenAI response to Anthropic format\\\\r\\\\nexport function convertO1ResponseToAnthropicMessage(\\\\r\\\\n\\\\tcompletion: OpenAI.Chat.Completions.ChatCompletion,\\\\r\\\\n): Anthropic.Messages.Message {\\\\r\\\\n\\\\tconst openAiMessage = completion.choices[0].message\\\\r\\\\n\\\\tconst { normalText, toolCalls } = parseAIResponse(openAiMessage.content || \\\\\\\"\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\tconst anthropicMessage: Anthropic.Messages.Message = {\\\\r\\\\n\\\\t\\\\tid: completion.id,\\\\r\\\\n\\\\t\\\\ttype: \\\\\\\"message\\\\\\\",\\\\r\\\\n\\\\t\\\\trole: openAiMessage.role, // always \\\\\\\"assistant\\\\\\\"\\\\r\\\\n\\\\t\\\\tcontent: [\\\\r\\\\n\\\\t\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttext: normalText,\\\\r\\\\n\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\tmodel: completion.model,\\\\r\\\\n\\\\t\\\\tstop_reason: (() => {\\\\r\\\\n\\\\t\\\\t\\\\tswitch (completion.choices[0].finish_reason) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"stop\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn \\\\\\\"end_turn\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"length\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn \\\\\\\"max_tokens\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"tool_calls\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn \\\\\\\"tool_use\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"content_filter\\\\\\\": // Anthropic doesn't have an exact equivalent\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tdefault:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn null\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t})(),\\\\r\\\\n\\\\t\\\\tstop_sequence: null, // which custom stop_sequence was generated, if any (not applicable if you don't use stop_sequence)\\\\r\\\\n\\\\t\\\\tusage: {\\\\r\\\\n\\\\t\\\\t\\\\tinput_tokens: completion.usage?.prompt_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\toutput_tokens: completion.usage?.completion_tokens || 0,\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tif (toolCalls.length > 0) {\\\\r\\\\n\\\\t\\\\tanthropicMessage.content.push(\\\\r\\\\n\\\\t\\\\t\\\\t...toolCalls.map((toolCall: ToolCall, index: number): Anthropic.ToolUseBlock => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"tool_use\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tid: `call_${index}_${Date.now()}`, // Generate a unique ID for each tool call\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tname: toolCall.tool,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tinput: toolCall.tool_input,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}),\\\\r\\\\n\\\\t\\\\t)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\treturn anthropicMessage\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n// Example usage:\\\\r\\\\n// const openAICompletion = {\\\\r\\\\n// id: \\\\\\\"cmpl-123\\\\\\\",\\\\r\\\\n// choices: [{\\\\r\\\\n// message: {\\\\r\\\\n// role: \\\\\\\"assistant\\\\\\\",\\\\r\\\\n// content: \\\\\\\"Here's my analysis...\\\\\\\\n\\\\\\\\n<execute_command>\\\\\\\\n <command>ls -la</command>\\\\\\\\n</execute_command>\\\\\\\"\\\\r\\\\n// },\\\\r\\\\n// finish_reason: \\\\\\\"stop\\\\\\\"\\\\r\\\\n// }],\\\\r\\\\n// model: \\\\\\\"gpt-3.5-turbo\\\\\\\",\\\\r\\\\n// usage: { prompt_tokens: 50, completion_tokens: 100 }\\\\r\\\\n// };\\\\r\\\\n// const anthropicMessage = convertO1ResponseToAnthropicMessage(openAICompletion);\\\\r\\\\n// console.log(anthropicMessage);\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":14688,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.936Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.936Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":433,\\\"nonEmptyLines\\\":362,\\\"commentLines\\\":57,\\\"complexity\\\":43},\\\"dependencies\\\":[\\\"@anthropic-ai/sdk\\\",\\\"openai\\\"],\\\"quality\\\":{\\\"score\\\":-192,\\\"issues\\\":[],\\\"duplicateLines\\\":50,\\\"longLines\\\":21,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.313Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":433},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\api\\\\\\\\transform\\\\\\\\openai-format.ts\\\":{\\\"content\\\":\\\"import { Anthropic } from \\\\\\\"@anthropic-ai/sdk\\\\\\\"\\\\r\\\\nimport OpenAI from \\\\\\\"openai\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport function convertToOpenAiMessages(\\\\r\\\\n\\\\tanthropicMessages: Anthropic.Messages.MessageParam[],\\\\r\\\\n): OpenAI.Chat.ChatCompletionMessageParam[] {\\\\r\\\\n\\\\tconst openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = []\\\\r\\\\n\\\\r\\\\n\\\\tfor (const anthropicMessage of anthropicMessages) {\\\\r\\\\n\\\\t\\\\tif (typeof anthropicMessage.content === \\\\\\\"string\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\topenAiMessages.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\trole: anthropicMessage.role,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcontent: anthropicMessage.content,\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t// image_url.url is base64 encoded image data\\\\r\\\\n\\\\t\\\\t\\\\t// ensure it contains the content-type of the image: data:image/png;base64,\\\\r\\\\n\\\\t\\\\t\\\\t/*\\\\r\\\\n { role: \\\\\\\"user\\\\\\\", content: \\\\\\\"\\\\\\\" | { type: \\\\\\\"text\\\\\\\", text: string } | { type: \\\\\\\"image_url\\\\\\\", image_url: { url: string } } },\\\\r\\\\n // content required unless tool_calls is present\\\\r\\\\n { role: \\\\\\\"assistant\\\\\\\", content?: \\\\\\\"\\\\\\\" | null, tool_calls?: [{ id: \\\\\\\"\\\\\\\", function: { name: \\\\\\\"\\\\\\\", arguments: \\\\\\\"\\\\\\\" }, type: \\\\\\\"function\\\\\\\" }] },\\\\r\\\\n { role: \\\\\\\"tool\\\\\\\", tool_call_id: \\\\\\\"\\\\\\\", content: \\\\\\\"\\\\\\\"}\\\\r\\\\n */\\\\r\\\\n\\\\t\\\\t\\\\tif (anthropicMessage.role === \\\\\\\"user\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst { nonToolMessages, toolMessages } = anthropicMessage.content.reduce<{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tnonToolMessages: (Anthropic.TextBlockParam | Anthropic.ImageBlockParam)[]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttoolMessages: Anthropic.ToolResultBlockParam[]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}>(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t(acc, part) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (part.type === \\\\\\\"tool_result\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tacc.toolMessages.push(part)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else if (part.type === \\\\\\\"text\\\\\\\" || part.type === \\\\\\\"image\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tacc.nonToolMessages.push(part)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} // user cannot send tool_use messages\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn acc\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t{ nonToolMessages: [], toolMessages: [] },\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Process tool result messages FIRST since they must follow the tool use messages\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlet toolResultImages: Anthropic.Messages.ImageBlockParam[] = []\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttoolMessages.forEach((toolMessage) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// The Anthropic SDK allows tool results to be a string or an array of text and image blocks, enabling rich and structured content. In contrast, the OpenAI SDK only supports tool results as a single string, so we map the Anthropic tool result parts into one concatenated string to maintain compatibility.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tlet content: string\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (typeof toolMessage.content === \\\\\\\"string\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontent = toolMessage.content\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontent =\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttoolMessage.content\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t?.map((part) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (part.type === \\\\\\\"image\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttoolResultImages.push(part)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn \\\\\\\"(see following user message for image)\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn part.text\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.join(\\\\\\\"\\\\\\\\n\\\\\\\") ?? \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\topenAiMessages.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\trole: \\\\\\\"tool\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttool_call_id: toolMessage.tool_use_id,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontent: content,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// If tool results contain images, send as a separate user message\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// I ran into an issue where if I gave feedback for one of many tool uses, the request would fail.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// \\\\\\\"Messages following `tool_use` blocks must begin with a matching number of `tool_result` blocks.\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Therefore we need to send these images after the tool result messages\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// NOTE: it's actually okay to have multiple user messages in a row, the model will treat them as a continuation of the same input (this way works better than combining them into one message, since the tool result specifically mentions (see following user message for image)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// UPDATE v2.0: we don't use tools anymore, but if we did it's important to note that the openrouter prompt caching mechanism requires one user message at a time, so we would need to add these images to the user content array instead.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// if (toolResultImages.length > 0) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// \\\\topenAiMessages.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// \\\\t\\\\trole: \\\\\\\"user\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// \\\\t\\\\tcontent: toolResultImages.map((part) => ({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// \\\\t\\\\t\\\\ttype: \\\\\\\"image_url\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// \\\\t\\\\t\\\\timage_url: { url: `data:${part.source.media_type};base64,${part.source.data}` },\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// \\\\t\\\\t})),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// \\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// }\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Process non-tool messages\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (nonToolMessages.length > 0) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\topenAiMessages.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\trole: \\\\\\\"user\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontent: nonToolMessages.map((part) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (part.type === \\\\\\\"image\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"image_url\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\timage_url: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\turl: `data:${part.source.media_type};base64,${part.source.data}`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn { type: \\\\\\\"text\\\\\\\", text: part.text }\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t} else if (anthropicMessage.role === \\\\\\\"assistant\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst { nonToolMessages, toolMessages } = anthropicMessage.content.reduce<{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tnonToolMessages: (Anthropic.TextBlockParam | Anthropic.ImageBlockParam)[]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttoolMessages: Anthropic.ToolUseBlockParam[]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}>(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t(acc, part) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (part.type === \\\\\\\"tool_use\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tacc.toolMessages.push(part)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else if (part.type === \\\\\\\"text\\\\\\\" || part.type === \\\\\\\"image\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tacc.nonToolMessages.push(part)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} // assistant cannot send tool_result messages\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn acc\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t{ nonToolMessages: [], toolMessages: [] },\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Process non-tool messages\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlet content: string | undefined\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (nonToolMessages.length > 0) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcontent = nonToolMessages\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.map((part) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (part.type === \\\\\\\"image\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn \\\\\\\"\\\\\\\" // impossible as the assistant cannot send images\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn part.text\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.join(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Process tool use messages\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlet tool_calls: OpenAI.Chat.ChatCompletionMessageToolCall[] = toolMessages.map((toolMessage) => ({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tid: toolMessage.id,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"function\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tfunction: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tname: toolMessage.name,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// json string\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\targuments: JSON.stringify(toolMessage.input),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}))\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\topenAiMessages.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\trole: \\\\\\\"assistant\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcontent,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Cannot be an empty array. API expects an array with minimum length 1, and will respond with an error if it's empty\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttool_calls: tool_calls.length > 0 ? tool_calls : undefined,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\treturn openAiMessages\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n// Convert OpenAI response to Anthropic format\\\\r\\\\nexport function convertToAnthropicMessage(completion: OpenAI.Chat.Completions.ChatCompletion): Anthropic.Messages.Message {\\\\r\\\\n\\\\tconst openAiMessage = completion.choices[0].message\\\\r\\\\n\\\\tconst anthropicMessage: Anthropic.Messages.Message = {\\\\r\\\\n\\\\t\\\\tid: completion.id,\\\\r\\\\n\\\\t\\\\ttype: \\\\\\\"message\\\\\\\",\\\\r\\\\n\\\\t\\\\trole: openAiMessage.role, // always \\\\\\\"assistant\\\\\\\"\\\\r\\\\n\\\\t\\\\tcontent: [\\\\r\\\\n\\\\t\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttext: openAiMessage.content || \\\\\\\"\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\tmodel: completion.model,\\\\r\\\\n\\\\t\\\\tstop_reason: (() => {\\\\r\\\\n\\\\t\\\\t\\\\tswitch (completion.choices[0].finish_reason) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"stop\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn \\\\\\\"end_turn\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"length\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn \\\\\\\"max_tokens\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"tool_calls\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn \\\\\\\"tool_use\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"content_filter\\\\\\\": // Anthropic doesn't have an exact equivalent\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tdefault:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn null\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t})(),\\\\r\\\\n\\\\t\\\\tstop_sequence: null, // which custom stop_sequence was generated, if any (not applicable if you don't use stop_sequence)\\\\r\\\\n\\\\t\\\\tusage: {\\\\r\\\\n\\\\t\\\\t\\\\tinput_tokens: completion.usage?.prompt_tokens || 0,\\\\r\\\\n\\\\t\\\\t\\\\toutput_tokens: completion.usage?.completion_tokens || 0,\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tif (openAiMessage.tool_calls && openAiMessage.tool_calls.length > 0) {\\\\r\\\\n\\\\t\\\\tanthropicMessage.content.push(\\\\r\\\\n\\\\t\\\\t\\\\t...openAiMessage.tool_calls.map((toolCall): Anthropic.ToolUseBlock => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlet parsedInput = {}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tparsedInput = JSON.parse(toolCall.function.arguments || \\\\\\\"{}\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Failed to parse tool arguments:\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"tool_use\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tid: toolCall.id,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tname: toolCall.function.name,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tinput: parsedInput,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}),\\\\r\\\\n\\\\t\\\\t)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\treturn anthropicMessage\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":7651,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.936Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.936Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":207,\\\"nonEmptyLines\\\":194,\\\"commentLines\\\":27,\\\"complexity\\\":45},\\\"dependencies\\\":[\\\"@anthropic-ai/sdk\\\",\\\"openai\\\"],\\\"quality\\\":{\\\"score\\\":-167,\\\"issues\\\":[],\\\"duplicateLines\\\":49,\\\"longLines\\\":11,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.316Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":207},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\api\\\\\\\\transform\\\\\\\\stream.ts\\\":{\\\"content\\\":\\\"export type ApiStream = AsyncGenerator<ApiStreamChunk>\\\\r\\\\nexport type ApiStreamChunk = ApiStreamTextChunk | ApiStreamUsageChunk\\\\r\\\\n\\\\r\\\\nexport interface ApiStreamTextChunk {\\\\r\\\\n\\\\ttype: \\\\\\\"text\\\\\\\"\\\\r\\\\n\\\\ttext: string\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport interface ApiStreamUsageChunk {\\\\r\\\\n\\\\ttype: \\\\\\\"usage\\\\\\\"\\\\r\\\\n\\\\tinputTokens: number\\\\r\\\\n\\\\toutputTokens: number\\\\r\\\\n\\\\tcacheWriteTokens?: number\\\\r\\\\n\\\\tcacheReadTokens?: number\\\\r\\\\n\\\\ttotalCost?: number // openrouter\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":397,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.839Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.839Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":17,\\\"nonEmptyLines\\\":14,\\\"commentLines\\\":0,\\\"complexity\\\":4},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":95,\\\"issues\\\":[],\\\"duplicateLines\\\":1,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.318Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":17},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\core\\\\\\\\assistant-message\\\\\\\\diff.ts\\\":{\\\"content\\\":\\\"/**\\\\r\\\\n * Attempts a line-trimmed fallback match for the given search content in the original content.\\\\r\\\\n * It tries to match `searchContent` lines against a block of lines in `originalContent` starting\\\\r\\\\n * from `lastProcessedIndex`. Lines are matched by trimming leading/trailing whitespace and ensuring\\\\r\\\\n * they are identical afterwards.\\\\r\\\\n *\\\\r\\\\n * Returns [matchIndexStart, matchIndexEnd] if found, or false if not found.\\\\r\\\\n */\\\\r\\\\nfunction lineTrimmedFallbackMatch(originalContent: string, searchContent: string, startIndex: number): [number, number] | false {\\\\r\\\\n\\\\t// Split both contents into lines\\\\r\\\\n\\\\tconst originalLines = originalContent.split(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\tconst searchLines = searchContent.split(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\t// Trim trailing empty line if exists (from the trailing \\\\\\\\n in searchContent)\\\\r\\\\n\\\\tif (searchLines[searchLines.length - 1] === \\\\\\\"\\\\\\\") {\\\\r\\\\n\\\\t\\\\tsearchLines.pop()\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Find the line number where startIndex falls\\\\r\\\\n\\\\tlet startLineNum = 0\\\\r\\\\n\\\\tlet currentIndex = 0\\\\r\\\\n\\\\twhile (currentIndex < startIndex && startLineNum < originalLines.length) {\\\\r\\\\n\\\\t\\\\tcurrentIndex += originalLines[startLineNum].length + 1 // +1 for \\\\\\\\n\\\\r\\\\n\\\\t\\\\tstartLineNum++\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// For each possible starting position in original content\\\\r\\\\n\\\\tfor (let i = startLineNum; i <= originalLines.length - searchLines.length; i++) {\\\\r\\\\n\\\\t\\\\tlet matches = true\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Try to match all search lines from this position\\\\r\\\\n\\\\t\\\\tfor (let j = 0; j < searchLines.length; j++) {\\\\r\\\\n\\\\t\\\\t\\\\tconst originalTrimmed = originalLines[i + j].trim()\\\\r\\\\n\\\\t\\\\t\\\\tconst searchTrimmed = searchLines[j].trim()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tif (originalTrimmed !== searchTrimmed) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tmatches = false\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// If we found a match, calculate the exact character positions\\\\r\\\\n\\\\t\\\\tif (matches) {\\\\r\\\\n\\\\t\\\\t\\\\t// Find start character index\\\\r\\\\n\\\\t\\\\t\\\\tlet matchStartIndex = 0\\\\r\\\\n\\\\t\\\\t\\\\tfor (let k = 0; k < i; k++) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tmatchStartIndex += originalLines[k].length + 1 // +1 for \\\\\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// Find end character index\\\\r\\\\n\\\\t\\\\t\\\\tlet matchEndIndex = matchStartIndex\\\\r\\\\n\\\\t\\\\t\\\\tfor (let k = 0; k < searchLines.length; k++) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tmatchEndIndex += originalLines[i + k].length + 1 // +1 for \\\\\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\treturn [matchStartIndex, matchEndIndex]\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\treturn false\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n/**\\\\r\\\\n * Attempts to match blocks of code by using the first and last lines as anchors.\\\\r\\\\n * This is a third-tier fallback strategy that helps match blocks where we can identify\\\\r\\\\n * the correct location by matching the beginning and end, even if the exact content\\\\r\\\\n * differs slightly.\\\\r\\\\n *\\\\r\\\\n * The matching strategy:\\\\r\\\\n * 1. Only attempts to match blocks of 3 or more lines to avoid false positives\\\\r\\\\n * 2. Extracts from the search content:\\\\r\\\\n * - First line as the \\\\\\\"start anchor\\\\\\\"\\\\r\\\\n * - Last line as the \\\\\\\"end anchor\\\\\\\"\\\\r\\\\n * 3. For each position in the original content:\\\\r\\\\n * - Checks if the next line matches the start anchor\\\\r\\\\n * - If it does, jumps ahead by the search block size\\\\r\\\\n * - Checks if that line matches the end anchor\\\\r\\\\n * - All comparisons are done after trimming whitespace\\\\r\\\\n *\\\\r\\\\n * This approach is particularly useful for matching blocks of code where:\\\\r\\\\n * - The exact content might have minor differences\\\\r\\\\n * - The beginning and end of the block are distinctive enough to serve as anchors\\\\r\\\\n * - The overall structure (number of lines) remains the same\\\\r\\\\n *\\\\r\\\\n * @param originalContent - The full content of the original file\\\\r\\\\n * @param searchContent - The content we're trying to find in the original file\\\\r\\\\n * @param startIndex - The character index in originalContent where to start searching\\\\r\\\\n * @returns A tuple of [startIndex, endIndex] if a match is found, false otherwise\\\\r\\\\n */\\\\r\\\\nfunction blockAnchorFallbackMatch(originalContent: string, searchContent: string, startIndex: number): [number, number] | false {\\\\r\\\\n\\\\tconst originalLines = originalContent.split(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\tconst searchLines = searchContent.split(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\t// Only use this approach for blocks of 3+ lines\\\\r\\\\n\\\\tif (searchLines.length < 3) {\\\\r\\\\n\\\\t\\\\treturn false\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Trim trailing empty line if exists\\\\r\\\\n\\\\tif (searchLines[searchLines.length - 1] === \\\\\\\"\\\\\\\") {\\\\r\\\\n\\\\t\\\\tsearchLines.pop()\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tconst firstLineSearch = searchLines[0].trim()\\\\r\\\\n\\\\tconst lastLineSearch = searchLines[searchLines.length - 1].trim()\\\\r\\\\n\\\\tconst searchBlockSize = searchLines.length\\\\r\\\\n\\\\r\\\\n\\\\t// Find the line number where startIndex falls\\\\r\\\\n\\\\tlet startLineNum = 0\\\\r\\\\n\\\\tlet currentIndex = 0\\\\r\\\\n\\\\twhile (currentIndex < startIndex && startLineNum < originalLines.length) {\\\\r\\\\n\\\\t\\\\tcurrentIndex += originalLines[startLineNum].length + 1\\\\r\\\\n\\\\t\\\\tstartLineNum++\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Look for matching start and end anchors\\\\r\\\\n\\\\tfor (let i = startLineNum; i <= originalLines.length - searchBlockSize; i++) {\\\\r\\\\n\\\\t\\\\t// Check if first line matches\\\\r\\\\n\\\\t\\\\tif (originalLines[i].trim() !== firstLineSearch) {\\\\r\\\\n\\\\t\\\\t\\\\tcontinue\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Check if last line matches at the expected position\\\\r\\\\n\\\\t\\\\tif (originalLines[i + searchBlockSize - 1].trim() !== lastLineSearch) {\\\\r\\\\n\\\\t\\\\t\\\\tcontinue\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Calculate exact character positions\\\\r\\\\n\\\\t\\\\tlet matchStartIndex = 0\\\\r\\\\n\\\\t\\\\tfor (let k = 0; k < i; k++) {\\\\r\\\\n\\\\t\\\\t\\\\tmatchStartIndex += originalLines[k].length + 1\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet matchEndIndex = matchStartIndex\\\\r\\\\n\\\\t\\\\tfor (let k = 0; k < searchBlockSize; k++) {\\\\r\\\\n\\\\t\\\\t\\\\tmatchEndIndex += originalLines[i + k].length + 1\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\treturn [matchStartIndex, matchEndIndex]\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\treturn false\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n/**\\\\r\\\\n * This function reconstructs the file content by applying a streamed diff (in a\\\\r\\\\n * specialized SEARCH/REPLACE block format) to the original file content. It is designed\\\\r\\\\n * to handle both incremental updates and the final resulting file after all chunks have\\\\r\\\\n * been processed.\\\\r\\\\n *\\\\r\\\\n * The diff format is a custom structure that uses three markers to define changes:\\\\r\\\\n *\\\\r\\\\n * <<<<<<< SEARCH\\\\r\\\\n * [Exact content to find in the original file]\\\\r\\\\n * =======\\\\r\\\\n * [Content to replace with]\\\\r\\\\n * >>>>>>> REPLACE\\\\r\\\\n *\\\\r\\\\n * Behavior and Assumptions:\\\\r\\\\n * 1. The file is processed chunk-by-chunk. Each chunk of `diffContent` may contain\\\\r\\\\n * partial or complete SEARCH/REPLACE blocks. By calling this function with each\\\\r\\\\n * incremental chunk (with `isFinal` indicating the last chunk), the final reconstructed\\\\r\\\\n * file content is produced.\\\\r\\\\n *\\\\r\\\\n * 2. Matching Strategy (in order of attempt):\\\\r\\\\n * a. Exact Match: First attempts to find the exact SEARCH block text in the original file\\\\r\\\\n * b. Line-Trimmed Match: Falls back to line-by-line comparison ignoring leading/trailing whitespace\\\\r\\\\n * c. Block Anchor Match: For blocks of 3+ lines, tries to match using first/last lines as anchors\\\\r\\\\n * If all matching strategies fail, an error is thrown.\\\\r\\\\n *\\\\r\\\\n * 3. Empty SEARCH Section:\\\\r\\\\n * - If SEARCH is empty and the original file is empty, this indicates creating a new file\\\\r\\\\n * (pure insertion).\\\\r\\\\n * - If SEARCH is empty and the original file is not empty, this indicates a complete\\\\r\\\\n * file replacement (the entire original content is considered matched and replaced).\\\\r\\\\n *\\\\r\\\\n * 4. Applying Changes:\\\\r\\\\n * - Before encountering the \\\\\\\"=======\\\\\\\" marker, lines are accumulated as search content.\\\\r\\\\n * - After \\\\\\\"=======\\\\\\\" and before \\\\\\\">>>>>>> REPLACE\\\\\\\", lines are accumulated as replacement content.\\\\r\\\\n * - Once the block is complete (\\\\\\\">>>>>>> REPLACE\\\\\\\"), the matched section in the original\\\\r\\\\n * file is replaced with the accumulated replacement lines, and the position in the original\\\\r\\\\n * file is advanced.\\\\r\\\\n *\\\\r\\\\n * 5. Incremental Output:\\\\r\\\\n * - As soon as the match location is found and we are in the REPLACE section, each new\\\\r\\\\n * replacement line is appended to the result so that partial updates can be viewed\\\\r\\\\n * incrementally.\\\\r\\\\n *\\\\r\\\\n * 6. Partial Markers:\\\\r\\\\n * - If the final line of the chunk looks like it might be part of a marker but is not one\\\\r\\\\n * of the known markers, it is removed. This prevents incomplete or partial markers\\\\r\\\\n * from corrupting the output.\\\\r\\\\n *\\\\r\\\\n * 7. Finalization:\\\\r\\\\n * - Once all chunks have been processed (when `isFinal` is true), any remaining original\\\\r\\\\n * content after the last replaced section is appended to the result.\\\\r\\\\n * - Trailing newlines are not forcibly added. The code tries to output exactly what is specified.\\\\r\\\\n *\\\\r\\\\n * Errors:\\\\r\\\\n * - If the search block cannot be matched using any of the available matching strategies,\\\\r\\\\n * an error is thrown.\\\\r\\\\n */\\\\r\\\\nexport async function constructNewFileContent(diffContent: string, originalContent: string, isFinal: boolean): Promise<string> {\\\\r\\\\n\\\\tlet result = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\tlet lastProcessedIndex = 0\\\\r\\\\n\\\\r\\\\n\\\\tlet currentSearchContent = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\tlet currentReplaceContent = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\tlet inSearch = false\\\\r\\\\n\\\\tlet inReplace = false\\\\r\\\\n\\\\r\\\\n\\\\tlet searchMatchIndex = -1\\\\r\\\\n\\\\tlet searchEndIndex = -1\\\\r\\\\n\\\\r\\\\n\\\\tlet lines = diffContent.split(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\t// If the last line looks like a partial marker but isn't recognized,\\\\r\\\\n\\\\t// remove it because it might be incomplete.\\\\r\\\\n\\\\tconst lastLine = lines[lines.length - 1]\\\\r\\\\n\\\\tif (\\\\r\\\\n\\\\t\\\\tlines.length > 0 &&\\\\r\\\\n\\\\t\\\\t(lastLine.startsWith(\\\\\\\"<\\\\\\\") || lastLine.startsWith(\\\\\\\"=\\\\\\\") || lastLine.startsWith(\\\\\\\">\\\\\\\")) &&\\\\r\\\\n\\\\t\\\\tlastLine !== \\\\\\\"<<<<<<< SEARCH\\\\\\\" &&\\\\r\\\\n\\\\t\\\\tlastLine !== \\\\\\\"=======\\\\\\\" &&\\\\r\\\\n\\\\t\\\\tlastLine !== \\\\\\\">>>>>>> REPLACE\\\\\\\"\\\\r\\\\n\\\\t) {\\\\r\\\\n\\\\t\\\\tlines.pop()\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tfor (const line of lines) {\\\\r\\\\n\\\\t\\\\tif (line === \\\\\\\"<<<<<<< SEARCH\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\tinSearch = true\\\\r\\\\n\\\\t\\\\t\\\\tcurrentSearchContent = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tcurrentReplaceContent = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tcontinue\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (line === \\\\\\\"=======\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\tinSearch = false\\\\r\\\\n\\\\t\\\\t\\\\tinReplace = true\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// Remove trailing linebreak for adding the === marker\\\\r\\\\n\\\\t\\\\t\\\\t// if (currentSearchContent.endsWith(\\\\\\\"\\\\\\\\r\\\\\\\\n\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\tcurrentSearchContent = currentSearchContent.slice(0, -2)\\\\r\\\\n\\\\t\\\\t\\\\t// } else if (currentSearchContent.endsWith(\\\\\\\"\\\\\\\\n\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\tcurrentSearchContent = currentSearchContent.slice(0, -1)\\\\r\\\\n\\\\t\\\\t\\\\t// }\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tif (!currentSearchContent) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Empty search block\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (originalContent.length === 0) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// New file scenario: nothing to match, just start inserting\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tsearchMatchIndex = 0\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tsearchEndIndex = 0\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Complete file replacement scenario: treat the entire file as matched\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tsearchMatchIndex = 0\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tsearchEndIndex = originalContent.length\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Add check for inefficient full-file search\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// if (currentSearchContent.trim() === originalContent.trim()) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// \\\\tthrow new Error(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// \\\\t\\\\t\\\\\\\"The SEARCH block contains the entire file content. Please either:\\\\\\\\n\\\\\\\" +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// \\\\t\\\\t\\\\t\\\\\\\"1. Use an empty SEARCH block to replace the entire file, or\\\\\\\\n\\\\\\\" +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// \\\\t\\\\t\\\\t\\\\\\\"2. Make focused changes to specific parts of the file that need modification.\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// \\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// }\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Exact search match scenario\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst exactIndex = originalContent.indexOf(currentSearchContent, lastProcessedIndex)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (exactIndex !== -1) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tsearchMatchIndex = exactIndex\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tsearchEndIndex = exactIndex + currentSearchContent.length\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Attempt fallback line-trimmed matching\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst lineMatch = lineTrimmedFallbackMatch(originalContent, currentSearchContent, lastProcessedIndex)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (lineMatch) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t;[searchMatchIndex, searchEndIndex] = lineMatch\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// Try block anchor fallback for larger blocks\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst blockMatch = blockAnchorFallbackMatch(originalContent, currentSearchContent, lastProcessedIndex)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (blockMatch) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t;[searchMatchIndex, searchEndIndex] = blockMatch\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthrow new Error(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`The SEARCH block:\\\\\\\\n${currentSearchContent.trimEnd()}\\\\\\\\n...does not match anything in the file.`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// Output everything up to the match location\\\\r\\\\n\\\\t\\\\t\\\\tresult += originalContent.slice(lastProcessedIndex, searchMatchIndex)\\\\r\\\\n\\\\t\\\\t\\\\tcontinue\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (line === \\\\\\\">>>>>>> REPLACE\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t// Finished one replace block\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// // Remove the artificially added linebreak in the last line of the REPLACE block\\\\r\\\\n\\\\t\\\\t\\\\t// if (result.endsWith(\\\\\\\"\\\\\\\\r\\\\\\\\n\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\tresult = result.slice(0, -2)\\\\r\\\\n\\\\t\\\\t\\\\t// } else if (result.endsWith(\\\\\\\"\\\\\\\\n\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\tresult = result.slice(0, -1)\\\\r\\\\n\\\\t\\\\t\\\\t// }\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// Advance lastProcessedIndex to after the matched section\\\\r\\\\n\\\\t\\\\t\\\\tlastProcessedIndex = searchEndIndex\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// Reset for next block\\\\r\\\\n\\\\t\\\\t\\\\tinSearch = false\\\\r\\\\n\\\\t\\\\t\\\\tinReplace = false\\\\r\\\\n\\\\t\\\\t\\\\tcurrentSearchContent = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tcurrentReplaceContent = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tsearchMatchIndex = -1\\\\r\\\\n\\\\t\\\\t\\\\tsearchEndIndex = -1\\\\r\\\\n\\\\t\\\\t\\\\tcontinue\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Accumulate content for search or replace\\\\r\\\\n\\\\t\\\\t// (currentReplaceContent is not being used for anything right now since we directly append to result.)\\\\r\\\\n\\\\t\\\\t// (We artificially add a linebreak since we split on \\\\\\\\n at the beginning. In order to not include a trailing linebreak in the final search/result blocks we need to remove it before using them. This allows for partial line matches to be correctly identified.)\\\\r\\\\n\\\\t\\\\t// NOTE: search/replace blocks must be arranged in the order they appear in the file due to how we build the content using lastProcessedIndex. We also cannot strip the trailing newline since for non-partial lines it would remove the linebreak from the original content. (If we remove end linebreak from search, then we'd also have to remove it from replace but we can't know if it's a partial line or not since the model may be using the line break to indicate the end of the block rather than as part of the search content.) We require the model to output full lines in order for our fallbacks to work as well.\\\\r\\\\n\\\\t\\\\tif (inSearch) {\\\\r\\\\n\\\\t\\\\t\\\\tcurrentSearchContent += line + \\\\\\\"\\\\\\\\n\\\\\\\"\\\\r\\\\n\\\\t\\\\t} else if (inReplace) {\\\\r\\\\n\\\\t\\\\t\\\\tcurrentReplaceContent += line + \\\\\\\"\\\\\\\\n\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t// Output replacement lines immediately if we know the insertion point\\\\r\\\\n\\\\t\\\\t\\\\tif (searchMatchIndex !== -1) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tresult += line + \\\\\\\"\\\\\\\\n\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// If this is the final chunk, append any remaining original content\\\\r\\\\n\\\\tif (isFinal && lastProcessedIndex < originalContent.length) {\\\\r\\\\n\\\\t\\\\tresult += originalContent.slice(lastProcessedIndex)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\treturn result\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":13557,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.944Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.944Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":344,\\\"nonEmptyLines\\\":305,\\\"commentLines\\\":56,\\\"complexity\\\":56},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":-311,\\\"issues\\\":[],\\\"duplicateLines\\\":77,\\\"longLines\\\":13,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.319Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":344},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\core\\\\\\\\assistant-message\\\\\\\\index.ts\\\":{\\\"content\\\":\\\"export type AssistantMessageContent = TextContent | ToolUse\\\\r\\\\n\\\\r\\\\nexport { parseAssistantMessage } from \\\\\\\"./parse-assistant-message\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport interface TextContent {\\\\r\\\\n\\\\ttype: \\\\\\\"text\\\\\\\"\\\\r\\\\n\\\\tcontent: string\\\\r\\\\n\\\\tpartial: boolean\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport const toolUseNames = [\\\\r\\\\n\\\\t\\\\\\\"execute_command\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"read_file\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"write_to_file\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"replace_in_file\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"search_files\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"list_files\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"list_code_definition_names\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"browser_action\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"use_mcp_tool\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"access_mcp_resource\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"ask_followup_question\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"attempt_completion\\\\\\\",\\\\r\\\\n] as const\\\\r\\\\n\\\\r\\\\n// Converts array of tool call names into a union type (\\\\\\\"execute_command\\\\\\\" | \\\\\\\"read_file\\\\\\\" | ...)\\\\r\\\\nexport type ToolUseName = (typeof toolUseNames)[number]\\\\r\\\\n\\\\r\\\\nexport const toolParamNames = [\\\\r\\\\n\\\\t\\\\\\\"command\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"requires_approval\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"path\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"content\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"diff\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"regex\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"file_pattern\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"recursive\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"action\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"url\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"coordinate\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"server_name\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"tool_name\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"arguments\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"uri\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"question\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"result\\\\\\\",\\\\r\\\\n] as const\\\\r\\\\n\\\\r\\\\nexport type ToolParamName = (typeof toolParamNames)[number]\\\\r\\\\n\\\\r\\\\nexport interface ToolUse {\\\\r\\\\n\\\\ttype: \\\\\\\"tool_use\\\\\\\"\\\\r\\\\n\\\\tname: ToolUseName\\\\r\\\\n\\\\t// params is a partial record, allowing only some or none of the possible parameters to be used\\\\r\\\\n\\\\tparams: Partial<Record<ToolParamName, string>>\\\\r\\\\n\\\\tpartial: boolean\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport interface ExecuteCommandToolUse extends ToolUse {\\\\r\\\\n\\\\tname: \\\\\\\"execute_command\\\\\\\"\\\\r\\\\n\\\\t// Pick<Record<ToolParamName, string>, \\\\\\\"command\\\\\\\"> makes \\\\\\\"command\\\\\\\" required, but Partial<> makes it optional\\\\r\\\\n\\\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\\\\\"command\\\\\\\" | \\\\\\\"requires_approval\\\\\\\">>\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport interface ReadFileToolUse extends ToolUse {\\\\r\\\\n\\\\tname: \\\\\\\"read_file\\\\\\\"\\\\r\\\\n\\\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\\\\\"path\\\\\\\">>\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport interface WriteToFileToolUse extends ToolUse {\\\\r\\\\n\\\\tname: \\\\\\\"write_to_file\\\\\\\"\\\\r\\\\n\\\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\\\\\"path\\\\\\\" | \\\\\\\"content\\\\\\\">>\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport interface ReplaceInFileToolUse extends ToolUse {\\\\r\\\\n\\\\tname: \\\\\\\"replace_in_file\\\\\\\"\\\\r\\\\n\\\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\\\\\"path\\\\\\\" | \\\\\\\"diff\\\\\\\">>\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport interface SearchFilesToolUse extends ToolUse {\\\\r\\\\n\\\\tname: \\\\\\\"search_files\\\\\\\"\\\\r\\\\n\\\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\\\\\"path\\\\\\\" | \\\\\\\"regex\\\\\\\" | \\\\\\\"file_pattern\\\\\\\">>\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport interface ListFilesToolUse extends ToolUse {\\\\r\\\\n\\\\tname: \\\\\\\"list_files\\\\\\\"\\\\r\\\\n\\\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\\\\\"path\\\\\\\" | \\\\\\\"recursive\\\\\\\">>\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport interface ListCodeDefinitionNamesToolUse extends ToolUse {\\\\r\\\\n\\\\tname: \\\\\\\"list_code_definition_names\\\\\\\"\\\\r\\\\n\\\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\\\\\"path\\\\\\\">>\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport interface BrowserActionToolUse extends ToolUse {\\\\r\\\\n\\\\tname: \\\\\\\"browser_action\\\\\\\"\\\\r\\\\n\\\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\\\\\"action\\\\\\\" | \\\\\\\"url\\\\\\\" | \\\\\\\"coordinate\\\\\\\" | \\\\\\\"text\\\\\\\">>\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport interface UseMcpToolToolUse extends ToolUse {\\\\r\\\\n\\\\tname: \\\\\\\"use_mcp_tool\\\\\\\"\\\\r\\\\n\\\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\\\\\"server_name\\\\\\\" | \\\\\\\"tool_name\\\\\\\" | \\\\\\\"arguments\\\\\\\">>\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport interface AccessMcpResourceToolUse extends ToolUse {\\\\r\\\\n\\\\tname: \\\\\\\"access_mcp_resource\\\\\\\"\\\\r\\\\n\\\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\\\\\"server_name\\\\\\\" | \\\\\\\"uri\\\\\\\">>\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport interface AskFollowupQuestionToolUse extends ToolUse {\\\\r\\\\n\\\\tname: \\\\\\\"ask_followup_question\\\\\\\"\\\\r\\\\n\\\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\\\\\"question\\\\\\\">>\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport interface AttemptCompletionToolUse extends ToolUse {\\\\r\\\\n\\\\tname: \\\\\\\"attempt_completion\\\\\\\"\\\\r\\\\n\\\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\\\\\"result\\\\\\\" | \\\\\\\"command\\\\\\\">>\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":3389,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.841Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.841Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":120,\\\"nonEmptyLines\\\":100,\\\"commentLines\\\":3,\\\"complexity\\\":1},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":18,\\\"issues\\\":[],\\\"duplicateLines\\\":16,\\\"longLines\\\":1,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.320Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":120},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\core\\\\\\\\assistant-message\\\\\\\\parse-assistant-message.ts\\\":{\\\"content\\\":\\\"import { AssistantMessageContent, TextContent, ToolUse, ToolParamName, toolParamNames, toolUseNames, ToolUseName } from \\\\\\\".\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport function parseAssistantMessage(assistantMessage: string) {\\\\r\\\\n\\\\tlet contentBlocks: AssistantMessageContent[] = []\\\\r\\\\n\\\\tlet currentTextContent: TextContent | undefined = undefined\\\\r\\\\n\\\\tlet currentTextContentStartIndex = 0\\\\r\\\\n\\\\tlet currentToolUse: ToolUse | undefined = undefined\\\\r\\\\n\\\\tlet currentToolUseStartIndex = 0\\\\r\\\\n\\\\tlet currentParamName: ToolParamName | undefined = undefined\\\\r\\\\n\\\\tlet currentParamValueStartIndex = 0\\\\r\\\\n\\\\tlet accumulator = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\r\\\\n\\\\tfor (let i = 0; i < assistantMessage.length; i++) {\\\\r\\\\n\\\\t\\\\tconst char = assistantMessage[i]\\\\r\\\\n\\\\t\\\\taccumulator += char\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// there should not be a param without a tool use\\\\r\\\\n\\\\t\\\\tif (currentToolUse && currentParamName) {\\\\r\\\\n\\\\t\\\\t\\\\tconst currentParamValue = accumulator.slice(currentParamValueStartIndex)\\\\r\\\\n\\\\t\\\\t\\\\tconst paramClosingTag = `</${currentParamName}>`\\\\r\\\\n\\\\t\\\\t\\\\tif (currentParamValue.endsWith(paramClosingTag)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// end of param value\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcurrentToolUse.params[currentParamName] = currentParamValue.slice(0, -paramClosingTag.length).trim()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcurrentParamName = undefined\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcontinue\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// partial param value is accumulating\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcontinue\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// no currentParamName\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (currentToolUse) {\\\\r\\\\n\\\\t\\\\t\\\\tconst currentToolValue = accumulator.slice(currentToolUseStartIndex)\\\\r\\\\n\\\\t\\\\t\\\\tconst toolUseClosingTag = `</${currentToolUse.name}>`\\\\r\\\\n\\\\t\\\\t\\\\tif (currentToolValue.endsWith(toolUseClosingTag)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// end of a tool use\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcurrentToolUse.partial = false\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcontentBlocks.push(currentToolUse)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcurrentToolUse = undefined\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcontinue\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst possibleParamOpeningTags = toolParamNames.map((name) => `<${name}>`)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tfor (const paramOpeningTag of possibleParamOpeningTags) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (accumulator.endsWith(paramOpeningTag)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// start of a new parameter\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcurrentParamName = paramOpeningTag.slice(1, -1) as ToolParamName\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcurrentParamValueStartIndex = accumulator.length\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// there's no current param, and not starting a new param\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// special case for write_to_file where file contents could contain the closing tag, in which case the param would have closed and we end up with the rest of the file contents here. To work around this, we get the string between the starting content tag and the LAST content tag.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst contentParamName: ToolParamName = \\\\\\\"content\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (currentToolUse.name === \\\\\\\"write_to_file\\\\\\\" && accumulator.endsWith(`</${contentParamName}>`)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst toolContent = accumulator.slice(currentToolUseStartIndex)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst contentStartTag = `<${contentParamName}>`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst contentEndTag = `</${contentParamName}>`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst contentStartIndex = toolContent.indexOf(contentStartTag) + contentStartTag.length\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst contentEndIndex = toolContent.lastIndexOf(contentEndTag)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (contentStartIndex !== -1 && contentEndIndex !== -1 && contentEndIndex > contentStartIndex) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcurrentToolUse.params[contentParamName] = toolContent.slice(contentStartIndex, contentEndIndex).trim()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// partial tool value is accumulating\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcontinue\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// no currentToolUse\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet didStartToolUse = false\\\\r\\\\n\\\\t\\\\tconst possibleToolUseOpeningTags = toolUseNames.map((name) => `<${name}>`)\\\\r\\\\n\\\\t\\\\tfor (const toolUseOpeningTag of possibleToolUseOpeningTags) {\\\\r\\\\n\\\\t\\\\t\\\\tif (accumulator.endsWith(toolUseOpeningTag)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// start of a new tool use\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcurrentToolUse = {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"tool_use\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tname: toolUseOpeningTag.slice(1, -1) as ToolUseName,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tparams: {},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tpartial: true,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcurrentToolUseStartIndex = accumulator.length\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// this also indicates the end of the current text content\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (currentTextContent) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcurrentTextContent.partial = false\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// remove the partially accumulated tool use tag from the end of text (<tool)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcurrentTextContent.content = currentTextContent.content\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.slice(0, -toolUseOpeningTag.slice(0, -1).length)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.trim()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcontentBlocks.push(currentTextContent)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcurrentTextContent = undefined\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tdidStartToolUse = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (!didStartToolUse) {\\\\r\\\\n\\\\t\\\\t\\\\t// no tool use, so it must be text either at the beginning or between tools\\\\r\\\\n\\\\t\\\\t\\\\tif (currentTextContent === undefined) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcurrentTextContentStartIndex = i\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tcurrentTextContent = {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcontent: accumulator.slice(currentTextContentStartIndex).trim(),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tpartial: true,\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tif (currentToolUse) {\\\\r\\\\n\\\\t\\\\t// stream did not complete tool call, add it as partial\\\\r\\\\n\\\\t\\\\tif (currentParamName) {\\\\r\\\\n\\\\t\\\\t\\\\t// tool call has a parameter that was not completed\\\\r\\\\n\\\\t\\\\t\\\\tcurrentToolUse.params[currentParamName] = accumulator.slice(currentParamValueStartIndex).trim()\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tcontentBlocks.push(currentToolUse)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Note: it doesnt matter if check for currentToolUse or currentTextContent, only one of them will be defined since only one can be partial at a time\\\\r\\\\n\\\\tif (currentTextContent) {\\\\r\\\\n\\\\t\\\\t// stream did not complete text content, add it as partial\\\\r\\\\n\\\\t\\\\tcontentBlocks.push(currentTextContent)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\treturn contentBlocks\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":5122,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.944Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.944Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":134,\\\"nonEmptyLines\\\":118,\\\"commentLines\\\":18,\\\"complexity\\\":26},\\\"dependencies\\\":[\\\".\\\"],\\\"quality\\\":{\\\"score\\\":-59,\\\"issues\\\":[],\\\"duplicateLines\\\":29,\\\"longLines\\\":7,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.323Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":134},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\core\\\\\\\\Cline.ts\\\":{\\\"content\\\":\\\"import { Anthropic } from \\\\\\\"@anthropic-ai/sdk\\\\\\\"\\\\r\\\\nimport cloneDeep from \\\\\\\"clone-deep\\\\\\\"\\\\r\\\\nimport delay from \\\\\\\"delay\\\\\\\"\\\\r\\\\nimport fs from \\\\\\\"fs/promises\\\\\\\"\\\\r\\\\nimport os from \\\\\\\"os\\\\\\\"\\\\r\\\\nimport pWaitFor from \\\\\\\"p-wait-for\\\\\\\"\\\\r\\\\nimport * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport { serializeError } from \\\\\\\"serialize-error\\\\\\\"\\\\r\\\\nimport * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\nimport { ApiHandler, buildApiHandler } from \\\\\\\"../api\\\\\\\"\\\\r\\\\nimport { ApiStream } from \\\\\\\"../api/transform/stream\\\\\\\"\\\\r\\\\nimport { DIFF_VIEW_URI_SCHEME, DiffViewProvider } from \\\\\\\"../integrations/editor/DiffViewProvider\\\\\\\"\\\\r\\\\nimport { findToolName, formatContentBlockToMarkdown } from \\\\\\\"../integrations/misc/export-markdown\\\\\\\"\\\\r\\\\nimport { extractTextFromFile } from \\\\\\\"../integrations/misc/extract-text\\\\\\\"\\\\r\\\\nimport { TerminalManager } from \\\\\\\"../integrations/terminal/TerminalManager\\\\\\\"\\\\r\\\\nimport { BrowserSession } from \\\\\\\"../services/browser/BrowserSession\\\\\\\"\\\\r\\\\nimport { UrlContentFetcher } from \\\\\\\"../services/browser/UrlContentFetcher\\\\\\\"\\\\r\\\\nimport { listFiles } from \\\\\\\"../services/glob/list-files\\\\\\\"\\\\r\\\\nimport { regexSearchFiles } from \\\\\\\"../services/ripgrep\\\\\\\"\\\\r\\\\nimport { parseSourceCodeForDefinitionsTopLevel } from \\\\\\\"../services/tree-sitter\\\\\\\"\\\\r\\\\nimport { ApiConfiguration } from \\\\\\\"../shared/api\\\\\\\"\\\\r\\\\nimport { findLast, findLastIndex } from \\\\\\\"../shared/array\\\\\\\"\\\\r\\\\nimport { AutoApprovalSettings } from \\\\\\\"../shared/AutoApprovalSettings\\\\\\\"\\\\r\\\\nimport { combineApiRequests } from \\\\\\\"../shared/combineApiRequests\\\\\\\"\\\\r\\\\nimport { combineCommandSequences, COMMAND_REQ_APP_STRING } from \\\\\\\"../shared/combineCommandSequences\\\\\\\"\\\\r\\\\nimport {\\\\r\\\\n\\\\tBrowserAction,\\\\r\\\\n\\\\tBrowserActionResult,\\\\r\\\\n\\\\tbrowserActions,\\\\r\\\\n\\\\tClineApiReqCancelReason,\\\\r\\\\n\\\\tClineApiReqInfo,\\\\r\\\\n\\\\tClineAsk,\\\\r\\\\n\\\\tClineAskUseMcpServer,\\\\r\\\\n\\\\tClineMessage,\\\\r\\\\n\\\\tClineSay,\\\\r\\\\n\\\\tClineSayBrowserAction,\\\\r\\\\n\\\\tClineSayTool,\\\\r\\\\n\\\\tCOMPLETION_RESULT_CHANGES_FLAG,\\\\r\\\\n} from \\\\\\\"../shared/ExtensionMessage\\\\\\\"\\\\r\\\\nimport { getApiMetrics } from \\\\\\\"../shared/getApiMetrics\\\\\\\"\\\\r\\\\nimport { HistoryItem } from \\\\\\\"../shared/HistoryItem\\\\\\\"\\\\r\\\\nimport { ClineAskResponse, ClineCheckpointRestore } from \\\\\\\"../shared/WebviewMessage\\\\\\\"\\\\r\\\\nimport { calculateApiCost } from \\\\\\\"../utils/cost\\\\\\\"\\\\r\\\\nimport { fileExistsAtPath } from \\\\\\\"../utils/fs\\\\\\\"\\\\r\\\\nimport { arePathsEqual, getReadablePath } from \\\\\\\"../utils/path\\\\\\\"\\\\r\\\\nimport { AssistantMessageContent, parseAssistantMessage, ToolParamName, ToolUseName } from \\\\\\\"./assistant-message\\\\\\\"\\\\r\\\\nimport { constructNewFileContent } from \\\\\\\"./assistant-message/diff\\\\\\\"\\\\r\\\\nimport { parseMentions } from \\\\\\\"./mentions\\\\\\\"\\\\r\\\\nimport { formatResponse } from \\\\\\\"./prompts/responses\\\\\\\"\\\\r\\\\nimport { addUserInstructions, SYSTEM_PROMPT } from \\\\\\\"./prompts/system\\\\\\\"\\\\r\\\\nimport { getNextTruncationRange, getTruncatedMessages } from \\\\\\\"./sliding-window\\\\\\\"\\\\r\\\\nimport { ClineProvider, GlobalFileNames } from \\\\\\\"./webview/ClineProvider\\\\\\\"\\\\r\\\\nimport { showSystemNotification } from \\\\\\\"../integrations/notifications\\\\\\\"\\\\r\\\\nimport { removeInvalidChars } from \\\\\\\"../utils/string\\\\\\\"\\\\r\\\\nimport { fixModelHtmlEscaping } from \\\\\\\"../utils/string\\\\\\\"\\\\r\\\\nimport { OpenAiHandler } from \\\\\\\"../api/providers/openai\\\\\\\"\\\\r\\\\nimport CheckpointTracker from \\\\\\\"../integrations/checkpoints/CheckpointTracker\\\\\\\"\\\\r\\\\nimport getFolderSize from \\\\\\\"get-folder-size\\\\\\\"\\\\r\\\\n\\\\r\\\\nconst 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\\\\r\\\\n\\\\r\\\\ntype ToolResponse = string | Array<Anthropic.TextBlockParam | Anthropic.ImageBlockParam>\\\\r\\\\ntype UserContent = Array<\\\\r\\\\n\\\\tAnthropic.TextBlockParam | Anthropic.ImageBlockParam | Anthropic.ToolUseBlockParam | Anthropic.ToolResultBlockParam\\\\r\\\\n>\\\\r\\\\n\\\\r\\\\nexport class Cline {\\\\r\\\\n\\\\treadonly taskId: string\\\\r\\\\n\\\\tapi: ApiHandler\\\\r\\\\n\\\\tprivate terminalManager: TerminalManager\\\\r\\\\n\\\\tprivate urlContentFetcher: UrlContentFetcher\\\\r\\\\n\\\\tprivate browserSession: BrowserSession\\\\r\\\\n\\\\tprivate didEditFile: boolean = false\\\\r\\\\n\\\\tcustomInstructions?: string\\\\r\\\\n\\\\tautoApprovalSettings: AutoApprovalSettings\\\\r\\\\n\\\\tapiConversationHistory: Anthropic.MessageParam[] = []\\\\r\\\\n\\\\tclineMessages: ClineMessage[] = []\\\\r\\\\n\\\\tprivate askResponse?: ClineAskResponse\\\\r\\\\n\\\\tprivate askResponseText?: string\\\\r\\\\n\\\\tprivate askResponseImages?: string[]\\\\r\\\\n\\\\tprivate lastMessageTs?: number\\\\r\\\\n\\\\tprivate consecutiveAutoApprovedRequestsCount: number = 0\\\\r\\\\n\\\\tprivate consecutiveMistakeCount: number = 0\\\\r\\\\n\\\\tprivate providerRef: WeakRef<ClineProvider>\\\\r\\\\n\\\\tprivate abort: boolean = false\\\\r\\\\n\\\\tdidFinishAbortingStream = false\\\\r\\\\n\\\\tabandoned = false\\\\r\\\\n\\\\tprivate diffViewProvider: DiffViewProvider\\\\r\\\\n\\\\tprivate checkpointTracker?: CheckpointTracker\\\\r\\\\n\\\\tcheckpointTrackerErrorMessage?: string\\\\r\\\\n\\\\tconversationHistoryDeletedRange?: [number, number]\\\\r\\\\n\\\\tisInitialized = false\\\\r\\\\n\\\\r\\\\n\\\\t// streaming\\\\r\\\\n\\\\tisStreaming = false\\\\r\\\\n\\\\tprivate currentStreamingContentIndex = 0\\\\r\\\\n\\\\tprivate assistantMessageContent: AssistantMessageContent[] = []\\\\r\\\\n\\\\tprivate presentAssistantMessageLocked = false\\\\r\\\\n\\\\tprivate presentAssistantMessageHasPendingUpdates = false\\\\r\\\\n\\\\tprivate userMessageContent: (Anthropic.TextBlockParam | Anthropic.ImageBlockParam)[] = []\\\\r\\\\n\\\\tprivate userMessageContentReady = false\\\\r\\\\n\\\\tprivate didRejectTool = false\\\\r\\\\n\\\\tprivate didAlreadyUseTool = false\\\\r\\\\n\\\\tprivate didCompleteReadingStream = false\\\\r\\\\n\\\\r\\\\n\\\\tconstructor(\\\\r\\\\n\\\\t\\\\tprovider: ClineProvider,\\\\r\\\\n\\\\t\\\\tapiConfiguration: ApiConfiguration,\\\\r\\\\n\\\\t\\\\tautoApprovalSettings: AutoApprovalSettings,\\\\r\\\\n\\\\t\\\\tcustomInstructions?: string,\\\\r\\\\n\\\\t\\\\ttask?: string,\\\\r\\\\n\\\\t\\\\timages?: string[],\\\\r\\\\n\\\\t\\\\thistoryItem?: HistoryItem,\\\\r\\\\n\\\\t) {\\\\r\\\\n\\\\t\\\\tthis.providerRef = new WeakRef(provider)\\\\r\\\\n\\\\t\\\\tthis.api = buildApiHandler(apiConfiguration)\\\\r\\\\n\\\\t\\\\tthis.terminalManager = new TerminalManager()\\\\r\\\\n\\\\t\\\\tthis.urlContentFetcher = new UrlContentFetcher(provider.context)\\\\r\\\\n\\\\t\\\\tthis.browserSession = new BrowserSession(provider.context)\\\\r\\\\n\\\\t\\\\tthis.diffViewProvider = new DiffViewProvider(cwd)\\\\r\\\\n\\\\t\\\\tthis.customInstructions = customInstructions\\\\r\\\\n\\\\t\\\\tthis.autoApprovalSettings = autoApprovalSettings\\\\r\\\\n\\\\t\\\\tif (historyItem) {\\\\r\\\\n\\\\t\\\\t\\\\tthis.taskId = historyItem.id\\\\r\\\\n\\\\t\\\\t\\\\tthis.conversationHistoryDeletedRange = historyItem.conversationHistoryDeletedRange\\\\r\\\\n\\\\t\\\\t\\\\tthis.resumeTaskFromHistory()\\\\r\\\\n\\\\t\\\\t} else if (task || images) {\\\\r\\\\n\\\\t\\\\t\\\\tthis.taskId = Date.now().toString()\\\\r\\\\n\\\\t\\\\t\\\\tthis.startTask(task, images)\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Either historyItem or task/images must be provided\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Storing task to disk for history\\\\r\\\\n\\\\r\\\\n\\\\tprivate async ensureTaskDirectoryExists(): Promise<string> {\\\\r\\\\n\\\\t\\\\tconst globalStoragePath = this.providerRef.deref()?.context.globalStorageUri.fsPath\\\\r\\\\n\\\\t\\\\tif (!globalStoragePath) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Global storage uri is invalid\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst taskDir = path.join(globalStoragePath, \\\\\\\"tasks\\\\\\\", this.taskId)\\\\r\\\\n\\\\t\\\\tawait fs.mkdir(taskDir, { recursive: true })\\\\r\\\\n\\\\t\\\\treturn taskDir\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async getSavedApiConversationHistory(): Promise<Anthropic.MessageParam[]> {\\\\r\\\\n\\\\t\\\\tconst filePath = path.join(await this.ensureTaskDirectoryExists(), GlobalFileNames.apiConversationHistory)\\\\r\\\\n\\\\t\\\\tconst fileExists = await fileExistsAtPath(filePath)\\\\r\\\\n\\\\t\\\\tif (fileExists) {\\\\r\\\\n\\\\t\\\\t\\\\treturn JSON.parse(await fs.readFile(filePath, \\\\\\\"utf8\\\\\\\"))\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn []\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async addToApiConversationHistory(message: Anthropic.MessageParam) {\\\\r\\\\n\\\\t\\\\tthis.apiConversationHistory.push(message)\\\\r\\\\n\\\\t\\\\tawait this.saveApiConversationHistory()\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async overwriteApiConversationHistory(newHistory: Anthropic.MessageParam[]) {\\\\r\\\\n\\\\t\\\\tthis.apiConversationHistory = newHistory\\\\r\\\\n\\\\t\\\\tawait this.saveApiConversationHistory()\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async saveApiConversationHistory() {\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tconst filePath = path.join(await this.ensureTaskDirectoryExists(), GlobalFileNames.apiConversationHistory)\\\\r\\\\n\\\\t\\\\t\\\\tawait fs.writeFile(filePath, JSON.stringify(this.apiConversationHistory))\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t// in the off chance this fails, we don't want to stop the task\\\\r\\\\n\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Failed to save API conversation history:\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async getSavedClineMessages(): Promise<ClineMessage[]> {\\\\r\\\\n\\\\t\\\\tconst filePath = path.join(await this.ensureTaskDirectoryExists(), GlobalFileNames.uiMessages)\\\\r\\\\n\\\\t\\\\tif (await fileExistsAtPath(filePath)) {\\\\r\\\\n\\\\t\\\\t\\\\treturn JSON.parse(await fs.readFile(filePath, \\\\\\\"utf8\\\\\\\"))\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t// check old location\\\\r\\\\n\\\\t\\\\t\\\\tconst oldPath = path.join(await this.ensureTaskDirectoryExists(), \\\\\\\"claude_messages.json\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tif (await fileExistsAtPath(oldPath)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst data = JSON.parse(await fs.readFile(oldPath, \\\\\\\"utf8\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait fs.unlink(oldPath) // remove old file\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn data\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn []\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async addToClineMessages(message: ClineMessage) {\\\\r\\\\n\\\\t\\\\t// these values allow us to reconstruct the conversation history at the time this cline message was created\\\\r\\\\n\\\\t\\\\t// it's important that apiConversationHistory is initialized before we add cline messages\\\\r\\\\n\\\\t\\\\tmessage.conversationHistoryIndex = this.apiConversationHistory.length - 1 // NOTE: this is the index of the last added message which is the user message, and once the clinemessages have been presented we update the apiconversationhistory with the completed assistant message. This means when reseting to a message, we need to +1 this index to get the correct assistant message that this tool use corresponds to\\\\r\\\\n\\\\t\\\\tmessage.conversationHistoryDeletedRange = this.conversationHistoryDeletedRange\\\\r\\\\n\\\\t\\\\tthis.clineMessages.push(message)\\\\r\\\\n\\\\t\\\\tawait this.saveClineMessages()\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async overwriteClineMessages(newMessages: ClineMessage[]) {\\\\r\\\\n\\\\t\\\\tthis.clineMessages = newMessages\\\\r\\\\n\\\\t\\\\tawait this.saveClineMessages()\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async saveClineMessages() {\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tconst taskDir = await this.ensureTaskDirectoryExists()\\\\r\\\\n\\\\t\\\\t\\\\tconst filePath = path.join(taskDir, GlobalFileNames.uiMessages)\\\\r\\\\n\\\\t\\\\t\\\\tawait fs.writeFile(filePath, JSON.stringify(this.clineMessages))\\\\r\\\\n\\\\t\\\\t\\\\t// combined as they are in ChatView\\\\r\\\\n\\\\t\\\\t\\\\tconst apiMetrics = getApiMetrics(combineApiRequests(combineCommandSequences(this.clineMessages.slice(1))))\\\\r\\\\n\\\\t\\\\t\\\\tconst taskMessage = this.clineMessages[0] // first message is always the task say\\\\r\\\\n\\\\t\\\\t\\\\tconst lastRelevantMessage =\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.clineMessages[\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tfindLastIndex(this.clineMessages, (m) => !(m.ask === \\\\\\\"resume_task\\\\\\\" || m.ask === \\\\\\\"resume_completed_task\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t]\\\\r\\\\n\\\\t\\\\t\\\\tlet taskDirSize = 0\\\\r\\\\n\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// getFolderSize.loose silently ignores errors\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// returns # of bytes, size/1000/1000 = MB\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttaskDirSize = await getFolderSize.loose(taskDir)\\\\r\\\\n\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Failed to get task directory size:\\\\\\\", taskDir, error)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tawait this.providerRef.deref()?.updateTaskHistory({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tid: this.taskId,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tts: lastRelevantMessage.ts,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttask: taskMessage.text ?? \\\\\\\"\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttokensIn: apiMetrics.totalTokensIn,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttokensOut: apiMetrics.totalTokensOut,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcacheWrites: apiMetrics.totalCacheWrites,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcacheReads: apiMetrics.totalCacheReads,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttotalCost: apiMetrics.totalCost,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tsize: taskDirSize,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tshadowGitConfigWorkTree: await this.checkpointTracker?.getShadowGitConfigWorkTree(),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconversationHistoryDeletedRange: this.conversationHistoryDeletedRange,\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Failed to save cline messages:\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync restoreCheckpoint(messageTs: number, restoreType: ClineCheckpointRestore) {\\\\r\\\\n\\\\t\\\\tconst messageIndex = this.clineMessages.findIndex((m) => m.ts === messageTs)\\\\r\\\\n\\\\t\\\\tconst message = this.clineMessages[messageIndex]\\\\r\\\\n\\\\t\\\\tif (!message) {\\\\r\\\\n\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Message not found\\\\\\\", this.clineMessages)\\\\r\\\\n\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet didWorkspaceRestoreFail = false\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tswitch (restoreType) {\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"task\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"taskAndWorkspace\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"workspace\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (!this.checkpointTracker) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.checkpointTracker = await CheckpointTracker.create(this.taskId, this.providerRef.deref())\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.checkpointTrackerErrorMessage = undefined\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst errorMessage = error instanceof Error ? error.message : \\\\\\\"Unknown error\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Failed to initialize checkpoint tracker:\\\\\\\", errorMessage)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.checkpointTrackerErrorMessage = errorMessage\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.providerRef.deref()?.postStateToWebview()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tvscode.window.showErrorMessage(errorMessage)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tdidWorkspaceRestoreFail = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (message.lastCheckpointHash && this.checkpointTracker) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.checkpointTracker.resetHead(message.lastCheckpointHash)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst errorMessage = error instanceof Error ? error.message : \\\\\\\"Unknown error\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tvscode.window.showErrorMessage(\\\\\\\"Failed to restore checkpoint: \\\\\\\" + errorMessage)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tdidWorkspaceRestoreFail = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (!didWorkspaceRestoreFail) {\\\\r\\\\n\\\\t\\\\t\\\\tswitch (restoreType) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"task\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"taskAndWorkspace\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.conversationHistoryDeletedRange = message.conversationHistoryDeletedRange\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst newConversationHistory = this.apiConversationHistory.slice(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t(message.conversationHistoryIndex || 0) + 2,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t) // +1 since this index corresponds to the last user message, and another +1 since slice end index is exclusive\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.overwriteApiConversationHistory(newConversationHistory)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// aggregate deleted api reqs info so we don't lose costs/tokens\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst deletedMessages = this.clineMessages.slice(messageIndex + 1)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst deletedApiReqsMetrics = getApiMetrics(combineApiRequests(combineCommandSequences(deletedMessages)))\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst newClineMessages = this.clineMessages.slice(0, messageIndex + 1)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.overwriteClineMessages(newClineMessages) // calls saveClineMessages which saves historyItem\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"deleted_api_reqs\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tJSON.stringify({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttokensIn: deletedApiReqsMetrics.totalTokensIn,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttokensOut: deletedApiReqsMetrics.totalTokensOut,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcacheWrites: deletedApiReqsMetrics.totalCacheWrites,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcacheReads: deletedApiReqsMetrics.totalCacheReads,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcost: deletedApiReqsMetrics.totalCost,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} satisfies ClineApiReqInfo),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"workspace\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tswitch (restoreType) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"task\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tvscode.window.showInformationMessage(\\\\\\\"Task messages have been restored to the checkpoint\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"workspace\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tvscode.window.showInformationMessage(\\\\\\\"Workspace files have been restored to the checkpoint\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"taskAndWorkspace\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tvscode.window.showInformationMessage(\\\\\\\"Task and workspace have been restored to the checkpoint\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tawait this.providerRef.deref()?.postMessageToWebview({ type: \\\\\\\"relinquishControl\\\\\\\" })\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tthis.providerRef.deref()?.cancelTask() // the task is already cancelled by the provider beforehand, but we need to re-init to get the updated messages\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\tawait this.providerRef.deref()?.postMessageToWebview({ type: \\\\\\\"relinquishControl\\\\\\\" })\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync presentMultifileDiff(messageTs: number, seeNewChangesSinceLastTaskCompletion: boolean) {\\\\r\\\\n\\\\t\\\\tconst relinquishButton = () => {\\\\r\\\\n\\\\t\\\\t\\\\tthis.providerRef.deref()?.postMessageToWebview({ type: \\\\\\\"relinquishControl\\\\\\\" })\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconsole.log(\\\\\\\"presentMultifileDiff\\\\\\\", messageTs)\\\\r\\\\n\\\\t\\\\tconst messageIndex = this.clineMessages.findIndex((m) => m.ts === messageTs)\\\\r\\\\n\\\\t\\\\tconst message = this.clineMessages[messageIndex]\\\\r\\\\n\\\\t\\\\tif (!message) {\\\\r\\\\n\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Message not found\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\trelinquishButton()\\\\r\\\\n\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst hash = message.lastCheckpointHash\\\\r\\\\n\\\\t\\\\tif (!hash) {\\\\r\\\\n\\\\t\\\\t\\\\tconsole.error(\\\\\\\"No checkpoint hash found\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\trelinquishButton()\\\\r\\\\n\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// TODO: handle if this is called from outside original workspace, in which case we need to show user error message we cant show diff outside of workspace?\\\\r\\\\n\\\\t\\\\tif (!this.checkpointTracker) {\\\\r\\\\n\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.checkpointTracker = await CheckpointTracker.create(this.taskId, this.providerRef.deref())\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.checkpointTrackerErrorMessage = undefined\\\\r\\\\n\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst errorMessage = error instanceof Error ? error.message : \\\\\\\"Unknown error\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Failed to initialize checkpoint tracker:\\\\\\\", errorMessage)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.checkpointTrackerErrorMessage = errorMessage\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait this.providerRef.deref()?.postStateToWebview()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tvscode.window.showErrorMessage(errorMessage)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\trelinquishButton()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet changedFiles:\\\\r\\\\n\\\\t\\\\t\\\\t| {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\trelativePath: string\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tabsolutePath: string\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbefore: string\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tafter: string\\\\r\\\\n\\\\t\\\\t\\\\t }[]\\\\r\\\\n\\\\t\\\\t\\\\t| undefined\\\\r\\\\n\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tif (seeNewChangesSinceLastTaskCompletion) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Get last task completed\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst lastTaskCompletedMessage = findLast(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.clineMessages.slice(0, messageIndex),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t(m) => m.say === \\\\\\\"completion_result\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t) // ask is only used to relinquish control, its the last say we care about\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// if undefined, then we get diff from beginning of git\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// if (!lastTaskCompletedMessage) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// \\\\tconsole.error(\\\\\\\"No previous task completion message found\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// \\\\treturn\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// }\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Get changed files between current state and commit\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tchangedFiles = await this.checkpointTracker?.getDiffSet(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tlastTaskCompletedMessage?.lastCheckpointHash, // if undefined, then we get diff from beginning of git history, AKA when the task was started\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\thash,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (!changedFiles?.length) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tvscode.window.showInformationMessage(\\\\\\\"No changes found\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\trelinquishButton()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Get changed files between current state and commit\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tchangedFiles = await this.checkpointTracker?.getDiffSet(hash)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (!changedFiles?.length) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tvscode.window.showInformationMessage(\\\\\\\"No changes found\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\trelinquishButton()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\tconst errorMessage = error instanceof Error ? error.message : \\\\\\\"Unknown error\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tvscode.window.showErrorMessage(\\\\\\\"Failed to retrieve diff set: \\\\\\\" + errorMessage)\\\\r\\\\n\\\\t\\\\t\\\\trelinquishButton()\\\\r\\\\n\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Check if multi-diff editor is enabled in VS Code settings\\\\r\\\\n\\\\t\\\\t// const config = vscode.workspace.getConfiguration()\\\\r\\\\n\\\\t\\\\t// const isMultiDiffEnabled = config.get(\\\\\\\"multiDiffEditor.experimental.enabled\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// if (!isMultiDiffEnabled) {\\\\r\\\\n\\\\t\\\\t// \\\\tvscode.window.showErrorMessage(\\\\r\\\\n\\\\t\\\\t// \\\\t\\\\t\\\\\\\"Please enable 'multiDiffEditor.experimental.enabled' in your VS Code settings to use this feature.\\\\\\\",\\\\r\\\\n\\\\t\\\\t// \\\\t)\\\\r\\\\n\\\\t\\\\t// \\\\trelinquishButton()\\\\r\\\\n\\\\t\\\\t// \\\\treturn\\\\r\\\\n\\\\t\\\\t// }\\\\r\\\\n\\\\t\\\\t// Open multi-diff editor\\\\r\\\\n\\\\t\\\\tawait vscode.commands.executeCommand(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"vscode.changes\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tseeNewChangesSinceLastTaskCompletion ? \\\\\\\"New changes\\\\\\\" : \\\\\\\"Changes since snapshot\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tchangedFiles.map((file) => [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tvscode.Uri.file(file.absolutePath),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tvscode.Uri.parse(`${DIFF_VIEW_URI_SCHEME}:${file.relativePath}`).with({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tquery: Buffer.from(file.before ?? \\\\\\\"\\\\\\\").toString(\\\\\\\"base64\\\\\\\"),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tvscode.Uri.parse(`${DIFF_VIEW_URI_SCHEME}:${file.relativePath}`).with({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tquery: Buffer.from(file.after ?? \\\\\\\"\\\\\\\").toString(\\\\\\\"base64\\\\\\\"),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}),\\\\r\\\\n\\\\t\\\\t\\\\t]),\\\\r\\\\n\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\trelinquishButton()\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync doesLatestTaskCompletionHaveNewChanges() {\\\\r\\\\n\\\\t\\\\tconst messageIndex = findLastIndex(this.clineMessages, (m) => m.say === \\\\\\\"completion_result\\\\\\\")\\\\r\\\\n\\\\t\\\\tconst message = this.clineMessages[messageIndex]\\\\r\\\\n\\\\t\\\\tif (!message) {\\\\r\\\\n\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Completion message not found\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\treturn false\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst hash = message.lastCheckpointHash\\\\r\\\\n\\\\t\\\\tif (!hash) {\\\\r\\\\n\\\\t\\\\t\\\\tconsole.error(\\\\\\\"No checkpoint hash found\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\treturn false\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (!this.checkpointTracker) {\\\\r\\\\n\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.checkpointTracker = await CheckpointTracker.create(this.taskId, this.providerRef.deref())\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.checkpointTrackerErrorMessage = undefined\\\\r\\\\n\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst errorMessage = error instanceof Error ? error.message : \\\\\\\"Unknown error\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Failed to initialize checkpoint tracker:\\\\\\\", errorMessage)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn false\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Get last task completed\\\\r\\\\n\\\\t\\\\tconst lastTaskCompletedMessage = findLast(this.clineMessages.slice(0, messageIndex), (m) => m.say === \\\\\\\"completion_result\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t// Get changed files between current state and commit\\\\r\\\\n\\\\t\\\\t\\\\tconst changedFiles = await this.checkpointTracker?.getDiffSet(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlastTaskCompletedMessage?.lastCheckpointHash, // if undefined, then we get diff from beginning of git history, AKA when the task was started\\\\r\\\\n\\\\t\\\\t\\\\t\\\\thash,\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\tconst changedFilesCount = changedFiles?.length || 0\\\\r\\\\n\\\\t\\\\t\\\\tif (changedFilesCount > 0) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn true\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Failed to get diff set:\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\treturn false\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\treturn false\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Communicate with webview\\\\r\\\\n\\\\r\\\\n\\\\t// partial has three valid states true (partial message), false (completion of partial message), undefined (individual complete message)\\\\r\\\\n\\\\tasync ask(\\\\r\\\\n\\\\t\\\\ttype: ClineAsk,\\\\r\\\\n\\\\t\\\\ttext?: string,\\\\r\\\\n\\\\t\\\\tpartial?: boolean,\\\\r\\\\n\\\\t): Promise<{\\\\r\\\\n\\\\t\\\\tresponse: ClineAskResponse\\\\r\\\\n\\\\t\\\\ttext?: string\\\\r\\\\n\\\\t\\\\timages?: string[]\\\\r\\\\n\\\\t}> {\\\\r\\\\n\\\\t\\\\t// If this Cline instance was aborted by the provider, then the only thing keeping us alive is a promise still running in the background, in which case we don't want to send its result to the webview as it is attached to a new instance of Cline now. So we can safely ignore the result of any active promises, and this class will be deallocated. (Although we set Cline = undefined in provider, that simply removes the reference to this instance, but the instance is still alive until this promise resolves or rejects.)\\\\r\\\\n\\\\t\\\\tif (this.abort) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Cline instance aborted\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tlet askTs: number\\\\r\\\\n\\\\t\\\\tif (partial !== undefined) {\\\\r\\\\n\\\\t\\\\t\\\\tconst lastMessage = this.clineMessages.at(-1)\\\\r\\\\n\\\\t\\\\t\\\\tconst isUpdatingPreviousPartial =\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlastMessage && lastMessage.partial && lastMessage.type === \\\\\\\"ask\\\\\\\" && lastMessage.ask === type\\\\r\\\\n\\\\t\\\\t\\\\tif (partial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (isUpdatingPreviousPartial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// existing partial message, so update it\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tlastMessage.text = text\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tlastMessage.partial = partial\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// 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\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// await this.saveClineMessages()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// await this.providerRef.deref()?.postStateToWebview()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.providerRef.deref()?.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"partialMessage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpartialMessage: lastMessage,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Current ask promise was ignored 1\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// this is a new partial message, so add it with partial state\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// this.askResponse = undefined\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// this.askResponseText = undefined\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// this.askResponseImages = undefined\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\taskTs = Date.now()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.lastMessageTs = askTs\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.addToClineMessages({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tts: askTs,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"ask\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\task: type,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpartial,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.providerRef.deref()?.postStateToWebview()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Current ask promise was ignored 2\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// partial=false means its a complete version of a previously partial message\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (isUpdatingPreviousPartial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// this is the complete version of a previously partial message, so replace the partial with the complete version\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.askResponse = undefined\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.askResponseText = undefined\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.askResponseImages = undefined\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t/*\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tBug for the history books:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tIn the webview we use the ts as the chatrow key for the virtuoso list. Since we would update this ts right at the end of streaming, it would cause the view to flicker. The key prop has to be stable otherwise react has trouble reconciling items between renders, causing unmounting and remounting of components (flickering).\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tThe lesson here is if you see flickering when rendering lists, it's likely because the key prop is not stable.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tSo in this case we must make sure that the message ts is never altered after first setting it.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t*/\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\taskTs = lastMessage.ts\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.lastMessageTs = askTs\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// lastMessage.ts = askTs\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tlastMessage.text = text\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tlastMessage.partial = false\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveClineMessages()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// await this.providerRef.deref()?.postStateToWebview()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.providerRef.deref()?.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"partialMessage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpartialMessage: lastMessage,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// this is a new partial=false message, so add it like normal\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.askResponse = undefined\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.askResponseText = undefined\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.askResponseImages = undefined\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\taskTs = Date.now()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.lastMessageTs = askTs\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.addToClineMessages({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tts: askTs,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"ask\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\task: type,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.providerRef.deref()?.postStateToWebview()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t// this is a new non-partial message, so add it like normal\\\\r\\\\n\\\\t\\\\t\\\\t// const lastMessage = this.clineMessages.at(-1)\\\\r\\\\n\\\\t\\\\t\\\\tthis.askResponse = undefined\\\\r\\\\n\\\\t\\\\t\\\\tthis.askResponseText = undefined\\\\r\\\\n\\\\t\\\\t\\\\tthis.askResponseImages = undefined\\\\r\\\\n\\\\t\\\\t\\\\taskTs = Date.now()\\\\r\\\\n\\\\t\\\\t\\\\tthis.lastMessageTs = askTs\\\\r\\\\n\\\\t\\\\t\\\\tawait this.addToClineMessages({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tts: askTs,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"ask\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\task: type,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttext,\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\tawait this.providerRef.deref()?.postStateToWebview()\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tawait pWaitFor(() => this.askResponse !== undefined || this.lastMessageTs !== askTs, { interval: 100 })\\\\r\\\\n\\\\t\\\\tif (this.lastMessageTs !== askTs) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Current ask promise was ignored\\\\\\\") // could happen if we send multiple asks in a row i.e. with command_output. It's important that when we know an ask could fail, it is handled gracefully\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst result = {\\\\r\\\\n\\\\t\\\\t\\\\tresponse: this.askResponse!,\\\\r\\\\n\\\\t\\\\t\\\\ttext: this.askResponseText,\\\\r\\\\n\\\\t\\\\t\\\\timages: this.askResponseImages,\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tthis.askResponse = undefined\\\\r\\\\n\\\\t\\\\tthis.askResponseText = undefined\\\\r\\\\n\\\\t\\\\tthis.askResponseImages = undefined\\\\r\\\\n\\\\t\\\\treturn result\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync handleWebviewAskResponse(askResponse: ClineAskResponse, text?: string, images?: string[]) {\\\\r\\\\n\\\\t\\\\tthis.askResponse = askResponse\\\\r\\\\n\\\\t\\\\tthis.askResponseText = text\\\\r\\\\n\\\\t\\\\tthis.askResponseImages = images\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync say(type: ClineSay, text?: string, images?: string[], partial?: boolean): Promise<undefined> {\\\\r\\\\n\\\\t\\\\tif (this.abort) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Cline instance aborted\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (partial !== undefined) {\\\\r\\\\n\\\\t\\\\t\\\\tconst lastMessage = this.clineMessages.at(-1)\\\\r\\\\n\\\\t\\\\t\\\\tconst isUpdatingPreviousPartial =\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlastMessage && lastMessage.partial && lastMessage.type === \\\\\\\"say\\\\\\\" && lastMessage.say === type\\\\r\\\\n\\\\t\\\\t\\\\tif (partial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (isUpdatingPreviousPartial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// existing partial message, so update it\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tlastMessage.text = text\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tlastMessage.images = images\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tlastMessage.partial = partial\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.providerRef.deref()?.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"partialMessage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpartialMessage: lastMessage,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// this is a new partial message, so add it with partial state\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst sayTs = Date.now()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.lastMessageTs = sayTs\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.addToClineMessages({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tts: sayTs,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"say\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tsay: type,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\timages,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpartial,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.providerRef.deref()?.postStateToWebview()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// partial=false means its a complete version of a previously partial message\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (isUpdatingPreviousPartial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// this is the complete version of a previously partial message, so replace the partial with the complete version\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.lastMessageTs = lastMessage.ts\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// lastMessage.ts = sayTs\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tlastMessage.text = text\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tlastMessage.images = images\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tlastMessage.partial = false\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// instead of streaming partialMessage events, we do a save and post like normal to persist to disk\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveClineMessages()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// await this.providerRef.deref()?.postStateToWebview()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.providerRef.deref()?.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"partialMessage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpartialMessage: lastMessage,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}) // more performant than an entire postStateToWebview\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// this is a new partial=false message, so add it like normal\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst sayTs = Date.now()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.lastMessageTs = sayTs\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.addToClineMessages({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tts: sayTs,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"say\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tsay: type,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\timages,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.providerRef.deref()?.postStateToWebview()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t// this is a new non-partial message, so add it like normal\\\\r\\\\n\\\\t\\\\t\\\\tconst sayTs = Date.now()\\\\r\\\\n\\\\t\\\\t\\\\tthis.lastMessageTs = sayTs\\\\r\\\\n\\\\t\\\\t\\\\tawait this.addToClineMessages({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tts: sayTs,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"say\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tsay: type,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttext,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\timages,\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\tawait this.providerRef.deref()?.postStateToWebview()\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync sayAndCreateMissingParamError(toolName: ToolUseName, paramName: string, relPath?: string) {\\\\r\\\\n\\\\t\\\\tawait this.say(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"error\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t`Cline tried to use ${toolName}${\\\\r\\\\n\\\\t\\\\t\\\\t\\\\trelPath ? ` for '${relPath.toPosix()}'` : \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t} without value for required parameter '${paramName}'. Retrying...`,\\\\r\\\\n\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\treturn formatResponse.toolError(formatResponse.missingToolParameterError(paramName))\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync removeLastPartialMessageIfExistsWithType(type: \\\\\\\"ask\\\\\\\" | \\\\\\\"say\\\\\\\", askOrSay: ClineAsk | ClineSay) {\\\\r\\\\n\\\\t\\\\tconst lastMessage = this.clineMessages.at(-1)\\\\r\\\\n\\\\t\\\\tif (lastMessage?.partial && lastMessage.type === type && (lastMessage.ask === askOrSay || lastMessage.say === askOrSay)) {\\\\r\\\\n\\\\t\\\\t\\\\tthis.clineMessages.pop()\\\\r\\\\n\\\\t\\\\t\\\\tawait this.saveClineMessages()\\\\r\\\\n\\\\t\\\\t\\\\tawait this.providerRef.deref()?.postStateToWebview()\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Task lifecycle\\\\r\\\\n\\\\r\\\\n\\\\tprivate async startTask(task?: string, images?: string[]): Promise<void> {\\\\r\\\\n\\\\t\\\\t// conversationHistory (for API) and clineMessages (for webview) need to be in sync\\\\r\\\\n\\\\t\\\\t// if the extension process were killed, then on restart the clineMessages might not be empty, so we need to set it to [] when we create a new Cline client (otherwise webview would show stale messages from previous session)\\\\r\\\\n\\\\t\\\\tthis.clineMessages = []\\\\r\\\\n\\\\t\\\\tthis.apiConversationHistory = []\\\\r\\\\n\\\\t\\\\tawait this.providerRef.deref()?.postStateToWebview()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tawait this.say(\\\\\\\"text\\\\\\\", task, images)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tthis.isInitialized = true\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet imageBlocks: Anthropic.ImageBlockParam[] = formatResponse.imageBlocks(images)\\\\r\\\\n\\\\t\\\\tawait this.initiateTaskLoop(\\\\r\\\\n\\\\t\\\\t\\\\t[\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttext: `<task>\\\\\\\\n${task}\\\\\\\\n</task>`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t...imageBlocks,\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\ttrue,\\\\r\\\\n\\\\t\\\\t)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async resumeTaskFromHistory() {\\\\r\\\\n\\\\t\\\\t// TODO: right now we let users init checkpoints for old tasks, assuming they're continuing them from the same workspace (which we never tied to tasks, so no way for us to know if it's opened in the right workspace)\\\\r\\\\n\\\\t\\\\t// const doesShadowGitExist = await CheckpointTracker.doesShadowGitExist(this.taskId, this.providerRef.deref())\\\\r\\\\n\\\\t\\\\t// if (!doesShadowGitExist) {\\\\r\\\\n\\\\t\\\\t// \\\\tthis.checkpointTrackerErrorMessage = \\\\\\\"Checkpoints are only available for new tasks\\\\\\\"\\\\r\\\\n\\\\t\\\\t// }\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst modifiedClineMessages = await this.getSavedClineMessages()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Remove any resume messages that may have been added before\\\\r\\\\n\\\\t\\\\tconst lastRelevantMessageIndex = findLastIndex(\\\\r\\\\n\\\\t\\\\t\\\\tmodifiedClineMessages,\\\\r\\\\n\\\\t\\\\t\\\\t(m) => !(m.ask === \\\\\\\"resume_task\\\\\\\" || m.ask === \\\\\\\"resume_completed_task\\\\\\\"),\\\\r\\\\n\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\tif (lastRelevantMessageIndex !== -1) {\\\\r\\\\n\\\\t\\\\t\\\\tmodifiedClineMessages.splice(lastRelevantMessageIndex + 1)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// since we don't use api_req_finished anymore, we need to check if the last api_req_started has a cost value, if it doesn't and no cancellation reason to present, then we remove it since it indicates an api request without any partial content streamed\\\\r\\\\n\\\\t\\\\tconst lastApiReqStartedIndex = findLastIndex(\\\\r\\\\n\\\\t\\\\t\\\\tmodifiedClineMessages,\\\\r\\\\n\\\\t\\\\t\\\\t(m) => m.type === \\\\\\\"say\\\\\\\" && m.say === \\\\\\\"api_req_started\\\\\\\",\\\\r\\\\n\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\tif (lastApiReqStartedIndex !== -1) {\\\\r\\\\n\\\\t\\\\t\\\\tconst lastApiReqStarted = modifiedClineMessages[lastApiReqStartedIndex]\\\\r\\\\n\\\\t\\\\t\\\\tconst { cost, cancelReason }: ClineApiReqInfo = JSON.parse(lastApiReqStarted.text || \\\\\\\"{}\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tif (cost === undefined && cancelReason === undefined) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tmodifiedClineMessages.splice(lastApiReqStartedIndex, 1)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tawait this.overwriteClineMessages(modifiedClineMessages)\\\\r\\\\n\\\\t\\\\tthis.clineMessages = await this.getSavedClineMessages()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Now present the cline messages to the user and ask if they want to resume (NOTE: we ran into a bug before where the apiconversationhistory wouldnt be initialized when opening a old task, and it was because we were waiting for resume)\\\\r\\\\n\\\\t\\\\t// This is important in case the user deletes messages without resuming the task first\\\\r\\\\n\\\\t\\\\tthis.apiConversationHistory = await this.getSavedApiConversationHistory()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst lastClineMessage = this.clineMessages\\\\r\\\\n\\\\t\\\\t\\\\t.slice()\\\\r\\\\n\\\\t\\\\t\\\\t.reverse()\\\\r\\\\n\\\\t\\\\t\\\\t.find((m) => !(m.ask === \\\\\\\"resume_task\\\\\\\" || m.ask === \\\\\\\"resume_completed_task\\\\\\\")) // could be multiple resume tasks\\\\r\\\\n\\\\t\\\\t// const lastClineMessage = this.clineMessages[lastClineMessageIndex]\\\\r\\\\n\\\\t\\\\t// could be a completion result with a command\\\\r\\\\n\\\\t\\\\t// const secondLastClineMessage = this.clineMessages\\\\r\\\\n\\\\t\\\\t// \\\\t.slice()\\\\r\\\\n\\\\t\\\\t// \\\\t.reverse()\\\\r\\\\n\\\\t\\\\t// \\\\t.find(\\\\r\\\\n\\\\t\\\\t// \\\\t\\\\t(m, index) =>\\\\r\\\\n\\\\t\\\\t// \\\\t\\\\t\\\\tindex !== lastClineMessageIndex && !(m.ask === \\\\\\\"resume_task\\\\\\\" || m.ask === \\\\\\\"resume_completed_task\\\\\\\")\\\\r\\\\n\\\\t\\\\t// \\\\t)\\\\r\\\\n\\\\t\\\\t// (lastClineMessage?.ask === \\\\\\\"command\\\\\\\" && secondLastClineMessage?.ask === \\\\\\\"completion_result\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet askType: ClineAsk\\\\r\\\\n\\\\t\\\\tif (lastClineMessage?.ask === \\\\\\\"completion_result\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\taskType = \\\\\\\"resume_completed_task\\\\\\\"\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\taskType = \\\\\\\"resume_task\\\\\\\"\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tthis.isInitialized = true\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst { response, text, images } = await this.ask(askType) // calls poststatetowebview\\\\r\\\\n\\\\t\\\\tlet responseText: string | undefined\\\\r\\\\n\\\\t\\\\tlet responseImages: string[] | undefined\\\\r\\\\n\\\\t\\\\tif (response === \\\\\\\"messageResponse\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\tawait this.say(\\\\\\\"user_feedback\\\\\\\", text, images)\\\\r\\\\n\\\\t\\\\t\\\\tresponseText = text\\\\r\\\\n\\\\t\\\\t\\\\tresponseImages = images\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// 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\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet existingApiConversationHistory: Anthropic.Messages.MessageParam[] = await this.getSavedApiConversationHistory()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// 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\\\\r\\\\n\\\\t\\\\tconst conversationWithoutToolBlocks = existingApiConversationHistory.map((message) => {\\\\r\\\\n\\\\t\\\\t\\\\tif (Array.isArray(message.content)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst newContent = message.content.map((block) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (block.type === \\\\\\\"tool_use\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// it's important we convert to the new tool schema format so the model doesn't get confused about how to invoke tools\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst inputAsXml = Object.entries(block.input as Record<string, string>)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.map(([key, value]) => `<${key}>\\\\\\\\n${value}\\\\\\\\n</${key}>`)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.join(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: `<${block.name}>\\\\\\\\n${inputAsXml}\\\\\\\\n</${block.name}>`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} as Anthropic.Messages.TextBlockParam\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t} else if (block.type === \\\\\\\"tool_result\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// Convert block.content to text block array, removing images\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst contentAsTextBlocks = Array.isArray(block.content)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t? block.content.filter((item) => item.type === \\\\\\\"text\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t: [{ type: \\\\\\\"text\\\\\\\", text: block.content }]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst textContent = contentAsTextBlocks.map((item) => item.text).join(\\\\\\\"\\\\\\\\n\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst toolName = findToolName(block.tool_use_id, existingApiConversationHistory)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: `[${toolName} Result]\\\\\\\\n\\\\\\\\n${textContent}`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} as Anthropic.Messages.TextBlockParam\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn block\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn { ...message, content: newContent }\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\treturn message\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\texistingApiConversationHistory = conversationWithoutToolBlocks\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// FIXME: remove tool use blocks altogether\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// if the last message is an assistant message, we need to check if there's tool use since every tool use has to have a tool response\\\\r\\\\n\\\\t\\\\t// if there's no tool use and only a text block, then we can just add a user message\\\\r\\\\n\\\\t\\\\t// (note this isn't relevant anymore since we use custom tool prompts instead of tool use blocks, but this is here for legacy purposes in case users resume old tasks)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// if the last message is a user message, we can need to get the assistant message before it to see if it made tool calls, and if so, fill in the remaining tool responses with 'interrupted'\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet modifiedOldUserContent: UserContent // either the last message if its user message, or the user message before the last (assistant) message\\\\r\\\\n\\\\t\\\\tlet modifiedApiConversationHistory: Anthropic.Messages.MessageParam[] // need to remove the last user message to replace with new modified user message\\\\r\\\\n\\\\t\\\\tif (existingApiConversationHistory.length > 0) {\\\\r\\\\n\\\\t\\\\t\\\\tconst lastMessage = existingApiConversationHistory[existingApiConversationHistory.length - 1]\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tif (lastMessage.role === \\\\\\\"assistant\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst content = Array.isArray(lastMessage.content)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t? lastMessage.content\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t: [{ type: \\\\\\\"text\\\\\\\", text: lastMessage.content }]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst hasToolUse = content.some((block) => block.type === \\\\\\\"tool_use\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (hasToolUse) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst toolUseBlocks = content.filter(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t(block) => block.type === \\\\\\\"tool_use\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t) as Anthropic.Messages.ToolUseBlock[]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst toolResponses: Anthropic.ToolResultBlockParam[] = toolUseBlocks.map((block) => ({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"tool_result\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttool_use_id: block.id,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontent: \\\\\\\"Task was interrupted before this tool call could be completed.\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmodifiedApiConversationHistory = [...existingApiConversationHistory] // no changes\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmodifiedOldUserContent = [...toolResponses]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmodifiedApiConversationHistory = [...existingApiConversationHistory]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmodifiedOldUserContent = []\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t} else if (lastMessage.role === \\\\\\\"user\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst previousAssistantMessage: Anthropic.Messages.MessageParam | undefined =\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\texistingApiConversationHistory[existingApiConversationHistory.length - 2]\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst existingUserContent: UserContent = Array.isArray(lastMessage.content)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t? lastMessage.content\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t: [{ type: \\\\\\\"text\\\\\\\", text: lastMessage.content }]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (previousAssistantMessage && previousAssistantMessage.role === \\\\\\\"assistant\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst assistantContent = Array.isArray(previousAssistantMessage.content)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t? previousAssistantMessage.content\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t: [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: previousAssistantMessage.content,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t]\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst toolUseBlocks = assistantContent.filter(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t(block) => block.type === \\\\\\\"tool_use\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t) as Anthropic.Messages.ToolUseBlock[]\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (toolUseBlocks.length > 0) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst existingToolResults = existingUserContent.filter(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t(block) => block.type === \\\\\\\"tool_result\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t) as Anthropic.ToolResultBlockParam[]\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst missingToolResponses: Anthropic.ToolResultBlockParam[] = toolUseBlocks\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.filter((toolUse) => !existingToolResults.some((result) => result.tool_use_id === toolUse.id))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.map((toolUse) => ({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"tool_result\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttool_use_id: toolUse.id,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontent: \\\\\\\"Task was interrupted before this tool call could be completed.\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}))\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodifiedApiConversationHistory = existingApiConversationHistory.slice(0, -1) // removes the last user message\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodifiedOldUserContent = [...existingUserContent, ...missingToolResponses]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodifiedApiConversationHistory = existingApiConversationHistory.slice(0, -1)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodifiedOldUserContent = [...existingUserContent]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmodifiedApiConversationHistory = existingApiConversationHistory.slice(0, -1)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmodifiedOldUserContent = [...existingUserContent]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Unexpected: Last message is not a user or assistant message\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Unexpected: No existing API conversation history\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t// console.error(\\\\\\\"Unexpected: No existing API conversation history\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t// modifiedApiConversationHistory = []\\\\r\\\\n\\\\t\\\\t\\\\t// modifiedOldUserContent = []\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet newUserContent: UserContent = [...modifiedOldUserContent]\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst agoText = (() => {\\\\r\\\\n\\\\t\\\\t\\\\tconst timestamp = lastClineMessage?.ts ?? Date.now()\\\\r\\\\n\\\\t\\\\t\\\\tconst now = Date.now()\\\\r\\\\n\\\\t\\\\t\\\\tconst diff = now - timestamp\\\\r\\\\n\\\\t\\\\t\\\\tconst minutes = Math.floor(diff / 60000)\\\\r\\\\n\\\\t\\\\t\\\\tconst hours = Math.floor(minutes / 60)\\\\r\\\\n\\\\t\\\\t\\\\tconst days = Math.floor(hours / 24)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tif (days > 0) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn `${days} day${days > 1 ? \\\\\\\"s\\\\\\\" : \\\\\\\"\\\\\\\"} ago`\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tif (hours > 0) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn `${hours} hour${hours > 1 ? \\\\\\\"s\\\\\\\" : \\\\\\\"\\\\\\\"} ago`\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tif (minutes > 0) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn `${minutes} minute${minutes > 1 ? \\\\\\\"s\\\\\\\" : \\\\\\\"\\\\\\\"} ago`\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\treturn \\\\\\\"just now\\\\\\\"\\\\r\\\\n\\\\t\\\\t})()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst wasRecent = lastClineMessage?.ts && Date.now() - lastClineMessage.ts < 30_000\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tnewUserContent.push({\\\\r\\\\n\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\ttext:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t`[TASK RESUMPTION] This task was interrupted ${agoText}. It may or may not be complete, so please reassess the task context. Be aware that the project state may have changed since then. The current working directory is now '${cwd.toPosix()}'. If the task has not been completed, retry the last step before interruption and proceed with completing the task.\\\\\\\\n\\\\\\\\nNote: If you previously attempted a tool use that the user did not provide a result for, you should assume the tool use was not successful and assess whether you should retry. If the last tool was a browser_action, the browser has been closed and you must launch a new browser if needed.${\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\twasRecent\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t? \\\\\\\"\\\\\\\\n\\\\\\\\nIMPORTANT: If the last tool use was a replace_in_file or write_to_file that was interrupted, the file was reverted back to its original state before the interrupted edit, and you do NOT need to re-read the file as you already have its up-to-date contents.\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t: \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}` +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t(responseText\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t? `\\\\\\\\n\\\\\\\\nNew instructions for task continuation:\\\\\\\\n<user_message>\\\\\\\\n${responseText}\\\\\\\\n</user_message>`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t: \\\\\\\"\\\\\\\"),\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (responseImages && responseImages.length > 0) {\\\\r\\\\n\\\\t\\\\t\\\\tnewUserContent.push(...formatResponse.imageBlocks(responseImages))\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tawait this.overwriteApiConversationHistory(modifiedApiConversationHistory)\\\\r\\\\n\\\\t\\\\tawait this.initiateTaskLoop(newUserContent, false)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async initiateTaskLoop(userContent: UserContent, isNewTask: boolean): Promise<void> {\\\\r\\\\n\\\\t\\\\tlet nextUserContent = userContent\\\\r\\\\n\\\\t\\\\tlet includeFileDetails = true\\\\r\\\\n\\\\t\\\\twhile (!this.abort) {\\\\r\\\\n\\\\t\\\\t\\\\tconst didEndLoop = await this.recursivelyMakeClineRequests(nextUserContent, includeFileDetails, isNewTask)\\\\r\\\\n\\\\t\\\\t\\\\tincludeFileDetails = false // we only need file details the first time\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// The way this agentic loop works is that cline will be given a task that he then calls tools to complete. unless there's an attempt_completion call, we keep responding back to him with his tool's responses until he either attempt_completion or does not use anymore tools. If he does not use anymore tools, we ask him to consider if he's completed the task and then call attempt_completion, otherwise proceed with completing the task.\\\\r\\\\n\\\\t\\\\t\\\\t// There is a MAX_REQUESTS_PER_TASK limit to prevent infinite requests, but Cline is prompted to finish the task as efficiently as he can.\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t//const totalCost = this.calculateApiCost(totalInputTokens, totalOutputTokens)\\\\r\\\\n\\\\t\\\\t\\\\tif (didEndLoop) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// For now a task never 'completes'. This will only happen if the user hits max requests and denies resetting the count.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t//this.say(\\\\\\\"task_completed\\\\\\\", `Task completed. Total API usage cost: ${totalCost}`)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// this.say(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// \\\\t\\\\\\\"tool\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// \\\\t\\\\\\\"Cline responded with only text blocks but has not called attempt_completion yet. Forcing him to continue with task...\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// )\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tnextUserContent = [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: formatResponse.noToolsUsed(),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync abortTask() {\\\\r\\\\n\\\\t\\\\tthis.abort = true // will stop any autonomously running promises\\\\r\\\\n\\\\t\\\\tthis.terminalManager.disposeAll()\\\\r\\\\n\\\\t\\\\tthis.urlContentFetcher.closeBrowser()\\\\r\\\\n\\\\t\\\\tthis.browserSession.closeBrowser()\\\\r\\\\n\\\\t\\\\tawait this.diffViewProvider.revertChanges() // need to await for when we want to make sure directories/files are reverted before re-starting the task from a checkpoint\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Checkpoints\\\\r\\\\n\\\\r\\\\n\\\\tasync saveCheckpoint() {\\\\r\\\\n\\\\t\\\\tconst commitHash = await this.checkpointTracker?.commit() // silently fails for now\\\\r\\\\n\\\\t\\\\tif (commitHash) {\\\\r\\\\n\\\\t\\\\t\\\\t// Start from the end and work backwards until we find a tool use or another message with a hash\\\\r\\\\n\\\\t\\\\t\\\\tfor (let i = this.clineMessages.length - 1; i >= 0; i--) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst message = this.clineMessages[i]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (message.lastCheckpointHash) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Found a message with a hash, so we can stop\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Update this message with a hash\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tmessage.lastCheckpointHash = commitHash\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// We only care about adding the hash to the last tool use (we don't want to add this hash to every prior message ie for tasks pre-checkpoint)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst isToolUse =\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmessage.say === \\\\\\\"tool\\\\\\\" ||\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmessage.ask === \\\\\\\"tool\\\\\\\" ||\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmessage.say === \\\\\\\"command\\\\\\\" ||\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmessage.ask === \\\\\\\"command\\\\\\\" ||\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmessage.say === \\\\\\\"completion_result\\\\\\\" ||\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmessage.ask === \\\\\\\"completion_result\\\\\\\" ||\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmessage.ask === \\\\\\\"followup\\\\\\\" ||\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmessage.say === \\\\\\\"use_mcp_server\\\\\\\" ||\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmessage.ask === \\\\\\\"use_mcp_server\\\\\\\" ||\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmessage.say === \\\\\\\"browser_action\\\\\\\" ||\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmessage.say === \\\\\\\"browser_action_launch\\\\\\\" ||\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmessage.ask === \\\\\\\"browser_action_launch\\\\\\\"\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (isToolUse) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t// Save the updated messages\\\\r\\\\n\\\\t\\\\t\\\\tawait this.saveClineMessages()\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Tools\\\\r\\\\n\\\\r\\\\n\\\\tasync executeCommandTool(command: string): Promise<[boolean, ToolResponse]> {\\\\r\\\\n\\\\t\\\\tconst terminalInfo = await this.terminalManager.getOrCreateTerminal(cwd)\\\\r\\\\n\\\\t\\\\tterminalInfo.terminal.show() // weird visual bug when creating new terminals (even manually) where there's an empty space at the top.\\\\r\\\\n\\\\t\\\\tconst process = this.terminalManager.runCommand(terminalInfo, command)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet userFeedback: { text?: string; images?: string[] } | undefined\\\\r\\\\n\\\\t\\\\tlet didContinue = false\\\\r\\\\n\\\\t\\\\tconst sendCommandOutput = async (line: string): Promise<void> => {\\\\r\\\\n\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst { response, text, images } = await this.ask(\\\\\\\"command_output\\\\\\\", line)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (response === \\\\\\\"yesButtonClicked\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// proceed while running\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tuserFeedback = { text, images }\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tdidContinue = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tprocess.continue() // continue past the await\\\\r\\\\n\\\\t\\\\t\\\\t} catch {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// This can only happen if this ask promise was ignored, so ignore this error\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet result = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\tprocess.on(\\\\\\\"line\\\\\\\", (line) => {\\\\r\\\\n\\\\t\\\\t\\\\tresult += line + \\\\\\\"\\\\\\\\n\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tif (!didContinue) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tsendCommandOutput(line)\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.say(\\\\\\\"command_output\\\\\\\", line)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet completed = false\\\\r\\\\n\\\\t\\\\tprocess.once(\\\\\\\"completed\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tcompleted = true\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tprocess.once(\\\\\\\"no_shell_integration\\\\\\\", async () => {\\\\r\\\\n\\\\t\\\\t\\\\tawait this.say(\\\\\\\"shell_integration_warning\\\\\\\")\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tawait process\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Wait for a short delay to ensure all messages are sent to the webview\\\\r\\\\n\\\\t\\\\t// This delay allows time for non-awaited promises to be created and\\\\r\\\\n\\\\t\\\\t// for their associated messages to be sent to the webview, maintaining\\\\r\\\\n\\\\t\\\\t// the correct order of messages (although the webview is smart about\\\\r\\\\n\\\\t\\\\t// grouping command_output messages despite any gaps anyways)\\\\r\\\\n\\\\t\\\\tawait delay(50)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tresult = result.trim()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (userFeedback) {\\\\r\\\\n\\\\t\\\\t\\\\tawait this.say(\\\\\\\"user_feedback\\\\\\\", userFeedback.text, userFeedback.images)\\\\r\\\\n\\\\t\\\\t\\\\treturn [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttrue,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tformatResponse.toolResult(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t`Command is still running in the user's terminal.${\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tresult.length > 0 ? `\\\\\\\\nHere's the output so far:\\\\\\\\n${result}` : \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\\\\\n\\\\\\\\nThe user provided the following feedback:\\\\\\\\n<feedback>\\\\\\\\n${userFeedback.text}\\\\\\\\n</feedback>`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tuserFeedback.images,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t),\\\\r\\\\n\\\\t\\\\t\\\\t]\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (completed) {\\\\r\\\\n\\\\t\\\\t\\\\treturn [false, `Command executed.${result.length > 0 ? `\\\\\\\\nOutput:\\\\\\\\n${result}` : \\\\\\\"\\\\\\\"}`]\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\treturn [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tfalse,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t`Command is still running in the user's terminal.${\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tresult.length > 0 ? `\\\\\\\\nHere's the output so far:\\\\\\\\n${result}` : \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\\\\\n\\\\\\\\nYou will be updated on the terminal status and new output in the future.`,\\\\r\\\\n\\\\t\\\\t\\\\t]\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tshouldAutoApproveTool(toolName: ToolUseName): boolean {\\\\r\\\\n\\\\t\\\\tif (this.autoApprovalSettings.enabled) {\\\\r\\\\n\\\\t\\\\t\\\\tswitch (toolName) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"read_file\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"list_files\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"list_code_definition_names\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"search_files\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn this.autoApprovalSettings.actions.readFiles\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"write_to_file\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"replace_in_file\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn this.autoApprovalSettings.actions.editFiles\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"execute_command\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn this.autoApprovalSettings.actions.executeCommands\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"browser_action\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn this.autoApprovalSettings.actions.useBrowser\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"access_mcp_resource\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"use_mcp_tool\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn this.autoApprovalSettings.actions.useMcp\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn false\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync *attemptApiRequest(previousApiReqIndex: number): ApiStream {\\\\r\\\\n\\\\t\\\\t// Wait for MCP servers to be connected before generating system prompt\\\\r\\\\n\\\\t\\\\tawait pWaitFor(() => this.providerRef.deref()?.mcpHub?.isConnecting !== true, { timeout: 10_000 }).catch(() => {\\\\r\\\\n\\\\t\\\\t\\\\tconsole.error(\\\\\\\"MCP servers failed to connect in time\\\\\\\")\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst mcpHub = this.providerRef.deref()?.mcpHub\\\\r\\\\n\\\\t\\\\tif (!mcpHub) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"MCP hub not available\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet systemPrompt = await SYSTEM_PROMPT(cwd, this.api.getModel().info.supportsComputerUse ?? false, mcpHub)\\\\r\\\\n\\\\t\\\\tlet settingsCustomInstructions = this.customInstructions?.trim()\\\\r\\\\n\\\\t\\\\tconst clineRulesFilePath = path.resolve(cwd, GlobalFileNames.clineRules)\\\\r\\\\n\\\\t\\\\tlet clineRulesFileInstructions: string | undefined\\\\r\\\\n\\\\t\\\\tif (await fileExistsAtPath(clineRulesFilePath)) {\\\\r\\\\n\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst ruleFileContent = (await fs.readFile(clineRulesFilePath, \\\\\\\"utf8\\\\\\\")).trim()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (ruleFileContent) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tclineRulesFileInstructions = `# .clinerules\\\\\\\\n\\\\\\\\nThe following is provided by a root-level .clinerules file where the user has specified instructions for this working directory (${cwd.toPosix()})\\\\\\\\n\\\\\\\\n${ruleFileContent}`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t} catch {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.error(`Failed to read .clinerules file at ${clineRulesFilePath}`)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (settingsCustomInstructions || clineRulesFileInstructions) {\\\\r\\\\n\\\\t\\\\t\\\\t// altering the system prompt mid-task will break the prompt cache, but in the grand scheme this will not change often so it's better to not pollute user messages with it the way we have to with <potentially relevant details>\\\\r\\\\n\\\\t\\\\t\\\\tsystemPrompt += addUserInstructions(settingsCustomInstructions, clineRulesFileInstructions)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// If the previous API request's total token usage is close to the context window, truncate the conversation history to free up space for the new request\\\\r\\\\n\\\\t\\\\tif (previousApiReqIndex >= 0) {\\\\r\\\\n\\\\t\\\\t\\\\tconst previousRequest = this.clineMessages[previousApiReqIndex]\\\\r\\\\n\\\\t\\\\t\\\\tif (previousRequest && previousRequest.text) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst { tokensIn, tokensOut, cacheWrites, cacheReads }: ClineApiReqInfo = JSON.parse(previousRequest.text)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst totalTokens = (tokensIn || 0) + (tokensOut || 0) + (cacheWrites || 0) + (cacheReads || 0)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlet contextWindow = this.api.getModel().info.contextWindow || 128_000\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// 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\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (this.api instanceof OpenAiHandler && this.api.getModel().id.toLowerCase().includes(\\\\\\\"deepseek\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcontextWindow = 64_000\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlet maxAllowedSize: number\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tswitch (contextWindow) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase 64_000: // deepseek models\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmaxAllowedSize = contextWindow - 27_000\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase 128_000: // most models\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmaxAllowedSize = contextWindow - 30_000\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase 200_000: // claude models\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmaxAllowedSize = contextWindow - 40_000\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tdefault:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmaxAllowedSize = Math.max(contextWindow - 40_000, contextWindow * 0.8) // for deepseek, 80% of 64k meant only ~10k buffer which was too small and resulted in users getting context window errors.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// This is the most reliable way to know when we're close to hitting the context window.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (totalTokens >= maxAllowedSize) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// NOTE: it's okay that we overwriteConversationHistory in resume task since we're only ever removing the last user message and not anything in the middle which would affect this range\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.conversationHistoryDeletedRange = getNextTruncationRange(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.apiConversationHistory,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.conversationHistoryDeletedRange,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveClineMessages() // saves task history item which we use to keep track of conversation history deleted range\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// await this.overwriteApiConversationHistory(truncatedMessages)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// conversationHistoryDeletedRange is updated only when we're close to hitting the context window, so we don't continuously break the prompt cache\\\\r\\\\n\\\\t\\\\tconst truncatedConversationHistory = getTruncatedMessages(\\\\r\\\\n\\\\t\\\\t\\\\tthis.apiConversationHistory,\\\\r\\\\n\\\\t\\\\t\\\\tthis.conversationHistoryDeletedRange,\\\\r\\\\n\\\\t\\\\t)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst stream = this.api.createMessage(systemPrompt, truncatedConversationHistory)\\\\r\\\\n\\\\t\\\\tconst iterator = stream[Symbol.asyncIterator]()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t// awaiting first chunk to see if it will throw an error\\\\r\\\\n\\\\t\\\\t\\\\tconst firstChunk = await iterator.next()\\\\r\\\\n\\\\t\\\\t\\\\tyield firstChunk.value\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t// 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.\\\\r\\\\n\\\\t\\\\t\\\\tconst { response } = await this.ask(\\\\\\\"api_req_failed\\\\\\\", error.message ?? JSON.stringify(serializeError(error), null, 2))\\\\r\\\\n\\\\t\\\\t\\\\tif (response !== \\\\\\\"yesButtonClicked\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// this will never happen since if noButtonClicked, we will clear current task, aborting this instance\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"API request failed\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tawait this.say(\\\\\\\"api_req_retried\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t// delegate generator output from the recursive call\\\\r\\\\n\\\\t\\\\t\\\\tyield* this.attemptApiRequest(previousApiReqIndex)\\\\r\\\\n\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// no error, so we can continue to yield all remaining chunks\\\\r\\\\n\\\\t\\\\t// (needs to be placed outside of try/catch since it we want caller to handle errors not with api_req_failed as that is reserved for first chunk failures only)\\\\r\\\\n\\\\t\\\\t// this delegates to another generator or iterable object. In this case, it's saying \\\\\\\"yield all remaining values from this iterator\\\\\\\". This effectively passes along all subsequent chunks from the original stream.\\\\r\\\\n\\\\t\\\\tyield* iterator\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync presentAssistantMessage() {\\\\r\\\\n\\\\t\\\\tif (this.abort) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Cline instance aborted\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (this.presentAssistantMessageLocked) {\\\\r\\\\n\\\\t\\\\t\\\\tthis.presentAssistantMessageHasPendingUpdates = true\\\\r\\\\n\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tthis.presentAssistantMessageLocked = true\\\\r\\\\n\\\\t\\\\tthis.presentAssistantMessageHasPendingUpdates = false\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (this.currentStreamingContentIndex >= this.assistantMessageContent.length) {\\\\r\\\\n\\\\t\\\\t\\\\t// this may happen if the last content block was completed before streaming could finish. if streaming is finished, and we're out of bounds then this means we already presented/executed the last content block and are ready to continue to next request\\\\r\\\\n\\\\t\\\\t\\\\tif (this.didCompleteReadingStream) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.userMessageContentReady = true\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t// console.log(\\\\\\\"no more content blocks to stream! this shouldn't happen?\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tthis.presentAssistantMessageLocked = false\\\\r\\\\n\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t\\\\t//throw new Error(\\\\\\\"No more content blocks to stream! This shouldn't happen...\\\\\\\") // remove and just return after testing\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst block = cloneDeep(this.assistantMessageContent[this.currentStreamingContentIndex]) // need to create copy bc while stream is updating the array, it could be updating the reference block properties too\\\\r\\\\n\\\\t\\\\tswitch (block.type) {\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"text\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (this.didRejectTool || this.didAlreadyUseTool) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlet content = block.content\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (content) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// (have to do this for partial and complete since sending content in thinking tags to markdown renderer will automatically be removed)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Remove end substrings of <thinking or </thinking (below xml parsing is only for opening tags)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// (this is done with the xml parsing below now, but keeping here for reference)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// content = content.replace(/<\\\\\\\\/?t(?:h(?:i(?:n(?:k(?:i(?:n(?:g)?)?)?)?)?)?)?$/, \\\\\\\"\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Remove all instances of <thinking> (with optional line break after) and </thinking> (with optional line break before)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// - Needs to be separate since we dont want to remove the line break before the first tag\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// - Needs to happen before the xml parsing below\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcontent = content.replace(/<thinking>\\\\\\\\s?/g, \\\\\\\"\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcontent = content.replace(/\\\\\\\\s?<\\\\\\\\/thinking>/g, \\\\\\\"\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Remove partial XML tag at the very end of the content (for tool use and thinking tags)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// (prevents scrollview from jumping when tags are automatically removed)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst lastOpenBracketIndex = content.lastIndexOf(\\\\\\\"<\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (lastOpenBracketIndex !== -1) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst possibleTag = content.slice(lastOpenBracketIndex)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// Check if there's a '>' after the last '<' (i.e., if the tag is complete) (complete thinking and tool tags will have been removed by now)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst hasCloseBracket = possibleTag.includes(\\\\\\\">\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!hasCloseBracket) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// Extract the potential tag name\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlet tagContent: string\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (possibleTag.startsWith(\\\\\\\"</\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttagContent = possibleTag.slice(2).trim()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttagContent = possibleTag.slice(1).trim()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// Check if tagContent is likely an incomplete tag name (letters and underscores only)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst isLikelyTagName = /^[a-zA-Z_]+$/.test(tagContent)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// Preemptively remove < or </ to keep from these artifacts showing up in chat (also handles closing thinking tags)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst isOpeningOrClosing = possibleTag === \\\\\\\"<\\\\\\\" || possibleTag === \\\\\\\"</\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// If the tag is incomplete and at the end, remove it from the content\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (isOpeningOrClosing || isLikelyTagName) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontent = content.slice(0, lastOpenBracketIndex).trim()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (!block.partial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Some models add code block artifacts (around the tool calls) which show up at the end of text content\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// matches ``` with atleast one char after the last backtick, at the end of the string\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst match = content?.trimEnd().match(/```[a-zA-Z0-9_-]+$/)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (match) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst matchLength = match[0].length\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontent = content.trimEnd().slice(0, -matchLength)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"text\\\\\\\", content, undefined, block.partial)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"tool_use\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst toolDescription = () => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tswitch (block.name) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"execute_command\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn `[${block.name} for '${block.params.command}']`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"read_file\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn `[${block.name} for '${block.params.path}']`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"write_to_file\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn `[${block.name} for '${block.params.path}']`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"replace_in_file\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn `[${block.name} for '${block.params.path}']`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"search_files\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn `[${block.name} for '${block.params.regex}'${\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tblock.params.file_pattern ? ` in '${block.params.file_pattern}'` : \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}]`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"list_files\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn `[${block.name} for '${block.params.path}']`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"list_code_definition_names\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn `[${block.name} for '${block.params.path}']`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"browser_action\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn `[${block.name} for '${block.params.action}']`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"use_mcp_tool\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn `[${block.name} for '${block.params.server_name}']`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"access_mcp_resource\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn `[${block.name} for '${block.params.server_name}']`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"ask_followup_question\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn `[${block.name} for '${block.params.question}']`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"attempt_completion\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn `[${block.name}]`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (this.didRejectTool) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// ignore any tool content after user has rejected tool once\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (!block.partial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.userMessageContent.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: `Skipping tool ${toolDescription()} due to user rejecting a previous tool.`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// partial tool after user rejected a previous tool\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.userMessageContent.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: `Tool ${toolDescription()} was interrupted and not executed due to user rejecting a previous tool.`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (this.didAlreadyUseTool) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// ignore any content after a tool has already been used\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.userMessageContent.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: `Tool [${block.name}] was not executed because a tool has already been used in this message. Only one tool may be used per message. You must assess the first tool's result before proceeding to use the next tool.`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst pushToolResult = (content: ToolResponse) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.userMessageContent.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: `${toolDescription()} Result:`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (typeof content === \\\\\\\"string\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.userMessageContent.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: content || \\\\\\\"(tool did not return anything)\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.userMessageContent.push(...content)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// once a tool result has been collected, ignore all other tool uses since we should only ever present one tool result per message\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.didAlreadyUseTool = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst askApproval = async (type: ClineAsk, partialMessage?: string) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst { response, text, images } = await this.ask(type, partialMessage, false)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (response !== \\\\\\\"yesButtonClicked\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (response === \\\\\\\"messageResponse\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"user_feedback\\\\\\\", text, images)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(formatResponse.toolResult(formatResponse.toolDeniedWithFeedback(text), images))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// this.userMessageContent.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\ttext: `${toolDescription()}`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// })\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// this.toolResults.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\ttype: \\\\\\\"tool_result\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\ttool_use_id: toolUseId,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\tcontent: this.formatToolResponseWithImages(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\t\\\\tawait this.formatToolDeniedFeedback(text),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\t\\\\timages\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\t),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// })\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.didRejectTool = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn false\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(formatResponse.toolDenied())\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// this.toolResults.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\ttype: \\\\\\\"tool_result\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\ttool_use_id: toolUseId,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\tcontent: await this.formatToolDenied(),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// })\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.didRejectTool = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn false\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst showNotificationForApprovalIfAutoApprovalEnabled = (message: string) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (this.autoApprovalSettings.enabled && this.autoApprovalSettings.enableNotifications) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tshowSystemNotification({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tsubtitle: \\\\\\\"Approval Required\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmessage,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst handleError = async (action: string, error: Error) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (this.abandoned) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconsole.log(\\\\\\\"Ignoring error since task was abandoned (i.e. from task cancellation after resetting)\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst errorString = `Error ${action}: ${JSON.stringify(serializeError(error))}`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"error\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`Error ${action}:\\\\\\\\n${error.message ?? JSON.stringify(serializeError(error), null, 2)}`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// this.toolResults.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\ttype: \\\\\\\"tool_result\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\ttool_use_id: toolUseId,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\tcontent: await this.formatToolError(errorString),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// })\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(formatResponse.toolError(errorString))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// If block is partial, remove partial closing tag so its not presented to user\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst removeClosingTag = (tag: ToolParamName, text?: string) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (!block.partial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn text || \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (!text) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// This regex dynamically constructs a pattern to match the closing tag:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// - Optionally matches whitespace before the tag\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// - Matches '<' or '</' optionally followed by any subset of characters from the tag name\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst tagRegex = new RegExp(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`\\\\\\\\\\\\\\\\s?<\\\\\\\\/?${tag\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.split(\\\\\\\"\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.map((char) => `(?:${char})?`)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.join(\\\\\\\"\\\\\\\")}$`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"g\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn text.replace(tagRegex, \\\\\\\"\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (block.name !== \\\\\\\"browser_action\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.browserSession.closeBrowser()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tswitch (block.name) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"write_to_file\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"replace_in_file\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst relPath: string | undefined = block.params.path\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlet content: string | undefined = block.params.content // for write_to_file\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlet diff: string | undefined = block.params.diff // for replace_in_file\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!relPath || (!content && !diff)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// checking for content/diff ensures relPath is complete\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// wait so we can determine if it's a new file or editing an existing file\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// Check if file exists using cached map or fs.access\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlet fileExists: boolean\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.diffViewProvider.editType !== undefined) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tfileExists = this.diffViewProvider.editType === \\\\\\\"modify\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst absolutePath = path.resolve(cwd, relPath)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tfileExists = await fileExistsAtPath(absolutePath)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.diffViewProvider.editType = fileExists ? \\\\\\\"modify\\\\\\\" : \\\\\\\"create\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// Construct newContent from diff\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlet newContent: string\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (diff) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!this.api.getModel().id.includes(\\\\\\\"claude\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// deepseek models tend to use unescaped html entities in diffs\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tdiff = fixModelHtmlEscaping(diff)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tdiff = removeInvalidChars(diff)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tnewContent = await constructNewFileContent(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tdiff,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.diffViewProvider.originalContent || \\\\\\\"\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t!block.partial,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"diff_error\\\\\\\", relPath)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tformatResponse.toolError(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`${(error as Error)?.message}\\\\\\\\n\\\\\\\\n` +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`This is likely because the SEARCH block content doesn't match exactly with what's in the file, or if you used multiple SEARCH/REPLACE blocks they may not have been in the order they appear in the file.\\\\\\\\n\\\\\\\\n` +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`The file was reverted to its original state:\\\\\\\\n\\\\\\\\n` +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`<file_content path=\\\\\\\"${relPath.toPosix()}\\\\\\\">\\\\\\\\n${this.diffViewProvider.originalContent}\\\\\\\\n</file_content>\\\\\\\\n\\\\\\\\n` +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`Try again with a more precise SEARCH block.\\\\\\\\n(If you keep running into this error, you may use the write_to_file tool as a workaround.)`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.diffViewProvider.revertChanges()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.diffViewProvider.reset()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else if (content) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tnewContent = content\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// pre-processing newContent for cases where weaker models might add artifacts like markdown codeblock markers (deepseek/llama) or extra escape characters (gemini)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (newContent.startsWith(\\\\\\\"```\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// this handles cases where it includes language specifiers like ```python ```js\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tnewContent = newContent.split(\\\\\\\"\\\\\\\\n\\\\\\\").slice(1).join(\\\\\\\"\\\\\\\\n\\\\\\\").trim()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (newContent.endsWith(\\\\\\\"```\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tnewContent = newContent.split(\\\\\\\"\\\\\\\\n\\\\\\\").slice(0, -1).join(\\\\\\\"\\\\\\\\n\\\\\\\").trim()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!this.api.getModel().id.includes(\\\\\\\"claude\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// it seems not just llama models are doing this, but also gemini and potentially others\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tnewContent = fixModelHtmlEscaping(newContent)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tnewContent = removeInvalidChars(newContent)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// can't happen, since we already checked for content/diff above. but need to do this for type error\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tnewContent = newContent.trimEnd() // remove any trailing newlines, since it's automatically inserted by the editor\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst sharedMessageProps: ClineSayTool = {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttool: fileExists ? \\\\\\\"editedExistingFile\\\\\\\" : \\\\\\\"newFileCreated\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpath: getReadablePath(cwd, removeClosingTag(\\\\\\\"path\\\\\\\", relPath)),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontent: diff || content,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (block.partial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// update gui message\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst partialMessage = JSON.stringify(sharedMessageProps)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.shouldAutoApproveTool(block.name)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"ask\\\\\\\", \\\\\\\"tool\\\\\\\") // in case the user changes auto-approval settings mid stream\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"tool\\\\\\\", partialMessage, undefined, block.partial)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"say\\\\\\\", \\\\\\\"tool\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.ask(\\\\\\\"tool\\\\\\\", partialMessage, block.partial).catch(() => {})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// update editor\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!this.diffViewProvider.isEditing) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// open the editor and prepare to stream content in\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.diffViewProvider.open(relPath)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// editor is open, stream content in\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.diffViewProvider.update(newContent, false)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!relPath) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(await this.sayAndCreateMissingParamError(block.name, \\\\\\\"path\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.diffViewProvider.reset()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (block.name === \\\\\\\"replace_in_file\\\\\\\" && !diff) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\\\\\"replace_in_file\\\\\\\", \\\\\\\"diff\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.diffViewProvider.reset()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (block.name === \\\\\\\"write_to_file\\\\\\\" && !content) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\\\\\"write_to_file\\\\\\\", \\\\\\\"content\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.diffViewProvider.reset()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount = 0\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// if isEditingFile false, that means we have the full contents of the file already.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// it's important to note how this function works, you can't make the assumption that the block.partial conditional will always be called since it may immediately get complete, non-partial data. So this part of the logic will always be called.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// in other words, you must always repeat the block.partial logic here\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!this.diffViewProvider.isEditing) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// show gui message before showing edit animation\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst partialMessage = JSON.stringify(sharedMessageProps)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.ask(\\\\\\\"tool\\\\\\\", partialMessage, true).catch(() => {}) // sending true for partial even though it's not a partial, this shows the edit row before the content is streamed into the editor\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.diffViewProvider.open(relPath)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.diffViewProvider.update(newContent, true)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait delay(300) // wait for diff view to update\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.diffViewProvider.scrollToFirstDiff()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// showOmissionWarning(this.diffViewProvider.originalContent || \\\\\\\"\\\\\\\", newContent)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst completeMessage = JSON.stringify({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t...sharedMessageProps,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontent: diff || content,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// ? formatResponse.createPrettyPatch(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\t\\\\trelPath,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\t\\\\tthis.diffViewProvider.originalContent,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\t\\\\tnewContent,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// : undefined,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} satisfies ClineSayTool)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.shouldAutoApproveTool(block.name)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"ask\\\\\\\", \\\\\\\"tool\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"tool\\\\\\\", completeMessage, undefined, false)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveAutoApprovedRequestsCount++\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// we need an artificial delay to let the diagnostics catch up to the changes\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait delay(3_500)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// If auto-approval is enabled but this tool wasn't auto-approved, send notification\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tshowNotificationForApprovalIfAutoApprovalEnabled(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`Cline wants to ${fileExists ? \\\\\\\"edit\\\\\\\" : \\\\\\\"create\\\\\\\"} ${path.basename(relPath)}`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"say\\\\\\\", \\\\\\\"tool\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// const didApprove = await askApproval(\\\\\\\"tool\\\\\\\", completeMessage)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// Need a more customized tool response for file edits to highlight the fact that the file was not updated (particularly important for deepseek)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlet didApprove = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst { response, text, images } = await this.ask(\\\\\\\"tool\\\\\\\", completeMessage, false)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (response !== \\\\\\\"yesButtonClicked\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// TODO: add similar context for other tool denial responses, to emphasize ie that a command was not run\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst fileDeniedNote = fileExists\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t? \\\\\\\"The file was not updated, and maintains its original contents.\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t: \\\\\\\"The file was not created.\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (response === \\\\\\\"messageResponse\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"user_feedback\\\\\\\", text, images)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tformatResponse.toolResult(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`The user denied this operation. ${fileDeniedNote}\\\\\\\\nThe user provided the following feedback:\\\\\\\\n<feedback>\\\\\\\\n${text}\\\\\\\\n</feedback>`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\timages,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.didRejectTool = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tdidApprove = false\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(`The user denied this operation. ${fileDeniedNote}`)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.didRejectTool = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tdidApprove = false\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!didApprove) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.diffViewProvider.revertChanges()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst { newProblemsMessage, userEdits, autoFormattingEdits, finalContent } =\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.diffViewProvider.saveChanges()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.didEditFile = true // used to determine if we should wait for busy terminal to update before sending api request\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (userEdits) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"user_feedback_diff\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tJSON.stringify({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttool: fileExists ? \\\\\\\"editedExistingFile\\\\\\\" : \\\\\\\"newFileCreated\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpath: getReadablePath(cwd, relPath),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tdiff: userEdits,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} satisfies ClineSayTool),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`The user made the following updates to your content:\\\\\\\\n\\\\\\\\n${userEdits}\\\\\\\\n\\\\\\\\n` +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t(autoFormattingEdits\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t? `The user's editor also applied the following auto-formatting to your content:\\\\\\\\n\\\\\\\\n${autoFormattingEdits}\\\\\\\\n\\\\\\\\n(Note: Pay close attention to changes such as single quotes being converted to double quotes, semicolons being removed or added, long lines being broken into multiple lines, adjusting indentation style, adding/removing trailing commas, etc. This will help you ensure future SEARCH/REPLACE operations to this file are accurate.)\\\\\\\\n\\\\\\\\n`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t: \\\\\\\"\\\\\\\") +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`The updated content, which includes both your original modifications and the additional edits, has been successfully saved to ${relPath.toPosix()}. Here is the full, updated content of the file that was saved:\\\\\\\\n\\\\\\\\n` +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`<final_file_content path=\\\\\\\"${relPath.toPosix()}\\\\\\\">\\\\\\\\n${finalContent}\\\\\\\\n</final_file_content>\\\\\\\\n\\\\\\\\n` +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`Please note:\\\\\\\\n` +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`1. You do not need to re-write the file with these changes, as they have already been applied.\\\\\\\\n` +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`2. Proceed with the task using this updated file content as the new baseline.\\\\\\\\n` +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`3. If the user's edits have addressed part of the task or changed the requirements, adjust your approach accordingly.` +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`4. IMPORTANT: For any future changes to this file, use the final_file_content shown above as your reference. This content reflects the current state of the file, including both user edits and any auto-formatting (e.g., if you used single quotes but the formatter converted them to double quotes). Always base your SEARCH/REPLACE operations on this final version to ensure accuracy.\\\\\\\\n` +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`${newProblemsMessage}`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`The content was successfully saved to ${relPath.toPosix()}.\\\\\\\\n\\\\\\\\n` +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t(autoFormattingEdits\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t? `Along with your edits, the user's editor applied the following auto-formatting to your content:\\\\\\\\n\\\\\\\\n${autoFormattingEdits}\\\\\\\\n\\\\\\\\n(Note: Pay close attention to changes such as single quotes being converted to double quotes, semicolons being removed or added, long lines being broken into multiple lines, adjusting indentation style, adding/removing trailing commas, etc. This will help you ensure future SEARCH/REPLACE operations to this file are accurate.)\\\\\\\\n\\\\\\\\n`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t: \\\\\\\"\\\\\\\") +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`Here is the full, updated content of the file that was saved:\\\\\\\\n\\\\\\\\n` +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`<final_file_content path=\\\\\\\"${relPath.toPosix()}\\\\\\\">\\\\\\\\n${finalContent}\\\\\\\\n</final_file_content>\\\\\\\\n\\\\\\\\n` +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`IMPORTANT: For any future changes to this file, use the final_file_content shown above as your reference. This content reflects the current state of the file, including any auto-formatting (e.g., if you used single quotes but the formatter converted them to double quotes). Always base your SEARCH/REPLACE operations on this final version to ensure accuracy.\\\\\\\\n\\\\\\\\n` +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`${newProblemsMessage}`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.diffViewProvider.reset()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait handleError(\\\\\\\"writing file\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.diffViewProvider.revertChanges()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.diffViewProvider.reset()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"read_file\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst relPath: string | undefined = block.params.path\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst sharedMessageProps: ClineSayTool = {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttool: \\\\\\\"readFile\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpath: getReadablePath(cwd, removeClosingTag(\\\\\\\"path\\\\\\\", relPath)),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (block.partial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst partialMessage = JSON.stringify({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t...sharedMessageProps,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontent: undefined,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} satisfies ClineSayTool)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.shouldAutoApproveTool(block.name)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"ask\\\\\\\", \\\\\\\"tool\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"tool\\\\\\\", partialMessage, undefined, block.partial)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"say\\\\\\\", \\\\\\\"tool\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.ask(\\\\\\\"tool\\\\\\\", partialMessage, block.partial).catch(() => {})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!relPath) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\\\\\"read_file\\\\\\\", \\\\\\\"path\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount = 0\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst absolutePath = path.resolve(cwd, relPath)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst completeMessage = JSON.stringify({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t...sharedMessageProps,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontent: absolutePath,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} satisfies ClineSayTool)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.shouldAutoApproveTool(block.name)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"ask\\\\\\\", \\\\\\\"tool\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"tool\\\\\\\", completeMessage, undefined, false) // need to be sending partialValue bool, since undefined has its own purpose in that the message is treated neither as a partial or completion of a partial, but as a single complete message\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveAutoApprovedRequestsCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tshowNotificationForApprovalIfAutoApprovalEnabled(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`Cline wants to read ${path.basename(absolutePath)}`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"say\\\\\\\", \\\\\\\"tool\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst didApprove = await askApproval(\\\\\\\"tool\\\\\\\", completeMessage)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!didApprove) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// now execute the tool like normal\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst content = await extractTextFromFile(absolutePath)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(content)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait handleError(\\\\\\\"reading file\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"list_files\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst relDirPath: string | undefined = block.params.path\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst recursiveRaw: string | undefined = block.params.recursive\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst recursive = recursiveRaw?.toLowerCase() === \\\\\\\"true\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst sharedMessageProps: ClineSayTool = {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttool: !recursive ? \\\\\\\"listFilesTopLevel\\\\\\\" : \\\\\\\"listFilesRecursive\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpath: getReadablePath(cwd, removeClosingTag(\\\\\\\"path\\\\\\\", relDirPath)),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (block.partial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst partialMessage = JSON.stringify({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t...sharedMessageProps,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontent: \\\\\\\"\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} satisfies ClineSayTool)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.shouldAutoApproveTool(block.name)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"ask\\\\\\\", \\\\\\\"tool\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"tool\\\\\\\", partialMessage, undefined, block.partial)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"say\\\\\\\", \\\\\\\"tool\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.ask(\\\\\\\"tool\\\\\\\", partialMessage, block.partial).catch(() => {})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!relDirPath) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\\\\\"list_files\\\\\\\", \\\\\\\"path\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount = 0\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst absolutePath = path.resolve(cwd, relDirPath)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst [files, didHitLimit] = await listFiles(absolutePath, recursive, 200)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst result = formatResponse.formatFilesList(absolutePath, files, didHitLimit)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst completeMessage = JSON.stringify({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t...sharedMessageProps,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontent: result,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} satisfies ClineSayTool)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.shouldAutoApproveTool(block.name)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"ask\\\\\\\", \\\\\\\"tool\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"tool\\\\\\\", completeMessage, undefined, false)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveAutoApprovedRequestsCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tshowNotificationForApprovalIfAutoApprovalEnabled(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`Cline wants to view directory ${path.basename(absolutePath)}/`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"say\\\\\\\", \\\\\\\"tool\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst didApprove = await askApproval(\\\\\\\"tool\\\\\\\", completeMessage)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!didApprove) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(result)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait handleError(\\\\\\\"listing files\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"list_code_definition_names\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst relDirPath: string | undefined = block.params.path\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst sharedMessageProps: ClineSayTool = {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttool: \\\\\\\"listCodeDefinitionNames\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpath: getReadablePath(cwd, removeClosingTag(\\\\\\\"path\\\\\\\", relDirPath)),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (block.partial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst partialMessage = JSON.stringify({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t...sharedMessageProps,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontent: \\\\\\\"\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} satisfies ClineSayTool)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.shouldAutoApproveTool(block.name)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"ask\\\\\\\", \\\\\\\"tool\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"tool\\\\\\\", partialMessage, undefined, block.partial)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"say\\\\\\\", \\\\\\\"tool\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.ask(\\\\\\\"tool\\\\\\\", partialMessage, block.partial).catch(() => {})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!relDirPath) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\\\\\"list_code_definition_names\\\\\\\", \\\\\\\"path\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount = 0\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst absolutePath = path.resolve(cwd, relDirPath)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst result = await parseSourceCodeForDefinitionsTopLevel(absolutePath)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst completeMessage = JSON.stringify({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t...sharedMessageProps,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontent: result,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} satisfies ClineSayTool)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.shouldAutoApproveTool(block.name)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"ask\\\\\\\", \\\\\\\"tool\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"tool\\\\\\\", completeMessage, undefined, false)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveAutoApprovedRequestsCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tshowNotificationForApprovalIfAutoApprovalEnabled(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`Cline wants to view source code definitions in ${path.basename(absolutePath)}/`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"say\\\\\\\", \\\\\\\"tool\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst didApprove = await askApproval(\\\\\\\"tool\\\\\\\", completeMessage)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!didApprove) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(result)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait handleError(\\\\\\\"parsing source code definitions\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"search_files\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst relDirPath: string | undefined = block.params.path\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst regex: string | undefined = block.params.regex\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst filePattern: string | undefined = block.params.file_pattern\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst sharedMessageProps: ClineSayTool = {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttool: \\\\\\\"searchFiles\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpath: getReadablePath(cwd, removeClosingTag(\\\\\\\"path\\\\\\\", relDirPath)),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tregex: removeClosingTag(\\\\\\\"regex\\\\\\\", regex),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tfilePattern: removeClosingTag(\\\\\\\"file_pattern\\\\\\\", filePattern),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (block.partial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst partialMessage = JSON.stringify({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t...sharedMessageProps,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontent: \\\\\\\"\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} satisfies ClineSayTool)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.shouldAutoApproveTool(block.name)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"ask\\\\\\\", \\\\\\\"tool\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"tool\\\\\\\", partialMessage, undefined, block.partial)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"say\\\\\\\", \\\\\\\"tool\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.ask(\\\\\\\"tool\\\\\\\", partialMessage, block.partial).catch(() => {})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!relDirPath) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\\\\\"search_files\\\\\\\", \\\\\\\"path\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!regex) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\\\\\"search_files\\\\\\\", \\\\\\\"regex\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount = 0\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst absolutePath = path.resolve(cwd, relDirPath)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst results = await regexSearchFiles(cwd, absolutePath, regex, filePattern)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst completeMessage = JSON.stringify({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t...sharedMessageProps,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontent: results,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} satisfies ClineSayTool)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.shouldAutoApproveTool(block.name)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"ask\\\\\\\", \\\\\\\"tool\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"tool\\\\\\\", completeMessage, undefined, false)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveAutoApprovedRequestsCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tshowNotificationForApprovalIfAutoApprovalEnabled(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`Cline wants to search files in ${path.basename(absolutePath)}/`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"say\\\\\\\", \\\\\\\"tool\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst didApprove = await askApproval(\\\\\\\"tool\\\\\\\", completeMessage)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!didApprove) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(results)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait handleError(\\\\\\\"searching files\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"browser_action\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst action: BrowserAction | undefined = block.params.action as BrowserAction\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst url: string | undefined = block.params.url\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst coordinate: string | undefined = block.params.coordinate\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst text: string | undefined = block.params.text\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!action || !browserActions.includes(action)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// checking for action to ensure it is complete and valid\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!block.partial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// if the block is complete and we don't have a valid action this is a mistake\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\\\\\"browser_action\\\\\\\", \\\\\\\"action\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.browserSession.closeBrowser()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (block.partial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (action === \\\\\\\"launch\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.shouldAutoApproveTool(block.name)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"ask\\\\\\\", \\\\\\\"browser_action_launch\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"browser_action_launch\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tremoveClosingTag(\\\\\\\"url\\\\\\\", url),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tundefined,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tblock.partial,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"say\\\\\\\", \\\\\\\"browser_action_launch\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.ask(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"browser_action_launch\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tremoveClosingTag(\\\\\\\"url\\\\\\\", url),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tblock.partial,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t).catch(() => {})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"browser_action\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tJSON.stringify({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\taction: action as BrowserAction,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcoordinate: removeClosingTag(\\\\\\\"coordinate\\\\\\\", coordinate),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: removeClosingTag(\\\\\\\"text\\\\\\\", text),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} satisfies ClineSayBrowserAction),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tundefined,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tblock.partial,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlet browserActionResult: BrowserActionResult\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (action === \\\\\\\"launch\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!url) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\\\\\"browser_action\\\\\\\", \\\\\\\"url\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.browserSession.closeBrowser()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount = 0\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.shouldAutoApproveTool(block.name)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"ask\\\\\\\", \\\\\\\"browser_action_launch\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"browser_action_launch\\\\\\\", url, undefined, false)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveAutoApprovedRequestsCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tshowNotificationForApprovalIfAutoApprovalEnabled(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`Cline wants to use a browser and launch ${url}`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"say\\\\\\\", \\\\\\\"browser_action_launch\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst didApprove = await askApproval(\\\\\\\"browser_action_launch\\\\\\\", url)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!didApprove) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// NOTE: it's okay that we call this message since the partial inspect_site is finished streaming. The only scenario we have to avoid is sending messages WHILE a partial message exists at the end of the messages array. For example the api_req_finished message would interfere with the partial message, so we needed to remove that.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// await this.say(\\\\\\\"inspect_site_result\\\\\\\", \\\\\\\"\\\\\\\") // no result, starts the loading spinner waiting for result\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"browser_action_result\\\\\\\", \\\\\\\"\\\\\\\") // starts loading spinner\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.browserSession.launchBrowser()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbrowserActionResult = await this.browserSession.navigateToUrl(url)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (action === \\\\\\\"click\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!coordinate) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.sayAndCreateMissingParamError(\\\\\\\"browser_action\\\\\\\", \\\\\\\"coordinate\\\\\\\"),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.browserSession.closeBrowser()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak // can't be within an inner switch\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (action === \\\\\\\"type\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!text) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\\\\\"browser_action\\\\\\\", \\\\\\\"text\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.browserSession.closeBrowser()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount = 0\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"browser_action\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tJSON.stringify({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\taction: action as BrowserAction,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcoordinate,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} satisfies ClineSayBrowserAction),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tundefined,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tfalse,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tswitch (action) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"click\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbrowserActionResult = await this.browserSession.click(coordinate!)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"type\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbrowserActionResult = await this.browserSession.type(text!)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"scroll_down\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbrowserActionResult = await this.browserSession.scrollDown()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"scroll_up\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbrowserActionResult = await this.browserSession.scrollUp()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"close\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbrowserActionResult = await this.browserSession.closeBrowser()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tswitch (action) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"launch\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"click\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"type\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"scroll_down\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"scroll_up\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"browser_action_result\\\\\\\", JSON.stringify(browserActionResult))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tformatResponse.toolResult(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`The browser action has been executed. The console logs and screenshot have been captured for your analysis.\\\\\\\\n\\\\\\\\nConsole logs:\\\\\\\\n${\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbrowserActionResult.logs || \\\\\\\"(No new logs)\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\\\\\n\\\\\\\\n(REMEMBER: if you need to proceed to using non-\\\\\\\\`browser_action\\\\\\\\` tools or launch a new browser, you MUST first close this browser. For example, if after analyzing the logs and screenshot you need to edit a file, you must first close the browser before you can use the write_to_file tool.)`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbrowserActionResult.screenshot ? [browserActionResult.screenshot] : [],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"close\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tformatResponse.toolResult(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`The browser has been closed. You may now proceed to using other tools.`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.browserSession.closeBrowser() // if any error occurs, the browser session is terminated\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait handleError(\\\\\\\"executing browser action\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"execute_command\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst command: string | undefined = block.params.command\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst requiresApprovalRaw: string | undefined = block.params.requires_approval\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst requiresApproval = requiresApprovalRaw?.toLowerCase() === \\\\\\\"true\\\\\\\"\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (block.partial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.shouldAutoApproveTool(block.name)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// since depending on an upcoming parameter, requiresApproval this may become an ask - we cant partially stream a say prematurely. So in this particular case we have to wait for the requiresApproval parameter to be completed before presenting it.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// await this.say(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\t\\\\\\\"command\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\tremoveClosingTag(\\\\\\\"command\\\\\\\", command),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\tundefined,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\tblock.partial,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// ).catch(() => {})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// don't need to remove last partial since we couldn't have streamed a say\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.ask(\\\\\\\"command\\\\\\\", removeClosingTag(\\\\\\\"command\\\\\\\", command), block.partial).catch(() => {})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!command) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\\\\\"execute_command\\\\\\\", \\\\\\\"command\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!requiresApprovalRaw) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.sayAndCreateMissingParamError(\\\\\\\"execute_command\\\\\\\", \\\\\\\"requires_approval\\\\\\\"),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount = 0\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlet didAutoApprove = false\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!requiresApproval && this.shouldAutoApproveTool(block.name)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"ask\\\\\\\", \\\\\\\"command\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"command\\\\\\\", command, undefined, false)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveAutoApprovedRequestsCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tdidAutoApprove = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tshowNotificationForApprovalIfAutoApprovalEnabled(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`Cline wants to execute a command: ${command}`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// this.removeLastPartialMessageIfExistsWithType(\\\\\\\"say\\\\\\\", \\\\\\\"command\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst didApprove = await askApproval(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"command\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcommand +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`${this.shouldAutoApproveTool(block.name) && requiresApproval ? COMMAND_REQ_APP_STRING : \\\\\\\"\\\\\\\"}`, // ugly hack until we refactor combineCommandSequences\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!didApprove) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlet timeoutId: NodeJS.Timeout | undefined\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (didAutoApprove && this.autoApprovalSettings.enableNotifications) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// if the command was auto-approved, and it's long running we need to notify the user after some time has passed without proceeding\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttimeoutId = setTimeout(() => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tshowSystemNotification({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tsubtitle: \\\\\\\"Command is still running\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmessage:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"An auto-approved command has been running for 30s, and may need your attention.\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}, 30_000)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst [userRejected, result] = await this.executeCommandTool(command)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (timeoutId) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tclearTimeout(timeoutId)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (userRejected) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.didRejectTool = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(result)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait handleError(\\\\\\\"executing command\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"use_mcp_tool\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst server_name: string | undefined = block.params.server_name\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst tool_name: string | undefined = block.params.tool_name\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst mcp_arguments: string | undefined = block.params.arguments\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (block.partial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst partialMessage = JSON.stringify({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"use_mcp_tool\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tserverName: removeClosingTag(\\\\\\\"server_name\\\\\\\", server_name),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttoolName: removeClosingTag(\\\\\\\"tool_name\\\\\\\", tool_name),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\targuments: removeClosingTag(\\\\\\\"arguments\\\\\\\", mcp_arguments),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} satisfies ClineAskUseMcpServer)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.shouldAutoApproveTool(block.name)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"ask\\\\\\\", \\\\\\\"use_mcp_server\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"use_mcp_server\\\\\\\", partialMessage, undefined, block.partial)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"say\\\\\\\", \\\\\\\"use_mcp_server\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.ask(\\\\\\\"use_mcp_server\\\\\\\", partialMessage, block.partial).catch(() => {})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!server_name) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\\\\\"use_mcp_tool\\\\\\\", \\\\\\\"server_name\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!tool_name) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\\\\\"use_mcp_tool\\\\\\\", \\\\\\\"tool_name\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// arguments are optional, but if they are provided they must be valid JSON\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// if (!mcp_arguments) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\\\\\"use_mcp_tool\\\\\\\", \\\\\\\"arguments\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// }\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlet parsedArguments: Record<string, unknown> | undefined\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (mcp_arguments) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tparsedArguments = JSON.parse(mcp_arguments)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"error\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`Cline tried to use ${tool_name} with an invalid JSON argument. Retrying...`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tformatResponse.toolError(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tformatResponse.invalidMcpToolArgumentError(server_name, tool_name),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount = 0\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst completeMessage = JSON.stringify({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"use_mcp_tool\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tserverName: server_name,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttoolName: tool_name,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\targuments: mcp_arguments,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} satisfies ClineAskUseMcpServer)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.shouldAutoApproveTool(block.name)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"ask\\\\\\\", \\\\\\\"use_mcp_server\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"use_mcp_server\\\\\\\", completeMessage, undefined, false)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveAutoApprovedRequestsCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tshowNotificationForApprovalIfAutoApprovalEnabled(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`Cline wants to use ${tool_name} on ${server_name}`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"say\\\\\\\", \\\\\\\"use_mcp_server\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst didApprove = await askApproval(\\\\\\\"use_mcp_server\\\\\\\", completeMessage)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!didApprove) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// now execute the tool\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"mcp_server_request_started\\\\\\\") // same as browser_action_result\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst toolResult = await this.providerRef\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.deref()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t?.mcpHub?.callTool(server_name, tool_name, parsedArguments)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// TODO: add progress indicator and ability to parse images and non-text responses\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst toolResultPretty =\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t(toolResult?.isError ? \\\\\\\"Error:\\\\\\\\n\\\\\\\" : \\\\\\\"\\\\\\\") +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttoolResult?.content\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.map((item) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (item.type === \\\\\\\"text\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn item.text\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (item.type === \\\\\\\"resource\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst { blob, ...rest } = item.resource\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn JSON.stringify(rest, null, 2)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.filter(Boolean)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.join(\\\\\\\"\\\\\\\\n\\\\\\\\n\\\\\\\") || \\\\\\\"(No response)\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"mcp_server_response\\\\\\\", toolResultPretty)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(formatResponse.toolResult(toolResultPretty))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait handleError(\\\\\\\"executing MCP tool\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"access_mcp_resource\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst server_name: string | undefined = block.params.server_name\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst uri: string | undefined = block.params.uri\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (block.partial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst partialMessage = JSON.stringify({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"access_mcp_resource\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tserverName: removeClosingTag(\\\\\\\"server_name\\\\\\\", server_name),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\turi: removeClosingTag(\\\\\\\"uri\\\\\\\", uri),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} satisfies ClineAskUseMcpServer)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.shouldAutoApproveTool(block.name)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"ask\\\\\\\", \\\\\\\"use_mcp_server\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"use_mcp_server\\\\\\\", partialMessage, undefined, block.partial)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"say\\\\\\\", \\\\\\\"use_mcp_server\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.ask(\\\\\\\"use_mcp_server\\\\\\\", partialMessage, block.partial).catch(() => {})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!server_name) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\\\\\"access_mcp_resource\\\\\\\", \\\\\\\"server_name\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!uri) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\\\\\"access_mcp_resource\\\\\\\", \\\\\\\"uri\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount = 0\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst completeMessage = JSON.stringify({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"access_mcp_resource\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tserverName: server_name,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\turi,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} satisfies ClineAskUseMcpServer)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.shouldAutoApproveTool(block.name)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"ask\\\\\\\", \\\\\\\"use_mcp_server\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"use_mcp_server\\\\\\\", completeMessage, undefined, false)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveAutoApprovedRequestsCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tshowNotificationForApprovalIfAutoApprovalEnabled(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`Cline wants to access ${uri} on ${server_name}`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.removeLastPartialMessageIfExistsWithType(\\\\\\\"say\\\\\\\", \\\\\\\"use_mcp_server\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst didApprove = await askApproval(\\\\\\\"use_mcp_server\\\\\\\", completeMessage)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!didApprove) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// now execute the tool\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"mcp_server_request_started\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst resourceResult = await this.providerRef.deref()?.mcpHub?.readResource(server_name, uri)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst resourceResultPretty =\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tresourceResult?.contents\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.map((item) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (item.text) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn item.text\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.filter(Boolean)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.join(\\\\\\\"\\\\\\\\n\\\\\\\\n\\\\\\\") || \\\\\\\"(Empty response)\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"mcp_server_response\\\\\\\", resourceResultPretty)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(formatResponse.toolResult(resourceResultPretty))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait handleError(\\\\\\\"accessing MCP resource\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"ask_followup_question\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst question: string | undefined = block.params.question\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (block.partial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.ask(\\\\\\\"followup\\\\\\\", removeClosingTag(\\\\\\\"question\\\\\\\", question), block.partial).catch(() => {})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!question) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\\\\\"ask_followup_question\\\\\\\", \\\\\\\"question\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount = 0\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.autoApprovalSettings.enabled && this.autoApprovalSettings.enableNotifications) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tshowSystemNotification({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tsubtitle: \\\\\\\"Cline has a question...\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmessage: question.replace(/\\\\\\\\n/g, \\\\\\\" \\\\\\\"),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst { text, images } = await this.ask(\\\\\\\"followup\\\\\\\", question, false)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"user_feedback\\\\\\\", text ?? \\\\\\\"\\\\\\\", images)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(formatResponse.toolResult(`<answer>\\\\\\\\n${text}\\\\\\\\n</answer>`, images))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait handleError(\\\\\\\"asking question\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"attempt_completion\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t/*\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount = 0\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlet resultToSend = result\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (command) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"completion_result\\\\\\\", resultToSend)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// TODO: currently we don't handle if this command fails, it could be useful to let cline know and retry\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst [didUserReject, commandResult] = await this.executeCommand(command, true)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// if we received non-empty string, the command was rejected or failed\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (commandResult) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn [didUserReject, commandResult]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tresultToSend = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst { response, text, images } = await this.ask(\\\\\\\"completion_result\\\\\\\", resultToSend) // this prompts webview to show 'new task' button, and enable text input (which would be the 'text' here)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (response === \\\\\\\"yesButtonClicked\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn [false, \\\\\\\"\\\\\\\"] // signals to recursive loop to stop (for now this never happens since yesButtonClicked will trigger a new task)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"user_feedback\\\\\\\", text ?? \\\\\\\"\\\\\\\", images)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t*/\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst result: string | undefined = block.params.result\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst command: string | undefined = block.params.command\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst addNewChangesFlagToLastCompletionResultMessage = async () => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// Add newchanges flag if there are new changes to the workspace\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst hasNewChanges = await this.doesLatestTaskCompletionHaveNewChanges()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst lastCompletionResultMessage = findLast(this.clineMessages, (m) => m.say === \\\\\\\"completion_result\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlastCompletionResultMessage &&\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\thasNewChanges &&\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t!lastCompletionResultMessage.text?.endsWith(COMPLETION_RESULT_CHANGES_FLAG)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlastCompletionResultMessage.text += COMPLETION_RESULT_CHANGES_FLAG\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveClineMessages()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst lastMessage = this.clineMessages.at(-1)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (block.partial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (command) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// the attempt_completion text is done, now we're getting command\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// remove the previous partial attempt_completion ask, replace with say, post state to webview, then stream command\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// const secondLastMessage = this.clineMessages.at(-2)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// NOTE: we do not want to auto approve a command run as part of the attempt_completion tool\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (lastMessage && lastMessage.ask === \\\\\\\"command\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// update command\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.ask(\\\\\\\"command\\\\\\\", removeClosingTag(\\\\\\\"command\\\\\\\", command), block.partial).catch(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t() => {},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// last message is completion_result\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// we have command string, which means we have the result as well, so finish it (doesnt have to exist yet)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"completion_result\\\\\\\", removeClosingTag(\\\\\\\"result\\\\\\\", result), undefined, false)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait addNewChangesFlagToLastCompletionResultMessage()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.ask(\\\\\\\"command\\\\\\\", removeClosingTag(\\\\\\\"command\\\\\\\", command), block.partial).catch(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t() => {},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// no command, still outputting partial result\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"completion_result\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tremoveClosingTag(\\\\\\\"result\\\\\\\", result),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tundefined,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tblock.partial,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!result) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\\\\\"attempt_completion\\\\\\\", \\\\\\\"result\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount = 0\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.autoApprovalSettings.enabled && this.autoApprovalSettings.enableNotifications) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tshowSystemNotification({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tsubtitle: \\\\\\\"Task Completed\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmessage: result.replace(/\\\\\\\\n/g, \\\\\\\" \\\\\\\"),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlet commandResult: ToolResponse | undefined\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (command) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (lastMessage && lastMessage.ask !== \\\\\\\"command\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// havent sent a command message yet so first send completion_result then command\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"completion_result\\\\\\\", result, undefined, false)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait addNewChangesFlagToLastCompletionResultMessage()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// we already sent a command message, meaning the complete completion message has also been sent\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// complete command message\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst didApprove = await askApproval(\\\\\\\"command\\\\\\\", command)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!didApprove) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst [userRejected, execCommandResult] = await this.executeCommandTool(command!)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (userRejected) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.didRejectTool = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(execCommandResult)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// user didn't reject, but the command may have output\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcommandResult = execCommandResult\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"completion_result\\\\\\\", result, undefined, false)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait addNewChangesFlagToLastCompletionResultMessage()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// we already sent completion_result says, an empty string asks relinquishes control over button and field\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst { response, text, images } = await this.ask(\\\\\\\"completion_result\\\\\\\", \\\\\\\"\\\\\\\", false)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (response === \\\\\\\"yesButtonClicked\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tpushToolResult(\\\\\\\"\\\\\\\") // signals to recursive loop to stop (for now this never happens since yesButtonClicked will trigger a new task)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\\\\"user_feedback\\\\\\\", text ?? \\\\\\\"\\\\\\\", images)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst toolResults: (Anthropic.TextBlockParam | Anthropic.ImageBlockParam)[] = []\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (commandResult) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (typeof commandResult === \\\\\\\"string\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttoolResults.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: commandResult,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else if (Array.isArray(commandResult)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttoolResults.push(...commandResult)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttoolResults.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: `The user has provided feedback on the results. Consider their input to continue the task, and then attempt completion again.\\\\\\\\n<feedback>\\\\\\\\n${text}\\\\\\\\n</feedback>`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttoolResults.push(...formatResponse.imageBlocks(images))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.userMessageContent.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: `${toolDescription()} Result:`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.userMessageContent.push(...toolResults)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// await this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait handleError(\\\\\\\"attempting completion\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.saveCheckpoint()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t/*\\\\r\\\\n\\\\t\\\\tSeeing out of bounds is fine, it means that the next too call is being built up and ready to add to assistantMessageContent to present. \\\\r\\\\n\\\\t\\\\tWhen you see the UI inactive during this, it means that a tool is breaking without presenting any UI. For example the write_to_file tool was breaking when relpath was undefined, and for invalid relpath it never presented UI.\\\\r\\\\n\\\\t\\\\t*/\\\\r\\\\n\\\\t\\\\tthis.presentAssistantMessageLocked = false // this needs to be placed here, if not then calling this.presentAssistantMessage below would fail (sometimes) since it's locked\\\\r\\\\n\\\\t\\\\t// NOTE: when tool is rejected, iterator stream is interrupted and it waits for userMessageContentReady to be true. Future calls to present will skip execution since didRejectTool and iterate until contentIndex is set to message length and it sets userMessageContentReady to true itself (instead of preemptively doing it in iterator)\\\\r\\\\n\\\\t\\\\tif (!block.partial || this.didRejectTool || this.didAlreadyUseTool) {\\\\r\\\\n\\\\t\\\\t\\\\t// block is finished streaming and executing\\\\r\\\\n\\\\t\\\\t\\\\tif (this.currentStreamingContentIndex === this.assistantMessageContent.length - 1) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// its okay that we increment if !didCompleteReadingStream, it'll just return bc out of bounds and as streaming continues it will call presentAssitantMessage if a new block is ready. if streaming is finished then we set userMessageContentReady to true when out of bounds. This gracefully allows the stream to continue on and all potential content blocks be presented.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// last block is complete and it is finished executing\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.userMessageContentReady = true // will allow pwaitfor to continue\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// call next block if it exists (if not then read stream will call it when its ready)\\\\r\\\\n\\\\t\\\\t\\\\tthis.currentStreamingContentIndex++ // need to increment regardless, so when read stream calls this function again it will be streaming the next block\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tif (this.currentStreamingContentIndex < this.assistantMessageContent.length) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// there are already more content blocks to stream, so we'll call this function ourselves\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// await this.presentAssistantContent()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.presentAssistantMessage()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t// block is partial, but the read stream may have finished\\\\r\\\\n\\\\t\\\\tif (this.presentAssistantMessageHasPendingUpdates) {\\\\r\\\\n\\\\t\\\\t\\\\tthis.presentAssistantMessage()\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync recursivelyMakeClineRequests(\\\\r\\\\n\\\\t\\\\tuserContent: UserContent,\\\\r\\\\n\\\\t\\\\tincludeFileDetails: boolean = false,\\\\r\\\\n\\\\t\\\\tisNewTask: boolean = false,\\\\r\\\\n\\\\t): Promise<boolean> {\\\\r\\\\n\\\\t\\\\tif (this.abort) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Cline instance aborted\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (this.consecutiveMistakeCount >= 3) {\\\\r\\\\n\\\\t\\\\t\\\\tif (this.autoApprovalSettings.enabled && this.autoApprovalSettings.enableNotifications) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tshowSystemNotification({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tsubtitle: \\\\\\\"Error\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmessage: \\\\\\\"Cline is having trouble. Would you like to continue the task?\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tconst { response, text, images } = await this.ask(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"mistake_limit_reached\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.api.getModel().id.includes(\\\\\\\"claude\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t? `This may indicate a failure in his thought process or inability to use a tool properly, which can be mitigated with some user guidance (e.g. \\\\\\\"Try breaking down the task into smaller steps\\\\\\\").`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t: \\\\\\\"Cline uses complex prompts and iterative task execution that may be challenging for less capable models. For best results, it's recommended to use Claude 3.5 Sonnet for its advanced agentic coding capabilities.\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\tif (response === \\\\\\\"messageResponse\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tuserContent.push(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t...[\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: formatResponse.tooManyMistakes(text),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} as Anthropic.Messages.TextBlockParam,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t...formatResponse.imageBlocks(images),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tthis.consecutiveMistakeCount = 0\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (\\\\r\\\\n\\\\t\\\\t\\\\tthis.autoApprovalSettings.enabled &&\\\\r\\\\n\\\\t\\\\t\\\\tthis.consecutiveAutoApprovedRequestsCount >= this.autoApprovalSettings.maxRequests\\\\r\\\\n\\\\t\\\\t) {\\\\r\\\\n\\\\t\\\\t\\\\tif (this.autoApprovalSettings.enableNotifications) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tshowSystemNotification({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tsubtitle: \\\\\\\"Max Requests Reached\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmessage: `Cline has auto-approved ${this.autoApprovalSettings.maxRequests.toString()} API requests.`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tawait this.ask(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"auto_approval_max_req_reached\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t`Cline has auto-approved ${this.autoApprovalSettings.maxRequests.toString()} API requests. Would you like to reset the count and proceed with the task?`,\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t// if we get past the promise it means the user approved and did not start a new task\\\\r\\\\n\\\\t\\\\t\\\\tthis.consecutiveAutoApprovedRequestsCount = 0\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// get previous api req's index to check token usage and determine if we need to truncate conversation history\\\\r\\\\n\\\\t\\\\tconst previousApiReqIndex = findLastIndex(this.clineMessages, (m) => m.say === \\\\\\\"api_req_started\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// getting verbose details is an expensive operation, it uses globby to top-down build file structure of project which for large projects can take a few seconds\\\\r\\\\n\\\\t\\\\t// for the best UX we show a placeholder api_req_started message with a loading spinner as this happens\\\\r\\\\n\\\\t\\\\tawait this.say(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"api_req_started\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tJSON.stringify({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\trequest: userContent.map((block) => formatContentBlockToMarkdown(block)).join(\\\\\\\"\\\\\\\\n\\\\\\\\n\\\\\\\") + \\\\\\\"\\\\\\\\n\\\\\\\\nLoading...\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t}),\\\\r\\\\n\\\\t\\\\t)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// use this opportunity to initialize the checkpoint tracker (can be expensive to initialize in the constructor)\\\\r\\\\n\\\\t\\\\t// FIXME: right now we're letting users init checkpoints for old tasks, but this could be a problem if opening a task in the wrong workspace\\\\r\\\\n\\\\t\\\\t// isNewTask &&\\\\r\\\\n\\\\t\\\\tif (!this.checkpointTracker) {\\\\r\\\\n\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.checkpointTracker = await CheckpointTracker.create(this.taskId, this.providerRef.deref())\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.checkpointTrackerErrorMessage = undefined\\\\r\\\\n\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst errorMessage = error instanceof Error ? error.message : \\\\\\\"Unknown error\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Failed to initialize checkpoint tracker:\\\\\\\", errorMessage)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.checkpointTrackerErrorMessage = errorMessage // will be displayed right away since we saveClineMessages next which posts state to webview\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst [parsedUserContent, environmentDetails] = await this.loadContext(userContent, includeFileDetails)\\\\r\\\\n\\\\t\\\\tuserContent = parsedUserContent\\\\r\\\\n\\\\t\\\\t// add environment details as its own text block, separate from tool results\\\\r\\\\n\\\\t\\\\tuserContent.push({ type: \\\\\\\"text\\\\\\\", text: environmentDetails })\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tawait this.addToApiConversationHistory({\\\\r\\\\n\\\\t\\\\t\\\\trole: \\\\\\\"user\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tcontent: userContent,\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// since we sent off a placeholder api_req_started message to update the webview while waiting to actually start the API request (to load potential details for example), we need to update the text of that message\\\\r\\\\n\\\\t\\\\tconst lastApiReqIndex = findLastIndex(this.clineMessages, (m) => m.say === \\\\\\\"api_req_started\\\\\\\")\\\\r\\\\n\\\\t\\\\tthis.clineMessages[lastApiReqIndex].text = JSON.stringify({\\\\r\\\\n\\\\t\\\\t\\\\trequest: userContent.map((block) => formatContentBlockToMarkdown(block)).join(\\\\\\\"\\\\\\\\n\\\\\\\\n\\\\\\\"),\\\\r\\\\n\\\\t\\\\t} satisfies ClineApiReqInfo)\\\\r\\\\n\\\\t\\\\tawait this.saveClineMessages()\\\\r\\\\n\\\\t\\\\tawait this.providerRef.deref()?.postStateToWebview()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tlet cacheWriteTokens = 0\\\\r\\\\n\\\\t\\\\t\\\\tlet cacheReadTokens = 0\\\\r\\\\n\\\\t\\\\t\\\\tlet inputTokens = 0\\\\r\\\\n\\\\t\\\\t\\\\tlet outputTokens = 0\\\\r\\\\n\\\\t\\\\t\\\\tlet totalCost: number | undefined\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// update api_req_started. we can't use api_req_finished anymore since it's a unique case where it could come after a streaming message (ie in the middle of being updated or executed)\\\\r\\\\n\\\\t\\\\t\\\\t// fortunately api_req_finished was always parsed out for the gui anyways, so it remains solely for legacy purposes to keep track of prices in tasks from history\\\\r\\\\n\\\\t\\\\t\\\\t// (it's worth removing a few months from now)\\\\r\\\\n\\\\t\\\\t\\\\tconst updateApiReqMsg = (cancelReason?: ClineApiReqCancelReason, streamingFailedMessage?: string) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.clineMessages[lastApiReqIndex].text = JSON.stringify({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t...JSON.parse(this.clineMessages[lastApiReqIndex].text || \\\\\\\"{}\\\\\\\"),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttokensIn: inputTokens,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttokensOut: outputTokens,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcacheWrites: cacheWriteTokens,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcacheReads: cacheReadTokens,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcost:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttotalCost ??\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcalculateApiCost(this.api.getModel().info, inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcancelReason,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tstreamingFailedMessage,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} satisfies ClineApiReqInfo)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tconst abortStream = async (cancelReason: ClineApiReqCancelReason, streamingFailedMessage?: string) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (this.diffViewProvider.isEditing) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.diffViewProvider.revertChanges() // closes diff view\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// if last message is a partial we need to update and save it\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst lastMessage = this.clineMessages.at(-1)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (lastMessage && lastMessage.partial) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// lastMessage.ts = Date.now() DO NOT update ts since it is used as a key for virtuoso list\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tlastMessage.partial = false\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// instead of streaming partialMessage events, we do a save and post like normal to persist to disk\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconsole.log(\\\\\\\"updating partial message\\\\\\\", lastMessage)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// await this.saveClineMessages()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Let assistant know their response was interrupted for when task is resumed\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait this.addToApiConversationHistory({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\trole: \\\\\\\"assistant\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcontent: [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tassistantMessage +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`\\\\\\\\n\\\\\\\\n[${\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcancelReason === \\\\\\\"streaming_failed\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t? \\\\\\\"Response interrupted by API Error\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t: \\\\\\\"Response interrupted by user\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}]`,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// update api_req_started to have cancelled and cost, so that we can display the cost of the partial stream\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tupdateApiReqMsg(cancelReason, streamingFailedMessage)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait this.saveClineMessages()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// signals to provider that it can retrieve the saved messages from disk, as abortTask can not be awaited on in nature\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.didFinishAbortingStream = true\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// reset streaming state\\\\r\\\\n\\\\t\\\\t\\\\tthis.currentStreamingContentIndex = 0\\\\r\\\\n\\\\t\\\\t\\\\tthis.assistantMessageContent = []\\\\r\\\\n\\\\t\\\\t\\\\tthis.didCompleteReadingStream = false\\\\r\\\\n\\\\t\\\\t\\\\tthis.userMessageContent = []\\\\r\\\\n\\\\t\\\\t\\\\tthis.userMessageContentReady = false\\\\r\\\\n\\\\t\\\\t\\\\tthis.didRejectTool = false\\\\r\\\\n\\\\t\\\\t\\\\tthis.didAlreadyUseTool = false\\\\r\\\\n\\\\t\\\\t\\\\tthis.presentAssistantMessageLocked = false\\\\r\\\\n\\\\t\\\\t\\\\tthis.presentAssistantMessageHasPendingUpdates = false\\\\r\\\\n\\\\t\\\\t\\\\tawait this.diffViewProvider.reset()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tconst stream = this.attemptApiRequest(previousApiReqIndex) // yields only if the first chunk is successful, otherwise will allow the user to retry the request (most likely due to rate limit error, which gets thrown on the first chunk)\\\\r\\\\n\\\\t\\\\t\\\\tlet assistantMessage = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tthis.isStreaming = true\\\\r\\\\n\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tfor await (const chunk of stream) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tswitch (chunk.type) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"usage\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tinputTokens += chunk.inputTokens\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\toutputTokens += chunk.outputTokens\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcacheWriteTokens += chunk.cacheWriteTokens ?? 0\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcacheReadTokens += chunk.cacheReadTokens ?? 0\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttotalCost = chunk.totalCost\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"text\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tassistantMessage += chunk.text\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// parse raw assistant message into content blocks\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst prevLength = this.assistantMessageContent.length\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.assistantMessageContent = parseAssistantMessage(assistantMessage)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.assistantMessageContent.length > prevLength) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.userMessageContentReady = false // new content we need to present, reset to false in case previous content set this to true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// present content to user\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.presentAssistantMessage()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (this.abort) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconsole.log(\\\\\\\"aborting stream...\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!this.abandoned) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// only need to gracefully abort if this instance isn't abandoned (sometimes openrouter stream hangs, in which case this would affect future instances of cline)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait abortStream(\\\\\\\"user_cancelled\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak // aborts the stream\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (this.didRejectTool) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// userContent has a tool rejection, so interrupt the assistant's response to present the user's feedback\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tassistantMessage += \\\\\\\"\\\\\\\\n\\\\\\\\n[Response interrupted by user feedback]\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// this.userMessageContentReady = true // instead of setting this premptively, we allow the present iterator to finish and set userMessageContentReady when its ready\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// PREV: we need to let the request finish for openrouter to get generation details\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// UPDATE: it's better UX to interrupt the request at the cost of the api cost not being retrieved\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (this.didAlreadyUseTool) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tassistantMessage +=\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"\\\\\\\\n\\\\\\\\n[Response interrupted by a tool use result. Only one tool may be used at a time and should be placed at the end of the message.]\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// 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)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (!this.abandoned) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.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\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait abortStream(\\\\\\\"streaming_failed\\\\\\\", error.message ?? JSON.stringify(serializeError(error), null, 2))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst history = await this.providerRef.deref()?.getTaskWithId(this.taskId)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (history) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.providerRef.deref()?.initClineWithHistoryItem(history.historyItem)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// await this.providerRef.deref()?.postStateToWebview()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t} finally {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.isStreaming = false\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// need to call here in case the stream was aborted\\\\r\\\\n\\\\t\\\\t\\\\tif (this.abort) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Cline instance aborted\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tthis.didCompleteReadingStream = true\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// set any blocks to be complete to allow presentAssistantMessage to finish and set userMessageContentReady to true\\\\r\\\\n\\\\t\\\\t\\\\t// (could be a text block that had no subsequent tool uses, or a text block at the very end, or an invalid tool use, etc. whatever the case, presentAssistantMessage relies on these blocks either to be completed or the user to reject a block in order to proceed and eventually set userMessageContentReady to true)\\\\r\\\\n\\\\t\\\\t\\\\tconst partialBlocks = this.assistantMessageContent.filter((block) => block.partial)\\\\r\\\\n\\\\t\\\\t\\\\tpartialBlocks.forEach((block) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tblock.partial = false\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t// this.assistantMessageContent.forEach((e) => (e.partial = false)) // cant just do this bc a tool could be in the middle of executing ()\\\\r\\\\n\\\\t\\\\t\\\\tif (partialBlocks.length > 0) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.presentAssistantMessage() // if there is content to update then it will complete and update this.userMessageContentReady to true, which we pwaitfor before making the next request. all this is really doing is presenting the last partial message that we just set to complete\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tupdateApiReqMsg()\\\\r\\\\n\\\\t\\\\t\\\\tawait this.saveClineMessages()\\\\r\\\\n\\\\t\\\\t\\\\tawait this.providerRef.deref()?.postStateToWebview()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// now add to apiconversationhistory\\\\r\\\\n\\\\t\\\\t\\\\t// need to save assistant responses to file before proceeding to tool use since user can exit at any moment and we wouldn't be able to save the assistant's response\\\\r\\\\n\\\\t\\\\t\\\\tlet didEndLoop = false\\\\r\\\\n\\\\t\\\\t\\\\tif (assistantMessage.length > 0) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait this.addToApiConversationHistory({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\trole: \\\\\\\"assistant\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcontent: [{ type: \\\\\\\"text\\\\\\\", text: assistantMessage }],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// NOTE: this comment is here for future reference - this was a workaround for userMessageContent not getting set to true. It was due to it not recursively calling for partial blocks when didRejectTool, so it would get stuck waiting for a partial block to complete before it could continue.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// in case the content blocks finished\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// it may be the api stream finished after the last parsed content block was executed, so we are able to detect out of bounds and set userMessageContentReady to true (note you should not call presentAssistantMessage since if the last block is completed it will be presented again)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// const completeBlocks = this.assistantMessageContent.filter((block) => !block.partial) // if there are any partial blocks after the stream ended we can consider them invalid\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// if (this.currentStreamingContentIndex >= completeBlocks.length) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// \\\\tthis.userMessageContentReady = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// }\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait pWaitFor(() => this.userMessageContentReady)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// if the model did not tool use, then we need to tell it to either use a tool or attempt_completion\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst didToolUse = this.assistantMessageContent.some((block) => block.type === \\\\\\\"tool_use\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (!didToolUse) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.userMessageContent.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: formatResponse.noToolsUsed(),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.consecutiveMistakeCount++\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst recDidEndLoop = await this.recursivelyMakeClineRequests(this.userMessageContent)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tdidEndLoop = recDidEndLoop\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// if there's no assistant_responses, that means we got no text or tool_use content blocks from API which we should assume is an error\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait this.say(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"error\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"Unexpected API Response: The language model did not provide any assistant messages. This may indicate an issue with the API or the model's output.\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait this.addToApiConversationHistory({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\trole: \\\\\\\"assistant\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcontent: [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: \\\\\\\"Failure: I did not provide a response.\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\treturn didEndLoop // will always be false for now\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t// this should never happen since the only thing that can throw an error is the attemptApiRequest, which is wrapped in a try catch that sends an ask where if noButtonClicked, will clear current task and destroy this instance. However to avoid unhandled promise rejection, we will end this loop which will end execution of this instance (see startTask)\\\\r\\\\n\\\\t\\\\t\\\\treturn true // needs to be true so parent loop knows to end task\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync loadContext(userContent: UserContent, includeFileDetails: boolean = false) {\\\\r\\\\n\\\\t\\\\treturn await Promise.all([\\\\r\\\\n\\\\t\\\\t\\\\t// This is a temporary solution to dynamically load context mentions from tool results. It checks for the presence of tags that indicate that the tool was rejected and feedback was provided (see formatToolDeniedFeedback, attemptCompletion, executeCommand, and consecutiveMistakeCount >= 3) or \\\\\\\"<answer>\\\\\\\" (see askFollowupQuestion), we place all user generated content in these tags so they can effectively be used as markers for when we should parse mentions). However if we allow multiple tools responses in the future, we will need to parse mentions specifically within the user content tags.\\\\r\\\\n\\\\t\\\\t\\\\t// (Note: this caused the @/ import alias bug where file contents were being parsed as well, since v2 converted tool results to text blocks)\\\\r\\\\n\\\\t\\\\t\\\\tPromise.all(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tuserContent.map(async (block) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (block.type === \\\\\\\"text\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// We need to ensure any user generated content is wrapped in one of these tags so that we know to parse mentions\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// FIXME: Only parse text in between these tags instead of the entire text block which may contain other tool results. This is part of a larger issue where we shouldn't be using regex to parse mentions in the first place (ie for cases where file paths have spaces)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tblock.text.includes(\\\\\\\"<feedback>\\\\\\\") ||\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tblock.text.includes(\\\\\\\"<answer>\\\\\\\") ||\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tblock.text.includes(\\\\\\\"<task>\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t...block,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: await parseMentions(block.text, cwd, this.urlContentFetcher),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn block\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}),\\\\r\\\\n\\\\t\\\\t\\\\t),\\\\r\\\\n\\\\t\\\\t\\\\tthis.getEnvironmentDetails(includeFileDetails),\\\\r\\\\n\\\\t\\\\t])\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync getEnvironmentDetails(includeFileDetails: boolean = false) {\\\\r\\\\n\\\\t\\\\tlet details = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// It could be useful for cline to know if the user went from one or no file to another between messages, so we always include this context\\\\r\\\\n\\\\t\\\\tdetails += \\\\\\\"\\\\\\\\n\\\\\\\\n# VSCode Visible Files\\\\\\\"\\\\r\\\\n\\\\t\\\\tconst visibleFiles = vscode.window.visibleTextEditors\\\\r\\\\n\\\\t\\\\t\\\\t?.map((editor) => editor.document?.uri?.fsPath)\\\\r\\\\n\\\\t\\\\t\\\\t.filter(Boolean)\\\\r\\\\n\\\\t\\\\t\\\\t.map((absolutePath) => path.relative(cwd, absolutePath).toPosix())\\\\r\\\\n\\\\t\\\\t\\\\t.join(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\t\\\\tif (visibleFiles) {\\\\r\\\\n\\\\t\\\\t\\\\tdetails += `\\\\\\\\n${visibleFiles}`\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\tdetails += \\\\\\\"\\\\\\\\n(No visible files)\\\\\\\"\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tdetails += \\\\\\\"\\\\\\\\n\\\\\\\\n# VSCode Open Tabs\\\\\\\"\\\\r\\\\n\\\\t\\\\tconst openTabs = vscode.window.tabGroups.all\\\\r\\\\n\\\\t\\\\t\\\\t.flatMap((group) => group.tabs)\\\\r\\\\n\\\\t\\\\t\\\\t.map((tab) => (tab.input as vscode.TabInputText)?.uri?.fsPath)\\\\r\\\\n\\\\t\\\\t\\\\t.filter(Boolean)\\\\r\\\\n\\\\t\\\\t\\\\t.map((absolutePath) => path.relative(cwd, absolutePath).toPosix())\\\\r\\\\n\\\\t\\\\t\\\\t.join(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\t\\\\tif (openTabs) {\\\\r\\\\n\\\\t\\\\t\\\\tdetails += `\\\\\\\\n${openTabs}`\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\tdetails += \\\\\\\"\\\\\\\\n(No open tabs)\\\\\\\"\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst busyTerminals = this.terminalManager.getTerminals(true)\\\\r\\\\n\\\\t\\\\tconst inactiveTerminals = this.terminalManager.getTerminals(false)\\\\r\\\\n\\\\t\\\\t// const allTerminals = [...busyTerminals, ...inactiveTerminals]\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (busyTerminals.length > 0 && this.didEditFile) {\\\\r\\\\n\\\\t\\\\t\\\\t// || this.didEditFile\\\\r\\\\n\\\\t\\\\t\\\\tawait delay(300) // delay after saving file to let terminals catch up\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// let terminalWasBusy = false\\\\r\\\\n\\\\t\\\\tif (busyTerminals.length > 0) {\\\\r\\\\n\\\\t\\\\t\\\\t// wait for terminals to cool down\\\\r\\\\n\\\\t\\\\t\\\\t// terminalWasBusy = allTerminals.some((t) => this.terminalManager.isProcessHot(t.id))\\\\r\\\\n\\\\t\\\\t\\\\tawait pWaitFor(() => busyTerminals.every((t) => !this.terminalManager.isProcessHot(t.id)), {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tinterval: 100,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttimeout: 15_000,\\\\r\\\\n\\\\t\\\\t\\\\t}).catch(() => {})\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// we want to get diagnostics AFTER terminal cools down for a few reasons: terminal could be scaffolding a project, dev servers (compilers like webpack) will first re-compile and then send diagnostics, etc\\\\r\\\\n\\\\t\\\\t/*\\\\r\\\\n\\\\t\\\\tlet diagnosticsDetails = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\tconst diagnostics = await this.diagnosticsMonitor.getCurrentDiagnostics(this.didEditFile || terminalWasBusy) // if cline ran a command (ie npm install) or edited the workspace then wait a bit for updated diagnostics\\\\r\\\\n\\\\t\\\\tfor (const [uri, fileDiagnostics] of diagnostics) {\\\\r\\\\n\\\\t\\\\t\\\\tconst problems = fileDiagnostics.filter((d) => d.severity === vscode.DiagnosticSeverity.Error)\\\\r\\\\n\\\\t\\\\t\\\\tif (problems.length > 0) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tdiagnosticsDetails += `\\\\\\\\n## ${path.relative(cwd, uri.fsPath)}`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tfor (const diagnostic of problems) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// let severity = diagnostic.severity === vscode.DiagnosticSeverity.Error ? \\\\\\\"Error\\\\\\\" : \\\\\\\"Warning\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst line = diagnostic.range.start.line + 1 // VSCode lines are 0-indexed\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst source = diagnostic.source ? `[${diagnostic.source}] ` : \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tdiagnosticsDetails += `\\\\\\\\n- ${source}Line ${line}: ${diagnostic.message}`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t*/\\\\r\\\\n\\\\t\\\\tthis.didEditFile = false // reset, this lets us know when to wait for saved files to update terminals\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// waiting for updated diagnostics lets terminal output be the most up-to-date possible\\\\r\\\\n\\\\t\\\\tlet terminalDetails = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\tif (busyTerminals.length > 0) {\\\\r\\\\n\\\\t\\\\t\\\\t// terminals are cool, let's retrieve their output\\\\r\\\\n\\\\t\\\\t\\\\tterminalDetails += \\\\\\\"\\\\\\\\n\\\\\\\\n# Actively Running Terminals\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tfor (const busyTerminal of busyTerminals) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tterminalDetails += `\\\\\\\\n## Original command: \\\\\\\\`${busyTerminal.lastCommand}\\\\\\\\``\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst newOutput = this.terminalManager.getUnretrievedOutput(busyTerminal.id)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (newOutput) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tterminalDetails += `\\\\\\\\n### New Output\\\\\\\\n${newOutput}`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// details += `\\\\\\\\n(Still running, no new output)` // don't want to show this right after running the command\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t// only show inactive terminals if there's output to show\\\\r\\\\n\\\\t\\\\tif (inactiveTerminals.length > 0) {\\\\r\\\\n\\\\t\\\\t\\\\tconst inactiveTerminalOutputs = new Map<number, string>()\\\\r\\\\n\\\\t\\\\t\\\\tfor (const inactiveTerminal of inactiveTerminals) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst newOutput = this.terminalManager.getUnretrievedOutput(inactiveTerminal.id)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (newOutput) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tinactiveTerminalOutputs.set(inactiveTerminal.id, newOutput)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tif (inactiveTerminalOutputs.size > 0) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tterminalDetails += \\\\\\\"\\\\\\\\n\\\\\\\\n# Inactive Terminals\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tfor (const [terminalId, newOutput] of inactiveTerminalOutputs) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst inactiveTerminal = inactiveTerminals.find((t) => t.id === terminalId)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (inactiveTerminal) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tterminalDetails += `\\\\\\\\n## ${inactiveTerminal.lastCommand}`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tterminalDetails += `\\\\\\\\n### New Output\\\\\\\\n${newOutput}`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// details += \\\\\\\"\\\\\\\\n\\\\\\\\n# VSCode Workspace Errors\\\\\\\"\\\\r\\\\n\\\\t\\\\t// if (diagnosticsDetails) {\\\\r\\\\n\\\\t\\\\t// \\\\tdetails += diagnosticsDetails\\\\r\\\\n\\\\t\\\\t// } else {\\\\r\\\\n\\\\t\\\\t// \\\\tdetails += \\\\\\\"\\\\\\\\n(No errors detected)\\\\\\\"\\\\r\\\\n\\\\t\\\\t// }\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (terminalDetails) {\\\\r\\\\n\\\\t\\\\t\\\\tdetails += terminalDetails\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Add current time information with timezone\\\\r\\\\n\\\\t\\\\tconst now = new Date()\\\\r\\\\n\\\\t\\\\tconst formatter = new Intl.DateTimeFormat(undefined, {\\\\r\\\\n\\\\t\\\\t\\\\tyear: \\\\\\\"numeric\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tmonth: \\\\\\\"numeric\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tday: \\\\\\\"numeric\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\thour: \\\\\\\"numeric\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tminute: \\\\\\\"numeric\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tsecond: \\\\\\\"numeric\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\thour12: true,\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\tconst timeZone = formatter.resolvedOptions().timeZone\\\\r\\\\n\\\\t\\\\tconst timeZoneOffset = -now.getTimezoneOffset() / 60 // Convert to hours and invert sign to match conventional notation\\\\r\\\\n\\\\t\\\\tconst timeZoneOffsetStr = `${timeZoneOffset >= 0 ? \\\\\\\"+\\\\\\\" : \\\\\\\"\\\\\\\"}${timeZoneOffset}:00`\\\\r\\\\n\\\\t\\\\tdetails += `\\\\\\\\n\\\\\\\\n# Current Time\\\\\\\\n${formatter.format(now)} (${timeZone}, UTC${timeZoneOffsetStr})`\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (includeFileDetails) {\\\\r\\\\n\\\\t\\\\t\\\\tdetails += `\\\\\\\\n\\\\\\\\n# Current Working Directory (${cwd.toPosix()}) Files\\\\\\\\n`\\\\r\\\\n\\\\t\\\\t\\\\tconst isDesktop = arePathsEqual(cwd, path.join(os.homedir(), \\\\\\\"Desktop\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\tif (isDesktop) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// don't want to immediately access desktop since it would show permission popup\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tdetails += \\\\\\\"(Desktop files not shown automatically. Use list_files to explore if needed.)\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst [files, didHitLimit] = await listFiles(cwd, true, 200)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst result = formatResponse.formatFilesList(cwd, files, didHitLimit)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tdetails += result\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\treturn `<environment_details>\\\\\\\\n${details.trim()}\\\\\\\\n</environment_details>`\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":139506,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.943Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.943Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":3183,\\\"nonEmptyLines\\\":2958,\\\"commentLines\\\":331,\\\"complexity\\\":713},\\\"dependencies\\\":[\\\"@anthropic-ai/sdk\\\",\\\"clone-deep\\\",\\\"delay\\\",\\\"fs/promises\\\",\\\"os\\\",\\\"p-wait-for\\\",\\\"path\\\",\\\"serialize-error\\\",\\\"vscode\\\",\\\"../api\\\",\\\"../api/transform/stream\\\",\\\"../integrations/editor/DiffViewProvider\\\",\\\"../integrations/misc/export-markdown\\\",\\\"../integrations/misc/extract-text\\\",\\\"../integrations/terminal/TerminalManager\\\",\\\"../services/browser/BrowserSession\\\",\\\"../services/browser/UrlContentFetcher\\\",\\\"../services/glob/list-files\\\",\\\"../services/ripgrep\\\",\\\"../services/tree-sitter\\\",\\\"../shared/api\\\",\\\"../shared/array\\\",\\\"../shared/AutoApprovalSettings\\\",\\\"../shared/combineApiRequests\\\",\\\"../shared/combineCommandSequences\\\",\\\"../shared/getApiMetrics\\\",\\\"../shared/HistoryItem\\\",\\\"../shared/WebviewMessage\\\",\\\"../utils/cost\\\",\\\"../utils/fs\\\",\\\"../utils/path\\\",\\\"./assistant-message\\\",\\\"./assistant-message/diff\\\",\\\"./mentions\\\",\\\"./prompts/responses\\\",\\\"./prompts/system\\\",\\\"./sliding-window\\\",\\\"./webview/ClineProvider\\\",\\\"../integrations/notifications\\\",\\\"../utils/string\\\",\\\"../api/providers/openai\\\",\\\"../integrations/checkpoints/CheckpointTracker\\\",\\\"get-folder-size\\\"],\\\"quality\\\":{\\\"score\\\":-6894,\\\"issues\\\":[],\\\"duplicateLines\\\":1318,\\\"longLines\\\":202,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.328Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":3183},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\core\\\\\\\\mentions\\\\\\\\index.ts\\\":{\\\"content\\\":\\\"import * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\nimport * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport { openFile } from \\\\\\\"../../integrations/misc/open-file\\\\\\\"\\\\r\\\\nimport { UrlContentFetcher } from \\\\\\\"../../services/browser/UrlContentFetcher\\\\\\\"\\\\r\\\\nimport { mentionRegexGlobal } from \\\\\\\"../../shared/context-mentions\\\\\\\"\\\\r\\\\nimport fs from \\\\\\\"fs/promises\\\\\\\"\\\\r\\\\nimport { extractTextFromFile } from \\\\\\\"../../integrations/misc/extract-text\\\\\\\"\\\\r\\\\nimport { isBinaryFile } from \\\\\\\"isbinaryfile\\\\\\\"\\\\r\\\\nimport { diagnosticsToProblemsString } from \\\\\\\"../../integrations/diagnostics\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport function openMention(mention?: string): void {\\\\r\\\\n\\\\tif (!mention) {\\\\r\\\\n\\\\t\\\\treturn\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tif (mention.startsWith(\\\\\\\"/\\\\\\\")) {\\\\r\\\\n\\\\t\\\\tconst relPath = mention.slice(1)\\\\r\\\\n\\\\t\\\\tconst cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0)\\\\r\\\\n\\\\t\\\\tif (!cwd) {\\\\r\\\\n\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst absPath = path.resolve(cwd, relPath)\\\\r\\\\n\\\\t\\\\tif (mention.endsWith(\\\\\\\"/\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\tvscode.commands.executeCommand(\\\\\\\"revealInExplorer\\\\\\\", vscode.Uri.file(absPath))\\\\r\\\\n\\\\t\\\\t\\\\t// vscode.commands.executeCommand(\\\\\\\"vscode.openFolder\\\\\\\", , { forceNewWindow: false }) opens in new window\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\topenFile(absPath)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t} else if (mention === \\\\\\\"problems\\\\\\\") {\\\\r\\\\n\\\\t\\\\tvscode.commands.executeCommand(\\\\\\\"workbench.actions.view.problems\\\\\\\")\\\\r\\\\n\\\\t} else if (mention.startsWith(\\\\\\\"http\\\\\\\")) {\\\\r\\\\n\\\\t\\\\tvscode.env.openExternal(vscode.Uri.parse(mention))\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport async function parseMentions(text: string, cwd: string, urlContentFetcher: UrlContentFetcher): Promise<string> {\\\\r\\\\n\\\\tconst mentions: Set<string> = new Set()\\\\r\\\\n\\\\tlet parsedText = text.replace(mentionRegexGlobal, (match, mention) => {\\\\r\\\\n\\\\t\\\\tmentions.add(mention)\\\\r\\\\n\\\\t\\\\tif (mention.startsWith(\\\\\\\"http\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\treturn `'${mention}' (see below for site content)`\\\\r\\\\n\\\\t\\\\t} else if (mention.startsWith(\\\\\\\"/\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\tconst mentionPath = mention.slice(1) // Remove the leading '/'\\\\r\\\\n\\\\t\\\\t\\\\treturn mentionPath.endsWith(\\\\\\\"/\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t? `'${mentionPath}' (see below for folder content)`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t: `'${mentionPath}' (see below for file content)`\\\\r\\\\n\\\\t\\\\t} else if (mention === \\\\\\\"problems\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\treturn `Workspace Problems (see below for diagnostics)`\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn match\\\\r\\\\n\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\tconst urlMention = Array.from(mentions).find((mention) => mention.startsWith(\\\\\\\"http\\\\\\\"))\\\\r\\\\n\\\\tlet launchBrowserError: Error | undefined\\\\r\\\\n\\\\tif (urlMention) {\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tawait urlContentFetcher.launchBrowser()\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\tlaunchBrowserError = error\\\\r\\\\n\\\\t\\\\t\\\\tvscode.window.showErrorMessage(`Error fetching content for ${urlMention}: ${error.message}`)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tfor (const mention of mentions) {\\\\r\\\\n\\\\t\\\\tif (mention.startsWith(\\\\\\\"http\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\tlet result: string\\\\r\\\\n\\\\t\\\\t\\\\tif (launchBrowserError) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tresult = `Error fetching content: ${launchBrowserError.message}`\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst markdown = await urlContentFetcher.urlToMarkdown(mention)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tresult = markdown\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tvscode.window.showErrorMessage(`Error fetching content for ${mention}: ${error.message}`)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tresult = `Error fetching content: ${error.message}`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tparsedText += `\\\\\\\\n\\\\\\\\n<url_content url=\\\\\\\"${mention}\\\\\\\">\\\\\\\\n${result}\\\\\\\\n</url_content>`\\\\r\\\\n\\\\t\\\\t} else if (mention.startsWith(\\\\\\\"/\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\tconst mentionPath = mention.slice(1)\\\\r\\\\n\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst content = await getFileOrFolderContent(mentionPath, cwd)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (mention.endsWith(\\\\\\\"/\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tparsedText += `\\\\\\\\n\\\\\\\\n<folder_content path=\\\\\\\"${mentionPath}\\\\\\\">\\\\\\\\n${content}\\\\\\\\n</folder_content>`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tparsedText += `\\\\\\\\n\\\\\\\\n<file_content path=\\\\\\\"${mentionPath}\\\\\\\">\\\\\\\\n${content}\\\\\\\\n</file_content>`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (mention.endsWith(\\\\\\\"/\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tparsedText += `\\\\\\\\n\\\\\\\\n<folder_content path=\\\\\\\"${mentionPath}\\\\\\\">\\\\\\\\nError fetching content: ${error.message}\\\\\\\\n</folder_content>`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tparsedText += `\\\\\\\\n\\\\\\\\n<file_content path=\\\\\\\"${mentionPath}\\\\\\\">\\\\\\\\nError fetching content: ${error.message}\\\\\\\\n</file_content>`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t} else if (mention === \\\\\\\"problems\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst problems = getWorkspaceProblems(cwd)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tparsedText += `\\\\\\\\n\\\\\\\\n<workspace_diagnostics>\\\\\\\\n${problems}\\\\\\\\n</workspace_diagnostics>`\\\\r\\\\n\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tparsedText += `\\\\\\\\n\\\\\\\\n<workspace_diagnostics>\\\\\\\\nError fetching diagnostics: ${error.message}\\\\\\\\n</workspace_diagnostics>`\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tif (urlMention) {\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tawait urlContentFetcher.closeBrowser()\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\tconsole.error(`Error closing browser: ${error.message}`)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\treturn parsedText\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nasync function getFileOrFolderContent(mentionPath: string, cwd: string): Promise<string> {\\\\r\\\\n\\\\tconst absPath = path.resolve(cwd, mentionPath)\\\\r\\\\n\\\\r\\\\n\\\\ttry {\\\\r\\\\n\\\\t\\\\tconst stats = await fs.stat(absPath)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (stats.isFile()) {\\\\r\\\\n\\\\t\\\\t\\\\tconst isBinary = await isBinaryFile(absPath).catch(() => false)\\\\r\\\\n\\\\t\\\\t\\\\tif (isBinary) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn \\\\\\\"(Binary file, unable to display content)\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tconst content = await extractTextFromFile(absPath)\\\\r\\\\n\\\\t\\\\t\\\\treturn content\\\\r\\\\n\\\\t\\\\t} else if (stats.isDirectory()) {\\\\r\\\\n\\\\t\\\\t\\\\tconst entries = await fs.readdir(absPath, { withFileTypes: true })\\\\r\\\\n\\\\t\\\\t\\\\tlet folderContent = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tconst fileContentPromises: Promise<string | undefined>[] = []\\\\r\\\\n\\\\t\\\\t\\\\tentries.forEach((entry, index) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst isLast = index === entries.length - 1\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst linePrefix = isLast ? \\\\\\\"└── \\\\\\\" : \\\\\\\"├── \\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (entry.isFile()) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tfolderContent += `${linePrefix}${entry.name}\\\\\\\\n`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst filePath = path.join(mentionPath, entry.name)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst absoluteFilePath = path.resolve(absPath, entry.name)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// const relativeFilePath = path.relative(cwd, absoluteFilePath);\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tfileContentPromises.push(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t(async () => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst isBinary = await isBinaryFile(absoluteFilePath).catch(() => false)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (isBinary) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn undefined\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst content = await extractTextFromFile(absoluteFilePath)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn `<file_content path=\\\\\\\"${filePath.toPosix()}\\\\\\\">\\\\\\\\n${content}\\\\\\\\n</file_content>`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn undefined\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})(),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} else if (entry.isDirectory()) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tfolderContent += `${linePrefix}${entry.name}/\\\\\\\\n`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// not recursively getting folder contents\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tfolderContent += `${linePrefix}${entry.name}\\\\\\\\n`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\tconst fileContents = (await Promise.all(fileContentPromises)).filter((content) => content)\\\\r\\\\n\\\\t\\\\t\\\\treturn `${folderContent}\\\\\\\\n${fileContents.join(\\\\\\\"\\\\\\\\n\\\\\\\\n\\\\\\\")}`.trim()\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\treturn `(Failed to read contents of ${mentionPath})`\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\tthrow new Error(`Failed to access path \\\\\\\"${mentionPath}\\\\\\\": ${error.message}`)\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nfunction getWorkspaceProblems(cwd: string): string {\\\\r\\\\n\\\\tconst diagnostics = vscode.languages.getDiagnostics()\\\\r\\\\n\\\\tconst result = diagnosticsToProblemsString(\\\\r\\\\n\\\\t\\\\tdiagnostics,\\\\r\\\\n\\\\t\\\\t[vscode.DiagnosticSeverity.Error, vscode.DiagnosticSeverity.Warning],\\\\r\\\\n\\\\t\\\\tcwd,\\\\r\\\\n\\\\t)\\\\r\\\\n\\\\tif (!result) {\\\\r\\\\n\\\\t\\\\treturn \\\\\\\"No errors or warnings detected.\\\\\\\"\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\treturn result\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":6697,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.842Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.842Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":184,\\\"nonEmptyLines\\\":172,\\\"commentLines\\\":3,\\\"complexity\\\":61},\\\"dependencies\\\":[\\\"vscode\\\",\\\"path\\\",\\\"../../integrations/misc/open-file\\\",\\\"../../services/browser/UrlContentFetcher\\\",\\\"../../shared/context-mentions\\\",\\\"fs/promises\\\",\\\"../../integrations/misc/extract-text\\\",\\\"isbinaryfile\\\",\\\"../../integrations/diagnostics\\\"],\\\"quality\\\":{\\\"score\\\":-195,\\\"issues\\\":[],\\\"duplicateLines\\\":57,\\\"longLines\\\":5,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.333Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":184},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\core\\\\\\\\prompts\\\\\\\\responses.ts\\\":{\\\"content\\\":\\\"import { Anthropic } from \\\\\\\"@anthropic-ai/sdk\\\\\\\"\\\\r\\\\nimport * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport * as diff from \\\\\\\"diff\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport const formatResponse = {\\\\r\\\\n\\\\ttoolDenied: () => `The user denied this operation.`,\\\\r\\\\n\\\\r\\\\n\\\\ttoolDeniedWithFeedback: (feedback?: string) =>\\\\r\\\\n\\\\t\\\\t`The user denied this operation and provided the following feedback:\\\\\\\\n<feedback>\\\\\\\\n${feedback}\\\\\\\\n</feedback>`,\\\\r\\\\n\\\\r\\\\n\\\\ttoolError: (error?: string) => `The tool execution failed with the following error:\\\\\\\\n<error>\\\\\\\\n${error}\\\\\\\\n</error>`,\\\\r\\\\n\\\\r\\\\n\\\\tnoToolsUsed: () =>\\\\r\\\\n\\\\t\\\\t`[ERROR] You did not use a tool in your previous response! Please retry with a tool use.\\\\r\\\\n\\\\r\\\\n${toolUseInstructionsReminder}\\\\r\\\\n\\\\r\\\\n# Next Steps\\\\r\\\\n\\\\r\\\\nIf you have completed the user's task, use the attempt_completion tool. \\\\r\\\\nIf you require additional information from the user, use the ask_followup_question tool. \\\\r\\\\nOtherwise, if you have not completed the task and do not need additional information, then proceed with the next step of the task. \\\\r\\\\n(This is an automated message, so do not respond to it conversationally.)`,\\\\r\\\\n\\\\r\\\\n\\\\ttooManyMistakes: (feedback?: string) =>\\\\r\\\\n\\\\t\\\\t`You seem to be having trouble proceeding. The user has provided the following feedback to help guide you:\\\\\\\\n<feedback>\\\\\\\\n${feedback}\\\\\\\\n</feedback>`,\\\\r\\\\n\\\\r\\\\n\\\\tmissingToolParameterError: (paramName: string) =>\\\\r\\\\n\\\\t\\\\t`Missing value for required parameter '${paramName}'. Please retry with complete response.\\\\\\\\n\\\\\\\\n${toolUseInstructionsReminder}`,\\\\r\\\\n\\\\r\\\\n\\\\tinvalidMcpToolArgumentError: (serverName: string, toolName: string) =>\\\\r\\\\n\\\\t\\\\t`Invalid JSON argument used with ${serverName} for ${toolName}. Please retry with a properly formatted JSON argument.`,\\\\r\\\\n\\\\r\\\\n\\\\ttoolResult: (text: string, images?: string[]): string | Array<Anthropic.TextBlockParam | Anthropic.ImageBlockParam> => {\\\\r\\\\n\\\\t\\\\tif (images && images.length > 0) {\\\\r\\\\n\\\\t\\\\t\\\\tconst textBlock: Anthropic.TextBlockParam = { type: \\\\\\\"text\\\\\\\", text }\\\\r\\\\n\\\\t\\\\t\\\\tconst imageBlocks: Anthropic.ImageBlockParam[] = formatImagesIntoBlocks(images)\\\\r\\\\n\\\\t\\\\t\\\\t// Placing images after text leads to better results\\\\r\\\\n\\\\t\\\\t\\\\treturn [textBlock, ...imageBlocks]\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\treturn text\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\r\\\\n\\\\timageBlocks: (images?: string[]): Anthropic.ImageBlockParam[] => {\\\\r\\\\n\\\\t\\\\treturn formatImagesIntoBlocks(images)\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\r\\\\n\\\\tformatFilesList: (absolutePath: string, files: string[], didHitLimit: boolean): string => {\\\\r\\\\n\\\\t\\\\tconst sorted = files\\\\r\\\\n\\\\t\\\\t\\\\t.map((file) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// convert absolute path to relative path\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst relativePath = path.relative(absolutePath, file).toPosix()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn file.endsWith(\\\\\\\"/\\\\\\\") ? relativePath + \\\\\\\"/\\\\\\\" : relativePath\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t// Sort so files are listed under their respective directories to make it clear what files are children of what directories. Since we build file list top down, even if file list is truncated it will show directories that cline can then explore further.\\\\r\\\\n\\\\t\\\\t\\\\t.sort((a, b) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst aParts = a.split(\\\\\\\"/\\\\\\\") // only works if we use toPosix first\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst bParts = b.split(\\\\\\\"/\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tfor (let i = 0; i < Math.min(aParts.length, bParts.length); i++) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (aParts[i] !== bParts[i]) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// If one is a directory and the other isn't at this level, sort the directory first\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (i + 1 === aParts.length && i + 1 < bParts.length) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn -1\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (i + 1 === bParts.length && i + 1 < aParts.length) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn 1\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// Otherwise, sort alphabetically\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn aParts[i].localeCompare(bParts[i], undefined, {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tnumeric: true,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tsensitivity: \\\\\\\"base\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// If all parts are the same up to the length of the shorter path,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// the shorter one comes first\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn aParts.length - bParts.length\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\tif (didHitLimit) {\\\\r\\\\n\\\\t\\\\t\\\\treturn `${sorted.join(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"\\\\\\\\n\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t)}\\\\\\\\n\\\\\\\\n(File list truncated. Use list_files on specific subdirectories if you need to explore further.)`\\\\r\\\\n\\\\t\\\\t} else if (sorted.length === 0 || (sorted.length === 1 && sorted[0] === \\\\\\\"\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\treturn \\\\\\\"No files found.\\\\\\\"\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\treturn sorted.join(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\r\\\\n\\\\tcreatePrettyPatch: (filename = \\\\\\\"file\\\\\\\", oldStr?: string, newStr?: string) => {\\\\r\\\\n\\\\t\\\\t// strings cannot be undefined or diff throws exception\\\\r\\\\n\\\\t\\\\tconst patch = diff.createPatch(filename.toPosix(), oldStr || \\\\\\\"\\\\\\\", newStr || \\\\\\\"\\\\\\\")\\\\r\\\\n\\\\t\\\\tconst lines = patch.split(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\t\\\\tconst prettyPatchLines = lines.slice(4)\\\\r\\\\n\\\\t\\\\treturn prettyPatchLines.join(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\t},\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n// to avoid circular dependency\\\\r\\\\nconst formatImagesIntoBlocks = (images?: string[]): Anthropic.ImageBlockParam[] => {\\\\r\\\\n\\\\treturn images\\\\r\\\\n\\\\t\\\\t? images.map((dataUrl) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// data:image/png;base64,base64string\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst [rest, base64] = dataUrl.split(\\\\\\\",\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst mimeType = rest.split(\\\\\\\":\\\\\\\")[1].split(\\\\\\\";\\\\\\\")[0]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"image\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tsource: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"base64\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmedia_type: mimeType,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tdata: base64,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} as Anthropic.ImageBlockParam\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t: []\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nconst toolUseInstructionsReminder = `# Reminder: Instructions for Tool Use\\\\r\\\\n\\\\r\\\\nTool uses are formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:\\\\r\\\\n\\\\r\\\\n<tool_name>\\\\r\\\\n<parameter1_name>value1</parameter1_name>\\\\r\\\\n<parameter2_name>value2</parameter2_name>\\\\r\\\\n...\\\\r\\\\n</tool_name>\\\\r\\\\n\\\\r\\\\nFor example:\\\\r\\\\n\\\\r\\\\n<attempt_completion>\\\\r\\\\n<result>\\\\r\\\\nI have completed the task...\\\\r\\\\n</result>\\\\r\\\\n</attempt_completion>\\\\r\\\\n\\\\r\\\\nAlways adhere to this format for all tool uses to ensure proper parsing and execution.`\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":5373,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.944Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.944Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":138,\\\"nonEmptyLines\\\":116,\\\"commentLines\\\":10,\\\"complexity\\\":29},\\\"dependencies\\\":[\\\"@anthropic-ai/sdk\\\",\\\"path\\\",\\\"diff\\\"],\\\"quality\\\":{\\\"score\\\":5,\\\"issues\\\":[],\\\"duplicateLines\\\":15,\\\"longLines\\\":10,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.335Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":138},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\core\\\\\\\\prompts\\\\\\\\system.ts\\\":{\\\"content\\\":\\\"import defaultShell from \\\\\\\"default-shell\\\\\\\"\\\\r\\\\nimport os from \\\\\\\"os\\\\\\\"\\\\r\\\\nimport osName from \\\\\\\"os-name\\\\\\\"\\\\r\\\\nimport { McpHub } from \\\\\\\"../../services/mcp/McpHub\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport const SYSTEM_PROMPT = async (\\\\r\\\\n\\\\tcwd: string,\\\\r\\\\n\\\\tsupportsComputerUse: boolean,\\\\r\\\\n\\\\tmcpHub: McpHub,\\\\r\\\\n) => `You are Cline, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\\\r\\\\n\\\\r\\\\n====\\\\r\\\\n\\\\r\\\\nTOOL USE\\\\r\\\\n\\\\r\\\\nYou have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.\\\\r\\\\n\\\\r\\\\n# Tool Use Formatting\\\\r\\\\n\\\\r\\\\nTool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:\\\\r\\\\n\\\\r\\\\n<tool_name>\\\\r\\\\n<parameter1_name>value1</parameter1_name>\\\\r\\\\n<parameter2_name>value2</parameter2_name>\\\\r\\\\n...\\\\r\\\\n</tool_name>\\\\r\\\\n\\\\r\\\\nFor example:\\\\r\\\\n\\\\r\\\\n<read_file>\\\\r\\\\n<path>src/main.js</path>\\\\r\\\\n</read_file>\\\\r\\\\n\\\\r\\\\nAlways adhere to this format for the tool use to ensure proper parsing and execution.\\\\r\\\\n\\\\r\\\\n# Tools\\\\r\\\\n\\\\r\\\\n## execute_command\\\\r\\\\nDescription: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: ${cwd.toPosix()}\\\\r\\\\nParameters:\\\\r\\\\n- command: (required) The CLI command to execute. This should be valid for the current operating system. Ensure the command is properly formatted and does not contain any harmful instructions.\\\\r\\\\n- requires_approval: (required) A boolean indicating whether this command requires explicit user approval before execution in case the user has auto-approve mode enabled. Set to 'true' for potentially impactful operations like installing/uninstalling packages, deleting/overwriting files, system configuration changes, network operations, or any commands that could have unintended side effects. Set to 'false' for safe operations like reading files/directories, running development servers, building projects, and other non-destructive operations.\\\\r\\\\nUsage:\\\\r\\\\n<execute_command>\\\\r\\\\n<command>Your command here</command>\\\\r\\\\n<requires_approval>true or false</requires_approval>\\\\r\\\\n</execute_command>\\\\r\\\\n\\\\r\\\\n## read_file\\\\r\\\\nDescription: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string.\\\\r\\\\nParameters:\\\\r\\\\n- path: (required) The path of the file to read (relative to the current working directory ${cwd.toPosix()})\\\\r\\\\nUsage:\\\\r\\\\n<read_file>\\\\r\\\\n<path>File path here</path>\\\\r\\\\n</read_file>\\\\r\\\\n\\\\r\\\\n## write_to_file\\\\r\\\\nDescription: Request to write content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file.\\\\r\\\\nParameters:\\\\r\\\\n- path: (required) The path of the file to write to (relative to the current working directory ${cwd.toPosix()})\\\\r\\\\n- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified.\\\\r\\\\nUsage:\\\\r\\\\n<write_to_file>\\\\r\\\\n<path>File path here</path>\\\\r\\\\n<content>\\\\r\\\\nYour file content here\\\\r\\\\n</content>\\\\r\\\\n</write_to_file>\\\\r\\\\n\\\\r\\\\n## replace_in_file\\\\r\\\\nDescription: Request to replace sections of content in an existing file using SEARCH/REPLACE blocks that define exact changes to specific parts of the file. This tool should be used when you need to make targeted changes to specific parts of a file.\\\\r\\\\nParameters:\\\\r\\\\n- path: (required) The path of the file to modify (relative to the current working directory ${cwd.toPosix()})\\\\r\\\\n- diff: (required) One or more SEARCH/REPLACE blocks following this exact format:\\\\r\\\\n \\\\\\\\`\\\\\\\\`\\\\\\\\`\\\\r\\\\n <<<<<<< SEARCH\\\\r\\\\n [exact content to find]\\\\r\\\\n =======\\\\r\\\\n [new content to replace with]\\\\r\\\\n >>>>>>> REPLACE\\\\r\\\\n \\\\\\\\`\\\\\\\\`\\\\\\\\`\\\\r\\\\n Critical rules:\\\\r\\\\n 1. SEARCH content must match the associated file section to find EXACTLY:\\\\r\\\\n * Match character-for-character including whitespace, indentation, line endings\\\\r\\\\n * Include all comments, docstrings, etc.\\\\r\\\\n 2. SEARCH/REPLACE blocks will ONLY replace the first match occurrence.\\\\r\\\\n * Including multiple unique SEARCH/REPLACE blocks if you need to make multiple changes.\\\\r\\\\n * Include *just* enough lines in each SEARCH section to uniquely match each set of lines that need to change.\\\\r\\\\n * When using multiple SEARCH/REPLACE blocks, list them in the order they appear in the file.\\\\r\\\\n 3. Keep SEARCH/REPLACE blocks concise:\\\\r\\\\n * Break large SEARCH/REPLACE blocks into a series of smaller blocks that each change a small portion of the file.\\\\r\\\\n * Include just the changing lines, and a few surrounding lines if needed for uniqueness.\\\\r\\\\n * Do not include long runs of unchanging lines in SEARCH/REPLACE blocks.\\\\r\\\\n * Each line must be complete. Never truncate lines mid-way through as this can cause matching failures.\\\\r\\\\n 4. Special operations:\\\\r\\\\n * To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)\\\\r\\\\n * To delete code: Use empty REPLACE section\\\\r\\\\nUsage:\\\\r\\\\n<replace_in_file>\\\\r\\\\n<path>File path here</path>\\\\r\\\\n<diff>\\\\r\\\\nSearch and replace blocks here\\\\r\\\\n</diff>\\\\r\\\\n</replace_in_file>\\\\r\\\\n\\\\r\\\\n## search_files\\\\r\\\\nDescription: Request to perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.\\\\r\\\\nParameters:\\\\r\\\\n- path: (required) The path of the directory to search in (relative to the current working directory ${cwd.toPosix()}). This directory will be recursively searched.\\\\r\\\\n- regex: (required) The regular expression pattern to search for. Uses Rust regex syntax.\\\\r\\\\n- file_pattern: (optional) Glob pattern to filter files (e.g., '*.ts' for TypeScript files). If not provided, it will search all files (*).\\\\r\\\\nUsage:\\\\r\\\\n<search_files>\\\\r\\\\n<path>Directory path here</path>\\\\r\\\\n<regex>Your regex pattern here</regex>\\\\r\\\\n<file_pattern>file pattern here (optional)</file_pattern>\\\\r\\\\n</search_files>\\\\r\\\\n\\\\r\\\\n## list_files\\\\r\\\\nDescription: Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not.\\\\r\\\\nParameters:\\\\r\\\\n- path: (required) The path of the directory to list contents for (relative to the current working directory ${cwd.toPosix()})\\\\r\\\\n- recursive: (optional) Whether to list files recursively. Use true for recursive listing, false or omit for top-level only.\\\\r\\\\nUsage:\\\\r\\\\n<list_files>\\\\r\\\\n<path>Directory path here</path>\\\\r\\\\n<recursive>true or false (optional)</recursive>\\\\r\\\\n</list_files>\\\\r\\\\n\\\\r\\\\n## list_code_definition_names\\\\r\\\\nDescription: Request to list definition names (classes, functions, methods, etc.) used in source code files at the top level of the specified directory. This tool provides insights into the codebase structure and important constructs, encapsulating high-level concepts and relationships that are crucial for understanding the overall architecture.\\\\r\\\\nParameters:\\\\r\\\\n- path: (required) The path of the directory (relative to the current working directory ${cwd.toPosix()}) to list top level source code definitions for.\\\\r\\\\nUsage:\\\\r\\\\n<list_code_definition_names>\\\\r\\\\n<path>Directory path here</path>\\\\r\\\\n</list_code_definition_names>${\\\\r\\\\n\\\\tsupportsComputerUse\\\\r\\\\n\\\\t\\\\t? `\\\\r\\\\n\\\\r\\\\n## browser_action\\\\r\\\\nDescription: Request to interact with a Puppeteer-controlled browser. Every action, except \\\\\\\\`close\\\\\\\\`, will be responded to with a screenshot of the browser's current state, along with any new console logs. You may only perform one browser action per message, and wait for the user's response including a screenshot and logs to determine the next action.\\\\r\\\\n- The sequence of actions **must always start with** launching the browser at a URL, and **must always end with** closing the browser. If you need to visit a new URL that is not possible to navigate to from the current webpage, you must first close the browser, then launch again at the new URL.\\\\r\\\\n- While the browser is active, only the \\\\\\\\`browser_action\\\\\\\\` tool can be used. No other tools should be called during this time. You may proceed to use other tools only after closing the browser. For example if you run into an error and need to fix a file, you must close the browser, then use other tools to make the necessary changes, then re-launch the browser to verify the result.\\\\r\\\\n- The browser window has a resolution of **900x600** pixels. When performing any click actions, ensure the coordinates are within this resolution range.\\\\r\\\\n- Before clicking on any elements such as icons, links, or buttons, you must consult the provided screenshot of the page to determine the coordinates of the element. The click should be targeted at the **center of the element**, not on its edges.\\\\r\\\\nParameters:\\\\r\\\\n- action: (required) The action to perform. The available actions are:\\\\r\\\\n * launch: Launch a new Puppeteer-controlled browser instance at the specified URL. This **must always be the first action**.\\\\r\\\\n - Use with the \\\\\\\\`url\\\\\\\\` parameter to provide the URL.\\\\r\\\\n - Ensure the URL is valid and includes the appropriate protocol (e.g. http://localhost:3000/page, file:///path/to/file.html, etc.)\\\\r\\\\n * click: Click at a specific x,y coordinate.\\\\r\\\\n - Use with the \\\\\\\\`coordinate\\\\\\\\` parameter to specify the location.\\\\r\\\\n - Always click in the center of an element (icon, button, link, etc.) based on coordinates derived from a screenshot.\\\\r\\\\n * type: Type a string of text on the keyboard. You might use this after clicking on a text field to input text.\\\\r\\\\n - Use with the \\\\\\\\`text\\\\\\\\` parameter to provide the string to type.\\\\r\\\\n * scroll_down: Scroll down the page by one page height.\\\\r\\\\n * scroll_up: Scroll up the page by one page height.\\\\r\\\\n * close: Close the Puppeteer-controlled browser instance. This **must always be the final browser action**.\\\\r\\\\n - Example: \\\\\\\\`<action>close</action>\\\\\\\\`\\\\r\\\\n- url: (optional) Use this for providing the URL for the \\\\\\\\`launch\\\\\\\\` action.\\\\r\\\\n * Example: <url>https://example.com</url>\\\\r\\\\n- coordinate: (optional) The X and Y coordinates for the \\\\\\\\`click\\\\\\\\` action. Coordinates should be within the **900x600** resolution.\\\\r\\\\n * Example: <coordinate>450,300</coordinate>\\\\r\\\\n- text: (optional) Use this for providing the text for the \\\\\\\\`type\\\\\\\\` action.\\\\r\\\\n * Example: <text>Hello, world!</text>\\\\r\\\\nUsage:\\\\r\\\\n<browser_action>\\\\r\\\\n<action>Action to perform (e.g., launch, click, type, scroll_down, scroll_up, close)</action>\\\\r\\\\n<url>URL to launch the browser at (optional)</url>\\\\r\\\\n<coordinate>x,y coordinates (optional)</coordinate>\\\\r\\\\n<text>Text to type (optional)</text>\\\\r\\\\n</browser_action>`\\\\r\\\\n\\\\t\\\\t: \\\\\\\"\\\\\\\"\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n## use_mcp_tool\\\\r\\\\nDescription: Request to use a tool provided by a connected MCP server. Each MCP server can provide multiple tools with different capabilities. Tools have defined input schemas that specify required and optional parameters.\\\\r\\\\nParameters:\\\\r\\\\n- server_name: (required) The name of the MCP server providing the tool\\\\r\\\\n- tool_name: (required) The name of the tool to execute\\\\r\\\\n- arguments: (required) A JSON object containing the tool's input parameters, following the tool's input schema\\\\r\\\\nUsage:\\\\r\\\\n<use_mcp_tool>\\\\r\\\\n<server_name>server name here</server_name>\\\\r\\\\n<tool_name>tool name here</tool_name>\\\\r\\\\n<arguments>\\\\r\\\\n{\\\\r\\\\n \\\\\\\"param1\\\\\\\": \\\\\\\"value1\\\\\\\",\\\\r\\\\n \\\\\\\"param2\\\\\\\": \\\\\\\"value2\\\\\\\"\\\\r\\\\n}\\\\r\\\\n</arguments>\\\\r\\\\n</use_mcp_tool>\\\\r\\\\n\\\\r\\\\n## access_mcp_resource\\\\r\\\\nDescription: Request to access a resource provided by a connected MCP server. Resources represent data sources that can be used as context, such as files, API responses, or system information.\\\\r\\\\nParameters:\\\\r\\\\n- server_name: (required) The name of the MCP server providing the resource\\\\r\\\\n- uri: (required) The URI identifying the specific resource to access\\\\r\\\\nUsage:\\\\r\\\\n<access_mcp_resource>\\\\r\\\\n<server_name>server name here</server_name>\\\\r\\\\n<uri>resource URI here</uri>\\\\r\\\\n</access_mcp_resource>\\\\r\\\\n\\\\r\\\\n## ask_followup_question\\\\r\\\\nDescription: Ask the user a question to gather additional information needed to complete the task. This tool should be used when you encounter ambiguities, need clarification, or require more details to proceed effectively. It allows for interactive problem-solving by enabling direct communication with the user. Use this tool judiciously to maintain a balance between gathering necessary information and avoiding excessive back-and-forth.\\\\r\\\\nParameters:\\\\r\\\\n- question: (required) The question to ask the user. This should be a clear, specific question that addresses the information you need.\\\\r\\\\nUsage:\\\\r\\\\n<ask_followup_question>\\\\r\\\\n<question>Your question here</question>\\\\r\\\\n</ask_followup_question>\\\\r\\\\n\\\\r\\\\n## attempt_completion\\\\r\\\\nDescription: After each tool use, the user will respond with the result of that tool use, i.e. if it succeeded or failed, along with any reasons for failure. Once you've received the results of tool uses and can confirm that the task is complete, use this tool to present the result of your work to the user. Optionally you may provide a CLI command to showcase the result of your work. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.\\\\r\\\\nIMPORTANT NOTE: This tool CANNOT be used until you've confirmed from the user that any previous tool uses were successful. Failure to do so will result in code corruption and system failure. Before using this tool, you must ask yourself in <thinking></thinking> tags if you've confirmed from the user that any previous tool uses were successful. If not, then DO NOT use this tool.\\\\r\\\\nParameters:\\\\r\\\\n- result: (required) The result of the task. Formulate this result in a way that is final and does not require further input from the user. Don't end your result with questions or offers for further assistance.\\\\r\\\\n- command: (optional) A CLI command to execute to show a live demo of the result to the user. For example, use \\\\\\\\`open index.html\\\\\\\\` to display a created html website, or \\\\\\\\`open localhost:3000\\\\\\\\` to display a locally running development server. But DO NOT use commands like \\\\\\\\`echo\\\\\\\\` or \\\\\\\\`cat\\\\\\\\` that merely print text. This command should be valid for the current operating system. Ensure the command is properly formatted and does not contain any harmful instructions.\\\\r\\\\nUsage:\\\\r\\\\n<attempt_completion>\\\\r\\\\n<result>\\\\r\\\\nYour final result description here\\\\r\\\\n</result>\\\\r\\\\n<command>Command to demonstrate result (optional)</command>\\\\r\\\\n</attempt_completion>\\\\r\\\\n\\\\r\\\\n# Tool Use Examples\\\\r\\\\n\\\\r\\\\n## Example 1: Requesting to execute a command\\\\r\\\\n\\\\r\\\\n<execute_command>\\\\r\\\\n<command>npm run dev</command>\\\\r\\\\n<requires_approval>false</requires_approval>\\\\r\\\\n</execute_command>\\\\r\\\\n\\\\r\\\\n## Example 2: Requesting to use an MCP tool\\\\r\\\\n\\\\r\\\\n<use_mcp_tool>\\\\r\\\\n<server_name>weather-server</server_name>\\\\r\\\\n<tool_name>get_forecast</tool_name>\\\\r\\\\n<arguments>\\\\r\\\\n{\\\\r\\\\n \\\\\\\"city\\\\\\\": \\\\\\\"San Francisco\\\\\\\",\\\\r\\\\n \\\\\\\"days\\\\\\\": 5\\\\r\\\\n}\\\\r\\\\n</arguments>\\\\r\\\\n</use_mcp_tool>\\\\r\\\\n\\\\r\\\\n## Example 3: Requesting to access an MCP resource\\\\r\\\\n\\\\r\\\\n<access_mcp_resource>\\\\r\\\\n<server_name>weather-server</server_name>\\\\r\\\\n<uri>weather://san-francisco/current</uri>\\\\r\\\\n</access_mcp_resource>\\\\r\\\\n\\\\r\\\\n## Example 4: Requesting to create a new file\\\\r\\\\n\\\\r\\\\n<write_to_file>\\\\r\\\\n<path>src/frontend-config.json</path>\\\\r\\\\n<content>\\\\r\\\\n{\\\\r\\\\n \\\\\\\"apiEndpoint\\\\\\\": \\\\\\\"https://api.example.com\\\\\\\",\\\\r\\\\n \\\\\\\"theme\\\\\\\": {\\\\r\\\\n \\\\\\\"primaryColor\\\\\\\": \\\\\\\"#007bff\\\\\\\",\\\\r\\\\n \\\\\\\"secondaryColor\\\\\\\": \\\\\\\"#6c757d\\\\\\\",\\\\r\\\\n \\\\\\\"fontFamily\\\\\\\": \\\\\\\"Arial, sans-serif\\\\\\\"\\\\r\\\\n },\\\\r\\\\n \\\\\\\"features\\\\\\\": {\\\\r\\\\n \\\\\\\"darkMode\\\\\\\": true,\\\\r\\\\n \\\\\\\"notifications\\\\\\\": true,\\\\r\\\\n \\\\\\\"analytics\\\\\\\": false\\\\r\\\\n },\\\\r\\\\n \\\\\\\"version\\\\\\\": \\\\\\\"1.0.0\\\\\\\"\\\\r\\\\n}\\\\r\\\\n</content>\\\\r\\\\n</write_to_file>\\\\r\\\\n\\\\r\\\\n## Example 6: Requesting to make targeted edits to a file\\\\r\\\\n\\\\r\\\\n<replace_in_file>\\\\r\\\\n<path>src/components/App.tsx</path>\\\\r\\\\n<diff>\\\\r\\\\n<<<<<<< SEARCH\\\\r\\\\nimport React from 'react';\\\\r\\\\n=======\\\\r\\\\nimport React, { useState } from 'react';\\\\r\\\\n>>>>>>> REPLACE\\\\r\\\\n\\\\r\\\\n<<<<<<< SEARCH\\\\r\\\\nfunction handleSubmit() {\\\\r\\\\n saveData();\\\\r\\\\n setLoading(false);\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n=======\\\\r\\\\n>>>>>>> REPLACE\\\\r\\\\n\\\\r\\\\n<<<<<<< SEARCH\\\\r\\\\nreturn (\\\\r\\\\n <div>\\\\r\\\\n=======\\\\r\\\\nfunction handleSubmit() {\\\\r\\\\n saveData();\\\\r\\\\n setLoading(false);\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nreturn (\\\\r\\\\n <div>\\\\r\\\\n>>>>>>> REPLACE\\\\r\\\\n</diff>\\\\r\\\\n</replace_in_file>\\\\r\\\\n\\\\r\\\\n# Tool Use Guidelines\\\\r\\\\n\\\\r\\\\n1. In <thinking> tags, assess what information you already have and what information you need to proceed with the task.\\\\r\\\\n2. Choose the most appropriate tool based on the task and the tool descriptions provided. Assess if you need additional information to proceed, and which of the available tools would be most effective for gathering this information. For example using the list_files tool is more effective than running a command like \\\\\\\\`ls\\\\\\\\` in the terminal. It's critical that you think about each available tool and use the one that best fits the current step in the task.\\\\r\\\\n3. If multiple actions are needed, use one tool at a time per message to accomplish the task iteratively, with each tool use being informed by the result of the previous tool use. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.\\\\r\\\\n4. Formulate your tool use using the XML format specified for each tool.\\\\r\\\\n5. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:\\\\r\\\\n - Information about whether the tool succeeded or failed, along with any reasons for failure.\\\\r\\\\n - Linter errors that may have arisen due to the changes you made, which you'll need to address.\\\\r\\\\n - New terminal output in reaction to the changes, which you may need to consider or act upon.\\\\r\\\\n - Any other relevant feedback or information related to the tool use.\\\\r\\\\n6. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.\\\\r\\\\n\\\\r\\\\nIt is crucial to proceed step-by-step, waiting for the user's message after each tool use before moving forward with the task. This approach allows you to:\\\\r\\\\n1. Confirm the success of each step before proceeding.\\\\r\\\\n2. Address any issues or errors that arise immediately.\\\\r\\\\n3. Adapt your approach based on new information or unexpected results.\\\\r\\\\n4. Ensure that each action builds correctly on the previous ones.\\\\r\\\\n\\\\r\\\\nBy waiting for and carefully considering the user's response after each tool use, you can react accordingly and make informed decisions about how to proceed with the task. This iterative process helps ensure the overall success and accuracy of your work.\\\\r\\\\n\\\\r\\\\n====\\\\r\\\\n\\\\r\\\\nMCP SERVERS\\\\r\\\\n\\\\r\\\\nThe Model Context Protocol (MCP) enables communication between the system and locally running MCP servers that provide additional tools and resources to extend your capabilities.\\\\r\\\\n\\\\r\\\\n# Connected MCP Servers\\\\r\\\\n\\\\r\\\\nWhen a server is connected, you can use the server's tools via the \\\\\\\\`use_mcp_tool\\\\\\\\` tool, and access the server's resources via the \\\\\\\\`access_mcp_resource\\\\\\\\` tool.\\\\r\\\\n\\\\r\\\\n${\\\\r\\\\n\\\\tmcpHub.getServers().length > 0\\\\r\\\\n\\\\t\\\\t? `${mcpHub\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t.getServers()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t.filter((server) => server.status === \\\\\\\"connected\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t.map((server) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst tools = server.tools\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t?.map((tool) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst schemaStr = tool.inputSchema\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t? ` Input Schema:\\\\r\\\\n ${JSON.stringify(tool.inputSchema, null, 2).split(\\\\\\\"\\\\\\\\n\\\\\\\").join(\\\\\\\"\\\\\\\\n \\\\\\\")}`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t: \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn `- ${tool.name}: ${tool.description}\\\\\\\\n${schemaStr}`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.join(\\\\\\\"\\\\\\\\n\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst templates = server.resourceTemplates\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t?.map((template) => `- ${template.uriTemplate} (${template.name}): ${template.description}`)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.join(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst resources = server.resources\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t?.map((resource) => `- ${resource.uri} (${resource.name}): ${resource.description}`)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.join(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst config = JSON.parse(server.config)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn (\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t`## ${server.name} (\\\\\\\\`${config.command}${config.args && Array.isArray(config.args) ? ` ${config.args.join(\\\\\\\" \\\\\\\")}` : \\\\\\\"\\\\\\\"}\\\\\\\\`)` +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t(tools ? `\\\\\\\\n\\\\\\\\n### Available Tools\\\\\\\\n${tools}` : \\\\\\\"\\\\\\\") +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t(templates ? `\\\\\\\\n\\\\\\\\n### Resource Templates\\\\\\\\n${templates}` : \\\\\\\"\\\\\\\") +\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t(resources ? `\\\\\\\\n\\\\\\\\n### Direct Resources\\\\\\\\n${resources}` : \\\\\\\"\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t.join(\\\\\\\"\\\\\\\\n\\\\\\\\n\\\\\\\")}`\\\\r\\\\n\\\\t\\\\t: \\\\\\\"(No MCP servers currently connected)\\\\\\\"\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n## Creating an MCP Server\\\\r\\\\n\\\\r\\\\nThe user may ask you something along the lines of \\\\\\\"add a tool\\\\\\\" that does some function, in other words to create an MCP server that provides tools and resources that may connect to external APIs for example. You have the ability to create an MCP server and add it to a configuration file that will then expose the tools and resources for you to use with \\\\\\\\`use_mcp_tool\\\\\\\\` and \\\\\\\\`access_mcp_resource\\\\\\\\`.\\\\r\\\\n\\\\r\\\\nWhen creating MCP servers, it's important to understand that they operate in a non-interactive environment. The server cannot initiate OAuth flows, open browser windows, or prompt for user input during runtime. All credentials and authentication tokens must be provided upfront through environment variables in the MCP settings configuration. For example, Spotify's API uses OAuth to get a refresh token for the user, but the MCP server cannot initiate this flow. While you can walk the user through obtaining an application client ID and secret, you may have to create a separate one-time setup script (like get-refresh-token.js) that captures and logs the final piece of the puzzle: the user's refresh token (i.e. you might run the script using execute_command which would open a browser for authentication, and then log the refresh token so that you can see it in the command output for you to use in the MCP settings configuration).\\\\r\\\\n\\\\r\\\\nUnless the user specifies otherwise, new MCP servers should be created in: ${await mcpHub.getMcpServersPath()}\\\\r\\\\n\\\\r\\\\n### Example MCP Server\\\\r\\\\n\\\\r\\\\nFor example, if the user wanted to give you the ability to retrieve weather information, you could create an MCP server that uses the OpenWeather API to get weather information, add it to the MCP settings configuration file, and then notice that you now have access to new tools and resources in the system prompt that you might use to show the user your new capabilities.\\\\r\\\\n\\\\r\\\\nThe following example demonstrates how to build an MCP server that provides weather data functionality. While this example shows how to implement resources, resource templates, and tools, in practice you should prefer using tools since they are more flexible and can handle dynamic parameters. The resource and resource template implementations are included here mainly for demonstration purposes of the different MCP capabilities, but a real weather server would likely just expose tools for fetching weather data. (The following steps are for macOS)\\\\r\\\\n\\\\r\\\\n1. Use the \\\\\\\\`create-typescript-server\\\\\\\\` tool to bootstrap a new project in the default MCP servers directory:\\\\r\\\\n\\\\r\\\\n\\\\\\\\`\\\\\\\\`\\\\\\\\`bash\\\\r\\\\ncd ${await mcpHub.getMcpServersPath()}\\\\r\\\\nnpx @modelcontextprotocol/create-server weather-server\\\\r\\\\ncd weather-server\\\\r\\\\n# Install dependencies\\\\r\\\\nnpm install axios\\\\r\\\\n\\\\\\\\`\\\\\\\\`\\\\\\\\`\\\\r\\\\n\\\\r\\\\nThis will create a new project with the following structure:\\\\r\\\\n\\\\r\\\\n\\\\\\\\`\\\\\\\\`\\\\\\\\`\\\\r\\\\nweather-server/\\\\r\\\\n ├── package.json\\\\r\\\\n {\\\\r\\\\n ...\\\\r\\\\n \\\\\\\"type\\\\\\\": \\\\\\\"module\\\\\\\", // added by default, uses ES module syntax (import/export) rather than CommonJS (require/module.exports) (Important to know if you create additional scripts in this server repository like a get-refresh-token.js script)\\\\r\\\\n \\\\\\\"scripts\\\\\\\": {\\\\r\\\\n \\\\\\\"build\\\\\\\": \\\\\\\"tsc && node -e \\\\\\\\\\\\\\\"require('fs').chmodSync('build/index.js', '755')\\\\\\\\\\\\\\\"\\\\\\\",\\\\r\\\\n ...\\\\r\\\\n }\\\\r\\\\n ...\\\\r\\\\n }\\\\r\\\\n ├── tsconfig.json\\\\r\\\\n └── src/\\\\r\\\\n └── weather-server/\\\\r\\\\n └── index.ts # Main server implementation\\\\r\\\\n\\\\\\\\`\\\\\\\\`\\\\\\\\`\\\\r\\\\n\\\\r\\\\n2. Replace \\\\\\\\`src/index.ts\\\\\\\\` with the following:\\\\r\\\\n\\\\r\\\\n\\\\\\\\`\\\\\\\\`\\\\\\\\`typescript\\\\r\\\\n#!/usr/bin/env node\\\\r\\\\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\\\\r\\\\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\\\\r\\\\nimport {\\\\r\\\\n CallToolRequestSchema,\\\\r\\\\n ErrorCode,\\\\r\\\\n ListResourcesRequestSchema,\\\\r\\\\n ListResourceTemplatesRequestSchema,\\\\r\\\\n ListToolsRequestSchema,\\\\r\\\\n McpError,\\\\r\\\\n ReadResourceRequestSchema,\\\\r\\\\n} from '@modelcontextprotocol/sdk/types.js';\\\\r\\\\nimport axios from 'axios';\\\\r\\\\n\\\\r\\\\nconst API_KEY = process.env.OPENWEATHER_API_KEY; // provided by MCP config\\\\r\\\\nif (!API_KEY) {\\\\r\\\\n throw new Error('OPENWEATHER_API_KEY environment variable is required');\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\ninterface OpenWeatherResponse {\\\\r\\\\n main: {\\\\r\\\\n temp: number;\\\\r\\\\n humidity: number;\\\\r\\\\n };\\\\r\\\\n weather: [{ description: string }];\\\\r\\\\n wind: { speed: number };\\\\r\\\\n dt_txt?: string;\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nconst isValidForecastArgs = (\\\\r\\\\n args: any\\\\r\\\\n): args is { city: string; days?: number } =>\\\\r\\\\n typeof args === 'object' &&\\\\r\\\\n args !== null &&\\\\r\\\\n typeof args.city === 'string' &&\\\\r\\\\n (args.days === undefined || typeof args.days === 'number');\\\\r\\\\n\\\\r\\\\nclass WeatherServer {\\\\r\\\\n private server: Server;\\\\r\\\\n private axiosInstance;\\\\r\\\\n\\\\r\\\\n constructor() {\\\\r\\\\n this.server = new Server(\\\\r\\\\n {\\\\r\\\\n name: 'example-weather-server',\\\\r\\\\n version: '0.1.0',\\\\r\\\\n },\\\\r\\\\n {\\\\r\\\\n capabilities: {\\\\r\\\\n resources: {},\\\\r\\\\n tools: {},\\\\r\\\\n },\\\\r\\\\n }\\\\r\\\\n );\\\\r\\\\n\\\\r\\\\n this.axiosInstance = axios.create({\\\\r\\\\n baseURL: 'http://api.openweathermap.org/data/2.5',\\\\r\\\\n params: {\\\\r\\\\n appid: API_KEY,\\\\r\\\\n units: 'metric',\\\\r\\\\n },\\\\r\\\\n });\\\\r\\\\n\\\\r\\\\n this.setupResourceHandlers();\\\\r\\\\n this.setupToolHandlers();\\\\r\\\\n \\\\r\\\\n // Error handling\\\\r\\\\n this.server.onerror = (error) => console.error('[MCP Error]', error);\\\\r\\\\n process.on('SIGINT', async () => {\\\\r\\\\n await this.server.close();\\\\r\\\\n process.exit(0);\\\\r\\\\n });\\\\r\\\\n }\\\\r\\\\n\\\\r\\\\n // MCP Resources represent any kind of UTF-8 encoded data that an MCP server wants to make available to clients, such as database records, API responses, log files, and more. Servers define direct resources with a static URI or dynamic resources with a URI template that follows the format \\\\\\\\`[protocol]://[host]/[path]\\\\\\\\`.\\\\r\\\\n private setupResourceHandlers() {\\\\r\\\\n // For static resources, servers can expose a list of resources:\\\\r\\\\n this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({\\\\r\\\\n resources: [\\\\r\\\\n // This is a poor example since you could use the resource template to get the same information but this demonstrates how to define a static resource\\\\r\\\\n {\\\\r\\\\n uri: \\\\\\\\`weather://San Francisco/current\\\\\\\\`, // Unique identifier for San Francisco weather resource\\\\r\\\\n name: \\\\\\\\`Current weather in San Francisco\\\\\\\\`, // Human-readable name\\\\r\\\\n mimeType: 'application/json', // Optional MIME type\\\\r\\\\n // Optional description\\\\r\\\\n description:\\\\r\\\\n 'Real-time weather data for San Francisco including temperature, conditions, humidity, and wind speed',\\\\r\\\\n },\\\\r\\\\n ],\\\\r\\\\n }));\\\\r\\\\n\\\\r\\\\n // For dynamic resources, servers can expose resource templates:\\\\r\\\\n this.server.setRequestHandler(\\\\r\\\\n ListResourceTemplatesRequestSchema,\\\\r\\\\n async () => ({\\\\r\\\\n resourceTemplates: [\\\\r\\\\n {\\\\r\\\\n uriTemplate: 'weather://{city}/current', // URI template (RFC 6570)\\\\r\\\\n name: 'Current weather for a given city', // Human-readable name\\\\r\\\\n mimeType: 'application/json', // Optional MIME type\\\\r\\\\n description: 'Real-time weather data for a specified city', // Optional description\\\\r\\\\n },\\\\r\\\\n ],\\\\r\\\\n })\\\\r\\\\n );\\\\r\\\\n\\\\r\\\\n // ReadResourceRequestSchema is used for both static resources and dynamic resource templates\\\\r\\\\n this.server.setRequestHandler(\\\\r\\\\n ReadResourceRequestSchema,\\\\r\\\\n async (request) => {\\\\r\\\\n const match = request.params.uri.match(\\\\r\\\\n /^weather:\\\\\\\\/\\\\\\\\/([^/]+)\\\\\\\\/current$/\\\\r\\\\n );\\\\r\\\\n if (!match) {\\\\r\\\\n throw new McpError(\\\\r\\\\n ErrorCode.InvalidRequest,\\\\r\\\\n \\\\\\\\`Invalid URI format: \\\\\\\\${request.params.uri}\\\\\\\\`\\\\r\\\\n );\\\\r\\\\n }\\\\r\\\\n const city = decodeURIComponent(match[1]);\\\\r\\\\n\\\\r\\\\n try {\\\\r\\\\n const response = await this.axiosInstance.get(\\\\r\\\\n 'weather', // current weather\\\\r\\\\n {\\\\r\\\\n params: { q: city },\\\\r\\\\n }\\\\r\\\\n );\\\\r\\\\n\\\\r\\\\n return {\\\\r\\\\n contents: [\\\\r\\\\n {\\\\r\\\\n uri: request.params.uri,\\\\r\\\\n mimeType: 'application/json',\\\\r\\\\n text: JSON.stringify(\\\\r\\\\n {\\\\r\\\\n temperature: response.data.main.temp,\\\\r\\\\n conditions: response.data.weather[0].description,\\\\r\\\\n humidity: response.data.main.humidity,\\\\r\\\\n wind_speed: response.data.wind.speed,\\\\r\\\\n timestamp: new Date().toISOString(),\\\\r\\\\n },\\\\r\\\\n null,\\\\r\\\\n 2\\\\r\\\\n ),\\\\r\\\\n },\\\\r\\\\n ],\\\\r\\\\n };\\\\r\\\\n } catch (error) {\\\\r\\\\n if (axios.isAxiosError(error)) {\\\\r\\\\n throw new McpError(\\\\r\\\\n ErrorCode.InternalError,\\\\r\\\\n \\\\\\\\`Weather API error: \\\\\\\\${\\\\r\\\\n error.response?.data.message ?? error.message\\\\r\\\\n }\\\\\\\\`\\\\r\\\\n );\\\\r\\\\n }\\\\r\\\\n throw error;\\\\r\\\\n }\\\\r\\\\n }\\\\r\\\\n );\\\\r\\\\n }\\\\r\\\\n\\\\r\\\\n /* MCP Tools enable servers to expose executable functionality to the system. Through these tools, you can interact with external systems, perform computations, and take actions in the real world.\\\\r\\\\n * - Like resources, tools are identified by unique names and can include descriptions to guide their usage. However, unlike resources, tools represent dynamic operations that can modify state or interact with external systems.\\\\r\\\\n * - While resources and tools are similar, you should prefer to create tools over resources when possible as they provide more flexibility.\\\\r\\\\n */\\\\r\\\\n private setupToolHandlers() {\\\\r\\\\n this.server.setRequestHandler(ListToolsRequestSchema, async () => ({\\\\r\\\\n tools: [\\\\r\\\\n {\\\\r\\\\n name: 'get_forecast', // Unique identifier\\\\r\\\\n description: 'Get weather forecast for a city', // Human-readable description\\\\r\\\\n inputSchema: {\\\\r\\\\n // JSON Schema for parameters\\\\r\\\\n type: 'object',\\\\r\\\\n properties: {\\\\r\\\\n city: {\\\\r\\\\n type: 'string',\\\\r\\\\n description: 'City name',\\\\r\\\\n },\\\\r\\\\n days: {\\\\r\\\\n type: 'number',\\\\r\\\\n description: 'Number of days (1-5)',\\\\r\\\\n minimum: 1,\\\\r\\\\n maximum: 5,\\\\r\\\\n },\\\\r\\\\n },\\\\r\\\\n required: ['city'], // Array of required property names\\\\r\\\\n },\\\\r\\\\n },\\\\r\\\\n ],\\\\r\\\\n }));\\\\r\\\\n\\\\r\\\\n this.server.setRequestHandler(CallToolRequestSchema, async (request) => {\\\\r\\\\n if (request.params.name !== 'get_forecast') {\\\\r\\\\n throw new McpError(\\\\r\\\\n ErrorCode.MethodNotFound,\\\\r\\\\n \\\\\\\\`Unknown tool: \\\\\\\\${request.params.name}\\\\\\\\`\\\\r\\\\n );\\\\r\\\\n }\\\\r\\\\n\\\\r\\\\n if (!isValidForecastArgs(request.params.arguments)) {\\\\r\\\\n throw new McpError(\\\\r\\\\n ErrorCode.InvalidParams,\\\\r\\\\n 'Invalid forecast arguments'\\\\r\\\\n );\\\\r\\\\n }\\\\r\\\\n\\\\r\\\\n const city = request.params.arguments.city;\\\\r\\\\n const days = Math.min(request.params.arguments.days || 3, 5);\\\\r\\\\n\\\\r\\\\n try {\\\\r\\\\n const response = await this.axiosInstance.get<{\\\\r\\\\n list: OpenWeatherResponse[];\\\\r\\\\n }>('forecast', {\\\\r\\\\n params: {\\\\r\\\\n q: city,\\\\r\\\\n cnt: days * 8,\\\\r\\\\n },\\\\r\\\\n });\\\\r\\\\n\\\\r\\\\n return {\\\\r\\\\n content: [\\\\r\\\\n {\\\\r\\\\n type: 'text',\\\\r\\\\n text: JSON.stringify(response.data.list, null, 2),\\\\r\\\\n },\\\\r\\\\n ],\\\\r\\\\n };\\\\r\\\\n } catch (error) {\\\\r\\\\n if (axios.isAxiosError(error)) {\\\\r\\\\n return {\\\\r\\\\n content: [\\\\r\\\\n {\\\\r\\\\n type: 'text',\\\\r\\\\n text: \\\\\\\\`Weather API error: \\\\\\\\${\\\\r\\\\n error.response?.data.message ?? error.message\\\\r\\\\n }\\\\\\\\`,\\\\r\\\\n },\\\\r\\\\n ],\\\\r\\\\n isError: true,\\\\r\\\\n };\\\\r\\\\n }\\\\r\\\\n throw error;\\\\r\\\\n }\\\\r\\\\n });\\\\r\\\\n }\\\\r\\\\n\\\\r\\\\n async run() {\\\\r\\\\n const transport = new StdioServerTransport();\\\\r\\\\n await this.server.connect(transport);\\\\r\\\\n console.error('Weather MCP server running on stdio');\\\\r\\\\n }\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nconst server = new WeatherServer();\\\\r\\\\nserver.run().catch(console.error);\\\\r\\\\n\\\\\\\\`\\\\\\\\`\\\\\\\\`\\\\r\\\\n\\\\r\\\\n(Remember: This is just an example–you may use different dependencies, break the implementation up into multiple files, etc.)\\\\r\\\\n\\\\r\\\\n3. Build and compile the executable JavaScript file\\\\r\\\\n\\\\r\\\\n\\\\\\\\`\\\\\\\\`\\\\\\\\`bash\\\\r\\\\nnpm run build\\\\r\\\\n\\\\\\\\`\\\\\\\\`\\\\\\\\`\\\\r\\\\n\\\\r\\\\n4. Whenever you need an environment variable such as an API key to configure the MCP server, walk the user through the process of getting the key. For example, they may need to create an account and go to a developer dashboard to generate the key. Provide step-by-step instructions and URLs to make it easy for the user to retrieve the necessary information. Then use the ask_followup_question tool to ask the user for the key, in this case the OpenWeather API key.\\\\r\\\\n\\\\r\\\\n5. Install the MCP Server by adding the MCP server configuration to the settings file located at '${await mcpHub.getMcpSettingsFilePath()}'. The settings file may have other MCP servers already configured, so you would read it first and then add your new server to the existing \\\\\\\\`mcpServers\\\\\\\\` object.\\\\r\\\\n\\\\r\\\\n\\\\\\\\`\\\\\\\\`\\\\\\\\`json\\\\r\\\\n{\\\\r\\\\n \\\\\\\"mcpServers\\\\\\\": {\\\\r\\\\n ...,\\\\r\\\\n \\\\\\\"weather\\\\\\\": {\\\\r\\\\n \\\\\\\"command\\\\\\\": \\\\\\\"node\\\\\\\",\\\\r\\\\n \\\\\\\"args\\\\\\\": [\\\\\\\"/path/to/weather-server/build/index.js\\\\\\\"],\\\\r\\\\n \\\\\\\"env\\\\\\\": {\\\\r\\\\n \\\\\\\"OPENWEATHER_API_KEY\\\\\\\": \\\\\\\"user-provided-api-key\\\\\\\"\\\\r\\\\n }\\\\r\\\\n },\\\\r\\\\n }\\\\r\\\\n}\\\\r\\\\n\\\\\\\\`\\\\\\\\`\\\\\\\\`\\\\r\\\\n\\\\r\\\\n(Note: the user may also ask you to install the MCP server to the Claude desktop app, in which case you would read then modify \\\\\\\\`~/Library/Application\\\\\\\\ Support/Claude/claude_desktop_config.json\\\\\\\\` on macOS for example. It follows the same format of a top level \\\\\\\\`mcpServers\\\\\\\\` object.)\\\\r\\\\n\\\\r\\\\n6. After you have edited the MCP settings configuration file, the system will automatically run all the servers and expose the available tools and resources in the 'Connected MCP Servers' section.\\\\r\\\\n\\\\r\\\\n7. Now that you have access to these new tools and resources, you may suggest ways the user can command you to invoke them - for example, with this new weather tool now available, you can invite the user to ask \\\\\\\"what's the weather in San Francisco?\\\\\\\"\\\\r\\\\n\\\\r\\\\n## Editing MCP Servers\\\\r\\\\n\\\\r\\\\nThe user may ask to add tools or resources that may make sense to add to an existing MCP server (listed under 'Connected MCP Servers' above: ${\\\\r\\\\n\\\\tmcpHub\\\\r\\\\n\\\\t\\\\t.getServers()\\\\r\\\\n\\\\t\\\\t.map((server) => server.name)\\\\r\\\\n\\\\t\\\\t.join(\\\\\\\", \\\\\\\") || \\\\\\\"(None running currently)\\\\\\\"\\\\r\\\\n}, e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use replace_in_file to make changes to the files.\\\\r\\\\n\\\\r\\\\nHowever some MCP servers may be running from installed packages rather than a local repository, in which case it may make more sense to create a new MCP server.\\\\r\\\\n\\\\r\\\\n# MCP Servers Are Not Always Necessary\\\\r\\\\n\\\\r\\\\nThe user may not always request the use or creation of MCP servers. Instead, they might provide tasks that can be completed with existing tools. While using the MCP SDK to extend your capabilities can be useful, it's important to understand that this is just one specialized type of task you can accomplish. You should only implement MCP servers when the user explicitly requests it (e.g., \\\\\\\"add a tool that...\\\\\\\").\\\\r\\\\n\\\\r\\\\nRemember: The MCP documentation and example provided above are to help you understand and work with existing MCP servers or create new ones when requested by the user. You already have access to tools and capabilities that can be used to accomplish a wide range of tasks.\\\\r\\\\n\\\\r\\\\n====\\\\r\\\\n\\\\r\\\\nEDITING FILES\\\\r\\\\n\\\\r\\\\nYou have access to two tools for working with files: **write_to_file** and **replace_in_file**. Understanding their roles and selecting the right one for the job will help ensure efficient and accurate modifications.\\\\r\\\\n\\\\r\\\\n# write_to_file\\\\r\\\\n\\\\r\\\\n## Purpose\\\\r\\\\n\\\\r\\\\n- Create a new file, or overwrite the entire contents of an existing file.\\\\r\\\\n\\\\r\\\\n## When to Use\\\\r\\\\n\\\\r\\\\n- Initial file creation, such as when scaffolding a new project. \\\\r\\\\n- Overwriting large boilerplate files where you want to replace the entire content at once.\\\\r\\\\n- When the complexity or number of changes would make replace_in_file unwieldy or error-prone.\\\\r\\\\n- When you need to completely restructure a file's content or change its fundamental organization.\\\\r\\\\n\\\\r\\\\n## Important Considerations\\\\r\\\\n\\\\r\\\\n- Using write_to_file requires providing the file’s complete final content. \\\\r\\\\n- If you only need to make small changes to an existing file, consider using replace_in_file instead to avoid unnecessarily rewriting the entire file.\\\\r\\\\n- While write_to_file should not be your default choice, don't hesitate to use it when the situation truly calls for it.\\\\r\\\\n\\\\r\\\\n# replace_in_file\\\\r\\\\n\\\\r\\\\n## Purpose\\\\r\\\\n\\\\r\\\\n- Make targeted edits to specific parts of an existing file without overwriting the entire file.\\\\r\\\\n\\\\r\\\\n## When to Use\\\\r\\\\n\\\\r\\\\n- Small, localized changes like updating a few lines, function implementations, changing variable names, modifying a section of text, etc.\\\\r\\\\n- Targeted improvements where only specific portions of the file’s content needs to be altered.\\\\r\\\\n- Especially useful for long files where much of the file will remain unchanged.\\\\r\\\\n\\\\r\\\\n## Advantages\\\\r\\\\n\\\\r\\\\n- More efficient for minor edits, since you don’t need to supply the entire file content. \\\\r\\\\n- Reduces the chance of errors that can occur when overwriting large files.\\\\r\\\\n\\\\r\\\\n# Choosing the Appropriate Tool\\\\r\\\\n\\\\r\\\\n- **Default to replace_in_file** for most changes. It's the safer, more precise option that minimizes potential issues.\\\\r\\\\n- **Use write_to_file** when:\\\\r\\\\n - Creating new files\\\\r\\\\n - The changes are so extensive that using replace_in_file would be more complex or risky\\\\r\\\\n - You need to completely reorganize or restructure a file\\\\r\\\\n - The file is relatively small and the changes affect most of its content\\\\r\\\\n - You're generating boilerplate or template files\\\\r\\\\n\\\\r\\\\n# Auto-formatting Considerations\\\\r\\\\n\\\\r\\\\n- After using either write_to_file or replace_in_file, the user's editor may automatically format the file\\\\r\\\\n- This auto-formatting may modify the file contents, for example:\\\\r\\\\n - Breaking single lines into multiple lines\\\\r\\\\n - Adjusting indentation to match project style (e.g. 2 spaces vs 4 spaces vs tabs)\\\\r\\\\n - Converting single quotes to double quotes (or vice versa based on project preferences)\\\\r\\\\n - Organizing imports (e.g. sorting, grouping by type)\\\\r\\\\n - Adding/removing trailing commas in objects and arrays\\\\r\\\\n - Enforcing consistent brace style (e.g. same-line vs new-line)\\\\r\\\\n - Standardizing semicolon usage (adding or removing based on style)\\\\r\\\\n- The write_to_file and replace_in_file tool responses will include the final state of the file after any auto-formatting\\\\r\\\\n- Use this final state as your reference point for any subsequent edits. This is ESPECIALLY important when crafting SEARCH blocks for replace_in_file which require the content to match what's in the file exactly.\\\\r\\\\n\\\\r\\\\n# Workflow Tips\\\\r\\\\n\\\\r\\\\n1. Before editing, assess the scope of your changes and decide which tool to use.\\\\r\\\\n2. For targeted edits, apply replace_in_file with carefully crafted SEARCH/REPLACE blocks. If you need multiple changes, you can stack multiple SEARCH/REPLACE blocks within a single replace_in_file call.\\\\r\\\\n3. For major overhauls or initial file creation, rely on write_to_file.\\\\r\\\\n4. Once the file has been edited with either write_to_file or replace_in_file, the system will provide you with the final state of the modified file. Use this updated content as the reference point for any subsequent SEARCH/REPLACE operations, since it reflects any auto-formatting or user-applied changes.\\\\r\\\\n\\\\r\\\\nBy thoughtfully selecting between write_to_file and replace_in_file, you can make your file editing process smoother, safer, and more efficient.\\\\r\\\\n\\\\r\\\\n====\\\\r\\\\n \\\\r\\\\nCAPABILITIES\\\\r\\\\n\\\\r\\\\n- You have access to tools that let you execute CLI commands on the user's computer, list files, view source code definitions, regex search${\\\\r\\\\n\\\\tsupportsComputerUse ? \\\\\\\", use the browser\\\\\\\" : \\\\\\\"\\\\\\\"\\\\r\\\\n}, read and edit files, and ask follow-up questions. These tools help you effectively accomplish a wide range of tasks, such as writing code, making edits or improvements to existing files, understanding the current state of a project, performing system operations, and much more.\\\\r\\\\n- When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('${cwd.toPosix()}') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop.\\\\r\\\\n- You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring.\\\\r\\\\n- You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task.\\\\r\\\\n\\\\t- For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the replace_in_file tool to implement changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed.\\\\r\\\\n- You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance.${\\\\r\\\\n\\\\tsupportsComputerUse\\\\r\\\\n\\\\t\\\\t? \\\\\\\"\\\\\\\\n- You can use the browser_action tool to interact with websites (including html files and locally running development servers) through a Puppeteer-controlled browser when you feel it is necessary in accomplishing the user's task. This tool is particularly useful for web development tasks as it allows you to launch a browser, navigate to pages, interact with elements through clicks and keyboard input, and capture the results through screenshots and console logs. This tool may be useful at key stages of web development tasks-such as after implementing new features, making substantial changes, when troubleshooting issues, or to verify the result of your work. You can analyze the provided screenshots to ensure correct rendering or identify errors, and review console logs for runtime issues.\\\\\\\\n\\\\t- For example, if asked to add a component to a react website, you might create the necessary files, use execute_command to run the site locally, then use browser_action to launch the browser, navigate to the local server, and verify the component renders & functions correctly before closing the browser.\\\\\\\"\\\\r\\\\n\\\\t\\\\t: \\\\\\\"\\\\\\\"\\\\r\\\\n}\\\\r\\\\n- You have access to MCP servers that may provide additional tools and resources. Each server may provide different capabilities that you can use to accomplish tasks more effectively.\\\\r\\\\n\\\\r\\\\n====\\\\r\\\\n\\\\r\\\\nRULES\\\\r\\\\n\\\\r\\\\n- Your current working directory is: ${cwd.toPosix()}\\\\r\\\\n- You cannot \\\\\\\\`cd\\\\\\\\` into a different directory to complete a task. You are stuck operating from '${cwd.toPosix()}', so be sure to pass in the correct 'path' parameter when using tools that require a path.\\\\r\\\\n- Do not use the ~ character or $HOME to refer to the home directory.\\\\r\\\\n- Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '${cwd.toPosix()}', and if so prepend with \\\\\\\\`cd\\\\\\\\`'ing into that directory && then executing the command (as one command since you are stuck operating from '${cwd.toPosix()}'). For example, if you needed to run \\\\\\\\`npm install\\\\\\\\` in a project outside of '${cwd.toPosix()}', you would need to prepend with a \\\\\\\\`cd\\\\\\\\` i.e. pseudocode for this would be \\\\\\\\`cd (path to project) && (command, in this case npm install)\\\\\\\\`.\\\\r\\\\n- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using replace_in_file to make informed changes.\\\\r\\\\n- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when creating files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser.\\\\r\\\\n- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.\\\\r\\\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\\\r\\\\n- When you want to modify a file, use the replace_in_file or write_to_file tool directly with the desired changes. You do not need to display the changes before using the tool.\\\\r\\\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively. When you've completed your task, you must use the attempt_completion tool to present the result to the user. The user may provide feedback, which you can use to make improvements and try again.\\\\r\\\\n- You are only allowed to ask the user questions using the ask_followup_question tool. Use this tool only when you need additional details to complete a task, and be sure to use a clear and concise question that will help you move forward with the task. However if you can use the available tools to avoid having to ask the user questions, you should do so. For example, if the user mentions a file that may be in an outside directory like the Desktop, you should use the list_files tool to list the files in the Desktop and check if the file they are talking about is there, rather than asking the user to provide the file path themselves.\\\\r\\\\n- When executing commands, if you don't see the expected output, assume the terminal executed the command successfully and proceed with the task. The user's terminal may be unable to stream the output back properly. If you absolutely need to see the actual terminal output, use the ask_followup_question tool to request the user to copy and paste it back to you.\\\\r\\\\n- The user may provide a file's contents directly in their message, in which case you shouldn't use the read_file tool to get the file contents again since you already have it.\\\\r\\\\n- Your goal is to try to accomplish the user's task, NOT engage in a back and forth conversation.${\\\\r\\\\n\\\\tsupportsComputerUse\\\\r\\\\n\\\\t\\\\t? '\\\\\\\\n- The user may ask generic non-development tasks, such as \\\\\\\"what\\\\\\\\'s the latest news\\\\\\\" or \\\\\\\"look up the weather in San Diego\\\\\\\", in which case you might use the browser_action tool to complete the task if it makes sense to do so, rather than trying to create a website or using curl to answer the question. However, if an available MCP server tool or resource can be used instead, you should prefer to use it over browser_action.'\\\\r\\\\n\\\\t\\\\t: \\\\\\\"\\\\\\\"\\\\r\\\\n}\\\\r\\\\n- NEVER end attempt_completion result with a question or request to engage in further conversation! Formulate the end of your result in a way that is final and does not require further input from the user.\\\\r\\\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\\\\\"Great\\\\\\\", \\\\\\\"Certainly\\\\\\\", \\\\\\\"Okay\\\\\\\", \\\\\\\"Sure\\\\\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\\\\\"Great, I've updated the CSS\\\\\\\" but instead something like \\\\\\\"I've updated the CSS\\\\\\\". It is important you be clear and technical in your messages.\\\\r\\\\n- When presented with images, utilize your vision capabilities to thoroughly examine them and extract meaningful information. Incorporate these insights into your thought process as you accomplish the user's task.\\\\r\\\\n- At the end of each user message, you will automatically receive environment_details. This information is not written by the user themselves, but is auto-generated to provide potentially relevant context about the project structure and environment. While this information can be valuable for understanding the project context, do not treat it as a direct part of the user's request or response. Use it to inform your actions and decisions, but don't assume the user is explicitly asking about or referring to this information unless they clearly do so in their message. When using environment_details, explain your actions clearly to ensure the user understands, as they may not be aware of these details.\\\\r\\\\n- Before executing commands, check the \\\\\\\"Actively Running Terminals\\\\\\\" section in environment_details. If present, consider how these active processes might impact your task. For example, if a local development server is already running, you wouldn't need to start it again. If no active terminals are listed, proceed with command execution as normal.\\\\r\\\\n- MCP operations should be used one at a time, similar to other tool usage. Wait for confirmation of success before proceeding with additional operations.\\\\r\\\\n- When using the replace_in_file tool, you must include complete lines in your SEARCH blocks, not partial lines. The system requires exact line matches and cannot match partial lines. For example, if you want to match a line containing \\\\\\\"const x = 5;\\\\\\\", your SEARCH block must include the entire line, not just \\\\\\\"x = 5\\\\\\\" or other fragments.\\\\r\\\\n- When using the replace_in_file tool, if you use multiple SEARCH/REPLACE blocks, list them in the order they appear in the file. For example if you need to make changes to both line 10 and line 50, first include the SEARCH/REPLACE block for line 10, followed by the SEARCH/REPLACE block for line 50.\\\\r\\\\n- It is critical you wait for the user's response after each tool use, in order to confirm the success of the tool use. For example, if asked to make a todo app, you would create a file, wait for the user's response it was created successfully, then create another file if needed, wait for the user's response it was created successfully, etc.${\\\\r\\\\n\\\\tsupportsComputerUse\\\\r\\\\n\\\\t\\\\t? \\\\\\\" Then if you want to test your work, you might use browser_action to launch the site, wait for the user's response confirming the site was launched along with a screenshot, then perhaps e.g., click a button to test functionality if needed, wait for the user's response confirming the button was clicked along with a screenshot of the new state, before finally closing the browser.\\\\\\\"\\\\r\\\\n\\\\t\\\\t: \\\\\\\"\\\\\\\"\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n====\\\\r\\\\n\\\\r\\\\nSYSTEM INFORMATION\\\\r\\\\n\\\\r\\\\nOperating System: ${osName()}\\\\r\\\\nDefault Shell: ${defaultShell}\\\\r\\\\nHome Directory: ${os.homedir().toPosix()}\\\\r\\\\nCurrent Working Directory: ${cwd.toPosix()}\\\\r\\\\n\\\\r\\\\n====\\\\r\\\\n\\\\r\\\\nOBJECTIVE\\\\r\\\\n\\\\r\\\\nYou accomplish a given task iteratively, breaking it down into clear steps and working through them methodically.\\\\r\\\\n\\\\r\\\\n1. Analyze the user's task and set clear, achievable goals to accomplish it. Prioritize these goals in a logical order.\\\\r\\\\n2. Work through these goals sequentially, utilizing available tools one at a time as necessary. Each goal should correspond to a distinct step in your problem-solving process. You will be informed on the work completed and what's remaining as you go.\\\\r\\\\n3. Remember, you have extensive capabilities with access to a wide range of tools that can be used in powerful and clever ways as necessary to accomplish each goal. Before calling a tool, do some analysis within <thinking></thinking> tags. First, analyze the file structure provided in environment_details to gain context and insights for proceeding effectively. Then, think about which of the provided tools is the most relevant tool to accomplish the user's task. Next, go through each of the required parameters of the relevant tool and determine if the user has directly provided or given enough information to infer a value. When deciding if the parameter can be inferred, carefully consider all the context to see if it supports a specific value. If all of the required parameters are present or can be reasonably inferred, close the thinking tag and proceed with the tool use. BUT, if one of the values for a required parameter is missing, DO NOT invoke the tool (not even with fillers for the missing params) and instead, ask the user to provide the missing parameters using the ask_followup_question tool. DO NOT ask for more information on optional parameters if it is not provided.\\\\r\\\\n4. Once you've completed the user's task, you must use the attempt_completion tool to present the result of the task to the user. You may also provide a CLI command to showcase the result of your task; this can be particularly useful for web development tasks, where you can run e.g. \\\\\\\\`open index.html\\\\\\\\` to show the website you've built.\\\\r\\\\n5. The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.`\\\\r\\\\n\\\\r\\\\nexport function addUserInstructions(settingsCustomInstructions?: string, clineRulesFileInstructions?: string) {\\\\r\\\\n\\\\tlet customInstructions = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\tif (settingsCustomInstructions) {\\\\r\\\\n\\\\t\\\\tcustomInstructions += settingsCustomInstructions + \\\\\\\"\\\\\\\\n\\\\\\\\n\\\\\\\"\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\tif (clineRulesFileInstructions) {\\\\r\\\\n\\\\t\\\\tcustomInstructions += clineRulesFileInstructions\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\treturn `\\\\r\\\\n====\\\\r\\\\n\\\\r\\\\nUSER'S CUSTOM INSTRUCTIONS\\\\r\\\\n\\\\r\\\\nThe following additional instructions are provided by the user, and should be followed to the best of your ability without interfering with the TOOL USE guidelines.\\\\r\\\\n\\\\r\\\\n${customInstructions.trim()}`\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":56940,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.946Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.946Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":910,\\\"nonEmptyLines\\\":765,\\\"commentLines\\\":9,\\\"complexity\\\":57},\\\"dependencies\\\":[\\\"default-shell\\\",\\\"os\\\",\\\"os-name\\\",\\\"../../services/mcp/McpHub\\\",\\\"react\\\",\\\"@modelcontextprotocol/sdk/server/index.js\\\",\\\"@modelcontextprotocol/sdk/server/stdio.js\\\",\\\"axios\\\",\\\"fs\\\"],\\\"quality\\\":{\\\"score\\\":-1130,\\\"issues\\\":[],\\\"duplicateLines\\\":194,\\\"longLines\\\":130,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.337Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":910},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\core\\\\\\\\sliding-window\\\\\\\\index.ts\\\":{\\\"content\\\":\\\"import { Anthropic } from \\\\\\\"@anthropic-ai/sdk\\\\\\\"\\\\r\\\\n\\\\r\\\\n/*\\\\r\\\\nWe can't implement a dynamically updating sliding window as it would break prompt cache\\\\r\\\\nevery time. To maintain the benefits of caching, we need to keep conversation history\\\\r\\\\nstatic. This operation should be performed as infrequently as possible. If a user reaches\\\\r\\\\na 200k context, we can assume that the first half is likely irrelevant to their current task.\\\\r\\\\nTherefore, this function should only be called when absolutely necessary to fit within\\\\r\\\\ncontext limits, not as a continuous process.\\\\r\\\\n*/\\\\r\\\\n// export function truncateHalfConversation(\\\\r\\\\n// \\\\tmessages: Anthropic.Messages.MessageParam[],\\\\r\\\\n// ): Anthropic.Messages.MessageParam[] {\\\\r\\\\n// \\\\t// 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.\\\\r\\\\n\\\\r\\\\n// \\\\t// Always keep the first Task message (this includes the project's file structure in environment_details)\\\\r\\\\n// \\\\tconst truncatedMessages = [messages[0]]\\\\r\\\\n\\\\r\\\\n// \\\\t// Remove half of user-assistant pairs\\\\r\\\\n// \\\\tconst messagesToRemove = Math.floor(messages.length / 4) * 2 // has to be even number\\\\r\\\\n\\\\r\\\\n// \\\\tconst remainingMessages = messages.slice(messagesToRemove + 1) // has to start with assistant message since tool result cannot follow assistant message with no tool use\\\\r\\\\n// \\\\ttruncatedMessages.push(...remainingMessages)\\\\r\\\\n\\\\r\\\\n// \\\\treturn truncatedMessages\\\\r\\\\n// }\\\\r\\\\n\\\\r\\\\n/*\\\\r\\\\ngetNextTruncationRange: Calculates the next range of messages to be \\\\\\\"deleted\\\\\\\"\\\\r\\\\n- Takes the full messages array and optional current deleted range\\\\r\\\\n- Always preserves the first message (task message)\\\\r\\\\n- Removes 1/2 of remaining messages (rounded down to even number) after current deleted range\\\\r\\\\n- Returns [startIndex, endIndex] representing inclusive range to delete\\\\r\\\\n\\\\r\\\\ngetTruncatedMessages: Constructs the truncated array using the deleted range\\\\r\\\\n- Takes full messages array and optional deleted range\\\\r\\\\n- Returns new array with messages in deleted range removed\\\\r\\\\n- Preserves order and structure of remaining messages\\\\r\\\\n\\\\r\\\\nThe range is represented as [startIndex, endIndex] where both indices are inclusive\\\\r\\\\nThe functions maintain the original array integrity while allowing progressive truncation \\\\r\\\\nthrough the deletedRange parameter\\\\r\\\\n\\\\r\\\\nUsage example:\\\\r\\\\nconst messages = [user1, assistant1, user2, assistant2, user3, assistant3];\\\\r\\\\nlet deletedRange = getNextTruncationRange(messages); // [1,2] (assistant1,user2)\\\\r\\\\nlet truncated = getTruncatedMessages(messages, deletedRange); \\\\r\\\\n// [user1, assistant2, user3, assistant3]\\\\r\\\\n\\\\r\\\\ndeletedRange = getNextTruncationRange(messages, deletedRange); // [2,3] (assistant2,user3) \\\\r\\\\ntruncated = getTruncatedMessages(messages, deletedRange);\\\\r\\\\n// [user1, assistant3]\\\\r\\\\n*/\\\\r\\\\n\\\\r\\\\nexport function getNextTruncationRange(\\\\r\\\\n\\\\tmessages: Anthropic.Messages.MessageParam[],\\\\r\\\\n\\\\tcurrentDeletedRange: [number, number] | undefined = undefined,\\\\r\\\\n): [number, number] {\\\\r\\\\n\\\\t// Since we always keep the first message, currentDeletedRange[0] will always be 1 (for now until we have a smarter truncation algorithm)\\\\r\\\\n\\\\tconst rangeStartIndex = 1\\\\r\\\\n\\\\tconst startOfRest = currentDeletedRange ? currentDeletedRange[1] + 1 : 1\\\\r\\\\n\\\\r\\\\n\\\\t// Remove half of user-assistant pairs\\\\r\\\\n\\\\tconst messagesToRemove = Math.floor((messages.length - startOfRest) / 4) * 2 // Keep even number\\\\r\\\\n\\\\tlet rangeEndIndex = startOfRest + messagesToRemove - 1\\\\r\\\\n\\\\r\\\\n\\\\t// Make sure the last message being removed is a user message, so that the next message after the initial task message is an assistant message. This preservers the user-assistant-user-assistant structure.\\\\r\\\\n\\\\t// NOTE: anthropic format messages are always user-assitant-user-assistant, while openai format messages can have multiple user messages in a row (we use anthropic format throughout cline)\\\\r\\\\n\\\\tif (messages[rangeEndIndex].role !== \\\\\\\"user\\\\\\\") {\\\\r\\\\n\\\\t\\\\trangeEndIndex -= 1\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// this is an inclusive range that will be removed from the conversation history\\\\r\\\\n\\\\treturn [rangeStartIndex, rangeEndIndex]\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport function getTruncatedMessages(\\\\r\\\\n\\\\tmessages: Anthropic.Messages.MessageParam[],\\\\r\\\\n\\\\tdeletedRange: [number, number] | undefined,\\\\r\\\\n): Anthropic.Messages.MessageParam[] {\\\\r\\\\n\\\\tif (!deletedRange) {\\\\r\\\\n\\\\t\\\\treturn messages\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tconst [start, end] = deletedRange\\\\r\\\\n\\\\t// the range is inclusive - both start and end indices and everything in between will be removed from the final result.\\\\r\\\\n\\\\t// NOTE: if you try to console log these, don't forget that logging a reference to an array may not provide the same result as logging a slice() snapshot of that array at that exact moment. The following DOES in fact include the latest assistant message.\\\\r\\\\n\\\\treturn [...messages.slice(0, start), ...messages.slice(end + 1)]\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":4658,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.947Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.947Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":90,\\\"nonEmptyLines\\\":73,\\\"commentLines\\\":23,\\\"complexity\\\":4},\\\"dependencies\\\":[\\\"@anthropic-ai/sdk\\\"],\\\"quality\\\":{\\\"score\\\":54,\\\"issues\\\":[],\\\"duplicateLines\\\":6,\\\"longLines\\\":8,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.341Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":90},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\core\\\\\\\\webview\\\\\\\\ClineProvider.ts\\\":{\\\"content\\\":\\\"import { Anthropic } from \\\\\\\"@anthropic-ai/sdk\\\\\\\"\\\\r\\\\nimport axios from \\\\\\\"axios\\\\\\\"\\\\r\\\\nimport fs from \\\\\\\"fs/promises\\\\\\\"\\\\r\\\\nimport os from \\\\\\\"os\\\\\\\"\\\\r\\\\nimport pWaitFor from \\\\\\\"p-wait-for\\\\\\\"\\\\r\\\\nimport * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\nimport { buildApiHandler } from \\\\\\\"../../api\\\\\\\"\\\\r\\\\nimport { downloadTask } from \\\\\\\"../../integrations/misc/export-markdown\\\\\\\"\\\\r\\\\nimport { openFile, openImage } from \\\\\\\"../../integrations/misc/open-file\\\\\\\"\\\\r\\\\nimport { selectImages } from \\\\\\\"../../integrations/misc/process-images\\\\\\\"\\\\r\\\\nimport { getTheme } from \\\\\\\"../../integrations/theme/getTheme\\\\\\\"\\\\r\\\\nimport WorkspaceTracker from \\\\\\\"../../integrations/workspace/WorkspaceTracker\\\\\\\"\\\\r\\\\nimport { McpHub } from \\\\\\\"../../services/mcp/McpHub\\\\\\\"\\\\r\\\\nimport { ApiProvider, ModelInfo } from \\\\\\\"../../shared/api\\\\\\\"\\\\r\\\\nimport { findLast } from \\\\\\\"../../shared/array\\\\\\\"\\\\r\\\\nimport { ExtensionMessage, ExtensionState } from \\\\\\\"../../shared/ExtensionMessage\\\\\\\"\\\\r\\\\nimport { HistoryItem } from \\\\\\\"../../shared/HistoryItem\\\\\\\"\\\\r\\\\nimport { ClineCheckpointRestore, WebviewMessage } from \\\\\\\"../../shared/WebviewMessage\\\\\\\"\\\\r\\\\nimport { fileExistsAtPath } from \\\\\\\"../../utils/fs\\\\\\\"\\\\r\\\\nimport { Cline } from \\\\\\\"../Cline\\\\\\\"\\\\r\\\\nimport { openMention } from \\\\\\\"../mentions\\\\\\\"\\\\r\\\\nimport { getNonce } from \\\\\\\"./getNonce\\\\\\\"\\\\r\\\\nimport { getUri } from \\\\\\\"./getUri\\\\\\\"\\\\r\\\\nimport { AutoApprovalSettings, DEFAULT_AUTO_APPROVAL_SETTINGS } from \\\\\\\"../../shared/AutoApprovalSettings\\\\\\\"\\\\r\\\\n\\\\r\\\\n/*\\\\r\\\\nhttps://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts\\\\r\\\\n\\\\r\\\\nhttps://github.com/KumarVariable/vscode-extension-sidebar-html/blob/master/src/customSidebarViewProvider.ts\\\\r\\\\n*/\\\\r\\\\n\\\\r\\\\ntype SecretKey =\\\\r\\\\n\\\\t| \\\\\\\"apiKey\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"openRouterApiKey\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"awsAccessKey\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"awsSecretKey\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"awsSessionToken\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"openAiApiKey\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"geminiApiKey\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"openAiNativeApiKey\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"deepSeekApiKey\\\\\\\"\\\\r\\\\ntype GlobalStateKey =\\\\r\\\\n\\\\t| \\\\\\\"apiProvider\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"apiModelId\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"awsRegion\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"awsUseCrossRegionInference\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"vertexProjectId\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"vertexRegion\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"lastShownAnnouncementId\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"customInstructions\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"taskHistory\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"openAiBaseUrl\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"openAiModelId\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"ollamaModelId\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"ollamaBaseUrl\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"lmStudioModelId\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"lmStudioBaseUrl\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"anthropicBaseUrl\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"azureApiVersion\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"openRouterModelId\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"openRouterModelInfo\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"autoApprovalSettings\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport const GlobalFileNames = {\\\\r\\\\n\\\\tapiConversationHistory: \\\\\\\"api_conversation_history.json\\\\\\\",\\\\r\\\\n\\\\tuiMessages: \\\\\\\"ui_messages.json\\\\\\\",\\\\r\\\\n\\\\topenRouterModels: \\\\\\\"openrouter_models.json\\\\\\\",\\\\r\\\\n\\\\tmcpSettings: \\\\\\\"cline_mcp_settings.json\\\\\\\",\\\\r\\\\n\\\\tclineRules: \\\\\\\".clinerules\\\\\\\",\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport class ClineProvider implements vscode.WebviewViewProvider {\\\\r\\\\n\\\\tpublic static readonly sideBarId = \\\\\\\"claude-dev.SidebarProvider\\\\\\\" // used in package.json as the view's id. This value cannot be changed due to how vscode caches views based on their id, and updating the id would break existing instances of the extension.\\\\r\\\\n\\\\tpublic static readonly tabPanelId = \\\\\\\"claude-dev.TabPanelProvider\\\\\\\"\\\\r\\\\n\\\\tprivate static activeInstances: Set<ClineProvider> = new Set()\\\\r\\\\n\\\\tprivate disposables: vscode.Disposable[] = []\\\\r\\\\n\\\\tprivate view?: vscode.WebviewView | vscode.WebviewPanel\\\\r\\\\n\\\\tprivate cline?: Cline\\\\r\\\\n\\\\tprivate workspaceTracker?: WorkspaceTracker\\\\r\\\\n\\\\tmcpHub?: McpHub\\\\r\\\\n\\\\tprivate latestAnnouncementId = \\\\\\\"jan-6-2025\\\\\\\" // update to some unique identifier when we add a new announcement\\\\r\\\\n\\\\r\\\\n\\\\tconstructor(\\\\r\\\\n\\\\t\\\\treadonly context: vscode.ExtensionContext,\\\\r\\\\n\\\\t\\\\tprivate readonly outputChannel: vscode.OutputChannel,\\\\r\\\\n\\\\t) {\\\\r\\\\n\\\\t\\\\tthis.outputChannel.appendLine(\\\\\\\"ClineProvider instantiated\\\\\\\")\\\\r\\\\n\\\\t\\\\tClineProvider.activeInstances.add(this)\\\\r\\\\n\\\\t\\\\tthis.workspaceTracker = new WorkspaceTracker(this)\\\\r\\\\n\\\\t\\\\tthis.mcpHub = new McpHub(this)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t/*\\\\r\\\\n\\\\tVSCode extensions use the disposable pattern to clean up resources when the sidebar/editor tab is closed by the user or system. This applies to event listening, commands, interacting with the UI, etc.\\\\r\\\\n\\\\t- https://vscode-docs.readthedocs.io/en/stable/extensions/patterns-and-principles/\\\\r\\\\n\\\\t- https://github.com/microsoft/vscode-extension-samples/blob/main/webview-sample/src/extension.ts\\\\r\\\\n\\\\t*/\\\\r\\\\n\\\\tasync dispose() {\\\\r\\\\n\\\\t\\\\tthis.outputChannel.appendLine(\\\\\\\"Disposing ClineProvider...\\\\\\\")\\\\r\\\\n\\\\t\\\\tawait this.clearTask()\\\\r\\\\n\\\\t\\\\tthis.outputChannel.appendLine(\\\\\\\"Cleared task\\\\\\\")\\\\r\\\\n\\\\t\\\\tif (this.view && \\\\\\\"dispose\\\\\\\" in this.view) {\\\\r\\\\n\\\\t\\\\t\\\\tthis.view.dispose()\\\\r\\\\n\\\\t\\\\t\\\\tthis.outputChannel.appendLine(\\\\\\\"Disposed webview\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\twhile (this.disposables.length) {\\\\r\\\\n\\\\t\\\\t\\\\tconst x = this.disposables.pop()\\\\r\\\\n\\\\t\\\\t\\\\tif (x) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tx.dispose()\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tthis.workspaceTracker?.dispose()\\\\r\\\\n\\\\t\\\\tthis.workspaceTracker = undefined\\\\r\\\\n\\\\t\\\\tthis.mcpHub?.dispose()\\\\r\\\\n\\\\t\\\\tthis.mcpHub = undefined\\\\r\\\\n\\\\t\\\\tthis.outputChannel.appendLine(\\\\\\\"Disposed all disposables\\\\\\\")\\\\r\\\\n\\\\t\\\\tClineProvider.activeInstances.delete(this)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tpublic static getVisibleInstance(): ClineProvider | undefined {\\\\r\\\\n\\\\t\\\\treturn findLast(Array.from(this.activeInstances), (instance) => instance.view?.visible === true)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tresolveWebviewView(\\\\r\\\\n\\\\t\\\\twebviewView: vscode.WebviewView | vscode.WebviewPanel,\\\\r\\\\n\\\\t\\\\t//context: vscode.WebviewViewResolveContext<unknown>, used to recreate a deallocated webview, but we don't need this since we use retainContextWhenHidden\\\\r\\\\n\\\\t\\\\t//token: vscode.CancellationToken\\\\r\\\\n\\\\t): void | Thenable<void> {\\\\r\\\\n\\\\t\\\\tthis.outputChannel.appendLine(\\\\\\\"Resolving webview view\\\\\\\")\\\\r\\\\n\\\\t\\\\tthis.view = webviewView\\\\r\\\\n\\\\r\\\\n\\\\t\\\\twebviewView.webview.options = {\\\\r\\\\n\\\\t\\\\t\\\\t// Allow scripts in the webview\\\\r\\\\n\\\\t\\\\t\\\\tenableScripts: true,\\\\r\\\\n\\\\t\\\\t\\\\tlocalResourceRoots: [this.context.extensionUri],\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\twebviewView.webview.html = this.getHtmlContent(webviewView.webview)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Sets up an event listener to listen for messages passed from the webview view context\\\\r\\\\n\\\\t\\\\t// and executes code based on the message that is recieved\\\\r\\\\n\\\\t\\\\tthis.setWebviewMessageListener(webviewView.webview)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Logs show up in bottom panel > Debug Console\\\\r\\\\n\\\\t\\\\t//console.log(\\\\\\\"registering listener\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Listen for when the panel becomes visible\\\\r\\\\n\\\\t\\\\t// https://github.com/microsoft/vscode-discussions/discussions/840\\\\r\\\\n\\\\t\\\\tif (\\\\\\\"onDidChangeViewState\\\\\\\" in webviewView) {\\\\r\\\\n\\\\t\\\\t\\\\t// WebviewView and WebviewPanel have all the same properties except for this visibility listener\\\\r\\\\n\\\\t\\\\t\\\\t// panel\\\\r\\\\n\\\\t\\\\t\\\\twebviewView.onDidChangeViewState(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t() => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (this.view?.visible) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"action\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\taction: \\\\\\\"didBecomeVisible\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tnull,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.disposables,\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t} else if (\\\\\\\"onDidChangeVisibility\\\\\\\" in webviewView) {\\\\r\\\\n\\\\t\\\\t\\\\t// sidebar\\\\r\\\\n\\\\t\\\\t\\\\twebviewView.onDidChangeVisibility(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t() => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (this.view?.visible) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"action\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\taction: \\\\\\\"didBecomeVisible\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tnull,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.disposables,\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Listen for when the view is disposed\\\\r\\\\n\\\\t\\\\t// This happens when the user closes the view or when the view is closed programmatically\\\\r\\\\n\\\\t\\\\twebviewView.onDidDispose(\\\\r\\\\n\\\\t\\\\t\\\\tasync () => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait this.dispose()\\\\r\\\\n\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\tnull,\\\\r\\\\n\\\\t\\\\t\\\\tthis.disposables,\\\\r\\\\n\\\\t\\\\t)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Listen for when color changes\\\\r\\\\n\\\\t\\\\tvscode.workspace.onDidChangeConfiguration(\\\\r\\\\n\\\\t\\\\t\\\\tasync (e) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (e && e.affectsConfiguration(\\\\\\\"workbench.colorTheme\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Sends latest theme name to webview\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"theme\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: JSON.stringify(await getTheme()),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\tnull,\\\\r\\\\n\\\\t\\\\t\\\\tthis.disposables,\\\\r\\\\n\\\\t\\\\t)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// if the extension is starting a new session, clear previous task state\\\\r\\\\n\\\\t\\\\tthis.clearTask()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tthis.outputChannel.appendLine(\\\\\\\"Webview view resolved\\\\\\\")\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync initClineWithTask(task?: string, images?: string[]) {\\\\r\\\\n\\\\t\\\\tawait this.clearTask() // ensures that an exising task doesn't exist before starting a new one, although this shouldn't be possible since user must clear task before starting a new one\\\\r\\\\n\\\\t\\\\tconst { apiConfiguration, customInstructions, autoApprovalSettings } = await this.getState()\\\\r\\\\n\\\\t\\\\tthis.cline = new Cline(this, apiConfiguration, autoApprovalSettings, customInstructions, task, images)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync initClineWithHistoryItem(historyItem: HistoryItem) {\\\\r\\\\n\\\\t\\\\tawait this.clearTask()\\\\r\\\\n\\\\t\\\\tconst { apiConfiguration, customInstructions, autoApprovalSettings } = await this.getState()\\\\r\\\\n\\\\t\\\\tthis.cline = new Cline(\\\\r\\\\n\\\\t\\\\t\\\\tthis,\\\\r\\\\n\\\\t\\\\t\\\\tapiConfiguration,\\\\r\\\\n\\\\t\\\\t\\\\tautoApprovalSettings,\\\\r\\\\n\\\\t\\\\t\\\\tcustomInstructions,\\\\r\\\\n\\\\t\\\\t\\\\tundefined,\\\\r\\\\n\\\\t\\\\t\\\\tundefined,\\\\r\\\\n\\\\t\\\\t\\\\thistoryItem,\\\\r\\\\n\\\\t\\\\t)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Send any JSON serializable data to the react app\\\\r\\\\n\\\\tasync postMessageToWebview(message: ExtensionMessage) {\\\\r\\\\n\\\\t\\\\tawait this.view?.webview.postMessage(message)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t/**\\\\r\\\\n\\\\t * Defines and returns the HTML that should be rendered within the webview panel.\\\\r\\\\n\\\\t *\\\\r\\\\n\\\\t * @remarks This is also the place where references to the React webview build files\\\\r\\\\n\\\\t * are created and inserted into the webview HTML.\\\\r\\\\n\\\\t *\\\\r\\\\n\\\\t * @param webview A reference to the extension webview\\\\r\\\\n\\\\t * @param extensionUri The URI of the directory containing the extension\\\\r\\\\n\\\\t * @returns A template string literal containing the HTML that should be\\\\r\\\\n\\\\t * rendered within the webview panel\\\\r\\\\n\\\\t */\\\\r\\\\n\\\\tprivate getHtmlContent(webview: vscode.Webview): string {\\\\r\\\\n\\\\t\\\\t// Get the local path to main script run in the webview,\\\\r\\\\n\\\\t\\\\t// then convert it to a uri we can use in the webview.\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// The CSS file from the React build output\\\\r\\\\n\\\\t\\\\tconst stylesUri = getUri(webview, this.context.extensionUri, [\\\\\\\"webview-ui\\\\\\\", \\\\\\\"build\\\\\\\", \\\\\\\"static\\\\\\\", \\\\\\\"css\\\\\\\", \\\\\\\"main.css\\\\\\\"])\\\\r\\\\n\\\\t\\\\t// The JS file from the React build output\\\\r\\\\n\\\\t\\\\tconst scriptUri = getUri(webview, this.context.extensionUri, [\\\\\\\"webview-ui\\\\\\\", \\\\\\\"build\\\\\\\", \\\\\\\"static\\\\\\\", \\\\\\\"js\\\\\\\", \\\\\\\"main.js\\\\\\\"])\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// The codicon font from the React build output\\\\r\\\\n\\\\t\\\\t// https://github.com/microsoft/vscode-extension-samples/blob/main/webview-codicons-sample/src/extension.ts\\\\r\\\\n\\\\t\\\\t// we installed this package in the extension so that we can access it how its intended from the extension (the font file is likely bundled in vscode), and we just import the css fileinto our react app we don't have access to it\\\\r\\\\n\\\\t\\\\t// don't forget to add font-src ${webview.cspSource};\\\\r\\\\n\\\\t\\\\tconst codiconsUri = getUri(webview, this.context.extensionUri, [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"node_modules\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"@vscode\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"codicons\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"dist\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"codicon.css\\\\\\\",\\\\r\\\\n\\\\t\\\\t])\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, \\\\\\\"assets\\\\\\\", \\\\\\\"main.js\\\\\\\"))\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// const styleResetUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, \\\\\\\"assets\\\\\\\", \\\\\\\"reset.css\\\\\\\"))\\\\r\\\\n\\\\t\\\\t// const styleVSCodeUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, \\\\\\\"assets\\\\\\\", \\\\\\\"vscode.css\\\\\\\"))\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// // Same for stylesheet\\\\r\\\\n\\\\t\\\\t// const stylesheetUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, \\\\\\\"assets\\\\\\\", \\\\\\\"main.css\\\\\\\"))\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Use a nonce to only allow a specific script to be run.\\\\r\\\\n\\\\t\\\\t/*\\\\r\\\\n content security policy of your webview to only allow scripts that have a specific nonce\\\\r\\\\n create a content security policy meta tag so that only loading scripts with a nonce is allowed\\\\r\\\\n As your extension grows you will likely want to add custom styles, fonts, and/or images to your webview. If you do, you will need to update the content security policy meta tag to explicity allow for these resources. E.g.\\\\r\\\\n <meta http-equiv=\\\\\\\"Content-Security-Policy\\\\\\\" content=\\\\\\\"default-src 'none'; style-src ${webview.cspSource}; font-src ${webview.cspSource}; img-src ${webview.cspSource} https:; script-src 'nonce-${nonce}';\\\\\\\">\\\\r\\\\n\\\\t\\\\t- 'unsafe-inline' is required for styles due to vscode-webview-toolkit's dynamic style injection\\\\r\\\\n\\\\t\\\\t- since we pass base64 images to the webview, we need to specify img-src ${webview.cspSource} data:;\\\\r\\\\n\\\\r\\\\n in meta tag we add nonce attribute: A cryptographic nonce (only used once) to allow scripts. The server must generate a unique nonce value each time it transmits a policy. It is critical to provide a nonce that cannot be guessed as bypassing a resource's policy is otherwise trivial.\\\\r\\\\n */\\\\r\\\\n\\\\t\\\\tconst nonce = getNonce()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Tip: Install the es6-string-html VS Code extension to enable code highlighting below\\\\r\\\\n\\\\t\\\\treturn /*html*/ `\\\\r\\\\n <!DOCTYPE html>\\\\r\\\\n <html lang=\\\\\\\"en\\\\\\\">\\\\r\\\\n <head>\\\\r\\\\n <meta charset=\\\\\\\"utf-8\\\\\\\">\\\\r\\\\n <meta name=\\\\\\\"viewport\\\\\\\" content=\\\\\\\"width=device-width,initial-scale=1,shrink-to-fit=no\\\\\\\">\\\\r\\\\n <meta name=\\\\\\\"theme-color\\\\\\\" content=\\\\\\\"#000000\\\\\\\">\\\\r\\\\n <meta http-equiv=\\\\\\\"Content-Security-Policy\\\\\\\" content=\\\\\\\"default-src 'none'; font-src ${webview.cspSource}; style-src ${webview.cspSource} 'unsafe-inline'; img-src ${webview.cspSource} data:; script-src 'nonce-${nonce}';\\\\\\\">\\\\r\\\\n <link rel=\\\\\\\"stylesheet\\\\\\\" type=\\\\\\\"text/css\\\\\\\" href=\\\\\\\"${stylesUri}\\\\\\\">\\\\r\\\\n\\\\t\\\\t\\\\t<link href=\\\\\\\"${codiconsUri}\\\\\\\" rel=\\\\\\\"stylesheet\\\\\\\" />\\\\r\\\\n <title>Cline</title>\\\\r\\\\n </head>\\\\r\\\\n <body>\\\\r\\\\n <noscript>You need to enable JavaScript to run this app.</noscript>\\\\r\\\\n <div id=\\\\\\\"root\\\\\\\"></div>\\\\r\\\\n <script nonce=\\\\\\\"${nonce}\\\\\\\" src=\\\\\\\"${scriptUri}\\\\\\\"></script>\\\\r\\\\n </body>\\\\r\\\\n </html>\\\\r\\\\n `\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t/**\\\\r\\\\n\\\\t * Sets up an event listener to listen for messages passed from the webview context and\\\\r\\\\n\\\\t * executes code based on the message that is recieved.\\\\r\\\\n\\\\t *\\\\r\\\\n\\\\t * @param webview A reference to the extension webview\\\\r\\\\n\\\\t */\\\\r\\\\n\\\\tprivate setWebviewMessageListener(webview: vscode.Webview) {\\\\r\\\\n\\\\t\\\\twebview.onDidReceiveMessage(\\\\r\\\\n\\\\t\\\\t\\\\tasync (message: WebviewMessage) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tswitch (message.type) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"webviewDidLaunch\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.postStateToWebview()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.workspaceTracker?.initializeFilePaths() // don't await\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tgetTheme().then((theme) =>\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"theme\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: JSON.stringify(theme),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// post last cached models in case the call to endpoint fails\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.readOpenRouterModels().then((openRouterModels) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (openRouterModels) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"openRouterModels\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\topenRouterModels,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// gui relies on model info to be up-to-date to provide the most accurate pricing, so we need to fetch the latest details on launch.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// we do this for all users since many users switch between api providers and if they were to switch back to openrouter it would be showing outdated model info if we hadn't retrieved the latest at this point\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// (see normalizeApiConfiguration > openrouter)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.refreshOpenRouterModels().then(async (openRouterModels) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (openRouterModels) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// update model info in state (this needs to be done here since we don't want to update state while settings is open, and we may refresh models there)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst { apiConfiguration } = await this.getState()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (apiConfiguration.openRouterModelId) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.updateGlobalState(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"openRouterModelInfo\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\topenRouterModels[apiConfiguration.openRouterModelId],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.postStateToWebview()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"newTask\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// Code that should run in response to the hello message command\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t//vscode.window.showInformationMessage(message.text!)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// Send a message to our webview.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// You can send any JSON serializable data.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// Could also do this in extension .ts\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t//this.postMessageToWebview({ type: \\\\\\\"text\\\\\\\", text: `Extension: ${Date.now()}` })\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// initializing new instance of Cline will make sure that any agentically running promises in old instance don't affect our new task. this essentially creates a fresh slate for the new task\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.initClineWithTask(message.text, message.images)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"apiConfiguration\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (message.apiConfiguration) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tapiProvider,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tapiModelId,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tapiKey,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\topenRouterApiKey,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawsAccessKey,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawsSecretKey,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawsSessionToken,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawsRegion,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawsUseCrossRegionInference,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tvertexProjectId,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tvertexRegion,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\topenAiBaseUrl,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\topenAiApiKey,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\topenAiModelId,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tollamaModelId,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tollamaBaseUrl,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlmStudioModelId,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlmStudioBaseUrl,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tanthropicBaseUrl,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tgeminiApiKey,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\topenAiNativeApiKey,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tdeepSeekApiKey,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tazureApiVersion,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\topenRouterModelId,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\topenRouterModelInfo,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} = message.apiConfiguration\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"apiProvider\\\\\\\", apiProvider)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"apiModelId\\\\\\\", apiModelId)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.storeSecret(\\\\\\\"apiKey\\\\\\\", apiKey)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.storeSecret(\\\\\\\"openRouterApiKey\\\\\\\", openRouterApiKey)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.storeSecret(\\\\\\\"awsAccessKey\\\\\\\", awsAccessKey)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.storeSecret(\\\\\\\"awsSecretKey\\\\\\\", awsSecretKey)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.storeSecret(\\\\\\\"awsSessionToken\\\\\\\", awsSessionToken)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"awsRegion\\\\\\\", awsRegion)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"awsUseCrossRegionInference\\\\\\\", awsUseCrossRegionInference)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"vertexProjectId\\\\\\\", vertexProjectId)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"vertexRegion\\\\\\\", vertexRegion)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"openAiBaseUrl\\\\\\\", openAiBaseUrl)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.storeSecret(\\\\\\\"openAiApiKey\\\\\\\", openAiApiKey)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"openAiModelId\\\\\\\", openAiModelId)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"ollamaModelId\\\\\\\", ollamaModelId)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"ollamaBaseUrl\\\\\\\", ollamaBaseUrl)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"lmStudioModelId\\\\\\\", lmStudioModelId)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"lmStudioBaseUrl\\\\\\\", lmStudioBaseUrl)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"anthropicBaseUrl\\\\\\\", anthropicBaseUrl)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.storeSecret(\\\\\\\"geminiApiKey\\\\\\\", geminiApiKey)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.storeSecret(\\\\\\\"openAiNativeApiKey\\\\\\\", openAiNativeApiKey)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.storeSecret(\\\\\\\"deepSeekApiKey\\\\\\\", deepSeekApiKey)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"azureApiVersion\\\\\\\", azureApiVersion)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"openRouterModelId\\\\\\\", openRouterModelId)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"openRouterModelInfo\\\\\\\", openRouterModelInfo)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.cline) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.cline.api = buildApiHandler(message.apiConfiguration)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.postStateToWebview()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"customInstructions\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.updateCustomInstructions(message.text)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"autoApprovalSettings\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (message.autoApprovalSettings) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"autoApprovalSettings\\\\\\\", message.autoApprovalSettings)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (this.cline) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.cline.autoApprovalSettings = message.autoApprovalSettings\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.postStateToWebview()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"askResponse\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.cline?.handleWebviewAskResponse(message.askResponse!, message.text, message.images)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"clearTask\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// newTask will start a new task with a given task text, while clear task resets the current session and allows for a new task to be started\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.clearTask()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.postStateToWebview()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"didShowAnnouncement\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"lastShownAnnouncementId\\\\\\\", this.latestAnnouncementId)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.postStateToWebview()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"selectImages\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst images = await selectImages()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"selectedImages\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\timages,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"exportCurrentTask\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst currentTaskId = this.cline?.taskId\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (currentTaskId) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.exportTaskWithId(currentTaskId)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"showTaskWithId\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.showTaskWithId(message.text!)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"deleteTaskWithId\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.deleteTaskWithId(message.text!)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"exportTaskWithId\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.exportTaskWithId(message.text!)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"resetState\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.resetState()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"requestOllamaModels\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst ollamaModels = await this.getOllamaModels(message.text)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"ollamaModels\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tollamaModels,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"requestLmStudioModels\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst lmStudioModels = await this.getLmStudioModels(message.text)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"lmStudioModels\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlmStudioModels,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"refreshOpenRouterModels\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.refreshOpenRouterModels()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"openImage\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\topenImage(message.text!)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"openFile\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\topenFile(message.text!)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"openMention\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\topenMention(message.text)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"checkpointDiff\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (message.number) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.cline?.presentMultifileDiff(message.number, false)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"checkpointRestore\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.cancelTask() // we cannot alter message history say if the task is active, as it could be in the middle of editing a file or running a command, which expect the ask to be responded to rather than being superceded by a new message eg add deleted_api_reqs\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// cancel task waits for any open editor to be reverted and starts a new cline instance\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (message.number) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// wait for messages to be loaded\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait pWaitFor(() => this.cline?.isInitialized === true, {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttimeout: 3_000,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}).catch(() => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Failed to init new cline instance\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// NOTE: cancelTask awaits abortTask, which awaits diffViewProvider.revertChanges, which reverts any edited files, allowing us to reset to a checkpoint rather than running into a state where the revertChanges function is called alongside or after the checkpoint reset\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.cline?.restoreCheckpoint(message.number, message.text! as ClineCheckpointRestore)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"taskCompletionViewChanges\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (message.number) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.cline?.presentMultifileDiff(message.number, true)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"cancelTask\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.cancelTask()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"openMcpSettings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst mcpSettingsFilePath = await this.mcpHub?.getMcpSettingsFilePath()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (mcpSettingsFilePath) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\topenFile(mcpSettingsFilePath)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"restartMcpServer\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.mcpHub?.restartConnection(message.text!)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconsole.error(`Failed to retry connection for ${message.text}:`, error)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Add more switch case statements here as more webview message commands\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// are created within the webview context (i.e. inside media/main.js)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\tnull,\\\\r\\\\n\\\\t\\\\t\\\\tthis.disposables,\\\\r\\\\n\\\\t\\\\t)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync cancelTask() {\\\\r\\\\n\\\\t\\\\tif (this.cline) {\\\\r\\\\n\\\\t\\\\t\\\\tconst { historyItem } = await this.getTaskWithId(this.cline.taskId)\\\\r\\\\n\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait this.cline.abortTask()\\\\r\\\\n\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Failed to abort task\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tawait pWaitFor(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t() => this.cline === undefined || this.cline.isStreaming === false || this.cline.didFinishAbortingStream,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttimeout: 3_000,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t).catch(() => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Failed to abort task\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\tif (this.cline) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// 'abandoned' will prevent this cline instance from affecting future cline instance gui. this may happen if its hanging on a streaming request\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.cline.abandoned = true\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tawait this.initClineWithHistoryItem(historyItem) // clears task again, so we need to abortTask manually above\\\\r\\\\n\\\\t\\\\t\\\\t// await this.postStateToWebview() // new Cline instance will post state when it's ready. having this here sent an empty messages array to webview leading to virtuoso having to reload the entire list\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync updateCustomInstructions(instructions?: string) {\\\\r\\\\n\\\\t\\\\t// User may be clearing the field\\\\r\\\\n\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"customInstructions\\\\\\\", instructions || undefined)\\\\r\\\\n\\\\t\\\\tif (this.cline) {\\\\r\\\\n\\\\t\\\\t\\\\tthis.cline.customInstructions = instructions || undefined\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tawait this.postStateToWebview()\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// MCP\\\\r\\\\n\\\\r\\\\n\\\\tasync ensureMcpServersDirectoryExists(): Promise<string> {\\\\r\\\\n\\\\t\\\\tconst mcpServersDir = path.join(os.homedir(), \\\\\\\"Documents\\\\\\\", \\\\\\\"Cline\\\\\\\", \\\\\\\"MCP\\\\\\\")\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tawait fs.mkdir(mcpServersDir, { recursive: true })\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\treturn \\\\\\\"~/Documents/Cline/MCP\\\\\\\" // in case creating a directory in documents fails for whatever reason (e.g. permissions) - this is fine since this path is only ever used in the system prompt\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn mcpServersDir\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync ensureSettingsDirectoryExists(): Promise<string> {\\\\r\\\\n\\\\t\\\\tconst settingsDir = path.join(this.context.globalStorageUri.fsPath, \\\\\\\"settings\\\\\\\")\\\\r\\\\n\\\\t\\\\tawait fs.mkdir(settingsDir, { recursive: true })\\\\r\\\\n\\\\t\\\\treturn settingsDir\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Ollama\\\\r\\\\n\\\\r\\\\n\\\\tasync getOllamaModels(baseUrl?: string) {\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tif (!baseUrl) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbaseUrl = \\\\\\\"http://localhost:11434\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tif (!URL.canParse(baseUrl)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn []\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tconst response = await axios.get(`${baseUrl}/api/tags`)\\\\r\\\\n\\\\t\\\\t\\\\tconst modelsArray = response.data?.models?.map((model: any) => model.name) || []\\\\r\\\\n\\\\t\\\\t\\\\tconst models = [...new Set<string>(modelsArray)]\\\\r\\\\n\\\\t\\\\t\\\\treturn models\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\treturn []\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// LM Studio\\\\r\\\\n\\\\r\\\\n\\\\tasync getLmStudioModels(baseUrl?: string) {\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tif (!baseUrl) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbaseUrl = \\\\\\\"http://localhost:1234\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tif (!URL.canParse(baseUrl)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn []\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tconst response = await axios.get(`${baseUrl}/v1/models`)\\\\r\\\\n\\\\t\\\\t\\\\tconst modelsArray = response.data?.data?.map((model: any) => model.id) || []\\\\r\\\\n\\\\t\\\\t\\\\tconst models = [...new Set<string>(modelsArray)]\\\\r\\\\n\\\\t\\\\t\\\\treturn models\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\treturn []\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// OpenRouter\\\\r\\\\n\\\\r\\\\n\\\\tasync handleOpenRouterCallback(code: string) {\\\\r\\\\n\\\\t\\\\tlet apiKey: string\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tconst response = await axios.post(\\\\\\\"https://openrouter.ai/api/v1/auth/keys\\\\\\\", { code })\\\\r\\\\n\\\\t\\\\t\\\\tif (response.data && response.data.key) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tapiKey = response.data.key\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Invalid response from OpenRouter API\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Error exchanging code for API key:\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\tthrow error\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst openrouter: ApiProvider = \\\\\\\"openrouter\\\\\\\"\\\\r\\\\n\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"apiProvider\\\\\\\", openrouter)\\\\r\\\\n\\\\t\\\\tawait this.storeSecret(\\\\\\\"openRouterApiKey\\\\\\\", apiKey)\\\\r\\\\n\\\\t\\\\tawait this.postStateToWebview()\\\\r\\\\n\\\\t\\\\tif (this.cline) {\\\\r\\\\n\\\\t\\\\t\\\\tthis.cline.api = buildApiHandler({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tapiProvider: openrouter,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\topenRouterApiKey: apiKey,\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t// await this.postMessageToWebview({ type: \\\\\\\"action\\\\\\\", action: \\\\\\\"settingsButtonClicked\\\\\\\" }) // bad ux if user is on welcome\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async ensureCacheDirectoryExists(): Promise<string> {\\\\r\\\\n\\\\t\\\\tconst cacheDir = path.join(this.context.globalStorageUri.fsPath, \\\\\\\"cache\\\\\\\")\\\\r\\\\n\\\\t\\\\tawait fs.mkdir(cacheDir, { recursive: true })\\\\r\\\\n\\\\t\\\\treturn cacheDir\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync readOpenRouterModels(): Promise<Record<string, ModelInfo> | undefined> {\\\\r\\\\n\\\\t\\\\tconst openRouterModelsFilePath = path.join(await this.ensureCacheDirectoryExists(), GlobalFileNames.openRouterModels)\\\\r\\\\n\\\\t\\\\tconst fileExists = await fileExistsAtPath(openRouterModelsFilePath)\\\\r\\\\n\\\\t\\\\tif (fileExists) {\\\\r\\\\n\\\\t\\\\t\\\\tconst fileContents = await fs.readFile(openRouterModelsFilePath, \\\\\\\"utf8\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\treturn JSON.parse(fileContents)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn undefined\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync refreshOpenRouterModels() {\\\\r\\\\n\\\\t\\\\tconst openRouterModelsFilePath = path.join(await this.ensureCacheDirectoryExists(), GlobalFileNames.openRouterModels)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet models: Record<string, ModelInfo> = {}\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tconst response = await axios.get(\\\\\\\"https://openrouter.ai/api/v1/models\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t/*\\\\r\\\\n\\\\t\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"id\\\\\\\": \\\\\\\"anthropic/claude-3.5-sonnet\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Anthropic: Claude 3.5 Sonnet\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"created\\\\\\\": 1718841600,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"description\\\\\\\": \\\\\\\"Claude 3.5 Sonnet delivers better-than-Opus capabilities, faster-than-Sonnet speeds, at the same Sonnet prices. Sonnet is particularly good at:\\\\\\\\n\\\\\\\\n- Coding: Autonomously writes, edits, and runs code with reasoning and troubleshooting\\\\\\\\n- Data science: Augments human data science expertise; navigates unstructured data while using multiple tools for insights\\\\\\\\n- Visual processing: excelling at interpreting charts, graphs, and images, accurately transcribing text to derive insights beyond just the text alone\\\\\\\\n- Agentic tasks: exceptional tool use, making it great at agentic tasks (i.e. complex, multi-step problem solving tasks that require engaging with other systems)\\\\\\\\n\\\\\\\\n#multimodal\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"context_length\\\\\\\": 200000,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"architecture\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"modality\\\\\\\": \\\\\\\"text+image-\\\\\\\\u003Etext\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"tokenizer\\\\\\\": \\\\\\\"Claude\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"instruct_type\\\\\\\": null\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"pricing\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"prompt\\\\\\\": \\\\\\\"0.000003\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"completion\\\\\\\": \\\\\\\"0.000015\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"image\\\\\\\": \\\\\\\"0.0048\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"request\\\\\\\": \\\\\\\"0\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"top_provider\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"context_length\\\\\\\": 200000,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"max_completion_tokens\\\\\\\": 8192,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"is_moderated\\\\\\\": true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"per_request_limits\\\\\\\": null\\\\r\\\\n\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t*/\\\\r\\\\n\\\\t\\\\t\\\\tif (response.data?.data) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst rawModels = response.data.data\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst parsePrice = (price: any) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (price) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn parseFloat(price) * 1_000_000\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn undefined\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tfor (const rawModel of rawModels) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst modelInfo: ModelInfo = {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmaxTokens: rawModel.top_provider?.max_completion_tokens,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcontextWindow: rawModel.context_length,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tsupportsImages: rawModel.architecture?.modality?.includes(\\\\\\\"image\\\\\\\"),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tinputPrice: parsePrice(rawModel.pricing?.prompt),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\toutputPrice: parsePrice(rawModel.pricing?.completion),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tdescription: rawModel.description,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tswitch (rawModel.id) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3.5-sonnet\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3.5-sonnet:beta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// NOTE: this needs to be synced with api.ts/openrouter default model info\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodelInfo.supportsComputerUse = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodelInfo.supportsPromptCache = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodelInfo.cacheWritesPrice = 3.75\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodelInfo.cacheReadsPrice = 0.3\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3.5-sonnet-20240620\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3.5-sonnet-20240620:beta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodelInfo.supportsPromptCache = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodelInfo.cacheWritesPrice = 3.75\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodelInfo.cacheReadsPrice = 0.3\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3-5-haiku\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3-5-haiku:beta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3-5-haiku-20241022\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3-5-haiku-20241022:beta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3.5-haiku\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3.5-haiku:beta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3.5-haiku-20241022\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3.5-haiku-20241022:beta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodelInfo.supportsPromptCache = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodelInfo.cacheWritesPrice = 1.25\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodelInfo.cacheReadsPrice = 0.1\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3-opus\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3-opus:beta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodelInfo.supportsPromptCache = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodelInfo.cacheWritesPrice = 18.75\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodelInfo.cacheReadsPrice = 1.5\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3-haiku\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"anthropic/claude-3-haiku:beta\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodelInfo.supportsPromptCache = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodelInfo.cacheWritesPrice = 0.3\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodelInfo.cacheReadsPrice = 0.03\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcase \\\\\\\"deepseek/deepseek-chat\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodelInfo.supportsPromptCache = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// see api.ts/deepSeekModels for more info\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodelInfo.inputPrice = 0\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodelInfo.cacheWritesPrice = 0.14\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmodelInfo.cacheReadsPrice = 0.014\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmodels[rawModel.id] = modelInfo\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Invalid response from OpenRouter API\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tawait fs.writeFile(openRouterModelsFilePath, JSON.stringify(models))\\\\r\\\\n\\\\t\\\\t\\\\tconsole.log(\\\\\\\"OpenRouter models fetched and saved\\\\\\\", models)\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Error fetching OpenRouter models:\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tawait this.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\ttype: \\\\\\\"openRouterModels\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\topenRouterModels: models,\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\treturn models\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Task history\\\\r\\\\n\\\\r\\\\n\\\\tasync getTaskWithId(id: string): Promise<{\\\\r\\\\n\\\\t\\\\thistoryItem: HistoryItem\\\\r\\\\n\\\\t\\\\ttaskDirPath: string\\\\r\\\\n\\\\t\\\\tapiConversationHistoryFilePath: string\\\\r\\\\n\\\\t\\\\tuiMessagesFilePath: string\\\\r\\\\n\\\\t\\\\tapiConversationHistory: Anthropic.MessageParam[]\\\\r\\\\n\\\\t}> {\\\\r\\\\n\\\\t\\\\tconst history = ((await this.getGlobalState(\\\\\\\"taskHistory\\\\\\\")) as HistoryItem[] | undefined) || []\\\\r\\\\n\\\\t\\\\tconst historyItem = history.find((item) => item.id === id)\\\\r\\\\n\\\\t\\\\tif (historyItem) {\\\\r\\\\n\\\\t\\\\t\\\\tconst taskDirPath = path.join(this.context.globalStorageUri.fsPath, \\\\\\\"tasks\\\\\\\", id)\\\\r\\\\n\\\\t\\\\t\\\\tconst apiConversationHistoryFilePath = path.join(taskDirPath, GlobalFileNames.apiConversationHistory)\\\\r\\\\n\\\\t\\\\t\\\\tconst uiMessagesFilePath = path.join(taskDirPath, GlobalFileNames.uiMessages)\\\\r\\\\n\\\\t\\\\t\\\\tconst fileExists = await fileExistsAtPath(apiConversationHistoryFilePath)\\\\r\\\\n\\\\t\\\\t\\\\tif (fileExists) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst apiConversationHistory = JSON.parse(await fs.readFile(apiConversationHistoryFilePath, \\\\\\\"utf8\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\thistoryItem,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttaskDirPath,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tapiConversationHistoryFilePath,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tuiMessagesFilePath,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tapiConversationHistory,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t// if we tried to get a task that doesn't exist, remove it from state\\\\r\\\\n\\\\t\\\\t// FIXME: this seems to happen sometimes when the json file doesnt save to disk for some reason\\\\r\\\\n\\\\t\\\\tawait this.deleteTaskFromState(id)\\\\r\\\\n\\\\t\\\\tthrow new Error(\\\\\\\"Task not found\\\\\\\")\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync showTaskWithId(id: string) {\\\\r\\\\n\\\\t\\\\tif (id !== this.cline?.taskId) {\\\\r\\\\n\\\\t\\\\t\\\\t// non-current task\\\\r\\\\n\\\\t\\\\t\\\\tconst { historyItem } = await this.getTaskWithId(id)\\\\r\\\\n\\\\t\\\\t\\\\tawait this.initClineWithHistoryItem(historyItem) // clears existing task\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tawait this.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\ttype: \\\\\\\"action\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\taction: \\\\\\\"chatButtonClicked\\\\\\\",\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync exportTaskWithId(id: string) {\\\\r\\\\n\\\\t\\\\tconst { historyItem, apiConversationHistory } = await this.getTaskWithId(id)\\\\r\\\\n\\\\t\\\\tawait downloadTask(historyItem.ts, apiConversationHistory)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync deleteTaskWithId(id: string) {\\\\r\\\\n\\\\t\\\\tif (id === this.cline?.taskId) {\\\\r\\\\n\\\\t\\\\t\\\\tawait this.clearTask()\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst { taskDirPath, apiConversationHistoryFilePath, uiMessagesFilePath } = await this.getTaskWithId(id)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tawait this.deleteTaskFromState(id)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Delete the task files\\\\r\\\\n\\\\t\\\\tconst apiConversationHistoryFileExists = await fileExistsAtPath(apiConversationHistoryFilePath)\\\\r\\\\n\\\\t\\\\tif (apiConversationHistoryFileExists) {\\\\r\\\\n\\\\t\\\\t\\\\tawait fs.unlink(apiConversationHistoryFilePath)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst uiMessagesFileExists = await fileExistsAtPath(uiMessagesFilePath)\\\\r\\\\n\\\\t\\\\tif (uiMessagesFileExists) {\\\\r\\\\n\\\\t\\\\t\\\\tawait fs.unlink(uiMessagesFilePath)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst legacyMessagesFilePath = path.join(taskDirPath, \\\\\\\"claude_messages.json\\\\\\\")\\\\r\\\\n\\\\t\\\\tif (await fileExistsAtPath(legacyMessagesFilePath)) {\\\\r\\\\n\\\\t\\\\t\\\\tawait fs.unlink(legacyMessagesFilePath)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Delete the checkpoints directory if it exists\\\\r\\\\n\\\\t\\\\tconst checkpointsDir = path.join(taskDirPath, \\\\\\\"checkpoints\\\\\\\")\\\\r\\\\n\\\\t\\\\tif (await fileExistsAtPath(checkpointsDir)) {\\\\r\\\\n\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait fs.rm(checkpointsDir, { recursive: true, force: true })\\\\r\\\\n\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.error(`Failed to delete checkpoints directory for task ${id}:`, error)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Continue with deletion of task directory - don't throw since this is a cleanup operation\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tawait fs.rmdir(taskDirPath) // succeeds if the dir is empty\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync deleteTaskFromState(id: string) {\\\\r\\\\n\\\\t\\\\t// Remove the task from history\\\\r\\\\n\\\\t\\\\tconst taskHistory = ((await this.getGlobalState(\\\\\\\"taskHistory\\\\\\\")) as HistoryItem[] | undefined) || []\\\\r\\\\n\\\\t\\\\tconst updatedTaskHistory = taskHistory.filter((task) => task.id !== id)\\\\r\\\\n\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"taskHistory\\\\\\\", updatedTaskHistory)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Notify the webview that the task has been deleted\\\\r\\\\n\\\\t\\\\tawait this.postStateToWebview()\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync postStateToWebview() {\\\\r\\\\n\\\\t\\\\tconst state = await this.getStateToPostToWebview()\\\\r\\\\n\\\\t\\\\tthis.postMessageToWebview({ type: \\\\\\\"state\\\\\\\", state })\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync getStateToPostToWebview(): Promise<ExtensionState> {\\\\r\\\\n\\\\t\\\\tconst { apiConfiguration, lastShownAnnouncementId, customInstructions, taskHistory, autoApprovalSettings } =\\\\r\\\\n\\\\t\\\\t\\\\tawait this.getState()\\\\r\\\\n\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\tversion: this.context.extension?.packageJSON?.version ?? \\\\\\\"\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tapiConfiguration,\\\\r\\\\n\\\\t\\\\t\\\\tcustomInstructions,\\\\r\\\\n\\\\t\\\\t\\\\turiScheme: vscode.env.uriScheme,\\\\r\\\\n\\\\t\\\\t\\\\tcurrentTaskItem: this.cline?.taskId ? (taskHistory || []).find((item) => item.id === this.cline?.taskId) : undefined,\\\\r\\\\n\\\\t\\\\t\\\\tcheckpointTrackerErrorMessage: this.cline?.checkpointTrackerErrorMessage,\\\\r\\\\n\\\\t\\\\t\\\\tclineMessages: this.cline?.clineMessages || [],\\\\r\\\\n\\\\t\\\\t\\\\ttaskHistory: (taskHistory || []).filter((item) => item.ts && item.task).sort((a, b) => b.ts - a.ts),\\\\r\\\\n\\\\t\\\\t\\\\tshouldShowAnnouncement: lastShownAnnouncementId !== this.latestAnnouncementId,\\\\r\\\\n\\\\t\\\\t\\\\tautoApprovalSettings,\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync clearTask() {\\\\r\\\\n\\\\t\\\\tthis.cline?.abortTask()\\\\r\\\\n\\\\t\\\\tthis.cline = undefined // removes reference to it, so once promises end it will be garbage collected\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Caching mechanism to keep track of webview messages + API conversation history per provider instance\\\\r\\\\n\\\\r\\\\n\\\\t/*\\\\r\\\\n\\\\tNow that we use retainContextWhenHidden, we don't have to store a cache of cline messages in the user's state, but we could to reduce memory footprint in long conversations.\\\\r\\\\n\\\\r\\\\n\\\\t- We have to be careful of what state is shared between ClineProvider instances since there could be multiple instances of the extension running at once. For example when we cached cline messages using the same key, two instances of the extension could end up using the same key and overwriting each other's messages.\\\\r\\\\n\\\\t- Some state does need to be shared between the instances, i.e. the API key--however there doesn't seem to be a good way to notfy the other instances that the API key has changed.\\\\r\\\\n\\\\r\\\\n\\\\tWe need to use a unique identifier for each ClineProvider instance's message cache since we could be running several instances of the extension outside of just the sidebar i.e. in editor panels.\\\\r\\\\n\\\\r\\\\n\\\\t// conversation history to send in API requests\\\\r\\\\n\\\\r\\\\n\\\\t/*\\\\r\\\\n\\\\tIt seems that some API messages do not comply with vscode state requirements. Either the Anthropic library is manipulating these values somehow in the backend in a way thats creating cyclic references, or the API returns a function or a Symbol as part of the message content.\\\\r\\\\n\\\\tVSCode docs about state: \\\\\\\"The value must be JSON-stringifyable ... value — A value. MUST not contain cyclic references.\\\\\\\"\\\\r\\\\n\\\\tFor now we'll store the conversation history in memory, and if we need to store in state directly we'd need to do a manual conversion to ensure proper json stringification.\\\\r\\\\n\\\\t*/\\\\r\\\\n\\\\r\\\\n\\\\t// getApiConversationHistory(): Anthropic.MessageParam[] {\\\\r\\\\n\\\\t// \\\\t// const history = (await this.getGlobalState(\\\\r\\\\n\\\\t// \\\\t// \\\\tthis.getApiConversationHistoryStateKey()\\\\r\\\\n\\\\t// \\\\t// )) as Anthropic.MessageParam[]\\\\r\\\\n\\\\t// \\\\t// return history || []\\\\r\\\\n\\\\t// \\\\treturn this.apiConversationHistory\\\\r\\\\n\\\\t// }\\\\r\\\\n\\\\r\\\\n\\\\t// setApiConversationHistory(history: Anthropic.MessageParam[] | undefined) {\\\\r\\\\n\\\\t// \\\\t// await this.updateGlobalState(this.getApiConversationHistoryStateKey(), history)\\\\r\\\\n\\\\t// \\\\tthis.apiConversationHistory = history || []\\\\r\\\\n\\\\t// }\\\\r\\\\n\\\\r\\\\n\\\\t// addMessageToApiConversationHistory(message: Anthropic.MessageParam): Anthropic.MessageParam[] {\\\\r\\\\n\\\\t// \\\\t// const history = await this.getApiConversationHistory()\\\\r\\\\n\\\\t// \\\\t// history.push(message)\\\\r\\\\n\\\\t// \\\\t// await this.setApiConversationHistory(history)\\\\r\\\\n\\\\t// \\\\t// return history\\\\r\\\\n\\\\t// \\\\tthis.apiConversationHistory.push(message)\\\\r\\\\n\\\\t// \\\\treturn this.apiConversationHistory\\\\r\\\\n\\\\t// }\\\\r\\\\n\\\\r\\\\n\\\\t/*\\\\r\\\\n\\\\tStorage\\\\r\\\\n\\\\thttps://dev.to/kompotkot/how-to-use-secretstorage-in-your-vscode-extensions-2hco\\\\r\\\\n\\\\thttps://www.eliostruyf.com/devhack-code-extension-storage-options/\\\\r\\\\n\\\\t*/\\\\r\\\\n\\\\r\\\\n\\\\tasync getState() {\\\\r\\\\n\\\\t\\\\tconst [\\\\r\\\\n\\\\t\\\\t\\\\tstoredApiProvider,\\\\r\\\\n\\\\t\\\\t\\\\tapiModelId,\\\\r\\\\n\\\\t\\\\t\\\\tapiKey,\\\\r\\\\n\\\\t\\\\t\\\\topenRouterApiKey,\\\\r\\\\n\\\\t\\\\t\\\\tawsAccessKey,\\\\r\\\\n\\\\t\\\\t\\\\tawsSecretKey,\\\\r\\\\n\\\\t\\\\t\\\\tawsSessionToken,\\\\r\\\\n\\\\t\\\\t\\\\tawsRegion,\\\\r\\\\n\\\\t\\\\t\\\\tawsUseCrossRegionInference,\\\\r\\\\n\\\\t\\\\t\\\\tvertexProjectId,\\\\r\\\\n\\\\t\\\\t\\\\tvertexRegion,\\\\r\\\\n\\\\t\\\\t\\\\topenAiBaseUrl,\\\\r\\\\n\\\\t\\\\t\\\\topenAiApiKey,\\\\r\\\\n\\\\t\\\\t\\\\topenAiModelId,\\\\r\\\\n\\\\t\\\\t\\\\tollamaModelId,\\\\r\\\\n\\\\t\\\\t\\\\tollamaBaseUrl,\\\\r\\\\n\\\\t\\\\t\\\\tlmStudioModelId,\\\\r\\\\n\\\\t\\\\t\\\\tlmStudioBaseUrl,\\\\r\\\\n\\\\t\\\\t\\\\tanthropicBaseUrl,\\\\r\\\\n\\\\t\\\\t\\\\tgeminiApiKey,\\\\r\\\\n\\\\t\\\\t\\\\topenAiNativeApiKey,\\\\r\\\\n\\\\t\\\\t\\\\tdeepSeekApiKey,\\\\r\\\\n\\\\t\\\\t\\\\tazureApiVersion,\\\\r\\\\n\\\\t\\\\t\\\\topenRouterModelId,\\\\r\\\\n\\\\t\\\\t\\\\topenRouterModelInfo,\\\\r\\\\n\\\\t\\\\t\\\\tlastShownAnnouncementId,\\\\r\\\\n\\\\t\\\\t\\\\tcustomInstructions,\\\\r\\\\n\\\\t\\\\t\\\\ttaskHistory,\\\\r\\\\n\\\\t\\\\t\\\\tautoApprovalSettings,\\\\r\\\\n\\\\t\\\\t] = await Promise.all([\\\\r\\\\n\\\\t\\\\t\\\\tthis.getGlobalState(\\\\\\\"apiProvider\\\\\\\") as Promise<ApiProvider | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getGlobalState(\\\\\\\"apiModelId\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getSecret(\\\\\\\"apiKey\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getSecret(\\\\\\\"openRouterApiKey\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getSecret(\\\\\\\"awsAccessKey\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getSecret(\\\\\\\"awsSecretKey\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getSecret(\\\\\\\"awsSessionToken\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getGlobalState(\\\\\\\"awsRegion\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getGlobalState(\\\\\\\"awsUseCrossRegionInference\\\\\\\") as Promise<boolean | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getGlobalState(\\\\\\\"vertexProjectId\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getGlobalState(\\\\\\\"vertexRegion\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getGlobalState(\\\\\\\"openAiBaseUrl\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getSecret(\\\\\\\"openAiApiKey\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getGlobalState(\\\\\\\"openAiModelId\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getGlobalState(\\\\\\\"ollamaModelId\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getGlobalState(\\\\\\\"ollamaBaseUrl\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getGlobalState(\\\\\\\"lmStudioModelId\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getGlobalState(\\\\\\\"lmStudioBaseUrl\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getGlobalState(\\\\\\\"anthropicBaseUrl\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getSecret(\\\\\\\"geminiApiKey\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getSecret(\\\\\\\"openAiNativeApiKey\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getSecret(\\\\\\\"deepSeekApiKey\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getGlobalState(\\\\\\\"azureApiVersion\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getGlobalState(\\\\\\\"openRouterModelId\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getGlobalState(\\\\\\\"openRouterModelInfo\\\\\\\") as Promise<ModelInfo | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getGlobalState(\\\\\\\"lastShownAnnouncementId\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getGlobalState(\\\\\\\"customInstructions\\\\\\\") as Promise<string | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getGlobalState(\\\\\\\"taskHistory\\\\\\\") as Promise<HistoryItem[] | undefined>,\\\\r\\\\n\\\\t\\\\t\\\\tthis.getGlobalState(\\\\\\\"autoApprovalSettings\\\\\\\") as Promise<AutoApprovalSettings | undefined>,\\\\r\\\\n\\\\t\\\\t])\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet apiProvider: ApiProvider\\\\r\\\\n\\\\t\\\\tif (storedApiProvider) {\\\\r\\\\n\\\\t\\\\t\\\\tapiProvider = storedApiProvider\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t// Either new user or legacy user that doesn't have the apiProvider stored in state\\\\r\\\\n\\\\t\\\\t\\\\t// (If they're using OpenRouter or Bedrock, then apiProvider state will exist)\\\\r\\\\n\\\\t\\\\t\\\\tif (apiKey) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tapiProvider = \\\\\\\"anthropic\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// New users should default to openrouter\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tapiProvider = \\\\\\\"openrouter\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\tapiConfiguration: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tapiProvider,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tapiModelId,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tapiKey,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\topenRouterApiKey,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawsAccessKey,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawsSecretKey,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawsSessionToken,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawsRegion,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawsUseCrossRegionInference,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tvertexProjectId,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tvertexRegion,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\topenAiBaseUrl,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\topenAiApiKey,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\topenAiModelId,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tollamaModelId,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tollamaBaseUrl,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlmStudioModelId,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlmStudioBaseUrl,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tanthropicBaseUrl,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tgeminiApiKey,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\topenAiNativeApiKey,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tdeepSeekApiKey,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tazureApiVersion,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\topenRouterModelId,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\topenRouterModelInfo,\\\\r\\\\n\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\tlastShownAnnouncementId,\\\\r\\\\n\\\\t\\\\t\\\\tcustomInstructions,\\\\r\\\\n\\\\t\\\\t\\\\ttaskHistory,\\\\r\\\\n\\\\t\\\\t\\\\tautoApprovalSettings: autoApprovalSettings || DEFAULT_AUTO_APPROVAL_SETTINGS, // default value can be 0 or empty string\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync updateTaskHistory(item: HistoryItem): Promise<HistoryItem[]> {\\\\r\\\\n\\\\t\\\\tconst history = ((await this.getGlobalState(\\\\\\\"taskHistory\\\\\\\")) as HistoryItem[]) || []\\\\r\\\\n\\\\t\\\\tconst existingItemIndex = history.findIndex((h) => h.id === item.id)\\\\r\\\\n\\\\t\\\\tif (existingItemIndex !== -1) {\\\\r\\\\n\\\\t\\\\t\\\\thistory[existingItemIndex] = item\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\thistory.push(item)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tawait this.updateGlobalState(\\\\\\\"taskHistory\\\\\\\", history)\\\\r\\\\n\\\\t\\\\treturn history\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// global\\\\r\\\\n\\\\r\\\\n\\\\tasync updateGlobalState(key: GlobalStateKey, value: any) {\\\\r\\\\n\\\\t\\\\tawait this.context.globalState.update(key, value)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync getGlobalState(key: GlobalStateKey) {\\\\r\\\\n\\\\t\\\\treturn await this.context.globalState.get(key)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// workspace\\\\r\\\\n\\\\r\\\\n\\\\tprivate async updateWorkspaceState(key: string, value: any) {\\\\r\\\\n\\\\t\\\\tawait this.context.workspaceState.update(key, value)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async getWorkspaceState(key: string) {\\\\r\\\\n\\\\t\\\\treturn await this.context.workspaceState.get(key)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// private async clearState() {\\\\r\\\\n\\\\t// \\\\tthis.context.workspaceState.keys().forEach((key) => {\\\\r\\\\n\\\\t// \\\\t\\\\tthis.context.workspaceState.update(key, undefined)\\\\r\\\\n\\\\t// \\\\t})\\\\r\\\\n\\\\t// \\\\tthis.context.globalState.keys().forEach((key) => {\\\\r\\\\n\\\\t// \\\\t\\\\tthis.context.globalState.update(key, undefined)\\\\r\\\\n\\\\t// \\\\t})\\\\r\\\\n\\\\t// \\\\tthis.context.secrets.delete(\\\\\\\"apiKey\\\\\\\")\\\\r\\\\n\\\\t// }\\\\r\\\\n\\\\r\\\\n\\\\t// secrets\\\\r\\\\n\\\\r\\\\n\\\\tprivate async storeSecret(key: SecretKey, value?: string) {\\\\r\\\\n\\\\t\\\\tif (value) {\\\\r\\\\n\\\\t\\\\t\\\\tawait this.context.secrets.store(key, value)\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\tawait this.context.secrets.delete(key)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async getSecret(key: SecretKey) {\\\\r\\\\n\\\\t\\\\treturn await this.context.secrets.get(key)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// dev\\\\r\\\\n\\\\r\\\\n\\\\tasync resetState() {\\\\r\\\\n\\\\t\\\\tvscode.window.showInformationMessage(\\\\\\\"Resetting state...\\\\\\\")\\\\r\\\\n\\\\t\\\\tfor (const key of this.context.globalState.keys()) {\\\\r\\\\n\\\\t\\\\t\\\\tawait this.context.globalState.update(key, undefined)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst secretKeys: SecretKey[] = [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"apiKey\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"openRouterApiKey\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"awsAccessKey\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"awsSecretKey\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"awsSessionToken\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"openAiApiKey\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"geminiApiKey\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"openAiNativeApiKey\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"deepSeekApiKey\\\\\\\",\\\\r\\\\n\\\\t\\\\t]\\\\r\\\\n\\\\t\\\\tfor (const key of secretKeys) {\\\\r\\\\n\\\\t\\\\t\\\\tawait this.storeSecret(key, undefined)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tif (this.cline) {\\\\r\\\\n\\\\t\\\\t\\\\tthis.cline.abortTask()\\\\r\\\\n\\\\t\\\\t\\\\tthis.cline = undefined\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tvscode.window.showInformationMessage(\\\\\\\"State reset\\\\\\\")\\\\r\\\\n\\\\t\\\\tawait this.postStateToWebview()\\\\r\\\\n\\\\t\\\\tawait this.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\ttype: \\\\\\\"action\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\taction: \\\\\\\"chatButtonClicked\\\\\\\",\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":45515,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.948Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.948Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":1179,\\\"nonEmptyLines\\\":1087,\\\"commentLines\\\":116,\\\"complexity\\\":179},\\\"dependencies\\\":[\\\"@anthropic-ai/sdk\\\",\\\"axios\\\",\\\"fs/promises\\\",\\\"os\\\",\\\"p-wait-for\\\",\\\"path\\\",\\\"vscode\\\",\\\"../../api\\\",\\\"../../integrations/misc/export-markdown\\\",\\\"../../integrations/misc/open-file\\\",\\\"../../integrations/misc/process-images\\\",\\\"../../integrations/theme/getTheme\\\",\\\"../../integrations/workspace/WorkspaceTracker\\\",\\\"../../services/mcp/McpHub\\\",\\\"../../shared/api\\\",\\\"../../shared/array\\\",\\\"../../shared/ExtensionMessage\\\",\\\"../../shared/HistoryItem\\\",\\\"../../shared/WebviewMessage\\\",\\\"../../utils/fs\\\",\\\"../Cline\\\",\\\"../mentions\\\",\\\"./getNonce\\\",\\\"./getUri\\\",\\\"../../shared/AutoApprovalSettings\\\"],\\\"quality\\\":{\\\"score\\\":-1707,\\\"issues\\\":[],\\\"duplicateLines\\\":339,\\\"longLines\\\":56,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.343Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":1179},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\core\\\\\\\\webview\\\\\\\\getNonce.ts\\\":{\\\"content\\\":\\\"/**\\\\r\\\\n * A helper function that returns a unique alphanumeric identifier called a nonce.\\\\r\\\\n *\\\\r\\\\n * @remarks This function is primarily used to help enforce content security\\\\r\\\\n * policies for resources/scripts being executed in a webview context.\\\\r\\\\n *\\\\r\\\\n * @returns A nonce\\\\r\\\\n */\\\\r\\\\nexport function getNonce() {\\\\r\\\\n\\\\tlet text = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\tconst possible = \\\\\\\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\\\\\\\"\\\\r\\\\n\\\\tfor (let i = 0; i < 32; i++) {\\\\r\\\\n\\\\t\\\\ttext += possible.charAt(Math.floor(Math.random() * possible.length))\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\treturn text\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":529,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.843Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.843Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":17,\\\"nonEmptyLines\\\":16,\\\"commentLines\\\":1,\\\"complexity\\\":2},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":90,\\\"issues\\\":[],\\\"duplicateLines\\\":2,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.345Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":17},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\core\\\\\\\\webview\\\\\\\\getUri.ts\\\":{\\\"content\\\":\\\"import { Uri, Webview } from \\\\\\\"vscode\\\\\\\"\\\\r\\\\n/**\\\\r\\\\n * A helper function which will get the webview URI of a given file or resource.\\\\r\\\\n *\\\\r\\\\n * @remarks This URI can be used within a webview's HTML as a link to the\\\\r\\\\n * given file/resource.\\\\r\\\\n *\\\\r\\\\n * @param webview A reference to the extension webview\\\\r\\\\n * @param extensionUri The URI of the directory containing the extension\\\\r\\\\n * @param pathList An array of strings representing the path to a file/resource\\\\r\\\\n * @returns A URI pointing to the file/resource\\\\r\\\\n */\\\\r\\\\nexport function getUri(webview: Webview, extensionUri: Uri, pathList: string[]) {\\\\r\\\\n\\\\treturn webview.asWebviewUri(Uri.joinPath(extensionUri, ...pathList))\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":656,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.843Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.843Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":16,\\\"nonEmptyLines\\\":15,\\\"commentLines\\\":1,\\\"complexity\\\":1},\\\"dependencies\\\":[\\\"vscode\\\"],\\\"quality\\\":{\\\"score\\\":95,\\\"issues\\\":[],\\\"duplicateLines\\\":1,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.346Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":16},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\exports\\\\\\\\cline.d.ts\\\":{\\\"content\\\":\\\"export interface ClineAPI {\\\\r\\\\n\\\\t/**\\\\r\\\\n\\\\t * Sets the custom instructions in the global storage.\\\\r\\\\n\\\\t * @param value The custom instructions to be saved.\\\\r\\\\n\\\\t */\\\\r\\\\n\\\\tsetCustomInstructions(value: string): Promise<void>\\\\r\\\\n\\\\r\\\\n\\\\t/**\\\\r\\\\n\\\\t * Retrieves the custom instructions from the global storage.\\\\r\\\\n\\\\t * @returns The saved custom instructions, or undefined if not set.\\\\r\\\\n\\\\t */\\\\r\\\\n\\\\tgetCustomInstructions(): Promise<string | undefined>\\\\r\\\\n\\\\r\\\\n\\\\t/**\\\\r\\\\n\\\\t * Starts a new task with an optional initial message and images.\\\\r\\\\n\\\\t * @param task Optional initial task message.\\\\r\\\\n\\\\t * @param images Optional array of image data URIs (e.g., \\\\\\\"data:image/webp;base64,...\\\\\\\").\\\\r\\\\n\\\\t */\\\\r\\\\n\\\\tstartNewTask(task?: string, images?: string[]): Promise<void>\\\\r\\\\n\\\\r\\\\n\\\\t/**\\\\r\\\\n\\\\t * Sends a message to the current task.\\\\r\\\\n\\\\t * @param message Optional message to send.\\\\r\\\\n\\\\t * @param images Optional array of image data URIs (e.g., \\\\\\\"data:image/webp;base64,...\\\\\\\").\\\\r\\\\n\\\\t */\\\\r\\\\n\\\\tsendMessage(message?: string, images?: string[]): Promise<void>\\\\r\\\\n\\\\r\\\\n\\\\t/**\\\\r\\\\n\\\\t * Simulates pressing the primary button in the chat interface.\\\\r\\\\n\\\\t */\\\\r\\\\n\\\\tpressPrimaryButton(): Promise<void>\\\\r\\\\n\\\\r\\\\n\\\\t/**\\\\r\\\\n\\\\t * Simulates pressing the secondary button in the chat interface.\\\\r\\\\n\\\\t */\\\\r\\\\n\\\\tpressSecondaryButton(): Promise<void>\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":1197,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.843Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.843Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":38,\\\"nonEmptyLines\\\":32,\\\"commentLines\\\":6,\\\"complexity\\\":5},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":45,\\\"issues\\\":[],\\\"duplicateLines\\\":11,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.347Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":38},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\exports\\\\\\\\index.ts\\\":{\\\"content\\\":\\\"import * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\nimport { ClineProvider } from \\\\\\\"../core/webview/ClineProvider\\\\\\\"\\\\r\\\\nimport { ClineAPI } from \\\\\\\"./cline\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport function createClineAPI(outputChannel: vscode.OutputChannel, sidebarProvider: ClineProvider): ClineAPI {\\\\r\\\\n\\\\tconst api: ClineAPI = {\\\\r\\\\n\\\\t\\\\tsetCustomInstructions: async (value: string) => {\\\\r\\\\n\\\\t\\\\t\\\\tawait sidebarProvider.updateCustomInstructions(value)\\\\r\\\\n\\\\t\\\\t\\\\toutputChannel.appendLine(\\\\\\\"Custom instructions set\\\\\\\")\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tgetCustomInstructions: async () => {\\\\r\\\\n\\\\t\\\\t\\\\treturn (await sidebarProvider.getGlobalState(\\\\\\\"customInstructions\\\\\\\")) as string | undefined\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tstartNewTask: async (task?: string, images?: string[]) => {\\\\r\\\\n\\\\t\\\\t\\\\toutputChannel.appendLine(\\\\\\\"Starting new task\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tawait sidebarProvider.clearTask()\\\\r\\\\n\\\\t\\\\t\\\\tawait sidebarProvider.postStateToWebview()\\\\r\\\\n\\\\t\\\\t\\\\tawait sidebarProvider.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"action\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\taction: \\\\\\\"chatButtonClicked\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\tawait sidebarProvider.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"invoke\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tinvoke: \\\\\\\"sendMessage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttext: task,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\timages: images,\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\toutputChannel.appendLine(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t`Task started with message: ${task ? `\\\\\\\"${task}\\\\\\\"` : \\\\\\\"undefined\\\\\\\"} and ${images?.length || 0} image(s)`,\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tsendMessage: async (message?: string, images?: string[]) => {\\\\r\\\\n\\\\t\\\\t\\\\toutputChannel.appendLine(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t`Sending message: ${message ? `\\\\\\\"${message}\\\\\\\"` : \\\\\\\"undefined\\\\\\\"} with ${images?.length || 0} image(s)`,\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\tawait sidebarProvider.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"invoke\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tinvoke: \\\\\\\"sendMessage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttext: message,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\timages: images,\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tpressPrimaryButton: async () => {\\\\r\\\\n\\\\t\\\\t\\\\toutputChannel.appendLine(\\\\\\\"Pressing primary button\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tawait sidebarProvider.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"invoke\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tinvoke: \\\\\\\"primaryButtonClick\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tpressSecondaryButton: async () => {\\\\r\\\\n\\\\t\\\\t\\\\toutputChannel.appendLine(\\\\\\\"Pressing secondary button\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tawait sidebarProvider.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"invoke\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tinvoke: \\\\\\\"secondaryButtonClick\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\treturn api\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":2005,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.948Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.948Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":66,\\\"nonEmptyLines\\\":58,\\\"commentLines\\\":0,\\\"complexity\\\":11},\\\"dependencies\\\":[\\\"vscode\\\",\\\"../core/webview/ClineProvider\\\",\\\"./cline\\\"],\\\"quality\\\":{\\\"score\\\":-11,\\\"issues\\\":[],\\\"duplicateLines\\\":21,\\\"longLines\\\":3,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.348Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":66},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\exports\\\\\\\\README.md\\\":{\\\"content\\\":\\\"# Cline API\\\\r\\\\n\\\\r\\\\nThe Cline extension exposes an API that can be used by other extensions. To use this API in your extension:\\\\r\\\\n\\\\r\\\\n1. Copy `src/extension-api/cline.d.ts` to your extension's source directory.\\\\r\\\\n2. Include `cline.d.ts` in your extension's compilation.\\\\r\\\\n3. Get access to the API with the following code:\\\\r\\\\n\\\\r\\\\n ```ts\\\\r\\\\n const clineExtension = vscode.extensions.getExtension<ClineAPI>(\\\\\\\"saoudrizwan.claude-dev\\\\\\\")\\\\r\\\\n\\\\r\\\\n if (!clineExtension?.isActive) {\\\\r\\\\n \\\\tthrow new Error(\\\\\\\"Cline extension is not activated\\\\\\\")\\\\r\\\\n }\\\\r\\\\n\\\\r\\\\n const cline = clineExtension.exports\\\\r\\\\n\\\\r\\\\n if (cline) {\\\\r\\\\n \\\\t// Now you can use the API\\\\r\\\\n\\\\r\\\\n \\\\t// Set custom instructions\\\\r\\\\n \\\\tawait cline.setCustomInstructions(\\\\\\\"Talk like a pirate\\\\\\\")\\\\r\\\\n\\\\r\\\\n \\\\t// Get custom instructions\\\\r\\\\n \\\\tconst instructions = await cline.getCustomInstructions()\\\\r\\\\n \\\\tconsole.log(\\\\\\\"Current custom instructions:\\\\\\\", instructions)\\\\r\\\\n\\\\r\\\\n \\\\t// Start a new task with an initial message\\\\r\\\\n \\\\tawait cline.startNewTask(\\\\\\\"Hello, Cline! Let's make a new project...\\\\\\\")\\\\r\\\\n\\\\r\\\\n \\\\t// Start a new task with an initial message and images\\\\r\\\\n \\\\tawait cline.startNewTask(\\\\\\\"Use this design language\\\\\\\", [\\\\\\\"data:image/webp;base64,...\\\\\\\"])\\\\r\\\\n\\\\r\\\\n \\\\t// Send a message to the current task\\\\r\\\\n \\\\tawait cline.sendMessage(\\\\\\\"Can you fix the @problems?\\\\\\\")\\\\r\\\\n\\\\r\\\\n \\\\t// Simulate pressing the primary button in the chat interface (e.g. 'Save' or 'Proceed While Running')\\\\r\\\\n \\\\tawait cline.pressPrimaryButton()\\\\r\\\\n\\\\r\\\\n \\\\t// Simulate pressing the secondary button in the chat interface (e.g. 'Reject')\\\\r\\\\n \\\\tawait cline.pressSecondaryButton()\\\\r\\\\n } else {\\\\r\\\\n \\\\tconsole.error(\\\\\\\"Cline API is not available\\\\\\\")\\\\r\\\\n }\\\\r\\\\n ```\\\\r\\\\n\\\\r\\\\n **Note:** To ensure that the `saoudrizwan.claude-dev` extension is activated before your extension, add it to the `extensionDependencies` in your `package.json`:\\\\r\\\\n\\\\r\\\\n ```json\\\\r\\\\n \\\\\\\"extensionDependencies\\\\\\\": [\\\\r\\\\n \\\\\\\"saoudrizwan.claude-dev\\\\\\\"\\\\r\\\\n ]\\\\r\\\\n ```\\\\r\\\\n\\\\r\\\\nFor detailed information on the available methods and their usage, refer to the `cline.d.ts` file.\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":1997,\\\"mimeType\\\":\\\"text/markdown\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.843Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.843Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":56,\\\"nonEmptyLines\\\":39,\\\"commentLines\\\":0,\\\"complexity\\\":6},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":84,\\\"issues\\\":[],\\\"duplicateLines\\\":2,\\\"longLines\\\":3,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.350Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":56},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\extension.ts\\\":{\\\"content\\\":\\\"// The module 'vscode' contains the VS Code extensibility API\\\\r\\\\n// Import the module and reference it with the alias vscode in your code below\\\\r\\\\nimport delay from \\\\\\\"delay\\\\\\\"\\\\r\\\\nimport * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\nimport { ClineProvider } from \\\\\\\"./core/webview/ClineProvider\\\\\\\"\\\\r\\\\nimport { createClineAPI } from \\\\\\\"./exports\\\\\\\"\\\\r\\\\nimport \\\\\\\"./utils/path\\\\\\\" // necessary to have access to String.prototype.toPosix\\\\r\\\\nimport { DIFF_VIEW_URI_SCHEME } from \\\\\\\"./integrations/editor/DiffViewProvider\\\\\\\"\\\\r\\\\n\\\\r\\\\n/*\\\\r\\\\nBuilt using https://github.com/microsoft/vscode-webview-ui-toolkit\\\\r\\\\n\\\\r\\\\nInspired by\\\\r\\\\nhttps://github.com/microsoft/vscode-webview-ui-toolkit-samples/tree/main/default/weather-webview\\\\r\\\\nhttps://github.com/microsoft/vscode-webview-ui-toolkit-samples/tree/main/frameworks/hello-world-react-cra\\\\r\\\\n\\\\r\\\\n*/\\\\r\\\\n\\\\r\\\\nlet outputChannel: vscode.OutputChannel\\\\r\\\\n\\\\r\\\\n// This method is called when your extension is activated\\\\r\\\\n// Your extension is activated the very first time the command is executed\\\\r\\\\nexport function activate(context: vscode.ExtensionContext) {\\\\r\\\\n\\\\toutputChannel = vscode.window.createOutputChannel(\\\\\\\"Cline\\\\\\\")\\\\r\\\\n\\\\tcontext.subscriptions.push(outputChannel)\\\\r\\\\n\\\\r\\\\n\\\\toutputChannel.appendLine(\\\\\\\"Cline extension activated\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\tconst sidebarProvider = new ClineProvider(context, outputChannel)\\\\r\\\\n\\\\r\\\\n\\\\tcontext.subscriptions.push(\\\\r\\\\n\\\\t\\\\tvscode.window.registerWebviewViewProvider(ClineProvider.sideBarId, sidebarProvider, {\\\\r\\\\n\\\\t\\\\t\\\\twebviewOptions: { retainContextWhenHidden: true },\\\\r\\\\n\\\\t\\\\t}),\\\\r\\\\n\\\\t)\\\\r\\\\n\\\\r\\\\n\\\\tcontext.subscriptions.push(\\\\r\\\\n\\\\t\\\\tvscode.commands.registerCommand(\\\\\\\"cline.plusButtonClicked\\\\\\\", async () => {\\\\r\\\\n\\\\t\\\\t\\\\toutputChannel.appendLine(\\\\\\\"Plus button Clicked\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tawait sidebarProvider.clearTask()\\\\r\\\\n\\\\t\\\\t\\\\tawait sidebarProvider.postStateToWebview()\\\\r\\\\n\\\\t\\\\t\\\\tawait sidebarProvider.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"action\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\taction: \\\\\\\"chatButtonClicked\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t}),\\\\r\\\\n\\\\t)\\\\r\\\\n\\\\r\\\\n\\\\tcontext.subscriptions.push(\\\\r\\\\n\\\\t\\\\tvscode.commands.registerCommand(\\\\\\\"cline.mcpButtonClicked\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tsidebarProvider.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"action\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\taction: \\\\\\\"mcpButtonClicked\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t}),\\\\r\\\\n\\\\t)\\\\r\\\\n\\\\r\\\\n\\\\tconst openClineInNewTab = async () => {\\\\r\\\\n\\\\t\\\\toutputChannel.appendLine(\\\\\\\"Opening Cline in new tab\\\\\\\")\\\\r\\\\n\\\\t\\\\t// (this example uses webviewProvider activation event which is necessary to deserialize cached webview, but since we use retainContextWhenHidden, we don't need to use that event)\\\\r\\\\n\\\\t\\\\t// https://github.com/microsoft/vscode-extension-samples/blob/main/webview-sample/src/extension.ts\\\\r\\\\n\\\\t\\\\tconst tabProvider = new ClineProvider(context, outputChannel)\\\\r\\\\n\\\\t\\\\t//const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined\\\\r\\\\n\\\\t\\\\tconst lastCol = Math.max(...vscode.window.visibleTextEditors.map((editor) => editor.viewColumn || 0))\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Check if there are any visible text editors, otherwise open a new group to the right\\\\r\\\\n\\\\t\\\\tconst hasVisibleEditors = vscode.window.visibleTextEditors.length > 0\\\\r\\\\n\\\\t\\\\tif (!hasVisibleEditors) {\\\\r\\\\n\\\\t\\\\t\\\\tawait vscode.commands.executeCommand(\\\\\\\"workbench.action.newGroupRight\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst targetCol = hasVisibleEditors ? Math.max(lastCol + 1, 1) : vscode.ViewColumn.Two\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst panel = vscode.window.createWebviewPanel(ClineProvider.tabPanelId, \\\\\\\"Cline\\\\\\\", targetCol, {\\\\r\\\\n\\\\t\\\\t\\\\tenableScripts: true,\\\\r\\\\n\\\\t\\\\t\\\\tretainContextWhenHidden: true,\\\\r\\\\n\\\\t\\\\t\\\\tlocalResourceRoots: [context.extensionUri],\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t// TODO: use better svg icon with light and dark variants (see https://stackoverflow.com/questions/58365687/vscode-extension-iconpath)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tpanel.iconPath = {\\\\r\\\\n\\\\t\\\\t\\\\tlight: vscode.Uri.joinPath(context.extensionUri, \\\\\\\"assets\\\\\\\", \\\\\\\"icons\\\\\\\", \\\\\\\"robot_panel_light.png\\\\\\\"),\\\\r\\\\n\\\\t\\\\t\\\\tdark: vscode.Uri.joinPath(context.extensionUri, \\\\\\\"assets\\\\\\\", \\\\\\\"icons\\\\\\\", \\\\\\\"robot_panel_dark.png\\\\\\\"),\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\ttabProvider.resolveWebviewView(panel)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Lock the editor group so clicking on files doesn't open them over the panel\\\\r\\\\n\\\\t\\\\tawait delay(100)\\\\r\\\\n\\\\t\\\\tawait vscode.commands.executeCommand(\\\\\\\"workbench.action.lockEditorGroup\\\\\\\")\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tcontext.subscriptions.push(vscode.commands.registerCommand(\\\\\\\"cline.popoutButtonClicked\\\\\\\", openClineInNewTab))\\\\r\\\\n\\\\tcontext.subscriptions.push(vscode.commands.registerCommand(\\\\\\\"cline.openInNewTab\\\\\\\", openClineInNewTab))\\\\r\\\\n\\\\r\\\\n\\\\tcontext.subscriptions.push(\\\\r\\\\n\\\\t\\\\tvscode.commands.registerCommand(\\\\\\\"cline.settingsButtonClicked\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\t//vscode.window.showInformationMessage(message)\\\\r\\\\n\\\\t\\\\t\\\\tsidebarProvider.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"action\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\taction: \\\\\\\"settingsButtonClicked\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t}),\\\\r\\\\n\\\\t)\\\\r\\\\n\\\\r\\\\n\\\\tcontext.subscriptions.push(\\\\r\\\\n\\\\t\\\\tvscode.commands.registerCommand(\\\\\\\"cline.historyButtonClicked\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tsidebarProvider.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"action\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\taction: \\\\\\\"historyButtonClicked\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t}),\\\\r\\\\n\\\\t)\\\\r\\\\n\\\\r\\\\n\\\\t/*\\\\r\\\\n\\\\tWe use the text document content provider API to show the left side for diff view by creating a virtual document for the original content. This makes it readonly so users know to edit the right side if they want to keep their changes.\\\\r\\\\n\\\\r\\\\n\\\\t- This API allows you to create readonly documents in VSCode from arbitrary sources, and works by claiming an uri-scheme for which your provider then returns text contents. The scheme must be provided when registering a provider and cannot change afterwards.\\\\r\\\\n\\\\t- Note how the provider doesn't create uris for virtual documents - its role is to provide contents given such an uri. In return, content providers are wired into the open document logic so that providers are always considered.\\\\r\\\\n\\\\thttps://code.visualstudio.com/api/extension-guides/virtual-documents\\\\r\\\\n\\\\t*/\\\\r\\\\n\\\\tconst diffContentProvider = new (class implements vscode.TextDocumentContentProvider {\\\\r\\\\n\\\\t\\\\tprovideTextDocumentContent(uri: vscode.Uri): string {\\\\r\\\\n\\\\t\\\\t\\\\treturn Buffer.from(uri.query, \\\\\\\"base64\\\\\\\").toString(\\\\\\\"utf-8\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t})()\\\\r\\\\n\\\\tcontext.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider(DIFF_VIEW_URI_SCHEME, diffContentProvider))\\\\r\\\\n\\\\r\\\\n\\\\t// URI Handler\\\\r\\\\n\\\\tconst handleUri = async (uri: vscode.Uri) => {\\\\r\\\\n\\\\t\\\\tconst path = uri.path\\\\r\\\\n\\\\t\\\\tconst query = new URLSearchParams(uri.query.replace(/\\\\\\\\+/g, \\\\\\\"%2B\\\\\\\"))\\\\r\\\\n\\\\t\\\\tconst visibleProvider = ClineProvider.getVisibleInstance()\\\\r\\\\n\\\\t\\\\tif (!visibleProvider) {\\\\r\\\\n\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tswitch (path) {\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"/openrouter\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst code = query.get(\\\\\\\"code\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (code) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait visibleProvider.handleOpenRouterCallback(code)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tdefault:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\tcontext.subscriptions.push(vscode.window.registerUriHandler({ handleUri }))\\\\r\\\\n\\\\r\\\\n\\\\treturn createClineAPI(outputChannel, sidebarProvider)\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n// This method is called when your extension is deactivated\\\\r\\\\nexport function deactivate() {\\\\r\\\\n\\\\toutputChannel.appendLine(\\\\\\\"Cline extension deactivated\\\\\\\")\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":6493,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.948Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.948Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":156,\\\"nonEmptyLines\\\":132,\\\"commentLines\\\":15,\\\"complexity\\\":8},\\\"dependencies\\\":[\\\"delay\\\",\\\"vscode\\\",\\\"./core/webview/ClineProvider\\\",\\\"./exports\\\",\\\"./integrations/editor/DiffViewProvider\\\"],\\\"quality\\\":{\\\"score\\\":-94,\\\"issues\\\":[],\\\"duplicateLines\\\":34,\\\"longLines\\\":12,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.351Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":156},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\checkpoints\\\\\\\\CheckpointTracker.ts\\\":{\\\"content\\\":\\\"import fs from \\\\\\\"fs/promises\\\\\\\"\\\\r\\\\nimport os from \\\\\\\"os\\\\\\\"\\\\r\\\\nimport * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport simpleGit, { SimpleGit } from \\\\\\\"simple-git\\\\\\\"\\\\r\\\\nimport * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\nimport { ClineProvider } from \\\\\\\"../../core/webview/ClineProvider\\\\\\\"\\\\r\\\\nimport { fileExistsAtPath } from \\\\\\\"../../utils/fs\\\\\\\"\\\\r\\\\nimport { globby } from \\\\\\\"globby\\\\\\\"\\\\r\\\\n\\\\r\\\\nclass CheckpointTracker {\\\\r\\\\n\\\\tprivate providerRef: WeakRef<ClineProvider>\\\\r\\\\n\\\\tprivate taskId: string\\\\r\\\\n\\\\tprivate disposables: vscode.Disposable[] = []\\\\r\\\\n\\\\tprivate cwd: string\\\\r\\\\n\\\\tprivate lastRetrievedShadowGitConfigWorkTree?: string\\\\r\\\\n\\\\tlastCheckpointHash?: string\\\\r\\\\n\\\\r\\\\n\\\\tprivate constructor(provider: ClineProvider, taskId: string, cwd: string) {\\\\r\\\\n\\\\t\\\\tthis.providerRef = new WeakRef(provider)\\\\r\\\\n\\\\t\\\\tthis.taskId = taskId\\\\r\\\\n\\\\t\\\\tthis.cwd = cwd\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tpublic static async create(taskId: string, provider?: ClineProvider): Promise<CheckpointTracker> {\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tif (!provider) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Provider is required to create a checkpoint tracker\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// Check if git is installed by attempting to get version\\\\r\\\\n\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait simpleGit().version()\\\\r\\\\n\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Git must be installed to use checkpoints.\\\\\\\") // FIXME: must match what we check for in TaskHeader to show link\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tconst cwd = await CheckpointTracker.getWorkingDirectory()\\\\r\\\\n\\\\t\\\\t\\\\tconst newTracker = new CheckpointTracker(provider, taskId, cwd)\\\\r\\\\n\\\\t\\\\t\\\\tawait newTracker.initShadowGit()\\\\r\\\\n\\\\t\\\\t\\\\treturn newTracker\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Failed to create CheckpointTracker:\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\tthrow error\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate static async getWorkingDirectory(): Promise<string> {\\\\r\\\\n\\\\t\\\\tconst cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0)\\\\r\\\\n\\\\t\\\\tif (!cwd) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"No workspace detected. Please open Cline in a workspace to use checkpoints.\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst homedir = os.homedir()\\\\r\\\\n\\\\t\\\\tconst desktopPath = path.join(homedir, \\\\\\\"Desktop\\\\\\\")\\\\r\\\\n\\\\t\\\\tconst documentsPath = path.join(homedir, \\\\\\\"Documents\\\\\\\")\\\\r\\\\n\\\\t\\\\tconst downloadsPath = path.join(homedir, \\\\\\\"Downloads\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tswitch (cwd) {\\\\r\\\\n\\\\t\\\\t\\\\tcase homedir:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Cannot use checkpoints in home directory\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tcase desktopPath:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Cannot use checkpoints in Desktop directory\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tcase documentsPath:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Cannot use checkpoints in Documents directory\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tcase downloadsPath:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Cannot use checkpoints in Downloads directory\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tdefault:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn cwd\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async getShadowGitPath(): Promise<string> {\\\\r\\\\n\\\\t\\\\tconst globalStoragePath = this.providerRef.deref()?.context.globalStorageUri.fsPath\\\\r\\\\n\\\\t\\\\tif (!globalStoragePath) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Global storage uri is invalid\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst checkpointsDir = path.join(globalStoragePath, \\\\\\\"tasks\\\\\\\", this.taskId, \\\\\\\"checkpoints\\\\\\\")\\\\r\\\\n\\\\t\\\\tawait fs.mkdir(checkpointsDir, { recursive: true })\\\\r\\\\n\\\\t\\\\tconst gitPath = path.join(checkpointsDir, \\\\\\\".git\\\\\\\")\\\\r\\\\n\\\\t\\\\treturn gitPath\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tpublic static async doesShadowGitExist(taskId: string, provider?: ClineProvider): Promise<boolean> {\\\\r\\\\n\\\\t\\\\tconst globalStoragePath = provider?.context.globalStorageUri.fsPath\\\\r\\\\n\\\\t\\\\tif (!globalStoragePath) {\\\\r\\\\n\\\\t\\\\t\\\\treturn false\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst gitPath = path.join(globalStoragePath, \\\\\\\"tasks\\\\\\\", taskId, \\\\\\\"checkpoints\\\\\\\", \\\\\\\".git\\\\\\\")\\\\r\\\\n\\\\t\\\\treturn await fileExistsAtPath(gitPath)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tpublic async initShadowGit(): Promise<string> {\\\\r\\\\n\\\\t\\\\tconst gitPath = await this.getShadowGitPath()\\\\r\\\\n\\\\t\\\\tif (await fileExistsAtPath(gitPath)) {\\\\r\\\\n\\\\t\\\\t\\\\t// Make sure it's the same cwd as the configured worktree\\\\r\\\\n\\\\t\\\\t\\\\tconst worktree = await this.getShadowGitConfigWorkTree()\\\\r\\\\n\\\\t\\\\t\\\\tif (worktree !== this.cwd) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Checkpoints can only be used in the original workspace: \\\\\\\" + worktree)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\treturn gitPath\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\tconst checkpointsDir = path.dirname(gitPath)\\\\r\\\\n\\\\t\\\\t\\\\tconst git = simpleGit(checkpointsDir)\\\\r\\\\n\\\\t\\\\t\\\\tawait git.init()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tawait git.addConfig(\\\\\\\"core.worktree\\\\\\\", this.cwd) // sets the working tree to the current workspace\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// Disable commit signing for shadow repo\\\\r\\\\n\\\\t\\\\t\\\\tawait git.addConfig(\\\\\\\"commit.gpgSign\\\\\\\", \\\\\\\"false\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// Get LFS patterns from workspace if they exist\\\\r\\\\n\\\\t\\\\t\\\\tlet lfsPatterns: string[] = []\\\\r\\\\n\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst attributesPath = path.join(this.cwd, \\\\\\\".gitattributes\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (await fileExistsAtPath(attributesPath)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst attributesContent = await fs.readFile(attributesPath, \\\\\\\"utf8\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tlfsPatterns = attributesContent\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.split(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.filter((line) => line.includes(\\\\\\\"filter=lfs\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t.map((line) => line.split(\\\\\\\" \\\\\\\")[0].trim())\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.warn(\\\\\\\"Failed to read .gitattributes:\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// Add basic excludes directly in git config, while respecting any .gitignore in the workspace\\\\r\\\\n\\\\t\\\\t\\\\t// .git/info/exclude is local to the shadow git repo, so it's not shared with the main repo - and won't conflict with user's .gitignore\\\\r\\\\n\\\\t\\\\t\\\\t// TODO: let user customize these\\\\r\\\\n\\\\t\\\\t\\\\tconst excludesPath = path.join(gitPath, \\\\\\\"info\\\\\\\", \\\\\\\"exclude\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tawait fs.mkdir(path.join(gitPath, \\\\\\\"info\\\\\\\"), { recursive: true })\\\\r\\\\n\\\\t\\\\t\\\\tawait fs.writeFile(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\texcludesPath,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t[\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\".git/\\\\\\\", // ignore the user's .git\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t`.git${GIT_DISABLED_SUFFIX}/`, // ignore the disabled nested git repos\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\".DS_Store\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.log\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"node_modules/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"__pycache__/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"env/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"venv/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"target/dependency/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"build/dependencies/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"dist/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"out/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"bundle/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"vendor/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"tmp/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"temp/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"deps/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"pkg/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"Pods/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Media files\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.jpg\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.jpeg\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.png\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.gif\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.bmp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.ico\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// \\\\\\\"*.svg\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.mp3\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.mp4\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.wav\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.avi\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.mov\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.wmv\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.webm\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.webp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.m4a\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.flac\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Build and dependency directories\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"build/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"bin/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"obj/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\".gradle/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\".idea/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\".vscode/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\".vs/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"coverage/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\".next/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\".nuxt/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Cache and temporary files\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.cache\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.tmp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.temp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.swp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.swo\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.pyc\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.pyo\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\".pytest_cache/\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\".eslintcache\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Environment and config files\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\".env*\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.local\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.development\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.production\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Large data files\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.zip\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.tar\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.gz\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.rar\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.7z\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.iso\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.bin\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.exe\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.dll\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.so\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.dylib\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Database files\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.sqlite\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.db\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.sql\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Log files\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.logs\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"*.error\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"npm-debug.log*\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"yarn-debug.log*\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"yarn-error.log*\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t...lfsPatterns,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t].join(\\\\\\\"\\\\\\\\n\\\\\\\"),\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// Set up git identity (git throws an error if user.name or user.email is not set)\\\\r\\\\n\\\\t\\\\t\\\\tawait git.addConfig(\\\\\\\"user.name\\\\\\\", \\\\\\\"Cline Checkpoint\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tawait git.addConfig(\\\\\\\"user.email\\\\\\\", \\\\\\\"noreply@example.com\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tawait this.addAllFiles(git)\\\\r\\\\n\\\\t\\\\t\\\\t// Initial commit (--allow-empty ensures it works even with no files)\\\\r\\\\n\\\\t\\\\t\\\\tawait git.commit(\\\\\\\"initial commit\\\\\\\", { \\\\\\\"--allow-empty\\\\\\\": null })\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\treturn gitPath\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tpublic async getShadowGitConfigWorkTree(): Promise<string | undefined> {\\\\r\\\\n\\\\t\\\\tif (this.lastRetrievedShadowGitConfigWorkTree) {\\\\r\\\\n\\\\t\\\\t\\\\treturn this.lastRetrievedShadowGitConfigWorkTree\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tconst gitPath = await this.getShadowGitPath()\\\\r\\\\n\\\\t\\\\t\\\\tconst git = simpleGit(path.dirname(gitPath))\\\\r\\\\n\\\\t\\\\t\\\\tconst worktree = await git.getConfig(\\\\\\\"core.worktree\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tthis.lastRetrievedShadowGitConfigWorkTree = worktree.value || undefined\\\\r\\\\n\\\\t\\\\t\\\\treturn this.lastRetrievedShadowGitConfigWorkTree\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Failed to get shadow git config worktree:\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\treturn undefined\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tpublic async commit(): Promise<string | undefined> {\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tconst gitPath = await this.getShadowGitPath()\\\\r\\\\n\\\\t\\\\t\\\\tconst git = simpleGit(path.dirname(gitPath))\\\\r\\\\n\\\\t\\\\t\\\\tawait this.addAllFiles(git)\\\\r\\\\n\\\\t\\\\t\\\\tconst result = await git.commit(\\\\\\\"checkpoint\\\\\\\", {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"--allow-empty\\\\\\\": null,\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\tconst commitHash = result.commit || \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tthis.lastCheckpointHash = commitHash\\\\r\\\\n\\\\t\\\\t\\\\treturn commitHash\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Failed to create checkpoint:\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\treturn undefined\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tpublic async resetHead(commitHash: string): Promise<void> {\\\\r\\\\n\\\\t\\\\tconst gitPath = await this.getShadowGitPath()\\\\r\\\\n\\\\t\\\\tconst git = simpleGit(path.dirname(gitPath))\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Clean working directory and force reset\\\\r\\\\n\\\\t\\\\t// This ensures that the operation will succeed regardless of:\\\\r\\\\n\\\\t\\\\t// - Untracked files in the workspace\\\\r\\\\n\\\\t\\\\t// - Staged changes\\\\r\\\\n\\\\t\\\\t// - Unstaged changes\\\\r\\\\n\\\\t\\\\t// - Partial commits\\\\r\\\\n\\\\t\\\\t// - Merge conflicts\\\\r\\\\n\\\\t\\\\tawait git.clean(\\\\\\\"f\\\\\\\", [\\\\\\\"-d\\\\\\\", \\\\\\\"-f\\\\\\\"]) // Remove untracked files and directories\\\\r\\\\n\\\\t\\\\tawait git.reset([\\\\\\\"--hard\\\\\\\", commitHash]) // Hard reset to target commit\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t/**\\\\r\\\\n\\\\t * Return an array describing changed files between one commit and either:\\\\r\\\\n\\\\t * - another commit, or\\\\r\\\\n\\\\t * - the current working directory (including uncommitted changes).\\\\r\\\\n\\\\t *\\\\r\\\\n\\\\t * If `rhsHash` is omitted, compares `lhsHash` to the working directory.\\\\r\\\\n\\\\t * If you want truly untracked files to appear, `git add` them first.\\\\r\\\\n\\\\t *\\\\r\\\\n\\\\t * @param lhsHash - The commit to compare from (older commit)\\\\r\\\\n\\\\t * @param rhsHash - The commit to compare to (newer commit).\\\\r\\\\n\\\\t * If omitted, we compare to the working directory.\\\\r\\\\n\\\\t * @returns Array of file changes with before/after content\\\\r\\\\n\\\\t */\\\\r\\\\n\\\\tpublic async getDiffSet(\\\\r\\\\n\\\\t\\\\tlhsHash?: string,\\\\r\\\\n\\\\t\\\\trhsHash?: string,\\\\r\\\\n\\\\t): Promise<\\\\r\\\\n\\\\t\\\\tArray<{\\\\r\\\\n\\\\t\\\\t\\\\trelativePath: string\\\\r\\\\n\\\\t\\\\t\\\\tabsolutePath: string\\\\r\\\\n\\\\t\\\\t\\\\tbefore: string\\\\r\\\\n\\\\t\\\\t\\\\tafter: string\\\\r\\\\n\\\\t\\\\t}>\\\\r\\\\n\\\\t> {\\\\r\\\\n\\\\t\\\\tconst gitPath = await this.getShadowGitPath()\\\\r\\\\n\\\\t\\\\tconst git = simpleGit(path.dirname(gitPath))\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// If lhsHash is missing, use the initial commit of the repo\\\\r\\\\n\\\\t\\\\tlet baseHash = lhsHash\\\\r\\\\n\\\\t\\\\tif (!baseHash) {\\\\r\\\\n\\\\t\\\\t\\\\tconst rootCommit = await git.raw([\\\\\\\"rev-list\\\\\\\", \\\\\\\"--max-parents=0\\\\\\\", \\\\\\\"HEAD\\\\\\\"])\\\\r\\\\n\\\\t\\\\t\\\\tbaseHash = rootCommit.trim()\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Stage all changes so that untracked files appear in diff summary\\\\r\\\\n\\\\t\\\\tawait this.addAllFiles(git)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst diffSummary = rhsHash ? await git.diffSummary([`${baseHash}..${rhsHash}`]) : await git.diffSummary([baseHash])\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// For each changed file, gather before/after content\\\\r\\\\n\\\\t\\\\tconst result = []\\\\r\\\\n\\\\t\\\\tconst cwdPath = (await this.getShadowGitConfigWorkTree()) || this.cwd || \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tfor (const file of diffSummary.files) {\\\\r\\\\n\\\\t\\\\t\\\\tconst filePath = file.file\\\\r\\\\n\\\\t\\\\t\\\\tconst absolutePath = path.join(cwdPath, filePath)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tlet beforeContent = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbeforeContent = await git.show([`${baseHash}:${filePath}`])\\\\r\\\\n\\\\t\\\\t\\\\t} catch (_) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// file didn't exist in older commit => remains empty\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tlet afterContent = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tif (rhsHash) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// if user provided a newer commit, use git.show at that commit\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tafterContent = await git.show([`${rhsHash}:${filePath}`])\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} catch (_) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// file didn't exist in newer commit => remains empty\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// otherwise, read from disk (includes uncommitted changes)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tafterContent = await fs.readFile(absolutePath, \\\\\\\"utf8\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} catch (_) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// file might be deleted => remains empty\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tresult.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\trelativePath: filePath,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tabsolutePath,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbefore: beforeContent,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tafter: afterContent,\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\treturn result\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async addAllFiles(git: SimpleGit) {\\\\r\\\\n\\\\t\\\\tawait this.renameNestedGitRepos(true)\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tawait git.add(\\\\\\\".\\\\\\\")\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Failed to add files to git:\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t} finally {\\\\r\\\\n\\\\t\\\\t\\\\tawait this.renameNestedGitRepos(false)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Since we use git to track checkpoints, we need to temporarily disable nested git repos to work around git's requirement of using submodules for nested repos.\\\\r\\\\n\\\\tprivate async renameNestedGitRepos(disable: boolean) {\\\\r\\\\n\\\\t\\\\t// Find all .git directories that are not at the root level\\\\r\\\\n\\\\t\\\\tconst gitPaths = await globby(\\\\\\\"**/.git\\\\\\\" + (disable ? \\\\\\\"\\\\\\\" : GIT_DISABLED_SUFFIX), {\\\\r\\\\n\\\\t\\\\t\\\\tcwd: this.cwd,\\\\r\\\\n\\\\t\\\\t\\\\tonlyDirectories: true,\\\\r\\\\n\\\\t\\\\t\\\\tignore: [\\\\\\\".git\\\\\\\"], // Ignore root level .git\\\\r\\\\n\\\\t\\\\t\\\\tdot: true,\\\\r\\\\n\\\\t\\\\t\\\\tmarkDirectories: false,\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// For each nested .git directory, rename it based on operation\\\\r\\\\n\\\\t\\\\tfor (const gitPath of gitPaths) {\\\\r\\\\n\\\\t\\\\t\\\\tconst fullPath = path.join(this.cwd, gitPath)\\\\r\\\\n\\\\t\\\\t\\\\tlet newPath: string\\\\r\\\\n\\\\t\\\\t\\\\tif (disable) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tnewPath = fullPath + GIT_DISABLED_SUFFIX\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tnewPath = fullPath.endsWith(GIT_DISABLED_SUFFIX) ? fullPath.slice(0, -GIT_DISABLED_SUFFIX.length) : fullPath\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait fs.rename(fullPath, newPath)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.log(`CheckpointTracker ${disable ? \\\\\\\"disabled\\\\\\\" : \\\\\\\"enabled\\\\\\\"} nested git repo ${gitPath}`)\\\\r\\\\n\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.error(`CheckpointTracker failed to ${disable ? \\\\\\\"disable\\\\\\\" : \\\\\\\"enable\\\\\\\"} nested git repo ${gitPath}:`, error)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tpublic dispose() {\\\\r\\\\n\\\\t\\\\tthis.disposables.forEach((d) => d.dispose())\\\\r\\\\n\\\\t\\\\tthis.disposables = []\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nconst GIT_DISABLED_SUFFIX = \\\\\\\"_disabled\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport default CheckpointTracker\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":12846,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.956Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.956Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":415,\\\"nonEmptyLines\\\":375,\\\"commentLines\\\":36,\\\"complexity\\\":49},\\\"dependencies\\\":[\\\"fs/promises\\\",\\\"os\\\",\\\"path\\\",\\\"simple-git\\\",\\\"vscode\\\",\\\"../../core/webview/ClineProvider\\\",\\\"../../utils/fs\\\",\\\"globby\\\"],\\\"quality\\\":{\\\"score\\\":-279,\\\"issues\\\":[],\\\"duplicateLines\\\":73,\\\"longLines\\\":7,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.354Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":415},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\diagnostics\\\\\\\\DiagnosticsMonitor.ts\\\":{\\\"content\\\":\\\"/*\\\\r\\\\nimport * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\nimport deepEqual from \\\\\\\"fast-deep-equal\\\\\\\"\\\\r\\\\n\\\\r\\\\ntype FileDiagnostics = [vscode.Uri, vscode.Diagnostic[]][]\\\\r\\\\n\\\\r\\\\n\\\\r\\\\nAbout Diagnostics:\\\\r\\\\nThe Problems tab shows diagnostics that have been reported for your project. These diagnostics are categorized into:\\\\r\\\\nErrors: Critical issues that usually prevent your code from compiling or running correctly.\\\\r\\\\nWarnings: Potential problems in the code that may not prevent it from running but could cause issues (e.g., bad practices, unused variables).\\\\r\\\\nInformation: Non-critical suggestions or tips (e.g., formatting issues or notes from linters).\\\\r\\\\nThe Problems tab displays diagnostics from various sources:\\\\r\\\\n1. Language Servers:\\\\r\\\\n - TypeScript: Type errors, missing imports, syntax issues\\\\r\\\\n - Python: Syntax errors, invalid type hints, undefined variables\\\\r\\\\n - JavaScript/Node.js: Parsing and execution errors\\\\r\\\\n2. Linters:\\\\r\\\\n - ESLint: Code style, best practices, potential bugs\\\\r\\\\n - Pylint: Unused imports, naming conventions\\\\r\\\\n - TSLint: Style and correctness issues in TypeScript\\\\r\\\\n3. Build Tools:\\\\r\\\\n - Webpack: Module resolution failures, build errors\\\\r\\\\n - Gulp: Build errors during task execution\\\\r\\\\n4. Custom Validators:\\\\r\\\\n - Extensions can generate custom diagnostics for specific languages or tools\\\\r\\\\nEach problem typically indicates its source (e.g., language server, linter, build tool).\\\\r\\\\nDiagnostics update in real-time as you edit code, helping identify issues quickly. For example, if you introduce a syntax error in a TypeScript file, the Problems tab will immediately display the new error.\\\\r\\\\n\\\\r\\\\nNotes on diagnostics:\\\\r\\\\n- linter diagnostics are only captured for open editors\\\\r\\\\n- this works great for us since when cline edits/creates files its through vscode's textedit api's and we get those diagnostics for free\\\\r\\\\n- some tools might require you to save the file or manually refresh to clear the problem from the list.\\\\r\\\\n\\\\r\\\\nSystem Prompt\\\\r\\\\n- You will automatically receive workspace error diagnostics in environment_details. Be mindful that this may include issues beyond the scope of your task or the user's request. Only address errors relevant to your work, and avoid fixing pre-existing or unrelated issues unless the user specifically instructs you to do so.\\\\r\\\\n- If you are unable to resolve errors provided in environment_details after two attempts, consider using ask_followup_question to ask the user for additional information, such as the latest documentation related to a problematic framework, to help you make progress on the task. If the error remains unresolved after this step, proceed with your task while disregarding the error.\\\\r\\\\n\\\\r\\\\nclass DiagnosticsMonitor {\\\\r\\\\n\\\\tprivate diagnosticsChangeEmitter: vscode.EventEmitter<void> = new vscode.EventEmitter<void>()\\\\r\\\\n\\\\tprivate disposables: vscode.Disposable[] = []\\\\r\\\\n\\\\tprivate lastDiagnostics: FileDiagnostics = []\\\\r\\\\n\\\\r\\\\n\\\\tconstructor() {\\\\r\\\\n\\\\t\\\\tthis.disposables.push(\\\\r\\\\n\\\\t\\\\t\\\\tvscode.languages.onDidChangeDiagnostics(() => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.diagnosticsChangeEmitter.fire()\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tpublic async getCurrentDiagnostics(shouldWaitForChanges: boolean): Promise<FileDiagnostics> {\\\\r\\\\n\\\\t\\\\tconst currentDiagnostics = this.getDiagnostics()\\\\r\\\\n\\\\t\\\\tif (!shouldWaitForChanges) {\\\\r\\\\n\\\\t\\\\t\\\\tthis.lastDiagnostics = currentDiagnostics\\\\r\\\\n\\\\t\\\\t\\\\treturn currentDiagnostics\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (!deepEqual(this.lastDiagnostics, currentDiagnostics)) {\\\\r\\\\n\\\\t\\\\t\\\\tthis.lastDiagnostics = currentDiagnostics\\\\r\\\\n\\\\t\\\\t\\\\treturn currentDiagnostics\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet timeout = 300 // only way this happens is if theres no errors\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// if diagnostics contain existing errors (since the check above didn't trigger) then it's likely cline just did something that should have fixed the error, so we'll give a longer grace period for diagnostics to catch up\\\\r\\\\n\\\\t\\\\tconst hasErrors = currentDiagnostics.some(([_, diagnostics]) =>\\\\r\\\\n\\\\t\\\\t\\\\tdiagnostics.some((d) => d.severity === vscode.DiagnosticSeverity.Error)\\\\r\\\\n\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\tif (hasErrors) {\\\\r\\\\n\\\\t\\\\t\\\\tconsole.log(\\\\\\\"Existing errors detected, extending timeout\\\\\\\", currentDiagnostics)\\\\r\\\\n\\\\t\\\\t\\\\ttimeout = 10_000\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\treturn this.waitForUpdatedDiagnostics(timeout)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async waitForUpdatedDiagnostics(timeout: number): Promise<FileDiagnostics> {\\\\r\\\\n\\\\t\\\\treturn new Promise((resolve, reject) => {\\\\r\\\\n\\\\t\\\\t\\\\tconst timer = setTimeout(() => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcleanup()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst finalDiagnostics = this.getDiagnostics()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.lastDiagnostics = finalDiagnostics\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tresolve(finalDiagnostics)\\\\r\\\\n\\\\t\\\\t\\\\t}, timeout)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tconst disposable = this.diagnosticsChangeEmitter.event(() => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst updatedDiagnostics = this.getDiagnostics() // I thought this would only trigger when diagnostics changed, but that's not the case.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (deepEqual(this.lastDiagnostics, updatedDiagnostics)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// diagnostics have not changed, ignoring...\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcleanup()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.lastDiagnostics = updatedDiagnostics\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tresolve(updatedDiagnostics)\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tconst cleanup = () => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tclearTimeout(timer)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tdisposable.dispose()\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate getDiagnostics(): FileDiagnostics {\\\\r\\\\n\\\\t\\\\tconst allDiagnostics = vscode.languages.getDiagnostics()\\\\r\\\\n\\\\t\\\\treturn allDiagnostics\\\\r\\\\n\\\\t\\\\t\\\\t.filter(([_, diagnostics]) => diagnostics.some((d) => d.severity === vscode.DiagnosticSeverity.Error))\\\\r\\\\n\\\\t\\\\t\\\\t.map(([uri, diagnostics]) => [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\turi,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tdiagnostics.filter((d) => d.severity === vscode.DiagnosticSeverity.Error),\\\\r\\\\n\\\\t\\\\t\\\\t])\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tpublic dispose() {\\\\r\\\\n\\\\t\\\\tthis.disposables.forEach((d) => d.dispose())\\\\r\\\\n\\\\t\\\\tthis.disposables = []\\\\r\\\\n\\\\t\\\\tthis.diagnosticsChangeEmitter.dispose()\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport default DiagnosticsMonitor\\\\r\\\\n*/\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":5486,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.843Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.843Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":124,\\\"nonEmptyLines\\\":105,\\\"commentLines\\\":3,\\\"complexity\\\":5},\\\"dependencies\\\":[\\\"vscode\\\",\\\"fast-deep-equal\\\"],\\\"quality\\\":{\\\"score\\\":0,\\\"issues\\\":[],\\\"duplicateLines\\\":16,\\\"longLines\\\":10,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.357Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":124},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\diagnostics\\\\\\\\index.ts\\\":{\\\"content\\\":\\\"import * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\nimport * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport deepEqual from \\\\\\\"fast-deep-equal\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport function getNewDiagnostics(\\\\r\\\\n\\\\toldDiagnostics: [vscode.Uri, vscode.Diagnostic[]][],\\\\r\\\\n\\\\tnewDiagnostics: [vscode.Uri, vscode.Diagnostic[]][],\\\\r\\\\n): [vscode.Uri, vscode.Diagnostic[]][] {\\\\r\\\\n\\\\tconst newProblems: [vscode.Uri, vscode.Diagnostic[]][] = []\\\\r\\\\n\\\\tconst oldMap = new Map(oldDiagnostics)\\\\r\\\\n\\\\r\\\\n\\\\tfor (const [uri, newDiags] of newDiagnostics) {\\\\r\\\\n\\\\t\\\\tconst oldDiags = oldMap.get(uri) || []\\\\r\\\\n\\\\t\\\\tconst newProblemsForUri = newDiags.filter((newDiag) => !oldDiags.some((oldDiag) => deepEqual(oldDiag, newDiag)))\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (newProblemsForUri.length > 0) {\\\\r\\\\n\\\\t\\\\t\\\\tnewProblems.push([uri, newProblemsForUri])\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\treturn newProblems\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n// Usage:\\\\r\\\\n// const oldDiagnostics = // ... your old diagnostics array\\\\r\\\\n// const newDiagnostics = // ... your new diagnostics array\\\\r\\\\n// const newProblems = getNewDiagnostics(oldDiagnostics, newDiagnostics);\\\\r\\\\n\\\\r\\\\n// Example usage with mocks:\\\\r\\\\n//\\\\r\\\\n// // Mock old diagnostics\\\\r\\\\n// const oldDiagnostics: [vscode.Uri, vscode.Diagnostic[]][] = [\\\\r\\\\n// [vscode.Uri.file(\\\\\\\"/path/to/file1.ts\\\\\\\"), [\\\\r\\\\n// new vscode.Diagnostic(new vscode.Range(0, 0, 0, 10), \\\\\\\"Old error in file1\\\\\\\", vscode.DiagnosticSeverity.Error)\\\\r\\\\n// ]],\\\\r\\\\n// [vscode.Uri.file(\\\\\\\"/path/to/file2.ts\\\\\\\"), [\\\\r\\\\n// new vscode.Diagnostic(new vscode.Range(5, 5, 5, 15), \\\\\\\"Old warning in file2\\\\\\\", vscode.DiagnosticSeverity.Warning)\\\\r\\\\n// ]]\\\\r\\\\n// ];\\\\r\\\\n//\\\\r\\\\n// // Mock new diagnostics\\\\r\\\\n// const newDiagnostics: [vscode.Uri, vscode.Diagnostic[]][] = [\\\\r\\\\n// [vscode.Uri.file(\\\\\\\"/path/to/file1.ts\\\\\\\"), [\\\\r\\\\n// new vscode.Diagnostic(new vscode.Range(0, 0, 0, 10), \\\\\\\"Old error in file1\\\\\\\", vscode.DiagnosticSeverity.Error),\\\\r\\\\n// new vscode.Diagnostic(new vscode.Range(2, 2, 2, 12), \\\\\\\"New error in file1\\\\\\\", vscode.DiagnosticSeverity.Error)\\\\r\\\\n// ]],\\\\r\\\\n// [vscode.Uri.file(\\\\\\\"/path/to/file2.ts\\\\\\\"), [\\\\r\\\\n// new vscode.Diagnostic(new vscode.Range(5, 5, 5, 15), \\\\\\\"Old warning in file2\\\\\\\", vscode.DiagnosticSeverity.Warning)\\\\r\\\\n// ]],\\\\r\\\\n// [vscode.Uri.file(\\\\\\\"/path/to/file3.ts\\\\\\\"), [\\\\r\\\\n// new vscode.Diagnostic(new vscode.Range(1, 1, 1, 11), \\\\\\\"New error in file3\\\\\\\", vscode.DiagnosticSeverity.Error)\\\\r\\\\n// ]]\\\\r\\\\n// ];\\\\r\\\\n//\\\\r\\\\n// const newProblems = getNewProblems(oldDiagnostics, newDiagnostics);\\\\r\\\\n//\\\\r\\\\n// console.log(\\\\\\\"New problems:\\\\\\\");\\\\r\\\\n// for (const [uri, diagnostics] of newProblems) {\\\\r\\\\n// console.log(`File: ${uri.fsPath}`);\\\\r\\\\n// for (const diagnostic of diagnostics) {\\\\r\\\\n// console.log(`- ${diagnostic.message} (${diagnostic.range.start.line}:${diagnostic.range.start.character})`);\\\\r\\\\n// }\\\\r\\\\n// }\\\\r\\\\n//\\\\r\\\\n// // Expected output:\\\\r\\\\n// // New problems:\\\\r\\\\n// // File: /path/to/file1.ts\\\\r\\\\n// // - New error in file1 (2:2)\\\\r\\\\n// // File: /path/to/file3.ts\\\\r\\\\n// // - New error in file3 (1:1)\\\\r\\\\n\\\\r\\\\n// will return empty string if no problems with the given severity are found\\\\r\\\\nexport function diagnosticsToProblemsString(\\\\r\\\\n\\\\tdiagnostics: [vscode.Uri, vscode.Diagnostic[]][],\\\\r\\\\n\\\\tseverities: vscode.DiagnosticSeverity[],\\\\r\\\\n\\\\tcwd: string,\\\\r\\\\n): string {\\\\r\\\\n\\\\tlet result = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\tfor (const [uri, fileDiagnostics] of diagnostics) {\\\\r\\\\n\\\\t\\\\tconst problems = fileDiagnostics.filter((d) => severities.includes(d.severity))\\\\r\\\\n\\\\t\\\\tif (problems.length > 0) {\\\\r\\\\n\\\\t\\\\t\\\\tresult += `\\\\\\\\n\\\\\\\\n${path.relative(cwd, uri.fsPath).toPosix()}`\\\\r\\\\n\\\\t\\\\t\\\\tfor (const diagnostic of problems) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlet label: string\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tswitch (diagnostic.severity) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase vscode.DiagnosticSeverity.Error:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlabel = \\\\\\\"Error\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase vscode.DiagnosticSeverity.Warning:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlabel = \\\\\\\"Warning\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase vscode.DiagnosticSeverity.Information:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlabel = \\\\\\\"Information\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcase vscode.DiagnosticSeverity.Hint:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlabel = \\\\\\\"Hint\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tdefault:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlabel = \\\\\\\"Diagnostic\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst line = diagnostic.range.start.line + 1 // VSCode lines are 0-indexed\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst source = diagnostic.source ? `${diagnostic.source} ` : \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tresult += `\\\\\\\\n- [${source}${label}] Line ${line}: ${diagnostic.message}`\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\treturn result.trim()\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":4038,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.845Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.843Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":109,\\\"nonEmptyLines\\\":101,\\\"commentLines\\\":47,\\\"complexity\\\":14},\\\"dependencies\\\":[\\\"vscode\\\",\\\"path\\\",\\\"fast-deep-equal\\\"],\\\"quality\\\":{\\\"score\\\":-21,\\\"issues\\\":[],\\\"duplicateLines\\\":21,\\\"longLines\\\":8,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.359Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":109},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\editor\\\\\\\\DecorationController.ts\\\":{\\\"content\\\":\\\"import * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\n\\\\r\\\\nconst fadedOverlayDecorationType = vscode.window.createTextEditorDecorationType({\\\\r\\\\n\\\\tbackgroundColor: \\\\\\\"rgba(255, 255, 0, 0.1)\\\\\\\",\\\\r\\\\n\\\\topacity: \\\\\\\"0.4\\\\\\\",\\\\r\\\\n\\\\tisWholeLine: true,\\\\r\\\\n})\\\\r\\\\n\\\\r\\\\nconst activeLineDecorationType = vscode.window.createTextEditorDecorationType({\\\\r\\\\n\\\\tbackgroundColor: \\\\\\\"rgba(255, 255, 0, 0.3)\\\\\\\",\\\\r\\\\n\\\\topacity: \\\\\\\"1\\\\\\\",\\\\r\\\\n\\\\tisWholeLine: true,\\\\r\\\\n\\\\tborder: \\\\\\\"1px solid rgba(255, 255, 0, 0.5)\\\\\\\",\\\\r\\\\n})\\\\r\\\\n\\\\r\\\\ntype DecorationType = \\\\\\\"fadedOverlay\\\\\\\" | \\\\\\\"activeLine\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport class DecorationController {\\\\r\\\\n\\\\tprivate decorationType: DecorationType\\\\r\\\\n\\\\tprivate editor: vscode.TextEditor\\\\r\\\\n\\\\tprivate ranges: vscode.Range[] = []\\\\r\\\\n\\\\r\\\\n\\\\tconstructor(decorationType: DecorationType, editor: vscode.TextEditor) {\\\\r\\\\n\\\\t\\\\tthis.decorationType = decorationType\\\\r\\\\n\\\\t\\\\tthis.editor = editor\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tgetDecoration() {\\\\r\\\\n\\\\t\\\\tswitch (this.decorationType) {\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"fadedOverlay\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn fadedOverlayDecorationType\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"activeLine\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn activeLineDecorationType\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\taddLines(startIndex: number, numLines: number) {\\\\r\\\\n\\\\t\\\\t// Guard against invalid inputs\\\\r\\\\n\\\\t\\\\tif (startIndex < 0 || numLines <= 0) {\\\\r\\\\n\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst lastRange = this.ranges[this.ranges.length - 1]\\\\r\\\\n\\\\t\\\\tif (lastRange && lastRange.end.line === startIndex - 1) {\\\\r\\\\n\\\\t\\\\t\\\\tthis.ranges[this.ranges.length - 1] = lastRange.with(undefined, lastRange.end.translate(numLines))\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\tconst endLine = startIndex + numLines - 1\\\\r\\\\n\\\\t\\\\t\\\\tthis.ranges.push(new vscode.Range(startIndex, 0, endLine, Number.MAX_SAFE_INTEGER))\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tthis.editor.setDecorations(this.getDecoration(), this.ranges)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tclear() {\\\\r\\\\n\\\\t\\\\tthis.ranges = []\\\\r\\\\n\\\\t\\\\tthis.editor.setDecorations(this.getDecoration(), this.ranges)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tupdateOverlayAfterLine(line: number, totalLines: number) {\\\\r\\\\n\\\\t\\\\t// Remove any existing ranges that start at or after the current line\\\\r\\\\n\\\\t\\\\tthis.ranges = this.ranges.filter((range) => range.end.line < line)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Add a new range for all lines after the current line\\\\r\\\\n\\\\t\\\\tif (line < totalLines - 1) {\\\\r\\\\n\\\\t\\\\t\\\\tthis.ranges.push(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tnew vscode.Range(new vscode.Position(line + 1, 0), new vscode.Position(totalLines - 1, Number.MAX_SAFE_INTEGER)),\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Apply the updated decorations\\\\r\\\\n\\\\t\\\\tthis.editor.setDecorations(this.getDecoration(), this.ranges)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tsetActiveLine(line: number) {\\\\r\\\\n\\\\t\\\\tthis.ranges = [new vscode.Range(line, 0, line, Number.MAX_SAFE_INTEGER)]\\\\r\\\\n\\\\t\\\\tthis.editor.setDecorations(this.getDecoration(), this.ranges)\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":2399,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.956Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.956Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":79,\\\"nonEmptyLines\\\":64,\\\"commentLines\\\":4,\\\"complexity\\\":9},\\\"dependencies\\\":[\\\"vscode\\\"],\\\"quality\\\":{\\\"score\\\":21,\\\"issues\\\":[],\\\"duplicateLines\\\":15,\\\"longLines\\\":2,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.360Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":79},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\editor\\\\\\\\detect-omission.ts\\\":{\\\"content\\\":\\\"import * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\n\\\\r\\\\n/**\\\\r\\\\n * Detects potential AI-generated code omissions in the given file content.\\\\r\\\\n * @param originalFileContent The original content of the file.\\\\r\\\\n * @param newFileContent The new content of the file to check.\\\\r\\\\n * @returns True if a potential omission is detected, false otherwise.\\\\r\\\\n */\\\\r\\\\nfunction detectCodeOmission(originalFileContent: string, newFileContent: string): boolean {\\\\r\\\\n\\\\tconst originalLines = originalFileContent.split(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\tconst newLines = newFileContent.split(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\tconst omissionKeywords = [\\\\\\\"remain\\\\\\\", \\\\\\\"remains\\\\\\\", \\\\\\\"unchanged\\\\\\\", \\\\\\\"rest\\\\\\\", \\\\\\\"previous\\\\\\\", \\\\\\\"existing\\\\\\\", \\\\\\\"...\\\\\\\"]\\\\r\\\\n\\\\r\\\\n\\\\tconst commentPatterns = [\\\\r\\\\n\\\\t\\\\t/^\\\\\\\\s*\\\\\\\\/\\\\\\\\//, // Single-line comment for most languages\\\\r\\\\n\\\\t\\\\t/^\\\\\\\\s*#/, // Single-line comment for Python, Ruby, etc.\\\\r\\\\n\\\\t\\\\t/^\\\\\\\\s*\\\\\\\\/\\\\\\\\*/, // Multi-line comment opening\\\\r\\\\n\\\\t\\\\t/^\\\\\\\\s*{\\\\\\\\s*\\\\\\\\/\\\\\\\\*/, // JSX comment opening\\\\r\\\\n\\\\t\\\\t/^\\\\\\\\s*<!--/, // HTML comment opening\\\\r\\\\n\\\\t]\\\\r\\\\n\\\\r\\\\n\\\\tfor (const line of newLines) {\\\\r\\\\n\\\\t\\\\tif (commentPatterns.some((pattern) => pattern.test(line))) {\\\\r\\\\n\\\\t\\\\t\\\\tconst words = line.toLowerCase().split(/\\\\\\\\s+/)\\\\r\\\\n\\\\t\\\\t\\\\tif (omissionKeywords.some((keyword) => words.includes(keyword))) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (!originalLines.includes(line)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\treturn false\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n/**\\\\r\\\\n * Shows a warning in VSCode if a potential code omission is detected.\\\\r\\\\n * @param originalFileContent The original content of the file.\\\\r\\\\n * @param newFileContent The new content of the file to check.\\\\r\\\\n */\\\\r\\\\nexport function showOmissionWarning(originalFileContent: string, newFileContent: string): void {\\\\r\\\\n\\\\tif (detectCodeOmission(originalFileContent, newFileContent)) {\\\\r\\\\n\\\\t\\\\tvscode.window\\\\r\\\\n\\\\t\\\\t\\\\t.showWarningMessage(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"Potential code truncation detected. This happens when the AI reaches its max output limit.\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"Follow this guide to fix the issue\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t.then((selection) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (selection === \\\\\\\"Follow this guide to fix the issue\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tvscode.env.openExternal(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tvscode.Uri.parse(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"https://github.com/cline/cline/wiki/Troubleshooting-%E2%80%90-Cline-Deleting-Code-with-%22Rest-of-Code-Here%22-Comments\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":2099,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.846Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.846Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":59,\\\"nonEmptyLines\\\":53,\\\"commentLines\\\":2,\\\"complexity\\\":7},\\\"dependencies\\\":[\\\"vscode\\\"],\\\"quality\\\":{\\\"score\\\":38,\\\"issues\\\":[],\\\"duplicateLines\\\":12,\\\"longLines\\\":1,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.362Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":59},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\editor\\\\\\\\DiffViewProvider.ts\\\":{\\\"content\\\":\\\"import * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\nimport * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport * as fs from \\\\\\\"fs/promises\\\\\\\"\\\\r\\\\nimport { createDirectoriesForFile } from \\\\\\\"../../utils/fs\\\\\\\"\\\\r\\\\nimport { arePathsEqual } from \\\\\\\"../../utils/path\\\\\\\"\\\\r\\\\nimport { formatResponse } from \\\\\\\"../../core/prompts/responses\\\\\\\"\\\\r\\\\nimport { DecorationController } from \\\\\\\"./DecorationController\\\\\\\"\\\\r\\\\nimport * as diff from \\\\\\\"diff\\\\\\\"\\\\r\\\\nimport { diagnosticsToProblemsString, getNewDiagnostics } from \\\\\\\"../diagnostics\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport const DIFF_VIEW_URI_SCHEME = \\\\\\\"cline-diff\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport class DiffViewProvider {\\\\r\\\\n\\\\teditType?: \\\\\\\"create\\\\\\\" | \\\\\\\"modify\\\\\\\"\\\\r\\\\n\\\\tisEditing = false\\\\r\\\\n\\\\toriginalContent: string | undefined\\\\r\\\\n\\\\tprivate createdDirs: string[] = []\\\\r\\\\n\\\\tprivate documentWasOpen = false\\\\r\\\\n\\\\tprivate relPath?: string\\\\r\\\\n\\\\tprivate newContent?: string\\\\r\\\\n\\\\tprivate activeDiffEditor?: vscode.TextEditor\\\\r\\\\n\\\\tprivate fadedOverlayController?: DecorationController\\\\r\\\\n\\\\tprivate activeLineController?: DecorationController\\\\r\\\\n\\\\tprivate streamedLines: string[] = []\\\\r\\\\n\\\\tprivate preDiagnostics: [vscode.Uri, vscode.Diagnostic[]][] = []\\\\r\\\\n\\\\r\\\\n\\\\tconstructor(private cwd: string) {}\\\\r\\\\n\\\\r\\\\n\\\\tasync open(relPath: string): Promise<void> {\\\\r\\\\n\\\\t\\\\tthis.relPath = relPath\\\\r\\\\n\\\\t\\\\tconst fileExists = this.editType === \\\\\\\"modify\\\\\\\"\\\\r\\\\n\\\\t\\\\tconst absolutePath = path.resolve(this.cwd, relPath)\\\\r\\\\n\\\\t\\\\tthis.isEditing = true\\\\r\\\\n\\\\t\\\\t// if the file is already open, ensure it's not dirty before getting its contents\\\\r\\\\n\\\\t\\\\tif (fileExists) {\\\\r\\\\n\\\\t\\\\t\\\\tconst existingDocument = vscode.workspace.textDocuments.find((doc) => arePathsEqual(doc.uri.fsPath, absolutePath))\\\\r\\\\n\\\\t\\\\t\\\\tif (existingDocument && existingDocument.isDirty) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait existingDocument.save()\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// get diagnostics before editing the file, we'll compare to diagnostics after editing to see if cline needs to fix anything\\\\r\\\\n\\\\t\\\\tthis.preDiagnostics = vscode.languages.getDiagnostics()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (fileExists) {\\\\r\\\\n\\\\t\\\\t\\\\tthis.originalContent = await fs.readFile(absolutePath, \\\\\\\"utf-8\\\\\\\")\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\tthis.originalContent = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t// for new files, create any necessary directories and keep track of new directories to delete if the user denies the operation\\\\r\\\\n\\\\t\\\\tthis.createdDirs = await createDirectoriesForFile(absolutePath)\\\\r\\\\n\\\\t\\\\t// make sure the file exists before we open it\\\\r\\\\n\\\\t\\\\tif (!fileExists) {\\\\r\\\\n\\\\t\\\\t\\\\tawait fs.writeFile(absolutePath, \\\\\\\"\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t// if the file was already open, close it (must happen after showing the diff view since if it's the only tab the column will close)\\\\r\\\\n\\\\t\\\\tthis.documentWasOpen = false\\\\r\\\\n\\\\t\\\\t// close the tab if it's open (it's already saved above)\\\\r\\\\n\\\\t\\\\tconst tabs = vscode.window.tabGroups.all\\\\r\\\\n\\\\t\\\\t\\\\t.map((tg) => tg.tabs)\\\\r\\\\n\\\\t\\\\t\\\\t.flat()\\\\r\\\\n\\\\t\\\\t\\\\t.filter((tab) => tab.input instanceof vscode.TabInputText && arePathsEqual(tab.input.uri.fsPath, absolutePath))\\\\r\\\\n\\\\t\\\\tfor (const tab of tabs) {\\\\r\\\\n\\\\t\\\\t\\\\tif (!tab.isDirty) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait vscode.window.tabGroups.close(tab)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tthis.documentWasOpen = true\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tthis.activeDiffEditor = await this.openDiffEditor()\\\\r\\\\n\\\\t\\\\tthis.fadedOverlayController = new DecorationController(\\\\\\\"fadedOverlay\\\\\\\", this.activeDiffEditor)\\\\r\\\\n\\\\t\\\\tthis.activeLineController = new DecorationController(\\\\\\\"activeLine\\\\\\\", this.activeDiffEditor)\\\\r\\\\n\\\\t\\\\t// Apply faded overlay to all lines initially\\\\r\\\\n\\\\t\\\\tthis.fadedOverlayController.addLines(0, this.activeDiffEditor.document.lineCount)\\\\r\\\\n\\\\t\\\\tthis.scrollEditorToLine(0) // will this crash for new files?\\\\r\\\\n\\\\t\\\\tthis.streamedLines = []\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync update(accumulatedContent: string, isFinal: boolean) {\\\\r\\\\n\\\\t\\\\tif (!this.relPath || !this.activeLineController || !this.fadedOverlayController) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Required values not set\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tthis.newContent = accumulatedContent\\\\r\\\\n\\\\t\\\\tconst accumulatedLines = accumulatedContent.split(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\t\\\\tif (!isFinal) {\\\\r\\\\n\\\\t\\\\t\\\\taccumulatedLines.pop() // remove the last partial line only if it's not the final update\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst diffLines = accumulatedLines.slice(this.streamedLines.length)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst diffEditor = this.activeDiffEditor\\\\r\\\\n\\\\t\\\\tconst document = diffEditor?.document\\\\r\\\\n\\\\t\\\\tif (!diffEditor || !document) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"User closed text editor, unable to edit file...\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Place cursor at the beginning of the diff editor to keep it out of the way of the stream animation\\\\r\\\\n\\\\t\\\\tconst beginningOfDocument = new vscode.Position(0, 0)\\\\r\\\\n\\\\t\\\\tdiffEditor.selection = new vscode.Selection(beginningOfDocument, beginningOfDocument)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tfor (let i = 0; i < diffLines.length; i++) {\\\\r\\\\n\\\\t\\\\t\\\\tconst currentLine = this.streamedLines.length + i\\\\r\\\\n\\\\t\\\\t\\\\t// Replace all content up to the current line with accumulated lines\\\\r\\\\n\\\\t\\\\t\\\\t// This is necessary (as compared to inserting one line at a time) to handle cases where html tags on previous lines are auto closed for example\\\\r\\\\n\\\\t\\\\t\\\\tconst edit = new vscode.WorkspaceEdit()\\\\r\\\\n\\\\t\\\\t\\\\tconst rangeToReplace = new vscode.Range(0, 0, currentLine + 1, 0)\\\\r\\\\n\\\\t\\\\t\\\\tconst contentToReplace = accumulatedLines.slice(0, currentLine + 1).join(\\\\\\\"\\\\\\\\n\\\\\\\") + \\\\\\\"\\\\\\\\n\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tedit.replace(document.uri, rangeToReplace, contentToReplace)\\\\r\\\\n\\\\t\\\\t\\\\tawait vscode.workspace.applyEdit(edit)\\\\r\\\\n\\\\t\\\\t\\\\t// Update decorations\\\\r\\\\n\\\\t\\\\t\\\\tthis.activeLineController.setActiveLine(currentLine)\\\\r\\\\n\\\\t\\\\t\\\\tthis.fadedOverlayController.updateOverlayAfterLine(currentLine, document.lineCount)\\\\r\\\\n\\\\t\\\\t\\\\t// Scroll to the current line\\\\r\\\\n\\\\t\\\\t\\\\tthis.scrollEditorToLine(currentLine)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t// Update the streamedLines with the new accumulated content\\\\r\\\\n\\\\t\\\\tthis.streamedLines = accumulatedLines\\\\r\\\\n\\\\t\\\\tif (isFinal) {\\\\r\\\\n\\\\t\\\\t\\\\t// Handle any remaining lines if the new content is shorter than the original\\\\r\\\\n\\\\t\\\\t\\\\tif (this.streamedLines.length < document.lineCount) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst edit = new vscode.WorkspaceEdit()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tedit.delete(document.uri, new vscode.Range(this.streamedLines.length, 0, document.lineCount, 0))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait vscode.workspace.applyEdit(edit)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t// Add empty last line if original content had one\\\\r\\\\n\\\\t\\\\t\\\\tconst hasEmptyLastLine = this.originalContent?.endsWith(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tif (hasEmptyLastLine) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst accumulatedLines = accumulatedContent.split(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (accumulatedLines[accumulatedLines.length - 1] !== \\\\\\\"\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\taccumulatedContent += \\\\\\\"\\\\\\\\n\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t// Clear all decorations at the end (before applying final edit)\\\\r\\\\n\\\\t\\\\t\\\\tthis.fadedOverlayController.clear()\\\\r\\\\n\\\\t\\\\t\\\\tthis.activeLineController.clear()\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync saveChanges(): Promise<{\\\\r\\\\n\\\\t\\\\tnewProblemsMessage: string | undefined\\\\r\\\\n\\\\t\\\\tuserEdits: string | undefined\\\\r\\\\n\\\\t\\\\tautoFormattingEdits: string | undefined\\\\r\\\\n\\\\t\\\\tfinalContent: string | undefined\\\\r\\\\n\\\\t}> {\\\\r\\\\n\\\\t\\\\tif (!this.relPath || !this.newContent || !this.activeDiffEditor) {\\\\r\\\\n\\\\t\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tnewProblemsMessage: undefined,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tuserEdits: undefined,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tautoFormattingEdits: undefined,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tfinalContent: undefined,\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst absolutePath = path.resolve(this.cwd, this.relPath)\\\\r\\\\n\\\\t\\\\tconst updatedDocument = this.activeDiffEditor.document\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// get the contents before save operation which may do auto-formatting\\\\r\\\\n\\\\t\\\\tconst preSaveContent = updatedDocument.getText()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (updatedDocument.isDirty) {\\\\r\\\\n\\\\t\\\\t\\\\tawait updatedDocument.save()\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// await delay(100)\\\\r\\\\n\\\\t\\\\t// get text after save in case there is any auto-formatting done by the editor\\\\r\\\\n\\\\t\\\\tconst postSaveContent = updatedDocument.getText()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tawait vscode.window.showTextDocument(vscode.Uri.file(absolutePath), {\\\\r\\\\n\\\\t\\\\t\\\\tpreview: false,\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\tawait this.closeAllDiffViews()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t/*\\\\r\\\\n\\\\t\\\\tGetting diagnostics before and after the file edit is a better approach than\\\\r\\\\n\\\\t\\\\tautomatically tracking problems in real-time. This method ensures we only\\\\r\\\\n\\\\t\\\\treport new problems that are a direct result of this specific edit.\\\\r\\\\n\\\\t\\\\tSince these are new problems resulting from Cline's edit, we know they're\\\\r\\\\n\\\\t\\\\tdirectly related to the work he's doing. This eliminates the risk of Cline\\\\r\\\\n\\\\t\\\\tgoing off-task or getting distracted by unrelated issues, which was a problem\\\\r\\\\n\\\\t\\\\twith the previous auto-debug approach. Some users' machines may be slow to\\\\r\\\\n\\\\t\\\\tupdate diagnostics, so this approach provides a good balance between automation\\\\r\\\\n\\\\t\\\\tand avoiding potential issues where Cline might get stuck in loops due to\\\\r\\\\n\\\\t\\\\toutdated problem information. If no new problems show up by the time the user\\\\r\\\\n\\\\t\\\\taccepts the changes, they can always debug later using the '@problems' mention.\\\\r\\\\n\\\\t\\\\tThis way, Cline only becomes aware of new problems resulting from his edits\\\\r\\\\n\\\\t\\\\tand can address them accordingly. If problems don't change immediately after\\\\r\\\\n\\\\t\\\\tapplying a fix, Cline won't be notified, which is generally fine since the\\\\r\\\\n\\\\t\\\\tinitial fix is usually correct and it may just take time for linters to catch up.\\\\r\\\\n\\\\t\\\\t*/\\\\r\\\\n\\\\t\\\\tconst postDiagnostics = vscode.languages.getDiagnostics()\\\\r\\\\n\\\\t\\\\tconst newProblems = diagnosticsToProblemsString(\\\\r\\\\n\\\\t\\\\t\\\\tgetNewDiagnostics(this.preDiagnostics, postDiagnostics),\\\\r\\\\n\\\\t\\\\t\\\\t[\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tvscode.DiagnosticSeverity.Error, // only including errors since warnings can be distracting (if user wants to fix warnings they can use the @problems mention)\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\tthis.cwd,\\\\r\\\\n\\\\t\\\\t) // will be empty string if no errors\\\\r\\\\n\\\\t\\\\tconst newProblemsMessage =\\\\r\\\\n\\\\t\\\\t\\\\tnewProblems.length > 0 ? `\\\\\\\\n\\\\\\\\nNew problems detected after saving the file:\\\\\\\\n${newProblems}` : \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// If the edited content has different EOL characters, we don't want to show a diff with all the EOL differences.\\\\r\\\\n\\\\t\\\\tconst newContentEOL = this.newContent.includes(\\\\\\\"\\\\\\\\r\\\\\\\\n\\\\\\\") ? \\\\\\\"\\\\\\\\r\\\\\\\\n\\\\\\\" : \\\\\\\"\\\\\\\\n\\\\\\\"\\\\r\\\\n\\\\t\\\\tconst normalizedPreSaveContent = preSaveContent.replace(/\\\\\\\\r\\\\\\\\n|\\\\\\\\n/g, newContentEOL).trimEnd() + newContentEOL // trimEnd to fix issue where editor adds in extra new line automatically\\\\r\\\\n\\\\t\\\\tconst normalizedPostSaveContent = postSaveContent.replace(/\\\\\\\\r\\\\\\\\n|\\\\\\\\n/g, newContentEOL).trimEnd() + newContentEOL // this is the final content we return to the model to use as the new baseline for future edits\\\\r\\\\n\\\\t\\\\t// just in case the new content has a mix of varying EOL characters\\\\r\\\\n\\\\t\\\\tconst normalizedNewContent = this.newContent.replace(/\\\\\\\\r\\\\\\\\n|\\\\\\\\n/g, newContentEOL).trimEnd() + newContentEOL\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet userEdits: string | undefined\\\\r\\\\n\\\\t\\\\tif (normalizedPreSaveContent !== normalizedNewContent) {\\\\r\\\\n\\\\t\\\\t\\\\t// user made changes before approving edit. let the model know about user made changes (not including post-save auto-formatting changes)\\\\r\\\\n\\\\t\\\\t\\\\tuserEdits = formatResponse.createPrettyPatch(this.relPath.toPosix(), normalizedNewContent, normalizedPreSaveContent)\\\\r\\\\n\\\\t\\\\t\\\\t// return { newProblemsMessage, userEdits, finalContent: normalizedPostSaveContent }\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t// no changes to cline's edits\\\\r\\\\n\\\\t\\\\t\\\\t// return { newProblemsMessage, userEdits: undefined, finalContent: normalizedPostSaveContent }\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet autoFormattingEdits: string | undefined\\\\r\\\\n\\\\t\\\\tif (normalizedPreSaveContent !== normalizedPostSaveContent) {\\\\r\\\\n\\\\t\\\\t\\\\t// auto-formatting was done by the editor\\\\r\\\\n\\\\t\\\\t\\\\tautoFormattingEdits = formatResponse.createPrettyPatch(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.relPath.toPosix(),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tnormalizedPreSaveContent,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tnormalizedPostSaveContent,\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\tnewProblemsMessage,\\\\r\\\\n\\\\t\\\\t\\\\tuserEdits,\\\\r\\\\n\\\\t\\\\t\\\\tautoFormattingEdits,\\\\r\\\\n\\\\t\\\\t\\\\tfinalContent: normalizedPostSaveContent,\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync revertChanges(): Promise<void> {\\\\r\\\\n\\\\t\\\\tif (!this.relPath || !this.activeDiffEditor) {\\\\r\\\\n\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst fileExists = this.editType === \\\\\\\"modify\\\\\\\"\\\\r\\\\n\\\\t\\\\tconst updatedDocument = this.activeDiffEditor.document\\\\r\\\\n\\\\t\\\\tconst absolutePath = path.resolve(this.cwd, this.relPath)\\\\r\\\\n\\\\t\\\\tif (!fileExists) {\\\\r\\\\n\\\\t\\\\t\\\\tif (updatedDocument.isDirty) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait updatedDocument.save()\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tawait this.closeAllDiffViews()\\\\r\\\\n\\\\t\\\\t\\\\tawait fs.unlink(absolutePath)\\\\r\\\\n\\\\t\\\\t\\\\t// Remove only the directories we created, in reverse order\\\\r\\\\n\\\\t\\\\t\\\\tfor (let i = this.createdDirs.length - 1; i >= 0; i--) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait fs.rmdir(this.createdDirs[i])\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.log(`Directory ${this.createdDirs[i]} has been deleted.`)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tconsole.log(`File ${absolutePath} has been deleted.`)\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t// revert document\\\\r\\\\n\\\\t\\\\t\\\\tconst edit = new vscode.WorkspaceEdit()\\\\r\\\\n\\\\t\\\\t\\\\tconst fullRange = new vscode.Range(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tupdatedDocument.positionAt(0),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tupdatedDocument.positionAt(updatedDocument.getText().length),\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\tedit.replace(updatedDocument.uri, fullRange, this.originalContent ?? \\\\\\\"\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t// Apply the edit and save, since contents shouldnt have changed this wont show in local history unless of course the user made changes and saved during the edit\\\\r\\\\n\\\\t\\\\t\\\\tawait vscode.workspace.applyEdit(edit)\\\\r\\\\n\\\\t\\\\t\\\\tawait updatedDocument.save()\\\\r\\\\n\\\\t\\\\t\\\\tconsole.log(`File ${absolutePath} has been reverted to its original content.`)\\\\r\\\\n\\\\t\\\\t\\\\tif (this.documentWasOpen) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait vscode.window.showTextDocument(vscode.Uri.file(absolutePath), {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tpreview: false,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tawait this.closeAllDiffViews()\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// edit is done\\\\r\\\\n\\\\t\\\\tawait this.reset()\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async closeAllDiffViews() {\\\\r\\\\n\\\\t\\\\tconst tabs = vscode.window.tabGroups.all\\\\r\\\\n\\\\t\\\\t\\\\t.flatMap((tg) => tg.tabs)\\\\r\\\\n\\\\t\\\\t\\\\t.filter((tab) => tab.input instanceof vscode.TabInputTextDiff && tab.input?.original?.scheme === DIFF_VIEW_URI_SCHEME)\\\\r\\\\n\\\\t\\\\tfor (const tab of tabs) {\\\\r\\\\n\\\\t\\\\t\\\\t// trying to close dirty views results in save popup\\\\r\\\\n\\\\t\\\\t\\\\tif (!tab.isDirty) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait vscode.window.tabGroups.close(tab)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async openDiffEditor(): Promise<vscode.TextEditor> {\\\\r\\\\n\\\\t\\\\tif (!this.relPath) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"No file path set\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst uri = vscode.Uri.file(path.resolve(this.cwd, this.relPath))\\\\r\\\\n\\\\t\\\\t// If this diff editor is already open (ie if a previous write file was interrupted) then we should activate that instead of opening a new diff\\\\r\\\\n\\\\t\\\\tconst diffTab = vscode.window.tabGroups.all\\\\r\\\\n\\\\t\\\\t\\\\t.flatMap((group) => group.tabs)\\\\r\\\\n\\\\t\\\\t\\\\t.find(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t(tab) =>\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttab.input instanceof vscode.TabInputTextDiff &&\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttab.input?.original?.scheme === DIFF_VIEW_URI_SCHEME &&\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tarePathsEqual(tab.input.modified.fsPath, uri.fsPath),\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\tif (diffTab && diffTab.input instanceof vscode.TabInputTextDiff) {\\\\r\\\\n\\\\t\\\\t\\\\tconst editor = await vscode.window.showTextDocument(diffTab.input.modified)\\\\r\\\\n\\\\t\\\\t\\\\treturn editor\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t// Open new diff editor\\\\r\\\\n\\\\t\\\\treturn new Promise<vscode.TextEditor>((resolve, reject) => {\\\\r\\\\n\\\\t\\\\t\\\\tconst fileName = path.basename(uri.fsPath)\\\\r\\\\n\\\\t\\\\t\\\\tconst fileExists = this.editType === \\\\\\\"modify\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tconst disposable = vscode.window.onDidChangeActiveTextEditor((editor) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (editor && arePathsEqual(editor.document.uri.fsPath, uri.fsPath)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tdisposable.dispose()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tresolve(editor)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\tvscode.commands.executeCommand(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"vscode.diff\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tvscode.Uri.parse(`${DIFF_VIEW_URI_SCHEME}:${fileName}`).with({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tquery: Buffer.from(this.originalContent ?? \\\\\\\"\\\\\\\").toString(\\\\\\\"base64\\\\\\\"),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\turi,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t`${fileName}: ${fileExists ? \\\\\\\"Original ↔ Cline's Changes\\\\\\\" : \\\\\\\"New File\\\\\\\"} (Editable)`,\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t// This may happen on very slow machines ie project idx\\\\r\\\\n\\\\t\\\\t\\\\tsetTimeout(() => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tdisposable.dispose()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treject(new Error(\\\\\\\"Failed to open diff editor, please try again...\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\t}, 10_000)\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate scrollEditorToLine(line: number) {\\\\r\\\\n\\\\t\\\\tif (this.activeDiffEditor) {\\\\r\\\\n\\\\t\\\\t\\\\tconst scrollLine = line + 4\\\\r\\\\n\\\\t\\\\t\\\\tthis.activeDiffEditor.revealRange(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tnew vscode.Range(scrollLine, 0, scrollLine, 0),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tvscode.TextEditorRevealType.InCenter,\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tscrollToFirstDiff() {\\\\r\\\\n\\\\t\\\\tif (!this.activeDiffEditor) {\\\\r\\\\n\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst currentContent = this.activeDiffEditor.document.getText()\\\\r\\\\n\\\\t\\\\tconst diffs = diff.diffLines(this.originalContent || \\\\\\\"\\\\\\\", currentContent)\\\\r\\\\n\\\\t\\\\tlet lineCount = 0\\\\r\\\\n\\\\t\\\\tfor (const part of diffs) {\\\\r\\\\n\\\\t\\\\t\\\\tif (part.added || part.removed) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Found the first diff, scroll to it\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.activeDiffEditor.revealRange(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tnew vscode.Range(lineCount, 0, lineCount, 0),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tvscode.TextEditorRevealType.InCenter,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tif (!part.removed) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlineCount += part.count || 0\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// close editor if open?\\\\r\\\\n\\\\tasync reset() {\\\\r\\\\n\\\\t\\\\tthis.editType = undefined\\\\r\\\\n\\\\t\\\\tthis.isEditing = false\\\\r\\\\n\\\\t\\\\tthis.originalContent = undefined\\\\r\\\\n\\\\t\\\\tthis.createdDirs = []\\\\r\\\\n\\\\t\\\\tthis.documentWasOpen = false\\\\r\\\\n\\\\t\\\\tthis.activeDiffEditor = undefined\\\\r\\\\n\\\\t\\\\tthis.fadedOverlayController = undefined\\\\r\\\\n\\\\t\\\\tthis.activeLineController = undefined\\\\r\\\\n\\\\t\\\\tthis.streamedLines = []\\\\r\\\\n\\\\t\\\\tthis.preDiagnostics = []\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":15618,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.956Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.956Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":378,\\\"nonEmptyLines\\\":350,\\\"commentLines\\\":37,\\\"complexity\\\":76},\\\"dependencies\\\":[\\\"vscode\\\",\\\"path\\\",\\\"fs/promises\\\",\\\"../../utils/fs\\\",\\\"../../utils/path\\\",\\\"../../core/prompts/responses\\\",\\\"./DecorationController\\\",\\\"diff\\\",\\\"../diagnostics\\\"],\\\"quality\\\":{\\\"score\\\":-356,\\\"issues\\\":[],\\\"duplicateLines\\\":84,\\\"longLines\\\":18,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.363Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":378},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\misc\\\\\\\\export-markdown.ts\\\":{\\\"content\\\":\\\"import { Anthropic } from \\\\\\\"@anthropic-ai/sdk\\\\\\\"\\\\r\\\\nimport os from \\\\\\\"os\\\\\\\"\\\\r\\\\nimport * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport async function downloadTask(dateTs: number, conversationHistory: Anthropic.MessageParam[]) {\\\\r\\\\n\\\\t// File name\\\\r\\\\n\\\\tconst date = new Date(dateTs)\\\\r\\\\n\\\\tconst month = date.toLocaleString(\\\\\\\"en-US\\\\\\\", { month: \\\\\\\"short\\\\\\\" }).toLowerCase()\\\\r\\\\n\\\\tconst day = date.getDate()\\\\r\\\\n\\\\tconst year = date.getFullYear()\\\\r\\\\n\\\\tlet hours = date.getHours()\\\\r\\\\n\\\\tconst minutes = date.getMinutes().toString().padStart(2, \\\\\\\"0\\\\\\\")\\\\r\\\\n\\\\tconst seconds = date.getSeconds().toString().padStart(2, \\\\\\\"0\\\\\\\")\\\\r\\\\n\\\\tconst ampm = hours >= 12 ? \\\\\\\"pm\\\\\\\" : \\\\\\\"am\\\\\\\"\\\\r\\\\n\\\\thours = hours % 12\\\\r\\\\n\\\\thours = hours ? hours : 12 // the hour '0' should be '12'\\\\r\\\\n\\\\tconst fileName = `cline_task_${month}-${day}-${year}_${hours}-${minutes}-${seconds}-${ampm}.md`\\\\r\\\\n\\\\r\\\\n\\\\t// Generate markdown\\\\r\\\\n\\\\tconst markdownContent = conversationHistory\\\\r\\\\n\\\\t\\\\t.map((message) => {\\\\r\\\\n\\\\t\\\\t\\\\tconst role = message.role === \\\\\\\"user\\\\\\\" ? \\\\\\\"**User:**\\\\\\\" : \\\\\\\"**Assistant:**\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tconst content = Array.isArray(message.content)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t? message.content.map((block) => formatContentBlockToMarkdown(block)).join(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t: message.content\\\\r\\\\n\\\\t\\\\t\\\\treturn `${role}\\\\\\\\n\\\\\\\\n${content}\\\\\\\\n\\\\\\\\n`\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t.join(\\\\\\\"---\\\\\\\\n\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\t// Prompt user for save location\\\\r\\\\n\\\\tconst saveUri = await vscode.window.showSaveDialog({\\\\r\\\\n\\\\t\\\\tfilters: { Markdown: [\\\\\\\"md\\\\\\\"] },\\\\r\\\\n\\\\t\\\\tdefaultUri: vscode.Uri.file(path.join(os.homedir(), \\\\\\\"Downloads\\\\\\\", fileName)),\\\\r\\\\n\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\tif (saveUri) {\\\\r\\\\n\\\\t\\\\t// Write content to the selected location\\\\r\\\\n\\\\t\\\\tawait vscode.workspace.fs.writeFile(saveUri, Buffer.from(markdownContent))\\\\r\\\\n\\\\t\\\\tvscode.window.showTextDocument(saveUri, { preview: true })\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport function formatContentBlockToMarkdown(\\\\r\\\\n\\\\tblock: Anthropic.TextBlockParam | Anthropic.ImageBlockParam | Anthropic.ToolUseBlockParam | Anthropic.ToolResultBlockParam,\\\\r\\\\n\\\\t// messages: Anthropic.MessageParam[]\\\\r\\\\n): string {\\\\r\\\\n\\\\tswitch (block.type) {\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"text\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn block.text\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"image\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn `[Image]`\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"tool_use\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tlet input: string\\\\r\\\\n\\\\t\\\\t\\\\tif (typeof block.input === \\\\\\\"object\\\\\\\" && block.input !== null) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tinput = Object.entries(block.input)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t.map(([key, value]) => `${key.charAt(0).toUpperCase() + key.slice(1)}: ${value}`)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t.join(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tinput = String(block.input)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\treturn `[Tool Use: ${block.name}]\\\\\\\\n${input}`\\\\r\\\\n\\\\t\\\\tcase \\\\\\\"tool_result\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t// For now we're not doing tool name lookup since we don't use tools anymore\\\\r\\\\n\\\\t\\\\t\\\\t// const toolName = findToolName(block.tool_use_id, messages)\\\\r\\\\n\\\\t\\\\t\\\\tconst toolName = \\\\\\\"Tool\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tif (typeof block.content === \\\\\\\"string\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn `[${toolName}${block.is_error ? \\\\\\\" (Error)\\\\\\\" : \\\\\\\"\\\\\\\"}]\\\\\\\\n${block.content}`\\\\r\\\\n\\\\t\\\\t\\\\t} else if (Array.isArray(block.content)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn `[${toolName}${block.is_error ? \\\\\\\" (Error)\\\\\\\" : \\\\\\\"\\\\\\\"}]\\\\\\\\n${block.content\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t.map((contentBlock) => formatContentBlockToMarkdown(contentBlock))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t.join(\\\\\\\"\\\\\\\\n\\\\\\\")}`\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn `[${toolName}${block.is_error ? \\\\\\\" (Error)\\\\\\\" : \\\\\\\"\\\\\\\"}]`\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tdefault:\\\\r\\\\n\\\\t\\\\t\\\\treturn \\\\\\\"[Unexpected content type]\\\\\\\"\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport function findToolName(toolCallId: string, messages: Anthropic.MessageParam[]): string {\\\\r\\\\n\\\\tfor (const message of messages) {\\\\r\\\\n\\\\t\\\\tif (Array.isArray(message.content)) {\\\\r\\\\n\\\\t\\\\t\\\\tfor (const block of message.content) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (block.type === \\\\\\\"tool_use\\\\\\\" && block.id === toolCallId) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn block.name\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\treturn \\\\\\\"Unknown Tool\\\\\\\"\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":3341,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.957Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.957Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":93,\\\"nonEmptyLines\\\":86,\\\"commentLines\\\":7,\\\"complexity\\\":26},\\\"dependencies\\\":[\\\"@anthropic-ai/sdk\\\",\\\"os\\\",\\\"path\\\",\\\"vscode\\\"],\\\"quality\\\":{\\\"score\\\":38,\\\"issues\\\":[],\\\"duplicateLines\\\":12,\\\"longLines\\\":1,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.365Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":93},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\misc\\\\\\\\extract-text.ts\\\":{\\\"content\\\":\\\"import * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\n// @ts-ignore-next-line\\\\r\\\\nimport pdf from \\\\\\\"pdf-parse/lib/pdf-parse\\\\\\\"\\\\r\\\\nimport mammoth from \\\\\\\"mammoth\\\\\\\"\\\\r\\\\nimport fs from \\\\\\\"fs/promises\\\\\\\"\\\\r\\\\nimport { isBinaryFile } from \\\\\\\"isbinaryfile\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport async function extractTextFromFile(filePath: string): Promise<string> {\\\\r\\\\n\\\\ttry {\\\\r\\\\n\\\\t\\\\tawait fs.access(filePath)\\\\r\\\\n\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\tthrow new Error(`File not found: ${filePath}`)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\tconst fileExtension = path.extname(filePath).toLowerCase()\\\\r\\\\n\\\\tswitch (fileExtension) {\\\\r\\\\n\\\\t\\\\tcase \\\\\\\".pdf\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn extractTextFromPDF(filePath)\\\\r\\\\n\\\\t\\\\tcase \\\\\\\".docx\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn extractTextFromDOCX(filePath)\\\\r\\\\n\\\\t\\\\tcase \\\\\\\".ipynb\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn extractTextFromIPYNB(filePath)\\\\r\\\\n\\\\t\\\\tdefault:\\\\r\\\\n\\\\t\\\\t\\\\tconst isBinary = await isBinaryFile(filePath).catch(() => false)\\\\r\\\\n\\\\t\\\\t\\\\tif (!isBinary) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn await fs.readFile(filePath, \\\\\\\"utf8\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthrow new Error(`Cannot read text for file type: ${fileExtension}`)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nasync function extractTextFromPDF(filePath: string): Promise<string> {\\\\r\\\\n\\\\tconst dataBuffer = await fs.readFile(filePath)\\\\r\\\\n\\\\tconst data = await pdf(dataBuffer)\\\\r\\\\n\\\\treturn data.text\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nasync function extractTextFromDOCX(filePath: string): Promise<string> {\\\\r\\\\n\\\\tconst result = await mammoth.extractRawText({ path: filePath })\\\\r\\\\n\\\\treturn result.value\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nasync function extractTextFromIPYNB(filePath: string): Promise<string> {\\\\r\\\\n\\\\tconst data = await fs.readFile(filePath, \\\\\\\"utf8\\\\\\\")\\\\r\\\\n\\\\tconst notebook = JSON.parse(data)\\\\r\\\\n\\\\tlet extractedText = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\r\\\\n\\\\tfor (const cell of notebook.cells) {\\\\r\\\\n\\\\t\\\\tif ((cell.cell_type === \\\\\\\"markdown\\\\\\\" || cell.cell_type === \\\\\\\"code\\\\\\\") && cell.source) {\\\\r\\\\n\\\\t\\\\t\\\\textractedText += cell.source.join(\\\\\\\"\\\\\\\\n\\\\\\\") + \\\\\\\"\\\\\\\\n\\\\\\\"\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\treturn extractedText\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":1659,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.846Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.846Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":56,\\\"nonEmptyLines\\\":49,\\\"commentLines\\\":1,\\\"complexity\\\":12},\\\"dependencies\\\":[\\\"path\\\",\\\"pdf-parse/lib/pdf-parse\\\",\\\"mammoth\\\",\\\"fs/promises\\\",\\\"isbinaryfile\\\"],\\\"quality\\\":{\\\"score\\\":60,\\\"issues\\\":[],\\\"duplicateLines\\\":8,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.366Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":56},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\misc\\\\\\\\open-file.ts\\\":{\\\"content\\\":\\\"import * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport * as os from \\\\\\\"os\\\\\\\"\\\\r\\\\nimport * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\nimport { arePathsEqual } from \\\\\\\"../../utils/path\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport async function openImage(dataUri: string) {\\\\r\\\\n\\\\tconst matches = dataUri.match(/^data:image\\\\\\\\/([a-zA-Z]+);base64,(.+)$/)\\\\r\\\\n\\\\tif (!matches) {\\\\r\\\\n\\\\t\\\\tvscode.window.showErrorMessage(\\\\\\\"Invalid data URI format\\\\\\\")\\\\r\\\\n\\\\t\\\\treturn\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\tconst [, format, base64Data] = matches\\\\r\\\\n\\\\tconst imageBuffer = Buffer.from(base64Data, \\\\\\\"base64\\\\\\\")\\\\r\\\\n\\\\tconst tempFilePath = path.join(os.tmpdir(), `temp_image_${Date.now()}.${format}`)\\\\r\\\\n\\\\ttry {\\\\r\\\\n\\\\t\\\\tawait vscode.workspace.fs.writeFile(vscode.Uri.file(tempFilePath), imageBuffer)\\\\r\\\\n\\\\t\\\\tawait vscode.commands.executeCommand(\\\\\\\"vscode.open\\\\\\\", vscode.Uri.file(tempFilePath))\\\\r\\\\n\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\tvscode.window.showErrorMessage(`Error opening image: ${error}`)\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport async function openFile(absolutePath: string) {\\\\r\\\\n\\\\ttry {\\\\r\\\\n\\\\t\\\\tconst uri = vscode.Uri.file(absolutePath)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Check if the document is already open in a tab group that's not in the active editor's column. If it is, then close it (if not dirty) so that we don't duplicate tabs\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tfor (const group of vscode.window.tabGroups.all) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst existingTab = group.tabs.find(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t(tab) => tab.input instanceof vscode.TabInputText && arePathsEqual(tab.input.uri.fsPath, uri.fsPath),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (existingTab) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst activeColumn = vscode.window.activeTextEditor?.viewColumn\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst tabColumn = vscode.window.tabGroups.all.find((group) => group.tabs.includes(existingTab))?.viewColumn\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (activeColumn && activeColumn !== tabColumn && !existingTab.isDirty) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait vscode.window.tabGroups.close(existingTab)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t} catch {} // not essential, sometimes tab operations fail\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst document = await vscode.workspace.openTextDocument(uri)\\\\r\\\\n\\\\t\\\\tawait vscode.window.showTextDocument(document, { preview: false })\\\\r\\\\n\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\tvscode.window.showErrorMessage(`Could not open file!`)\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":1979,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.957Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.957Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":50,\\\"nonEmptyLines\\\":45,\\\"commentLines\\\":1,\\\"complexity\\\":12},\\\"dependencies\\\":[\\\"path\\\",\\\"os\\\",\\\"vscode\\\",\\\"../../utils/path\\\"],\\\"quality\\\":{\\\"score\\\":44,\\\"issues\\\":[],\\\"duplicateLines\\\":10,\\\"longLines\\\":3,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.368Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":50},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\misc\\\\\\\\process-images.ts\\\":{\\\"content\\\":\\\"import * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\nimport fs from \\\\\\\"fs/promises\\\\\\\"\\\\r\\\\nimport * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport async function selectImages(): Promise<string[]> {\\\\r\\\\n\\\\tconst options: vscode.OpenDialogOptions = {\\\\r\\\\n\\\\t\\\\tcanSelectMany: true,\\\\r\\\\n\\\\t\\\\topenLabel: \\\\\\\"Select\\\\\\\",\\\\r\\\\n\\\\t\\\\tfilters: {\\\\r\\\\n\\\\t\\\\t\\\\tImages: [\\\\\\\"png\\\\\\\", \\\\\\\"jpg\\\\\\\", \\\\\\\"jpeg\\\\\\\", \\\\\\\"webp\\\\\\\"], // supported by anthropic and openrouter\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tconst fileUris = await vscode.window.showOpenDialog(options)\\\\r\\\\n\\\\r\\\\n\\\\tif (!fileUris || fileUris.length === 0) {\\\\r\\\\n\\\\t\\\\treturn []\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\treturn await Promise.all(\\\\r\\\\n\\\\t\\\\tfileUris.map(async (uri) => {\\\\r\\\\n\\\\t\\\\t\\\\tconst imagePath = uri.fsPath\\\\r\\\\n\\\\t\\\\t\\\\tconst buffer = await fs.readFile(imagePath)\\\\r\\\\n\\\\t\\\\t\\\\tconst base64 = buffer.toString(\\\\\\\"base64\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tconst mimeType = getMimeType(imagePath)\\\\r\\\\n\\\\t\\\\t\\\\tconst dataUrl = `data:${mimeType};base64,${base64}`\\\\r\\\\n\\\\t\\\\t\\\\treturn dataUrl\\\\r\\\\n\\\\t\\\\t}),\\\\r\\\\n\\\\t)\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nfunction getMimeType(filePath: string): string {\\\\r\\\\n\\\\tconst ext = path.extname(filePath).toLowerCase()\\\\r\\\\n\\\\tswitch (ext) {\\\\r\\\\n\\\\t\\\\tcase \\\\\\\".png\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn \\\\\\\"image/png\\\\\\\"\\\\r\\\\n\\\\t\\\\tcase \\\\\\\".jpeg\\\\\\\":\\\\r\\\\n\\\\t\\\\tcase \\\\\\\".jpg\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn \\\\\\\"image/jpeg\\\\\\\"\\\\r\\\\n\\\\t\\\\tcase \\\\\\\".webp\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\treturn \\\\\\\"image/webp\\\\\\\"\\\\r\\\\n\\\\t\\\\tdefault:\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(`Unsupported file type: ${ext}`)\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":1137,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.846Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.846Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":46,\\\"nonEmptyLines\\\":40,\\\"commentLines\\\":0,\\\"complexity\\\":7},\\\"dependencies\\\":[\\\"vscode\\\",\\\"fs/promises\\\",\\\"path\\\"],\\\"quality\\\":{\\\"score\\\":80,\\\"issues\\\":[],\\\"duplicateLines\\\":4,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.369Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":46},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\notifications\\\\\\\\index.ts\\\":{\\\"content\\\":\\\"import { execa } from \\\\\\\"execa\\\\\\\"\\\\r\\\\nimport { platform } from \\\\\\\"os\\\\\\\"\\\\r\\\\n\\\\r\\\\ninterface NotificationOptions {\\\\r\\\\n\\\\ttitle?: string\\\\r\\\\n\\\\tsubtitle?: string\\\\r\\\\n\\\\tmessage: string\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nasync function showMacOSNotification(options: NotificationOptions): Promise<void> {\\\\r\\\\n\\\\tconst { title, subtitle = \\\\\\\"\\\\\\\", message } = options\\\\r\\\\n\\\\r\\\\n\\\\tconst script = `display notification \\\\\\\"${message}\\\\\\\" with title \\\\\\\"${title}\\\\\\\" subtitle \\\\\\\"${subtitle}\\\\\\\" sound name \\\\\\\"Tink\\\\\\\"`\\\\r\\\\n\\\\r\\\\n\\\\ttry {\\\\r\\\\n\\\\t\\\\tawait execa(\\\\\\\"osascript\\\\\\\", [\\\\\\\"-e\\\\\\\", script])\\\\r\\\\n\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\tthrow new Error(`Failed to show macOS notification: ${error}`)\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nasync function showWindowsNotification(options: NotificationOptions): Promise<void> {\\\\r\\\\n\\\\tconst { subtitle, message } = options\\\\r\\\\n\\\\r\\\\n\\\\tconst script = `\\\\r\\\\n [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null\\\\r\\\\n [Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] | Out-Null\\\\r\\\\n\\\\r\\\\n $template = @\\\\\\\"\\\\r\\\\n <toast>\\\\r\\\\n <visual>\\\\r\\\\n <binding template=\\\\\\\"ToastText02\\\\\\\">\\\\r\\\\n <text id=\\\\\\\"1\\\\\\\">${subtitle}</text>\\\\r\\\\n <text id=\\\\\\\"2\\\\\\\">${message}</text>\\\\r\\\\n </binding>\\\\r\\\\n </visual>\\\\r\\\\n </toast>\\\\r\\\\n\\\\\\\"@\\\\r\\\\n\\\\r\\\\n $xml = New-Object Windows.Data.Xml.Dom.XmlDocument\\\\r\\\\n $xml.LoadXml($template)\\\\r\\\\n $toast = [Windows.UI.Notifications.ToastNotification]::new($xml)\\\\r\\\\n [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier(\\\\\\\"Cline\\\\\\\").Show($toast)\\\\r\\\\n `\\\\r\\\\n\\\\r\\\\n\\\\ttry {\\\\r\\\\n\\\\t\\\\tawait execa(\\\\\\\"powershell\\\\\\\", [\\\\\\\"-Command\\\\\\\", script])\\\\r\\\\n\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\tthrow new Error(`Failed to show Windows notification: ${error}`)\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nasync function showLinuxNotification(options: NotificationOptions): Promise<void> {\\\\r\\\\n\\\\tconst { title = \\\\\\\"\\\\\\\", subtitle = \\\\\\\"\\\\\\\", message } = options\\\\r\\\\n\\\\r\\\\n\\\\t// Combine subtitle and message if subtitle exists\\\\r\\\\n\\\\tconst fullMessage = subtitle ? `${subtitle}\\\\\\\\n${message}` : message\\\\r\\\\n\\\\r\\\\n\\\\ttry {\\\\r\\\\n\\\\t\\\\tawait execa(\\\\\\\"notify-send\\\\\\\", [title, fullMessage])\\\\r\\\\n\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\tthrow new Error(`Failed to show Linux notification: ${error}`)\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport async function showSystemNotification(options: NotificationOptions): Promise<void> {\\\\r\\\\n\\\\ttry {\\\\r\\\\n\\\\t\\\\tconst { title = \\\\\\\"Cline\\\\\\\", message } = options\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (!message) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Message is required\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst escapedOptions = {\\\\r\\\\n\\\\t\\\\t\\\\t...options,\\\\r\\\\n\\\\t\\\\t\\\\ttitle: title.replace(/\\\\\\\"/g, '\\\\\\\\\\\\\\\\\\\\\\\"'),\\\\r\\\\n\\\\t\\\\t\\\\tmessage: message.replace(/\\\\\\\"/g, '\\\\\\\\\\\\\\\\\\\\\\\"'),\\\\r\\\\n\\\\t\\\\t\\\\tsubtitle: options.subtitle?.replace(/\\\\\\\"/g, '\\\\\\\\\\\\\\\\\\\\\\\"') || \\\\\\\"\\\\\\\",\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tswitch (platform()) {\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"darwin\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait showMacOSNotification(escapedOptions)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"win32\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait showWindowsNotification(escapedOptions)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"linux\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait showLinuxNotification(escapedOptions)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\tdefault:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Unsupported platform\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\tconsole.error(\\\\\\\"Could not show system notification\\\\\\\", error)\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":2877,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.958Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.958Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":98,\\\"nonEmptyLines\\\":81,\\\"commentLines\\\":1,\\\"complexity\\\":14},\\\"dependencies\\\":[\\\"execa\\\",\\\"os\\\"],\\\"quality\\\":{\\\"score\\\":-1,\\\"issues\\\":[],\\\"duplicateLines\\\":19,\\\"longLines\\\":3,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.371Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":98},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\terminal\\\\\\\\TerminalManager.ts\\\":{\\\"content\\\":\\\"import pWaitFor from \\\\\\\"p-wait-for\\\\\\\"\\\\r\\\\nimport * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\nimport { arePathsEqual } from \\\\\\\"../../utils/path\\\\\\\"\\\\r\\\\nimport { mergePromise, TerminalProcess, TerminalProcessResultPromise } from \\\\\\\"./TerminalProcess\\\\\\\"\\\\r\\\\nimport { TerminalInfo, TerminalRegistry } from \\\\\\\"./TerminalRegistry\\\\\\\"\\\\r\\\\n\\\\r\\\\n/*\\\\r\\\\nTerminalManager:\\\\r\\\\n- Creates/reuses terminals\\\\r\\\\n- Runs commands via runCommand(), returning a TerminalProcess\\\\r\\\\n- Handles shell integration events\\\\r\\\\n\\\\r\\\\nTerminalProcess extends EventEmitter and implements Promise:\\\\r\\\\n- Emits 'line' events with output while promise is pending\\\\r\\\\n- process.continue() resolves promise and stops event emission\\\\r\\\\n- Allows real-time output handling or background execution\\\\r\\\\n\\\\r\\\\ngetUnretrievedOutput() fetches latest output for ongoing commands\\\\r\\\\n\\\\r\\\\nEnables flexible command execution:\\\\r\\\\n- Await for completion\\\\r\\\\n- Listen to real-time events\\\\r\\\\n- Continue execution in background\\\\r\\\\n- Retrieve missed output later\\\\r\\\\n\\\\r\\\\nNotes:\\\\r\\\\n- it turns out some shellIntegration APIs are available on cursor, although not on older versions of vscode\\\\r\\\\n- \\\\\\\"By default, the shell integration script should automatically activate on supported shells launched from VS Code.\\\\\\\"\\\\r\\\\nSupported shells:\\\\r\\\\nLinux/macOS: bash, fish, pwsh, zsh\\\\r\\\\nWindows: pwsh\\\\r\\\\n\\\\r\\\\n\\\\r\\\\nExample:\\\\r\\\\n\\\\r\\\\nconst terminalManager = new TerminalManager(context);\\\\r\\\\n\\\\r\\\\n// Run a command\\\\r\\\\nconst process = terminalManager.runCommand('npm install', '/path/to/project');\\\\r\\\\n\\\\r\\\\nprocess.on('line', (line) => {\\\\r\\\\n console.log(line);\\\\r\\\\n});\\\\r\\\\n\\\\r\\\\n// To wait for the process to complete naturally:\\\\r\\\\nawait process;\\\\r\\\\n\\\\r\\\\n// Or to continue execution even if the command is still running:\\\\r\\\\nprocess.continue();\\\\r\\\\n\\\\r\\\\n// Later, if you need to get the unretrieved output:\\\\r\\\\nconst unretrievedOutput = terminalManager.getUnretrievedOutput(terminalId);\\\\r\\\\nconsole.log('Unretrieved output:', unretrievedOutput);\\\\r\\\\n\\\\r\\\\nResources:\\\\r\\\\n- https://github.com/microsoft/vscode/issues/226655\\\\r\\\\n- https://code.visualstudio.com/updates/v1_93#_terminal-shell-integration-api\\\\r\\\\n- https://code.visualstudio.com/docs/terminal/shell-integration\\\\r\\\\n- https://code.visualstudio.com/api/references/vscode-api#Terminal\\\\r\\\\n- https://github.com/microsoft/vscode-extension-samples/blob/main/terminal-sample/src/extension.ts\\\\r\\\\n- https://github.com/microsoft/vscode-extension-samples/blob/main/shell-integration-sample/src/extension.ts\\\\r\\\\n*/\\\\r\\\\n\\\\r\\\\n/*\\\\r\\\\nThe new shellIntegration API gives us access to terminal command execution output handling.\\\\r\\\\nHowever, we don't update our VSCode type definitions or engine requirements to maintain compatibility\\\\r\\\\nwith older VSCode versions. Users on older versions will automatically fall back to using sendText\\\\r\\\\nfor terminal command execution.\\\\r\\\\nInterestingly, some environments like Cursor enable these APIs even without the latest VSCode engine.\\\\r\\\\nThis approach allows us to leverage advanced features when available while ensuring broad compatibility.\\\\r\\\\n*/\\\\r\\\\ndeclare module \\\\\\\"vscode\\\\\\\" {\\\\r\\\\n\\\\t// https://github.com/microsoft/vscode/blob/f0417069c62e20f3667506f4b7e53ca0004b4e3e/src/vscode-dts/vscode.d.ts#L7442\\\\r\\\\n\\\\tinterface Terminal {\\\\r\\\\n\\\\t\\\\tshellIntegration?: {\\\\r\\\\n\\\\t\\\\t\\\\tcwd?: vscode.Uri\\\\r\\\\n\\\\t\\\\t\\\\texecuteCommand?: (command: string) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tread: () => AsyncIterable<string>\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\t// https://github.com/microsoft/vscode/blob/f0417069c62e20f3667506f4b7e53ca0004b4e3e/src/vscode-dts/vscode.d.ts#L10794\\\\r\\\\n\\\\tinterface Window {\\\\r\\\\n\\\\t\\\\tonDidStartTerminalShellExecution?: (\\\\r\\\\n\\\\t\\\\t\\\\tlistener: (e: any) => any,\\\\r\\\\n\\\\t\\\\t\\\\tthisArgs?: any,\\\\r\\\\n\\\\t\\\\t\\\\tdisposables?: vscode.Disposable[],\\\\r\\\\n\\\\t\\\\t) => vscode.Disposable\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport class TerminalManager {\\\\r\\\\n\\\\tprivate terminalIds: Set<number> = new Set()\\\\r\\\\n\\\\tprivate processes: Map<number, TerminalProcess> = new Map()\\\\r\\\\n\\\\tprivate disposables: vscode.Disposable[] = []\\\\r\\\\n\\\\r\\\\n\\\\tconstructor() {\\\\r\\\\n\\\\t\\\\tlet disposable: vscode.Disposable | undefined\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tdisposable = (vscode.window as vscode.Window).onDidStartTerminalShellExecution?.(async (e) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Creating a read stream here results in a more consistent output. This is most obvious when running the `date` command.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\te?.execution?.read()\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t// console.error(\\\\\\\"Error setting up onDidEndTerminalShellExecution\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tif (disposable) {\\\\r\\\\n\\\\t\\\\t\\\\tthis.disposables.push(disposable)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\trunCommand(terminalInfo: TerminalInfo, command: string): TerminalProcessResultPromise {\\\\r\\\\n\\\\t\\\\tterminalInfo.busy = true\\\\r\\\\n\\\\t\\\\tterminalInfo.lastCommand = command\\\\r\\\\n\\\\t\\\\tconst process = new TerminalProcess()\\\\r\\\\n\\\\t\\\\tthis.processes.set(terminalInfo.id, process)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tprocess.once(\\\\\\\"completed\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tterminalInfo.busy = false\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// if shell integration is not available, remove terminal so it does not get reused as it may be running a long-running process\\\\r\\\\n\\\\t\\\\tprocess.once(\\\\\\\"no_shell_integration\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tconsole.log(`no_shell_integration received for terminal ${terminalInfo.id}`)\\\\r\\\\n\\\\t\\\\t\\\\t// Remove the terminal so we can't reuse it (in case it's running a long-running process)\\\\r\\\\n\\\\t\\\\t\\\\tTerminalRegistry.removeTerminal(terminalInfo.id)\\\\r\\\\n\\\\t\\\\t\\\\tthis.terminalIds.delete(terminalInfo.id)\\\\r\\\\n\\\\t\\\\t\\\\tthis.processes.delete(terminalInfo.id)\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst promise = new Promise<void>((resolve, reject) => {\\\\r\\\\n\\\\t\\\\t\\\\tprocess.once(\\\\\\\"continue\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tresolve()\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\tprocess.once(\\\\\\\"error\\\\\\\", (error) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.error(`Error in terminal ${terminalInfo.id}:`, error)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treject(error)\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// if shell integration is already active, run the command immediately\\\\r\\\\n\\\\t\\\\tif (terminalInfo.terminal.shellIntegration) {\\\\r\\\\n\\\\t\\\\t\\\\tprocess.waitForShellIntegration = false\\\\r\\\\n\\\\t\\\\t\\\\tprocess.run(terminalInfo.terminal, command)\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t// docs recommend waiting 3s for shell integration to activate\\\\r\\\\n\\\\t\\\\t\\\\tpWaitFor(() => terminalInfo.terminal.shellIntegration !== undefined, { timeout: 4000 }).finally(() => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst existingProcess = this.processes.get(terminalInfo.id)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (existingProcess && existingProcess.waitForShellIntegration) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\texistingProcess.waitForShellIntegration = false\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\texistingProcess.run(terminalInfo.terminal, command)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\treturn mergePromise(process, promise)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync getOrCreateTerminal(cwd: string): Promise<TerminalInfo> {\\\\r\\\\n\\\\t\\\\t// Find available terminal from our pool first (created for this task)\\\\r\\\\n\\\\t\\\\tconst availableTerminal = TerminalRegistry.getAllTerminals().find((t) => {\\\\r\\\\n\\\\t\\\\t\\\\tif (t.busy) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn false\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tconst terminalCwd = t.terminal.shellIntegration?.cwd // one of cline's commands could have changed the cwd of the terminal\\\\r\\\\n\\\\t\\\\t\\\\tif (!terminalCwd) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn false\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\treturn arePathsEqual(vscode.Uri.file(cwd).fsPath, terminalCwd.fsPath)\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\tif (availableTerminal) {\\\\r\\\\n\\\\t\\\\t\\\\tthis.terminalIds.add(availableTerminal.id)\\\\r\\\\n\\\\t\\\\t\\\\treturn availableTerminal\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst newTerminalInfo = TerminalRegistry.createTerminal(cwd)\\\\r\\\\n\\\\t\\\\tthis.terminalIds.add(newTerminalInfo.id)\\\\r\\\\n\\\\t\\\\treturn newTerminalInfo\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tgetTerminals(busy: boolean): { id: number; lastCommand: string }[] {\\\\r\\\\n\\\\t\\\\treturn Array.from(this.terminalIds)\\\\r\\\\n\\\\t\\\\t\\\\t.map((id) => TerminalRegistry.getTerminal(id))\\\\r\\\\n\\\\t\\\\t\\\\t.filter((t): t is TerminalInfo => t !== undefined && t.busy === busy)\\\\r\\\\n\\\\t\\\\t\\\\t.map((t) => ({ id: t.id, lastCommand: t.lastCommand }))\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tgetUnretrievedOutput(terminalId: number): string {\\\\r\\\\n\\\\t\\\\tif (!this.terminalIds.has(terminalId)) {\\\\r\\\\n\\\\t\\\\t\\\\treturn \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst process = this.processes.get(terminalId)\\\\r\\\\n\\\\t\\\\treturn process ? process.getUnretrievedOutput() : \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tisProcessHot(terminalId: number): boolean {\\\\r\\\\n\\\\t\\\\tconst process = this.processes.get(terminalId)\\\\r\\\\n\\\\t\\\\treturn process ? process.isHot : false\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tdisposeAll() {\\\\r\\\\n\\\\t\\\\t// for (const info of this.terminals) {\\\\r\\\\n\\\\t\\\\t// \\\\t//info.terminal.dispose() // dont want to dispose terminals when task is aborted\\\\r\\\\n\\\\t\\\\t// }\\\\r\\\\n\\\\t\\\\tthis.terminalIds.clear()\\\\r\\\\n\\\\t\\\\tthis.processes.clear()\\\\r\\\\n\\\\t\\\\tthis.disposables.forEach((disposable) => disposable.dispose())\\\\r\\\\n\\\\t\\\\tthis.disposables = []\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":7715,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.846Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.846Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":211,\\\"nonEmptyLines\\\":181,\\\"commentLines\\\":18,\\\"complexity\\\":26},\\\"dependencies\\\":[\\\"p-wait-for\\\",\\\"vscode\\\",\\\"../../utils/path\\\",\\\"./TerminalProcess\\\",\\\"./TerminalRegistry\\\"],\\\"quality\\\":{\\\"score\\\":-79,\\\"issues\\\":[],\\\"duplicateLines\\\":31,\\\"longLines\\\":12,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.372Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":211},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\terminal\\\\\\\\TerminalProcess.ts\\\":{\\\"content\\\":\\\"import { EventEmitter } from \\\\\\\"events\\\\\\\"\\\\r\\\\nimport stripAnsi from \\\\\\\"strip-ansi\\\\\\\"\\\\r\\\\nimport * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport interface TerminalProcessEvents {\\\\r\\\\n\\\\tline: [line: string]\\\\r\\\\n\\\\tcontinue: []\\\\r\\\\n\\\\tcompleted: []\\\\r\\\\n\\\\terror: [error: Error]\\\\r\\\\n\\\\tno_shell_integration: []\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n// how long to wait after a process outputs anything before we consider it \\\\\\\"cool\\\\\\\" again\\\\r\\\\nconst PROCESS_HOT_TIMEOUT_NORMAL = 2_000\\\\r\\\\nconst PROCESS_HOT_TIMEOUT_COMPILING = 15_000\\\\r\\\\n\\\\r\\\\nexport class TerminalProcess extends EventEmitter<TerminalProcessEvents> {\\\\r\\\\n\\\\twaitForShellIntegration: boolean = true\\\\r\\\\n\\\\tprivate isListening: boolean = true\\\\r\\\\n\\\\tprivate buffer: string = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\tprivate fullOutput: string = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\tprivate lastRetrievedIndex: number = 0\\\\r\\\\n\\\\tisHot: boolean = false\\\\r\\\\n\\\\tprivate hotTimer: NodeJS.Timeout | null = null\\\\r\\\\n\\\\r\\\\n\\\\t// constructor() {\\\\r\\\\n\\\\t// \\\\tsuper()\\\\r\\\\n\\\\r\\\\n\\\\tasync run(terminal: vscode.Terminal, command: string) {\\\\r\\\\n\\\\t\\\\tif (terminal.shellIntegration && terminal.shellIntegration.executeCommand) {\\\\r\\\\n\\\\t\\\\t\\\\tconst execution = terminal.shellIntegration.executeCommand(command)\\\\r\\\\n\\\\t\\\\t\\\\tconst stream = execution.read()\\\\r\\\\n\\\\t\\\\t\\\\t// todo: need to handle errors\\\\r\\\\n\\\\t\\\\t\\\\tlet isFirstChunk = true\\\\r\\\\n\\\\t\\\\t\\\\tlet didOutputNonCommand = false\\\\r\\\\n\\\\t\\\\t\\\\tlet didEmitEmptyLine = false\\\\r\\\\n\\\\t\\\\t\\\\tfor await (let data of stream) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// 1. Process chunk and remove artifacts\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (isFirstChunk) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t/*\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tThe first chunk we get from this stream needs to be processed to be more human readable, ie remove vscode's custom escape sequences and identifiers, removing duplicate first char bug, etc.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t*/\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// bug where sometimes the command output makes its way into vscode shell integration metadata\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t/*\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t]633 is a custom sequence number used by VSCode shell integration:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t- OSC 633 ; A ST - Mark prompt start\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t- OSC 633 ; B ST - Mark prompt end\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t- OSC 633 ; C ST - Mark pre-execution (start of command output)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t- OSC 633 ; D [; <exitcode>] ST - Mark execution finished with optional exit code\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t- OSC 633 ; E ; <commandline> [; <nonce>] ST - Explicitly set command line with optional nonce\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t*/\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// if you print this data you might see something like \\\\\\\"eecho hello worldo hello world;5ba85d14-e92a-40c4-b2fd-71525581eeb0]633;C\\\\\\\" but this is actually just a bunch of escape sequences, ignore up to the first ;C\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t/* ddateb15026-6a64-40db-b21f-2a621a9830f0]633;CTue Sep 17 06:37:04 EDT 2024 % ]633;D;0]633;P;Cwd=/Users/saoud/Repositories/test */\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Gets output between ]633;C (command start) and ]633;D (command end)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst outputBetweenSequences = this.removeLastLineArtifacts(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tdata.match(/\\\\\\\\]633;C([\\\\\\\\s\\\\\\\\S]*?)\\\\\\\\]633;D/)?.[1] || \\\\\\\"\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t).trim()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Once we've retrieved any potential output between sequences, we can remove everything up to end of the last sequence\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// https://code.visualstudio.com/docs/terminal/shell-integration#_vs-code-custom-sequences-osc-633-st\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst vscodeSequenceRegex = /\\\\\\\\x1b\\\\\\\\]633;.[^\\\\\\\\x07]*\\\\\\\\x07/g\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst lastMatch = [...data.matchAll(vscodeSequenceRegex)].pop()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (lastMatch && lastMatch.index !== undefined) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tdata = data.slice(lastMatch.index + lastMatch[0].length)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Place output back after removing vscode sequences\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (outputBetweenSequences) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tdata = outputBetweenSequences + \\\\\\\"\\\\\\\\n\\\\\\\" + data\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// remove ansi\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tdata = stripAnsi(data)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Split data by newlines\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tlet lines = data ? data.split(\\\\\\\"\\\\\\\\n\\\\\\\") : []\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Remove non-human readable characters from the first line\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (lines.length > 0) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlines[0] = lines[0].replace(/[^\\\\\\\\x20-\\\\\\\\x7E]/g, \\\\\\\"\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Check if first two characters are the same, if so remove the first character\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (lines.length > 0 && lines[0].length >= 2 && lines[0][0] === lines[0][1]) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlines[0] = lines[0].slice(1)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Remove everything up to the first alphanumeric character for first two lines\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (lines.length > 0) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlines[0] = lines[0].replace(/^[^a-zA-Z0-9]*/, \\\\\\\"\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (lines.length > 1) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlines[1] = lines[1].replace(/^[^a-zA-Z0-9]*/, \\\\\\\"\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Join lines back\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tdata = lines.join(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tisFirstChunk = false\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tdata = stripAnsi(data)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// first few chunks could be the command being echoed back, so we must ignore\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// note this means that 'echo' commands wont work\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (!didOutputNonCommand) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst lines = data.split(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tfor (let i = 0; i < lines.length; i++) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (command.includes(lines[i].trim())) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tlines.splice(i, 1)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ti-- // Adjust index after removal\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tdidOutputNonCommand = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tdata = lines.join(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// FIXME: right now it seems that data chunks returned to us from the shell integration stream contains random commas, which from what I can tell is not the expected behavior. There has to be a better solution here than just removing all commas.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tdata = data.replace(/,/g, \\\\\\\"\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// 2. Set isHot depending on the command\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Set to hot to stall API requests until terminal is cool again\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.isHot = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (this.hotTimer) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tclearTimeout(this.hotTimer)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// these markers indicate the command is some kind of local dev server recompiling the app, which we want to wait for output of before sending request to cline\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst compilingMarkers = [\\\\\\\"compiling\\\\\\\", \\\\\\\"building\\\\\\\", \\\\\\\"bundling\\\\\\\", \\\\\\\"transpiling\\\\\\\", \\\\\\\"generating\\\\\\\", \\\\\\\"starting\\\\\\\"]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst markerNullifiers = [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"compiled\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"success\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"finish\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"complete\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"succeed\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"done\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"end\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"stop\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"exit\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"terminate\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"error\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"fail\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst isCompiling =\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcompilingMarkers.some((marker) => data.toLowerCase().includes(marker.toLowerCase())) &&\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t!markerNullifiers.some((nullifier) => data.toLowerCase().includes(nullifier.toLowerCase()))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.hotTimer = setTimeout(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t() => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.isHot = false\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tisCompiling ? PROCESS_HOT_TIMEOUT_COMPILING : PROCESS_HOT_TIMEOUT_NORMAL,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// For non-immediately returning commands we want to show loading spinner right away but this wouldnt happen until it emits a line break, so as soon as we get any output we emit \\\\\\\"\\\\\\\" to let webview know to show spinner\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (!didEmitEmptyLine && !this.fullOutput && data) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.emit(\\\\\\\"line\\\\\\\", \\\\\\\"\\\\\\\") // empty line to indicate start of command output stream\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tdidEmitEmptyLine = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.fullOutput += data\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (this.isListening) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.emitIfEol(data)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.lastRetrievedIndex = this.fullOutput.length - this.buffer.length\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tthis.emitRemainingBufferIfListening()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// for now we don't want this delaying requests since we don't send diagnostics automatically anymore (previous: \\\\\\\"even though the command is finished, we still want to consider it 'hot' in case so that api request stalls to let diagnostics catch up\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tif (this.hotTimer) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tclearTimeout(this.hotTimer)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tthis.isHot = false\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tthis.emit(\\\\\\\"completed\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tthis.emit(\\\\\\\"continue\\\\\\\")\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\tterminal.sendText(command, true)\\\\r\\\\n\\\\t\\\\t\\\\t// For terminals without shell integration, we can't know when the command completes\\\\r\\\\n\\\\t\\\\t\\\\t// So we'll just emit the continue event after a delay\\\\r\\\\n\\\\t\\\\t\\\\tthis.emit(\\\\\\\"completed\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tthis.emit(\\\\\\\"continue\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tthis.emit(\\\\\\\"no_shell_integration\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t// setTimeout(() => {\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\tconsole.log(`Emitting continue after delay for terminal`)\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\t// can't emit completed since we don't if the command actually completed, it could still be running server\\\\r\\\\n\\\\t\\\\t\\\\t// }, 500) // Adjust this delay as needed\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Inspired by https://github.com/sindresorhus/execa/blob/main/lib/transform/split.js\\\\r\\\\n\\\\tprivate emitIfEol(chunk: string) {\\\\r\\\\n\\\\t\\\\tthis.buffer += chunk\\\\r\\\\n\\\\t\\\\tlet lineEndIndex: number\\\\r\\\\n\\\\t\\\\twhile ((lineEndIndex = this.buffer.indexOf(\\\\\\\"\\\\\\\\n\\\\\\\")) !== -1) {\\\\r\\\\n\\\\t\\\\t\\\\tlet line = this.buffer.slice(0, lineEndIndex).trimEnd() // removes trailing \\\\\\\\r\\\\r\\\\n\\\\t\\\\t\\\\t// Remove \\\\\\\\r if present (for Windows-style line endings)\\\\r\\\\n\\\\t\\\\t\\\\t// if (line.endsWith(\\\\\\\"\\\\\\\\r\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\tline = line.slice(0, -1)\\\\r\\\\n\\\\t\\\\t\\\\t// }\\\\r\\\\n\\\\t\\\\t\\\\tthis.emit(\\\\\\\"line\\\\\\\", line)\\\\r\\\\n\\\\t\\\\t\\\\tthis.buffer = this.buffer.slice(lineEndIndex + 1)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate emitRemainingBufferIfListening() {\\\\r\\\\n\\\\t\\\\tif (this.buffer && this.isListening) {\\\\r\\\\n\\\\t\\\\t\\\\tconst remainingBuffer = this.removeLastLineArtifacts(this.buffer)\\\\r\\\\n\\\\t\\\\t\\\\tif (remainingBuffer) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.emit(\\\\\\\"line\\\\\\\", remainingBuffer)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tthis.buffer = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tthis.lastRetrievedIndex = this.fullOutput.length\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tcontinue() {\\\\r\\\\n\\\\t\\\\tthis.emitRemainingBufferIfListening()\\\\r\\\\n\\\\t\\\\tthis.isListening = false\\\\r\\\\n\\\\t\\\\tthis.removeAllListeners(\\\\\\\"line\\\\\\\")\\\\r\\\\n\\\\t\\\\tthis.emit(\\\\\\\"continue\\\\\\\")\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tgetUnretrievedOutput(): string {\\\\r\\\\n\\\\t\\\\tconst unretrieved = this.fullOutput.slice(this.lastRetrievedIndex)\\\\r\\\\n\\\\t\\\\tthis.lastRetrievedIndex = this.fullOutput.length\\\\r\\\\n\\\\t\\\\treturn this.removeLastLineArtifacts(unretrieved)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// some processing to remove artifacts like '%' at the end of the buffer (it seems that since vsode uses % at the beginning of newlines in terminal, it makes its way into the stream)\\\\r\\\\n\\\\t// This modification will remove '%', '$', '#', or '>' followed by optional whitespace\\\\r\\\\n\\\\tremoveLastLineArtifacts(output: string) {\\\\r\\\\n\\\\t\\\\tconst lines = output.trimEnd().split(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\t\\\\tif (lines.length > 0) {\\\\r\\\\n\\\\t\\\\t\\\\tconst lastLine = lines[lines.length - 1]\\\\r\\\\n\\\\t\\\\t\\\\t// Remove prompt characters and trailing whitespace from the last line\\\\r\\\\n\\\\t\\\\t\\\\tlines[lines.length - 1] = lastLine.replace(/[%$#>]\\\\\\\\s*$/, \\\\\\\"\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn lines.join(\\\\\\\"\\\\\\\\n\\\\\\\").trimEnd()\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport type TerminalProcessResultPromise = TerminalProcess & Promise<void>\\\\r\\\\n\\\\r\\\\n// Similar to execa's ResultPromise, this lets us create a mixin of both a TerminalProcess and a Promise: https://github.com/sindresorhus/execa/blob/main/lib/methods/promise.js\\\\r\\\\nexport function mergePromise(process: TerminalProcess, promise: Promise<void>): TerminalProcessResultPromise {\\\\r\\\\n\\\\tconst nativePromisePrototype = (async () => {})().constructor.prototype\\\\r\\\\n\\\\tconst descriptors = [\\\\\\\"then\\\\\\\", \\\\\\\"catch\\\\\\\", \\\\\\\"finally\\\\\\\"].map(\\\\r\\\\n\\\\t\\\\t(property) => [property, Reflect.getOwnPropertyDescriptor(nativePromisePrototype, property)] as const,\\\\r\\\\n\\\\t)\\\\r\\\\n\\\\tfor (const [property, descriptor] of descriptors) {\\\\r\\\\n\\\\t\\\\tif (descriptor) {\\\\r\\\\n\\\\t\\\\t\\\\tconst value = descriptor.value.bind(promise)\\\\r\\\\n\\\\t\\\\t\\\\tReflect.defineProperty(process, property, { ...descriptor, value })\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\treturn process as TerminalProcessResultPromise\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":10363,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.846Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.846Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":253,\\\"nonEmptyLines\\\":230,\\\"commentLines\\\":43,\\\"complexity\\\":40},\\\"dependencies\\\":[\\\"events\\\",\\\"strip-ansi\\\",\\\"vscode\\\"],\\\"quality\\\":{\\\"score\\\":-165,\\\"issues\\\":[],\\\"duplicateLines\\\":47,\\\"longLines\\\":15,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.374Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":253},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\terminal\\\\\\\\TerminalRegistry.ts\\\":{\\\"content\\\":\\\"import * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport interface TerminalInfo {\\\\r\\\\n\\\\tterminal: vscode.Terminal\\\\r\\\\n\\\\tbusy: boolean\\\\r\\\\n\\\\tlastCommand: string\\\\r\\\\n\\\\tid: number\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n// Although vscode.window.terminals provides a list of all open terminals, there's no way to know whether they're busy or not (exitStatus does not provide useful information for most commands). In order to prevent creating too many terminals, we need to keep track of terminals through the life of the extension, as well as session specific terminals for the life of a task (to get latest unretrieved output).\\\\r\\\\n// Since we have promises keeping track of terminal processes, we get the added benefit of keep track of busy terminals even after a task is closed.\\\\r\\\\nexport class TerminalRegistry {\\\\r\\\\n\\\\tprivate static terminals: TerminalInfo[] = []\\\\r\\\\n\\\\tprivate static nextTerminalId = 1\\\\r\\\\n\\\\r\\\\n\\\\tstatic createTerminal(cwd?: string | vscode.Uri | undefined): TerminalInfo {\\\\r\\\\n\\\\t\\\\tconst terminal = vscode.window.createTerminal({\\\\r\\\\n\\\\t\\\\t\\\\tcwd,\\\\r\\\\n\\\\t\\\\t\\\\tname: \\\\\\\"Cline\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\ticonPath: new vscode.ThemeIcon(\\\\\\\"robot\\\\\\\"),\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\tconst newInfo: TerminalInfo = {\\\\r\\\\n\\\\t\\\\t\\\\tterminal,\\\\r\\\\n\\\\t\\\\t\\\\tbusy: false,\\\\r\\\\n\\\\t\\\\t\\\\tlastCommand: \\\\\\\"\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tid: this.nextTerminalId++,\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tthis.terminals.push(newInfo)\\\\r\\\\n\\\\t\\\\treturn newInfo\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tstatic getTerminal(id: number): TerminalInfo | undefined {\\\\r\\\\n\\\\t\\\\tconst terminalInfo = this.terminals.find((t) => t.id === id)\\\\r\\\\n\\\\t\\\\tif (terminalInfo && this.isTerminalClosed(terminalInfo.terminal)) {\\\\r\\\\n\\\\t\\\\t\\\\tthis.removeTerminal(id)\\\\r\\\\n\\\\t\\\\t\\\\treturn undefined\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn terminalInfo\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tstatic updateTerminal(id: number, updates: Partial<TerminalInfo>) {\\\\r\\\\n\\\\t\\\\tconst terminal = this.getTerminal(id)\\\\r\\\\n\\\\t\\\\tif (terminal) {\\\\r\\\\n\\\\t\\\\t\\\\tObject.assign(terminal, updates)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tstatic removeTerminal(id: number) {\\\\r\\\\n\\\\t\\\\tthis.terminals = this.terminals.filter((t) => t.id !== id)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tstatic getAllTerminals(): TerminalInfo[] {\\\\r\\\\n\\\\t\\\\tthis.terminals = this.terminals.filter((t) => !this.isTerminalClosed(t.terminal))\\\\r\\\\n\\\\t\\\\treturn this.terminals\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// The exit status of the terminal will be undefined while the terminal is active. (This value is set when onDidCloseTerminal is fired.)\\\\r\\\\n\\\\tprivate static isTerminalClosed(terminal: vscode.Terminal): boolean {\\\\r\\\\n\\\\t\\\\treturn terminal.exitStatus !== undefined\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":2207,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.846Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.846Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":62,\\\"nonEmptyLines\\\":53,\\\"commentLines\\\":3,\\\"complexity\\\":5},\\\"dependencies\\\":[\\\"vscode\\\"],\\\"quality\\\":{\\\"score\\\":44,\\\"issues\\\":[],\\\"duplicateLines\\\":10,\\\"longLines\\\":3,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.376Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":62},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\theme\\\\\\\\default-themes\\\\\\\\dark_modern.json\\\":{\\\"content\\\":\\\"{\\\\r\\\\n\\\\t\\\\\\\"$schema\\\\\\\": \\\\\\\"vscode://schemas/color-theme\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Default Dark Modern\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"include\\\\\\\": \\\\\\\"./dark_plus.json\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"colors\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\\\\"activityBar.activeBorder\\\\\\\": \\\\\\\"#0078D4\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"activityBar.background\\\\\\\": \\\\\\\"#181818\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"activityBar.border\\\\\\\": \\\\\\\"#2B2B2B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"activityBar.foreground\\\\\\\": \\\\\\\"#D7D7D7\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"activityBar.inactiveForeground\\\\\\\": \\\\\\\"#868686\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"activityBarBadge.background\\\\\\\": \\\\\\\"#0078D4\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"activityBarBadge.foreground\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"badge.background\\\\\\\": \\\\\\\"#616161\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"badge.foreground\\\\\\\": \\\\\\\"#F8F8F8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"button.background\\\\\\\": \\\\\\\"#0078D4\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"button.border\\\\\\\": \\\\\\\"#FFFFFF12\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"button.foreground\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"button.hoverBackground\\\\\\\": \\\\\\\"#026EC1\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"button.secondaryBackground\\\\\\\": \\\\\\\"#313131\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"button.secondaryForeground\\\\\\\": \\\\\\\"#CCCCCC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"button.secondaryHoverBackground\\\\\\\": \\\\\\\"#3C3C3C\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"chat.slashCommandBackground\\\\\\\": \\\\\\\"#34414B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"chat.slashCommandForeground\\\\\\\": \\\\\\\"#40A6FF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"checkbox.background\\\\\\\": \\\\\\\"#313131\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"checkbox.border\\\\\\\": \\\\\\\"#3C3C3C\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"debugToolBar.background\\\\\\\": \\\\\\\"#181818\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"descriptionForeground\\\\\\\": \\\\\\\"#9D9D9D\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"dropdown.background\\\\\\\": \\\\\\\"#313131\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"dropdown.border\\\\\\\": \\\\\\\"#3C3C3C\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"dropdown.foreground\\\\\\\": \\\\\\\"#CCCCCC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"dropdown.listBackground\\\\\\\": \\\\\\\"#1F1F1F\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editor.background\\\\\\\": \\\\\\\"#1F1F1F\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editor.findMatchBackground\\\\\\\": \\\\\\\"#9E6A03\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editor.foreground\\\\\\\": \\\\\\\"#CCCCCC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorGroup.border\\\\\\\": \\\\\\\"#FFFFFF17\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorGroupHeader.tabsBackground\\\\\\\": \\\\\\\"#181818\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorGroupHeader.tabsBorder\\\\\\\": \\\\\\\"#2B2B2B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorGutter.addedBackground\\\\\\\": \\\\\\\"#2EA043\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorGutter.deletedBackground\\\\\\\": \\\\\\\"#F85149\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorGutter.modifiedBackground\\\\\\\": \\\\\\\"#0078D4\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorLineNumber.activeForeground\\\\\\\": \\\\\\\"#CCCCCC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorLineNumber.foreground\\\\\\\": \\\\\\\"#6E7681\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorOverviewRuler.border\\\\\\\": \\\\\\\"#010409\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorWidget.background\\\\\\\": \\\\\\\"#202020\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"errorForeground\\\\\\\": \\\\\\\"#F85149\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"focusBorder\\\\\\\": \\\\\\\"#0078D4\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#CCCCCC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"icon.foreground\\\\\\\": \\\\\\\"#CCCCCC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"input.background\\\\\\\": \\\\\\\"#313131\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"input.border\\\\\\\": \\\\\\\"#3C3C3C\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"input.foreground\\\\\\\": \\\\\\\"#CCCCCC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"input.placeholderForeground\\\\\\\": \\\\\\\"#989898\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"inputOption.activeBackground\\\\\\\": \\\\\\\"#2489DB82\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"inputOption.activeBorder\\\\\\\": \\\\\\\"#2488DB\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"keybindingLabel.foreground\\\\\\\": \\\\\\\"#CCCCCC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"menu.background\\\\\\\": \\\\\\\"#1F1F1F\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"notificationCenterHeader.background\\\\\\\": \\\\\\\"#1F1F1F\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"notificationCenterHeader.foreground\\\\\\\": \\\\\\\"#CCCCCC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"notifications.background\\\\\\\": \\\\\\\"#1F1F1F\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"notifications.border\\\\\\\": \\\\\\\"#2B2B2B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"notifications.foreground\\\\\\\": \\\\\\\"#CCCCCC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"panel.background\\\\\\\": \\\\\\\"#181818\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"panel.border\\\\\\\": \\\\\\\"#2B2B2B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"panelInput.border\\\\\\\": \\\\\\\"#2B2B2B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"panelTitle.activeBorder\\\\\\\": \\\\\\\"#0078D4\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"panelTitle.activeForeground\\\\\\\": \\\\\\\"#CCCCCC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"panelTitle.inactiveForeground\\\\\\\": \\\\\\\"#9D9D9D\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"peekViewEditor.background\\\\\\\": \\\\\\\"#1F1F1F\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"peekViewEditor.matchHighlightBackground\\\\\\\": \\\\\\\"#BB800966\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"peekViewResult.background\\\\\\\": \\\\\\\"#1F1F1F\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"peekViewResult.matchHighlightBackground\\\\\\\": \\\\\\\"#BB800966\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"pickerGroup.border\\\\\\\": \\\\\\\"#3C3C3C\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"progressBar.background\\\\\\\": \\\\\\\"#0078D4\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"quickInput.background\\\\\\\": \\\\\\\"#222222\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"quickInput.foreground\\\\\\\": \\\\\\\"#CCCCCC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"settings.dropdownBackground\\\\\\\": \\\\\\\"#313131\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"settings.dropdownBorder\\\\\\\": \\\\\\\"#3C3C3C\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"settings.headerForeground\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"settings.modifiedItemIndicator\\\\\\\": \\\\\\\"#BB800966\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"sideBar.background\\\\\\\": \\\\\\\"#181818\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"sideBar.border\\\\\\\": \\\\\\\"#2B2B2B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"sideBar.foreground\\\\\\\": \\\\\\\"#CCCCCC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"sideBarSectionHeader.background\\\\\\\": \\\\\\\"#181818\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"sideBarSectionHeader.border\\\\\\\": \\\\\\\"#2B2B2B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"sideBarSectionHeader.foreground\\\\\\\": \\\\\\\"#CCCCCC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"sideBarTitle.foreground\\\\\\\": \\\\\\\"#CCCCCC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBar.background\\\\\\\": \\\\\\\"#181818\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBar.border\\\\\\\": \\\\\\\"#2B2B2B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBar.debuggingBackground\\\\\\\": \\\\\\\"#0078D4\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBar.debuggingForeground\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBar.focusBorder\\\\\\\": \\\\\\\"#0078D4\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBar.foreground\\\\\\\": \\\\\\\"#CCCCCC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBar.noFolderBackground\\\\\\\": \\\\\\\"#1F1F1F\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBarItem.focusBorder\\\\\\\": \\\\\\\"#0078D4\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBarItem.prominentBackground\\\\\\\": \\\\\\\"#6E768166\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBarItem.remoteBackground\\\\\\\": \\\\\\\"#0078D4\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBarItem.remoteForeground\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.activeBackground\\\\\\\": \\\\\\\"#1F1F1F\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.activeBorder\\\\\\\": \\\\\\\"#1F1F1F\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.activeBorderTop\\\\\\\": \\\\\\\"#0078D4\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.activeForeground\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.border\\\\\\\": \\\\\\\"#2B2B2B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.hoverBackground\\\\\\\": \\\\\\\"#1F1F1F\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.inactiveBackground\\\\\\\": \\\\\\\"#181818\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.inactiveForeground\\\\\\\": \\\\\\\"#9D9D9D\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.unfocusedActiveBorder\\\\\\\": \\\\\\\"#1F1F1F\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.unfocusedActiveBorderTop\\\\\\\": \\\\\\\"#2B2B2B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.unfocusedHoverBackground\\\\\\\": \\\\\\\"#1F1F1F\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"terminal.foreground\\\\\\\": \\\\\\\"#CCCCCC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"terminal.tab.activeBorder\\\\\\\": \\\\\\\"#0078D4\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"textBlockQuote.background\\\\\\\": \\\\\\\"#2B2B2B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"textBlockQuote.border\\\\\\\": \\\\\\\"#616161\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"textCodeBlock.background\\\\\\\": \\\\\\\"#2B2B2B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"textLink.activeForeground\\\\\\\": \\\\\\\"#4daafc\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"textLink.foreground\\\\\\\": \\\\\\\"#4daafc\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"textPreformat.foreground\\\\\\\": \\\\\\\"#D0D0D0\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"textPreformat.background\\\\\\\": \\\\\\\"#3C3C3C\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"textSeparator.foreground\\\\\\\": \\\\\\\"#21262D\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"titleBar.activeBackground\\\\\\\": \\\\\\\"#181818\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"titleBar.activeForeground\\\\\\\": \\\\\\\"#CCCCCC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"titleBar.border\\\\\\\": \\\\\\\"#2B2B2B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"titleBar.inactiveBackground\\\\\\\": \\\\\\\"#1F1F1F\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"titleBar.inactiveForeground\\\\\\\": \\\\\\\"#9D9D9D\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"welcomePage.tileBackground\\\\\\\": \\\\\\\"#2B2B2B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"welcomePage.progress.foreground\\\\\\\": \\\\\\\"#0078D4\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"widget.border\\\\\\\": \\\\\\\"#313131\\\\\\\"\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":5107,\\\"mimeType\\\":\\\"application/json\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.846Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.846Z\\\",\\\"isDirectory\\\":false},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":129},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\theme\\\\\\\\default-themes\\\\\\\\dark_plus.json\\\":{\\\"content\\\":\\\"{\\\\r\\\\n\\\\t\\\\\\\"$schema\\\\\\\": \\\\\\\"vscode://schemas/color-theme\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Dark+\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"include\\\\\\\": \\\\\\\"./dark_vs.json\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"tokenColors\\\\\\\": [\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Function declarations\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.function\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.function\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.handlebars\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.powershell variable.other.member\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.operator.custom-literal\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#DCDCAA\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Types declaration and references\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.class\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.type\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.type\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.namespace\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.scope-resolution\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.class\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.numeric.go\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.byte.go\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.boolean.go\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.string.go\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.uintptr.go\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.error.go\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.rune.go\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.cs\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.generic.cs\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.modifier.cs\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.variable.cs\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.annotation.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.generic.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.object.array.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.primitive.array.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.primitive.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.token.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.annotation.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.parameters.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.generic.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.object.array.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.primitive.array.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.primitive.groovy\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#4EC9B0\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Types declaration and references, TS grammar specific\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"meta.type.cast.expr\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"meta.type.new.expr\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.math\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.dom\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.json\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.inherited-class\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#4EC9B0\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Control flow / Special keywords\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.control\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.cpp keyword.operator.new\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.delete\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.other.using\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.other.directive.using\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.other.operator\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.operator\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#C586C0\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Variable and parameter name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"variable\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"meta.definition.variable.name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.variable\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.variable\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.other.placeholder\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#9CDCFE\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Constants and enums\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"variable.other.constant\\\\\\\", \\\\\\\"variable.other.enummember\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#4FC1FF\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Object keys, TS grammar specific\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"meta.object-literal.key\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#9CDCFE\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"CSS property value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.property-value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.font-name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.media-type\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.media\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.other.color.rgb-value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.other.rgb-value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.color\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#CE9178\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Regular expression groups\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.definition.group.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.definition.group.assertion.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.definition.character-class.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.character.set.begin.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.character.set.end.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.negation.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.other.parenthesis.regexp\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#CE9178\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.character.character-class.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.other.character-class.set.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.other.character-class.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.character.set.regexp\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#d16969\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"keyword.operator.or.regexp\\\\\\\", \\\\\\\"keyword.control.anchor.regexp\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#DCDCAA\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"keyword.operator.quantifier.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#d7ba7d\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"constant.character\\\\\\\", \\\\\\\"constant.other.option\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"constant.character.escape\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#d7ba7d\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"entity.name.label\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#C8C8C8\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t],\\\\r\\\\n\\\\t\\\\\\\"semanticTokenColors\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\\\\"newOperator\\\\\\\": \\\\\\\"#C586C0\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"stringLiteral\\\\\\\": \\\\\\\"#ce9178\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"customLiteral\\\\\\\": \\\\\\\"#DCDCAA\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"numberLiteral\\\\\\\": \\\\\\\"#b5cea8\\\\\\\"\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":4710,\\\"mimeType\\\":\\\"application/json\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.846Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.846Z\\\",\\\"isDirectory\\\":false},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":194},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\theme\\\\\\\\default-themes\\\\\\\\dark_vs.json\\\":{\\\"content\\\":\\\"{\\\\r\\\\n\\\\t\\\\\\\"$schema\\\\\\\": \\\\\\\"vscode://schemas/color-theme\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Dark (Visual Studio)\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"colors\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\\\\"checkbox.border\\\\\\\": \\\\\\\"#6B6B6B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editor.background\\\\\\\": \\\\\\\"#1E1E1E\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editor.foreground\\\\\\\": \\\\\\\"#D4D4D4\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editor.inactiveSelectionBackground\\\\\\\": \\\\\\\"#3A3D41\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorIndentGuide.background1\\\\\\\": \\\\\\\"#404040\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorIndentGuide.activeBackground1\\\\\\\": \\\\\\\"#707070\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editor.selectionHighlightBackground\\\\\\\": \\\\\\\"#ADD6FF26\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"list.dropBackground\\\\\\\": \\\\\\\"#383B3D\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"activityBarBadge.background\\\\\\\": \\\\\\\"#007ACC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"sideBarTitle.foreground\\\\\\\": \\\\\\\"#BBBBBB\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"input.placeholderForeground\\\\\\\": \\\\\\\"#A6A6A6\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"menu.background\\\\\\\": \\\\\\\"#252526\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"menu.foreground\\\\\\\": \\\\\\\"#CCCCCC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"menu.separatorBackground\\\\\\\": \\\\\\\"#454545\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"menu.border\\\\\\\": \\\\\\\"#454545\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBarItem.remoteForeground\\\\\\\": \\\\\\\"#FFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBarItem.remoteBackground\\\\\\\": \\\\\\\"#16825D\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"ports.iconRunningProcessForeground\\\\\\\": \\\\\\\"#369432\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"sideBarSectionHeader.background\\\\\\\": \\\\\\\"#0000\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"sideBarSectionHeader.border\\\\\\\": \\\\\\\"#ccc3\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.lastPinnedBorder\\\\\\\": \\\\\\\"#ccc3\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"list.activeSelectionIconForeground\\\\\\\": \\\\\\\"#FFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"terminal.inactiveSelectionBackground\\\\\\\": \\\\\\\"#3A3D41\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"widget.border\\\\\\\": \\\\\\\"#303031\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"actionBar.toggledBackground\\\\\\\": \\\\\\\"#383a49\\\\\\\"\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"tokenColors\\\\\\\": [\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"meta.embedded\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.groovy.embedded\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string meta.image.inline.markdown\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"variable.legacy.builtin.python\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#D4D4D4\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"emphasis\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"italic\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"strong\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"bold\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"header\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#000080\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"comment\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#6A9955\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"constant.language\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.numeric\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"variable.other.enummember\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.plus.exponent\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.minus.exponent\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#b5cea8\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"constant.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#646695\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"entity.name.tag\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"entity.name.tag.css\\\\\\\", \\\\\\\"entity.name.tag.less\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#d7ba7d\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"entity.other.attribute-name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#9cdcfe\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.class.css\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.css entity.other.attribute-name.class\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.id.css\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.parent-selector.css\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.parent.less\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.css entity.other.attribute-name.pseudo-class\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.pseudo-element.css\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.css.less entity.other.attribute-name.id\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.scss\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#d7ba7d\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"invalid\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#f44747\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.underline\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"underline\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.bold\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"bold\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.heading\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"bold\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.italic\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"italic\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.strikethrough\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"strikethrough\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.inserted\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#b5cea8\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.deleted\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#ce9178\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.changed\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"punctuation.definition.quote.begin.markdown\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#6A9955\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"punctuation.definition.list.begin.markdown\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#6796e6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.inline.raw\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#ce9178\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"brackets of XML/HTML tags\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"punctuation.definition.tag\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#808080\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"meta.preprocessor\\\\\\\", \\\\\\\"entity.name.function.preprocessor\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"meta.preprocessor.string\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#ce9178\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"meta.preprocessor.numeric\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#b5cea8\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"meta.structure.dictionary.key.python\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#9cdcfe\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"meta.diff.header\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"storage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"storage.type\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"storage.modifier\\\\\\\", \\\\\\\"keyword.operator.noexcept\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"string\\\\\\\", \\\\\\\"meta.embedded.assembly\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#ce9178\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"string.tag\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#ce9178\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"string.value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#ce9178\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"string.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#d16969\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"String interpolation\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.definition.template-expression.begin\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.definition.template-expression.end\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.section.embedded\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Reset JavaScript string interpolation expression\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"meta.template.expression\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#d4d4d4\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.type.vendored.property-name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.type.property-name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.css variable\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.coffee.embedded\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#9cdcfe\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"keyword\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"keyword.control\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"keyword.operator\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#d4d4d4\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.new\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.expression\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.cast\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.sizeof\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.alignof\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.typeid\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.alignas\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.instanceof\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.logical.python\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.wordlike\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"keyword.other.unit\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#b5cea8\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"punctuation.section.embedded.begin.php\\\\\\\", \\\\\\\"punctuation.section.embedded.end.php\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"support.function.git-rebase\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#9cdcfe\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"constant.sha.git-rebase\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#b5cea8\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"coloring of the Java import and package identifiers\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"storage.modifier.import.java\\\\\\\", \\\\\\\"variable.language.wildcard.java\\\\\\\", \\\\\\\"storage.modifier.package.java\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#d4d4d4\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"this.self\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"variable.language\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t],\\\\r\\\\n\\\\t\\\\\\\"semanticHighlighting\\\\\\\": true,\\\\r\\\\n\\\\t\\\\\\\"semanticTokenColors\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\\\\"newOperator\\\\\\\": \\\\\\\"#d4d4d4\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"stringLiteral\\\\\\\": \\\\\\\"#ce9178\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"customLiteral\\\\\\\": \\\\\\\"#D4D4D4\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"numberLiteral\\\\\\\": \\\\\\\"#b5cea8\\\\\\\"\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":8100,\\\"mimeType\\\":\\\"application/json\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.958Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.958Z\\\",\\\"isDirectory\\\":false},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":388},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\theme\\\\\\\\default-themes\\\\\\\\hc_black.json\\\":{\\\"content\\\":\\\"{\\\\r\\\\n\\\\t\\\\\\\"$schema\\\\\\\": \\\\\\\"vscode://schemas/color-theme\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Dark High Contrast\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"colors\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\\\\"editor.background\\\\\\\": \\\\\\\"#000000\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editor.foreground\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorIndentGuide.background1\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorIndentGuide.activeBackground1\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"sideBarTitle.foreground\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"selection.background\\\\\\\": \\\\\\\"#008000\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editor.selectionBackground\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBarItem.remoteBackground\\\\\\\": \\\\\\\"#00000000\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"ports.iconRunningProcessForeground\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorWhitespace.foreground\\\\\\\": \\\\\\\"#7c7c7c\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"actionBar.toggledBackground\\\\\\\": \\\\\\\"#383a49\\\\\\\"\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"tokenColors\\\\\\\": [\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"meta.embedded\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.groovy.embedded\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string meta.image.inline.markdown\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"variable.legacy.builtin.python\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"emphasis\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"italic\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"strong\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"bold\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"meta.diff.header\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#000080\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"comment\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#7ca668\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"constant.language\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"constant.numeric\\\\\\\", \\\\\\\"constant.other.color.rgb-value\\\\\\\", \\\\\\\"constant.other.rgb-value\\\\\\\", \\\\\\\"support.constant.color\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#b5cea8\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"constant.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#b46695\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"constant.character\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"entity.name.tag\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"entity.name.tag.css\\\\\\\", \\\\\\\"entity.name.tag.less\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#d7ba7d\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"entity.other.attribute-name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#9cdcfe\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.class.css\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.css entity.other.attribute-name.class\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.id.css\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.parent-selector.css\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.parent.less\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.css entity.other.attribute-name.pseudo-class\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.pseudo-element.css\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.css.less entity.other.attribute-name.id\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.scss\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#d7ba7d\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"invalid\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#f44747\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.underline\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"underline\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.bold\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"bold\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.heading\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"bold\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#6796e6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.italic\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"italic\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.strikethrough\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"strikethrough\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.inserted\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#b5cea8\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.deleted\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#ce9178\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.changed\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"brackets of XML/HTML tags\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"punctuation.definition.tag\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#808080\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"meta.preprocessor\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"meta.preprocessor.string\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#ce9178\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"meta.preprocessor.numeric\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#b5cea8\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"meta.structure.dictionary.key.python\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#9cdcfe\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"storage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"storage.type\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"storage.modifier\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"string\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#ce9178\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"string.tag\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#ce9178\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"string.value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#ce9178\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"string.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#d16969\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"String interpolation\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.definition.template-expression.begin\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.definition.template-expression.end\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.section.embedded\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Reset JavaScript string interpolation expression\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"meta.template.expression\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#ffffff\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.type.vendored.property-name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.type.property-name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.css variable\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.coffee.embedded\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#d4d4d4\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"keyword\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"keyword.control\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"keyword.operator\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#d4d4d4\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.new\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.expression\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.cast\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.sizeof\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.logical.python\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"keyword.other.unit\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#b5cea8\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"support.function.git-rebase\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#d4d4d4\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"constant.sha.git-rebase\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#b5cea8\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"coloring of the Java import and package identifiers\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"storage.modifier.import.java\\\\\\\", \\\\\\\"variable.language.wildcard.java\\\\\\\", \\\\\\\"storage.modifier.package.java\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#d4d4d4\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"coloring of the TS this\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"variable.language.this\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#569cd6\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Function declarations\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.function\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.function\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.handlebars\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.powershell variable.other.member\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#DCDCAA\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Types declaration and references\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.class\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.type\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.type\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.namespace\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.scope-resolution\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.class\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.cs\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.generic.cs\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.modifier.cs\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.variable.cs\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.annotation.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.generic.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.object.array.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.primitive.array.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.primitive.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.token.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.annotation.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.parameters.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.generic.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.object.array.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.primitive.array.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.primitive.groovy\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#4EC9B0\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Types declaration and references, TS grammar specific\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"meta.type.cast.expr\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"meta.type.new.expr\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.math\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.dom\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.json\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.inherited-class\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#4EC9B0\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Control flow / Special keywords\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.control\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.cpp keyword.operator.new\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.cpp keyword.operator.delete\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.other.using\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.other.directive.using\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.other.operator\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#C586C0\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Variable and parameter name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"variable\\\\\\\", \\\\\\\"meta.definition.variable.name\\\\\\\", \\\\\\\"support.variable\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#9CDCFE\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Object keys, TS grammar specific\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"meta.object-literal.key\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#9CDCFE\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"CSS property value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.property-value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.font-name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.media-type\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.media\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.other.color.rgb-value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.other.rgb-value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.color\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#CE9178\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"HC Search Editor context line override\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"meta.resultLinePrefix.contextLinePrefix.search\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#CBEDCB\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t],\\\\r\\\\n\\\\t\\\\\\\"semanticHighlighting\\\\\\\": true,\\\\r\\\\n\\\\t\\\\\\\"semanticTokenColors\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\\\\"newOperator\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"stringLiteral\\\\\\\": \\\\\\\"#ce9178\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"customLiteral\\\\\\\": \\\\\\\"#DCDCAA\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"numberLiteral\\\\\\\": \\\\\\\"#b5cea8\\\\\\\"\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":9457,\\\"mimeType\\\":\\\"application/json\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.959Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.959Z\\\",\\\"isDirectory\\\":false},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":447},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\theme\\\\\\\\default-themes\\\\\\\\hc_light.json\\\":{\\\"content\\\":\\\"{\\\\r\\\\n\\\\t\\\\\\\"$schema\\\\\\\": \\\\\\\"vscode://schemas/color-theme\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Light High Contrast\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"tokenColors\\\\\\\": [\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"meta.embedded\\\\\\\", \\\\\\\"source.groovy.embedded\\\\\\\", \\\\\\\"variable.legacy.builtin.python\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#292929\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"emphasis\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"italic\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"strong\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"bold\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"meta.diff.header\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#062F4A\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"comment\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#515151\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"constant.language\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0F4A85\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.numeric\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"variable.other.enummember\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.plus.exponent\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.minus.exponent\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#096d48\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"constant.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#811F3F\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"entity.name.tag\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0F4A85\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"entity.name.selector\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0F4A85\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"entity.other.attribute-name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#264F78\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.class.css\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.css entity.other.attribute-name.class\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.id.css\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.parent-selector.css\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.parent.less\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.css entity.other.attribute-name.pseudo-class\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.pseudo-element.css\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.css.less entity.other.attribute-name.id\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.scss\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0F4A85\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"invalid\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#B5200D\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.underline\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"underline\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.bold\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#000080\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"bold\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.heading\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0F4A85\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"bold\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.italic\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"italic\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.strikethrough\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"strikethrough\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.inserted\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#096d48\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.deleted\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#5A5A5A\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.changed\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0451A5\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"punctuation.definition.quote.begin.markdown\\\\\\\", \\\\\\\"punctuation.definition.list.begin.markdown\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0451A5\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.inline.raw\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0F4A85\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"punctuation.definition.tag\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0F4A85\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"meta.preprocessor\\\\\\\", \\\\\\\"entity.name.function.preprocessor\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0F4A85\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"meta.preprocessor.string\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#b5200d\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"meta.preprocessor.numeric\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#096d48\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"meta.structure.dictionary.key.python\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0451A5\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"storage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0F4A85\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"storage.type\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0F4A85\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"storage.modifier\\\\\\\", \\\\\\\"keyword.operator.noexcept\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0F4A85\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"string\\\\\\\", \\\\\\\"meta.embedded.assembly\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0F4A85\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.comment.buffered.block.pug\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.quoted.pug\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.interpolated.pug\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.unquoted.plain.in.yaml\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.unquoted.plain.out.yaml\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.unquoted.block.yaml\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.quoted.single.yaml\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.quoted.double.xml\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.quoted.single.xml\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.unquoted.cdata.xml\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.quoted.double.html\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.quoted.single.html\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.unquoted.html\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.quoted.single.handlebars\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.quoted.double.handlebars\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0F4A85\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"string.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#811F3F\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.definition.template-expression.begin\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.definition.template-expression.end\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.section.embedded\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0F4A85\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"meta.template.expression\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#000000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.property-value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.font-name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.media-type\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.media\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.other.color.rgb-value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.other.rgb-value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.color\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0451A5\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.type.vendored.property-name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.type.property-name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.css variable\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.coffee.embedded\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#264F78\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"support.type.property-name.json\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0451A5\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"keyword\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0F4A85\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"keyword.control\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0F4A85\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"keyword.operator\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#000000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.new\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.expression\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.cast\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.sizeof\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.alignof\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.typeid\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.alignas\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.instanceof\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.logical.python\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.wordlike\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0F4A85\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"keyword.other.unit\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#096d48\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"punctuation.section.embedded.begin.php\\\\\\\", \\\\\\\"punctuation.section.embedded.end.php\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0F4A85\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"support.function.git-rebase\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0451A5\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"constant.sha.git-rebase\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#096d48\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"storage.modifier.import.java\\\\\\\", \\\\\\\"variable.language.wildcard.java\\\\\\\", \\\\\\\"storage.modifier.package.java\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#000000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"variable.language\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0F4A85\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.function\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.function\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.handlebars\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.powershell variable.other.member\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.operator.custom-literal\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#5e2cbc\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.class\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.type\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.type\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.namespace\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.scope-resolution\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.class\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.numeric.go\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.byte.go\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.boolean.go\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.string.go\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.uintptr.go\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.error.go\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.rune.go\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.cs\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.generic.cs\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.modifier.cs\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.variable.cs\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.annotation.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.generic.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.object.array.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.primitive.array.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.primitive.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.token.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.annotation.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.parameters.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.generic.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.object.array.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.primitive.array.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.primitive.groovy\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#185E73\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"meta.type.cast.expr\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"meta.type.new.expr\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.math\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.dom\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.json\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.inherited-class\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#185E73\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.control\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.cpp keyword.operator.new\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.cpp keyword.operator.delete\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.other.using\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.other.directive.using\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.other.operator\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.operator\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#b5200d\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"variable\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"meta.definition.variable.name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.variable\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.variable\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.other.placeholder\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#001080\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"variable.other.constant\\\\\\\", \\\\\\\"variable.other.enummember\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#02715D\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"meta.object-literal.key\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#001080\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.property-value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.font-name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.media-type\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.media\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.other.color.rgb-value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.other.rgb-value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.color\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0451A5\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.definition.group.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.definition.group.assertion.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.definition.character-class.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.character.set.begin.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.character.set.end.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.negation.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.other.parenthesis.regexp\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#D16969\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.character.character-class.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.other.character-class.set.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.other.character-class.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.character.set.regexp\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#811F3F\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"keyword.operator.quantifier.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#000000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"keyword.operator.or.regexp\\\\\\\", \\\\\\\"keyword.control.anchor.regexp\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#EE0000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"constant.character\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0F4A85\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"constant.character.escape\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#EE0000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"entity.name.label\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#000000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"token.info-token\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#316BCD\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"token.warn-token\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#CD9731\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"token.error-token\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#CD3131\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"token.debug-token\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#800080\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t],\\\\r\\\\n\\\\t\\\\\\\"colors\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\\\\"actionBar.toggledBackground\\\\\\\": \\\\\\\"#dddddd\\\\\\\"\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":11685,\\\"mimeType\\\":\\\"application/json\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.959Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.959Z\\\",\\\"isDirectory\\\":false},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":560},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\theme\\\\\\\\default-themes\\\\\\\\light_modern.json\\\":{\\\"content\\\":\\\"{\\\\r\\\\n\\\\t\\\\\\\"$schema\\\\\\\": \\\\\\\"vscode://schemas/color-theme\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Default Light Modern\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"include\\\\\\\": \\\\\\\"./light_plus.json\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"colors\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\\\\"activityBar.activeBorder\\\\\\\": \\\\\\\"#005FB8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"activityBar.background\\\\\\\": \\\\\\\"#F8F8F8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"activityBar.border\\\\\\\": \\\\\\\"#E5E5E5\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"activityBar.foreground\\\\\\\": \\\\\\\"#1F1F1F\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"activityBar.inactiveForeground\\\\\\\": \\\\\\\"#616161\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"activityBarBadge.background\\\\\\\": \\\\\\\"#005FB8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"activityBarBadge.foreground\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"badge.background\\\\\\\": \\\\\\\"#CCCCCC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"badge.foreground\\\\\\\": \\\\\\\"#3B3B3B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"button.background\\\\\\\": \\\\\\\"#005FB8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"button.border\\\\\\\": \\\\\\\"#0000001a\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"button.foreground\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"button.hoverBackground\\\\\\\": \\\\\\\"#0258A8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"button.secondaryBackground\\\\\\\": \\\\\\\"#E5E5E5\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"button.secondaryForeground\\\\\\\": \\\\\\\"#3B3B3B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"button.secondaryHoverBackground\\\\\\\": \\\\\\\"#CCCCCC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"chat.slashCommandBackground\\\\\\\": \\\\\\\"#D2ECFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"chat.slashCommandForeground\\\\\\\": \\\\\\\"#306CA2\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"checkbox.background\\\\\\\": \\\\\\\"#F8F8F8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"checkbox.border\\\\\\\": \\\\\\\"#CECECE\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"descriptionForeground\\\\\\\": \\\\\\\"#3B3B3B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"dropdown.background\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"dropdown.border\\\\\\\": \\\\\\\"#CECECE\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"dropdown.foreground\\\\\\\": \\\\\\\"#3B3B3B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"dropdown.listBackground\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editor.background\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editor.foreground\\\\\\\": \\\\\\\"#3B3B3B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editor.inactiveSelectionBackground\\\\\\\": \\\\\\\"#E5EBF1\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editor.selectionHighlightBackground\\\\\\\": \\\\\\\"#ADD6FF80\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorGroup.border\\\\\\\": \\\\\\\"#E5E5E5\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorGroupHeader.tabsBackground\\\\\\\": \\\\\\\"#F8F8F8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorGroupHeader.tabsBorder\\\\\\\": \\\\\\\"#E5E5E5\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorGutter.addedBackground\\\\\\\": \\\\\\\"#2EA043\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorGutter.deletedBackground\\\\\\\": \\\\\\\"#F85149\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorGutter.modifiedBackground\\\\\\\": \\\\\\\"#005FB8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorIndentGuide.background1\\\\\\\": \\\\\\\"#D3D3D3\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorLineNumber.activeForeground\\\\\\\": \\\\\\\"#171184\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorLineNumber.foreground\\\\\\\": \\\\\\\"#6E7681\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorOverviewRuler.border\\\\\\\": \\\\\\\"#E5E5E5\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorSuggestWidget.background\\\\\\\": \\\\\\\"#F8F8F8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorWidget.background\\\\\\\": \\\\\\\"#F8F8F8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"errorForeground\\\\\\\": \\\\\\\"#F85149\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"focusBorder\\\\\\\": \\\\\\\"#005FB8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#3B3B3B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"icon.foreground\\\\\\\": \\\\\\\"#3B3B3B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"input.background\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"input.border\\\\\\\": \\\\\\\"#CECECE\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"input.foreground\\\\\\\": \\\\\\\"#3B3B3B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"input.placeholderForeground\\\\\\\": \\\\\\\"#767676\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"inputOption.activeBackground\\\\\\\": \\\\\\\"#BED6ED\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"inputOption.activeBorder\\\\\\\": \\\\\\\"#005FB8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"inputOption.activeForeground\\\\\\\": \\\\\\\"#000000\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"keybindingLabel.foreground\\\\\\\": \\\\\\\"#3B3B3B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"list.activeSelectionBackground\\\\\\\": \\\\\\\"#E8E8E8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"list.activeSelectionForeground\\\\\\\": \\\\\\\"#000000\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"list.activeSelectionIconForeground\\\\\\\": \\\\\\\"#000000\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"list.hoverBackground\\\\\\\": \\\\\\\"#F2F2F2\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"list.focusAndSelectionOutline\\\\\\\": \\\\\\\"#005FB8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"menu.border\\\\\\\": \\\\\\\"#CECECE\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"notebook.cellBorderColor\\\\\\\": \\\\\\\"#E5E5E5\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"notebook.selectedCellBackground\\\\\\\": \\\\\\\"#C8DDF150\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"notificationCenterHeader.background\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"notificationCenterHeader.foreground\\\\\\\": \\\\\\\"#3B3B3B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"notifications.background\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"notifications.border\\\\\\\": \\\\\\\"#E5E5E5\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"notifications.foreground\\\\\\\": \\\\\\\"#3B3B3B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"panel.background\\\\\\\": \\\\\\\"#F8F8F8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"panel.border\\\\\\\": \\\\\\\"#E5E5E5\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"panelInput.border\\\\\\\": \\\\\\\"#E5E5E5\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"panelTitle.activeBorder\\\\\\\": \\\\\\\"#005FB8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"panelTitle.activeForeground\\\\\\\": \\\\\\\"#3B3B3B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"panelTitle.inactiveForeground\\\\\\\": \\\\\\\"#3B3B3B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"peekViewEditor.matchHighlightBackground\\\\\\\": \\\\\\\"#BB800966\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"peekViewResult.background\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"peekViewResult.matchHighlightBackground\\\\\\\": \\\\\\\"#BB800966\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"pickerGroup.border\\\\\\\": \\\\\\\"#E5E5E5\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"pickerGroup.foreground\\\\\\\": \\\\\\\"#8B949E\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"ports.iconRunningProcessForeground\\\\\\\": \\\\\\\"#369432\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"progressBar.background\\\\\\\": \\\\\\\"#005FB8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"quickInput.background\\\\\\\": \\\\\\\"#F8F8F8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"quickInput.foreground\\\\\\\": \\\\\\\"#3B3B3B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"searchEditor.textInputBorder\\\\\\\": \\\\\\\"#CECECE\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"settings.dropdownBackground\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"settings.dropdownBorder\\\\\\\": \\\\\\\"#CECECE\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"settings.headerForeground\\\\\\\": \\\\\\\"#1F1F1F\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"settings.modifiedItemIndicator\\\\\\\": \\\\\\\"#BB800966\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"settings.numberInputBorder\\\\\\\": \\\\\\\"#CECECE\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"settings.textInputBorder\\\\\\\": \\\\\\\"#CECECE\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"sideBar.background\\\\\\\": \\\\\\\"#F8F8F8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"sideBar.border\\\\\\\": \\\\\\\"#E5E5E5\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"sideBar.foreground\\\\\\\": \\\\\\\"#3B3B3B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"sideBarSectionHeader.background\\\\\\\": \\\\\\\"#F8F8F8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"sideBarSectionHeader.border\\\\\\\": \\\\\\\"#E5E5E5\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"sideBarSectionHeader.foreground\\\\\\\": \\\\\\\"#3B3B3B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"sideBarTitle.foreground\\\\\\\": \\\\\\\"#3B3B3B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBar.background\\\\\\\": \\\\\\\"#F8F8F8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBar.foreground\\\\\\\": \\\\\\\"#3B3B3B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBar.border\\\\\\\": \\\\\\\"#E5E5E5\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBar.debuggingBackground\\\\\\\": \\\\\\\"#FD716C\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBar.debuggingForeground\\\\\\\": \\\\\\\"#000000\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBar.focusBorder\\\\\\\": \\\\\\\"#005FB8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBar.noFolderBackground\\\\\\\": \\\\\\\"#F8F8F8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBarItem.errorBackground\\\\\\\": \\\\\\\"#C72E0F\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBarItem.focusBorder\\\\\\\": \\\\\\\"#005FB8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBarItem.prominentBackground\\\\\\\": \\\\\\\"#6E768166\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBarItem.remoteBackground\\\\\\\": \\\\\\\"#005FB8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBarItem.remoteForeground\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.activeBackground\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.activeBorder\\\\\\\": \\\\\\\"#F8F8F8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.activeBorderTop\\\\\\\": \\\\\\\"#005FB8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.activeForeground\\\\\\\": \\\\\\\"#3B3B3B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.border\\\\\\\": \\\\\\\"#E5E5E5\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.hoverBackground\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.inactiveBackground\\\\\\\": \\\\\\\"#F8F8F8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.inactiveForeground\\\\\\\": \\\\\\\"#868686\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.lastPinnedBorder\\\\\\\": \\\\\\\"#D4D4D4\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.unfocusedActiveBorder\\\\\\\": \\\\\\\"#F8F8F8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.unfocusedActiveBorderTop\\\\\\\": \\\\\\\"#E5E5E5\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.unfocusedHoverBackground\\\\\\\": \\\\\\\"#F8F8F8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"terminalCursor.foreground\\\\\\\": \\\\\\\"#005FB8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"terminal.foreground\\\\\\\": \\\\\\\"#3B3B3B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"terminal.inactiveSelectionBackground\\\\\\\": \\\\\\\"#E5EBF1\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"terminal.tab.activeBorder\\\\\\\": \\\\\\\"#005FB8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"textBlockQuote.background\\\\\\\": \\\\\\\"#F8F8F8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"textBlockQuote.border\\\\\\\": \\\\\\\"#E5E5E5\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"textCodeBlock.background\\\\\\\": \\\\\\\"#F8F8F8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"textLink.activeForeground\\\\\\\": \\\\\\\"#005FB8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"textLink.foreground\\\\\\\": \\\\\\\"#005FB8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"textPreformat.foreground\\\\\\\": \\\\\\\"#3B3B3B\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"textPreformat.background\\\\\\\": \\\\\\\"#0000001F\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"textSeparator.foreground\\\\\\\": \\\\\\\"#21262D\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"titleBar.activeBackground\\\\\\\": \\\\\\\"#F8F8F8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"titleBar.activeForeground\\\\\\\": \\\\\\\"#1E1E1E\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"titleBar.border\\\\\\\": \\\\\\\"#E5E5E5\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"titleBar.inactiveBackground\\\\\\\": \\\\\\\"#F8F8F8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"titleBar.inactiveForeground\\\\\\\": \\\\\\\"#8B949E\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"welcomePage.tileBackground\\\\\\\": \\\\\\\"#F3F3F3\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"widget.border\\\\\\\": \\\\\\\"#E5E5E5\\\\\\\"\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":5906,\\\"mimeType\\\":\\\"application/json\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.848Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.848Z\\\",\\\"isDirectory\\\":false},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":146},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\theme\\\\\\\\default-themes\\\\\\\\light_plus.json\\\":{\\\"content\\\":\\\"{\\\\r\\\\n\\\\t\\\\\\\"$schema\\\\\\\": \\\\\\\"vscode://schemas/color-theme\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Light+\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"include\\\\\\\": \\\\\\\"./light_vs.json\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"tokenColors\\\\\\\": [\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Function declarations\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.function\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.function\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.handlebars\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.powershell variable.other.member\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.operator.custom-literal\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#795E26\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Types declaration and references\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.class\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.type\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.type\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.namespace\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.scope-resolution\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.class\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.numeric.go\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.byte.go\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.boolean.go\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.string.go\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.uintptr.go\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.error.go\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.rune.go\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.cs\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.generic.cs\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.modifier.cs\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.variable.cs\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.annotation.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.generic.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.object.array.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.primitive.array.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.primitive.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.token.java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.annotation.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.parameters.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.generic.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.object.array.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.primitive.array.groovy\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"storage.type.primitive.groovy\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#267f99\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Types declaration and references, TS grammar specific\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"meta.type.cast.expr\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"meta.type.new.expr\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.math\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.dom\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.json\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.inherited-class\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#267f99\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Control flow / Special keywords\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.control\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.cpp keyword.operator.new\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.cpp keyword.operator.delete\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.other.using\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.other.directive.using\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.other.operator\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.operator\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#AF00DB\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Variable and parameter name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"variable\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"meta.definition.variable.name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.variable\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.name.variable\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.other.placeholder\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#001080\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Constants and enums\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"variable.other.constant\\\\\\\", \\\\\\\"variable.other.enummember\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0070C1\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Object keys, TS grammar specific\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"meta.object-literal.key\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#001080\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"CSS property value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.property-value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.font-name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.media-type\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.media\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.other.color.rgb-value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.other.rgb-value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.color\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0451a5\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Regular expression groups\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.definition.group.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.definition.group.assertion.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.definition.character-class.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.character.set.begin.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.character.set.end.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.negation.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.other.parenthesis.regexp\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#d16969\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.character.character-class.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.other.character-class.set.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.other.character-class.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.character.set.regexp\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#811f3f\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"keyword.operator.quantifier.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#000000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"keyword.operator.or.regexp\\\\\\\", \\\\\\\"keyword.control.anchor.regexp\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#EE0000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"constant.character\\\\\\\", \\\\\\\"constant.other.option\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0000ff\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"constant.character.escape\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#EE0000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"entity.name.label\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#000000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t],\\\\r\\\\n\\\\t\\\\\\\"semanticHighlighting\\\\\\\": true,\\\\r\\\\n\\\\t\\\\\\\"semanticTokenColors\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\\\\"newOperator\\\\\\\": \\\\\\\"#AF00DB\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"stringLiteral\\\\\\\": \\\\\\\"#a31515\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"customLiteral\\\\\\\": \\\\\\\"#795E26\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"numberLiteral\\\\\\\": \\\\\\\"#098658\\\\\\\"\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":4755,\\\"mimeType\\\":\\\"application/json\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.849Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.848Z\\\",\\\"isDirectory\\\":false},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":195},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\theme\\\\\\\\default-themes\\\\\\\\light_vs.json\\\":{\\\"content\\\":\\\"{\\\\r\\\\n\\\\t\\\\\\\"$schema\\\\\\\": \\\\\\\"vscode://schemas/color-theme\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Light (Visual Studio)\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"colors\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\\\\"checkbox.border\\\\\\\": \\\\\\\"#919191\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editor.background\\\\\\\": \\\\\\\"#FFFFFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editor.foreground\\\\\\\": \\\\\\\"#000000\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editor.inactiveSelectionBackground\\\\\\\": \\\\\\\"#E5EBF1\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorIndentGuide.background1\\\\\\\": \\\\\\\"#D3D3D3\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorIndentGuide.activeBackground1\\\\\\\": \\\\\\\"#939393\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editor.selectionHighlightBackground\\\\\\\": \\\\\\\"#ADD6FF80\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"editorSuggestWidget.background\\\\\\\": \\\\\\\"#F3F3F3\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"activityBarBadge.background\\\\\\\": \\\\\\\"#007ACC\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"sideBarTitle.foreground\\\\\\\": \\\\\\\"#6F6F6F\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"list.hoverBackground\\\\\\\": \\\\\\\"#E8E8E8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"menu.border\\\\\\\": \\\\\\\"#D4D4D4\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"input.placeholderForeground\\\\\\\": \\\\\\\"#767676\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"searchEditor.textInputBorder\\\\\\\": \\\\\\\"#CECECE\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"settings.textInputBorder\\\\\\\": \\\\\\\"#CECECE\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"settings.numberInputBorder\\\\\\\": \\\\\\\"#CECECE\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBarItem.remoteForeground\\\\\\\": \\\\\\\"#FFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBarItem.remoteBackground\\\\\\\": \\\\\\\"#16825D\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"ports.iconRunningProcessForeground\\\\\\\": \\\\\\\"#369432\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"sideBarSectionHeader.background\\\\\\\": \\\\\\\"#0000\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"sideBarSectionHeader.border\\\\\\\": \\\\\\\"#61616130\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tab.lastPinnedBorder\\\\\\\": \\\\\\\"#61616130\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"notebook.cellBorderColor\\\\\\\": \\\\\\\"#E8E8E8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"notebook.selectedCellBackground\\\\\\\": \\\\\\\"#c8ddf150\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"statusBarItem.errorBackground\\\\\\\": \\\\\\\"#c72e0f\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"list.activeSelectionIconForeground\\\\\\\": \\\\\\\"#FFF\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"list.focusAndSelectionOutline\\\\\\\": \\\\\\\"#90C2F9\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"terminal.inactiveSelectionBackground\\\\\\\": \\\\\\\"#E5EBF1\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"widget.border\\\\\\\": \\\\\\\"#d4d4d4\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"actionBar.toggledBackground\\\\\\\": \\\\\\\"#dddddd\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"diffEditor.unchangedRegionBackground\\\\\\\": \\\\\\\"#f8f8f8\\\\\\\"\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"tokenColors\\\\\\\": [\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"meta.embedded\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.groovy.embedded\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string meta.image.inline.markdown\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"variable.legacy.builtin.python\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#000000ff\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"emphasis\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"italic\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"strong\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"bold\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"meta.diff.header\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#000080\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"comment\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#008000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"constant.language\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0000ff\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.numeric\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"variable.other.enummember\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.plus.exponent\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.minus.exponent\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#098658\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"constant.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#811f3f\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"css tags in selectors, xml tags\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"entity.name.tag\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#800000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"entity.name.selector\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#800000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"entity.other.attribute-name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#e50000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.class.css\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.css entity.other.attribute-name.class\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.id.css\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.parent-selector.css\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.parent.less\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.css entity.other.attribute-name.pseudo-class\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.pseudo-element.css\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.css.less entity.other.attribute-name.id\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"entity.other.attribute-name.scss\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#800000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"invalid\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#cd3131\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.underline\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"underline\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.bold\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"bold\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#000080\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.heading\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"bold\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#800000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.italic\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"italic\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.strikethrough\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"fontStyle\\\\\\\": \\\\\\\"strikethrough\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.inserted\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#098658\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.deleted\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#a31515\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.changed\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0451a5\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"punctuation.definition.quote.begin.markdown\\\\\\\", \\\\\\\"punctuation.definition.list.begin.markdown\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0451a5\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"markup.inline.raw\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#800000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"brackets of XML/HTML tags\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"punctuation.definition.tag\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#800000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"meta.preprocessor\\\\\\\", \\\\\\\"entity.name.function.preprocessor\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0000ff\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"meta.preprocessor.string\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#a31515\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"meta.preprocessor.numeric\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#098658\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"meta.structure.dictionary.key.python\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0451a5\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"storage\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0000ff\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"storage.type\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0000ff\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"storage.modifier\\\\\\\", \\\\\\\"keyword.operator.noexcept\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0000ff\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"string\\\\\\\", \\\\\\\"meta.embedded.assembly\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#a31515\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.comment.buffered.block.pug\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.quoted.pug\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.interpolated.pug\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.unquoted.plain.in.yaml\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.unquoted.plain.out.yaml\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.unquoted.block.yaml\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.quoted.single.yaml\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.quoted.double.xml\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.quoted.single.xml\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.unquoted.cdata.xml\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.quoted.double.html\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.quoted.single.html\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.unquoted.html\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.quoted.single.handlebars\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"string.quoted.double.handlebars\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0000ff\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"string.regexp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#811f3f\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"String interpolation\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.definition.template-expression.begin\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.definition.template-expression.end\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"punctuation.section.embedded\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0000ff\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"Reset JavaScript string interpolation expression\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"meta.template.expression\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#000000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.property-value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.font-name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.media-type\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.media\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.other.color.rgb-value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"constant.other.rgb-value\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.constant.color\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0451a5\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.type.vendored.property-name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"support.type.property-name\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.css variable\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"source.coffee.embedded\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#e50000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"support.type.property-name.json\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0451a5\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"keyword\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0000ff\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"keyword.control\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0000ff\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"keyword.operator\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#000000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.new\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.expression\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.cast\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.sizeof\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.alignof\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.typeid\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.alignas\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.instanceof\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.logical.python\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"keyword.operator.wordlike\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0000ff\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"keyword.other.unit\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#098658\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"punctuation.section.embedded.begin.php\\\\\\\", \\\\\\\"punctuation.section.embedded.end.php\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#800000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"support.function.git-rebase\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0451a5\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"constant.sha.git-rebase\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#098658\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"coloring of the Java import and package identifiers\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": [\\\\\\\"storage.modifier.import.java\\\\\\\", \\\\\\\"variable.language.wildcard.java\\\\\\\", \\\\\\\"storage.modifier.package.java\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#000000\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"name\\\\\\\": \\\\\\\"this.self\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"scope\\\\\\\": \\\\\\\"variable.language\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\\\\"settings\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"foreground\\\\\\\": \\\\\\\"#0000ff\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t],\\\\r\\\\n\\\\t\\\\\\\"semanticHighlighting\\\\\\\": true,\\\\r\\\\n\\\\t\\\\\\\"semanticTokenColors\\\\\\\": {\\\\r\\\\n\\\\t\\\\t\\\\\\\"newOperator\\\\\\\": \\\\\\\"#0000ff\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"stringLiteral\\\\\\\": \\\\\\\"#a31515\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"customLiteral\\\\\\\": \\\\\\\"#000000\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"numberLiteral\\\\\\\": \\\\\\\"#098658\\\\\\\"\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":9153,\\\"mimeType\\\":\\\"application/json\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.959Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.959Z\\\",\\\"isDirectory\\\":false},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":413},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\theme\\\\\\\\getTheme.ts\\\":{\\\"content\\\":\\\"import * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\nimport * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport * as fs from \\\\\\\"fs/promises\\\\\\\"\\\\r\\\\nimport { convertTheme } from \\\\\\\"monaco-vscode-textmate-theme-converter/lib/cjs\\\\\\\"\\\\r\\\\n\\\\r\\\\nconst defaultThemes: Record<string, string> = {\\\\r\\\\n\\\\t\\\\\\\"Default Dark Modern\\\\\\\": \\\\\\\"dark_modern\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"Dark+\\\\\\\": \\\\\\\"dark_plus\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"Default Dark+\\\\\\\": \\\\\\\"dark_plus\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"Dark (Visual Studio)\\\\\\\": \\\\\\\"dark_vs\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"Visual Studio Dark\\\\\\\": \\\\\\\"dark_vs\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"Dark High Contrast\\\\\\\": \\\\\\\"hc_black\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"Default High Contrast\\\\\\\": \\\\\\\"hc_black\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"Light High Contrast\\\\\\\": \\\\\\\"hc_light\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"Default High Contrast Light\\\\\\\": \\\\\\\"hc_light\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"Default Light Modern\\\\\\\": \\\\\\\"light_modern\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"Light+\\\\\\\": \\\\\\\"light_plus\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"Default Light+\\\\\\\": \\\\\\\"light_plus\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"Light (Visual Studio)\\\\\\\": \\\\\\\"light_vs\\\\\\\",\\\\r\\\\n\\\\t\\\\\\\"Visual Studio Light\\\\\\\": \\\\\\\"light_vs\\\\\\\",\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nfunction parseThemeString(themeString: string | undefined): any {\\\\r\\\\n\\\\tthemeString = themeString\\\\r\\\\n\\\\t\\\\t?.split(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\t\\\\t.filter((line) => {\\\\r\\\\n\\\\t\\\\t\\\\treturn !line.trim().startsWith(\\\\\\\"//\\\\\\\")\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t.join(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\treturn JSON.parse(themeString ?? \\\\\\\"{}\\\\\\\")\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport async function getTheme() {\\\\r\\\\n\\\\tlet currentTheme = undefined\\\\r\\\\n\\\\tconst colorTheme = vscode.workspace.getConfiguration(\\\\\\\"workbench\\\\\\\").get<string>(\\\\\\\"colorTheme\\\\\\\") || \\\\\\\"Default Dark Modern\\\\\\\"\\\\r\\\\n\\\\r\\\\n\\\\ttry {\\\\r\\\\n\\\\t\\\\tfor (let i = vscode.extensions.all.length - 1; i >= 0; i--) {\\\\r\\\\n\\\\t\\\\t\\\\tif (currentTheme) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tconst extension = vscode.extensions.all[i]\\\\r\\\\n\\\\t\\\\t\\\\tif (extension.packageJSON?.contributes?.themes?.length > 0) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tfor (const theme of extension.packageJSON.contributes.themes) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (theme.label === colorTheme) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconst themePath = path.join(extension.extensionPath, theme.path)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcurrentTheme = await fs.readFile(themePath, \\\\\\\"utf-8\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (currentTheme === undefined && defaultThemes[colorTheme]) {\\\\r\\\\n\\\\t\\\\t\\\\tconst filename = `${defaultThemes[colorTheme]}.json`\\\\r\\\\n\\\\t\\\\t\\\\tcurrentTheme = await fs.readFile(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tpath.join(getExtensionUri().fsPath, \\\\\\\"src\\\\\\\", \\\\\\\"integrations\\\\\\\", \\\\\\\"theme\\\\\\\", \\\\\\\"default-themes\\\\\\\", filename),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"utf-8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Strip comments from theme\\\\r\\\\n\\\\t\\\\tlet parsed = parseThemeString(currentTheme)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (parsed.include) {\\\\r\\\\n\\\\t\\\\t\\\\tconst includeThemeString = await fs.readFile(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tpath.join(getExtensionUri().fsPath, \\\\\\\"src\\\\\\\", \\\\\\\"integrations\\\\\\\", \\\\\\\"theme\\\\\\\", \\\\\\\"default-themes\\\\\\\", parsed.include),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"utf-8\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t\\\\tconst includeTheme = parseThemeString(includeThemeString)\\\\r\\\\n\\\\t\\\\t\\\\tparsed = mergeJson(parsed, includeTheme)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst converted = convertTheme(parsed)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconverted.base = (\\\\r\\\\n\\\\t\\\\t\\\\t[\\\\\\\"vs\\\\\\\", \\\\\\\"hc-black\\\\\\\"].includes(converted.base) ? converted.base : colorTheme.includes(\\\\\\\"Light\\\\\\\") ? \\\\\\\"vs\\\\\\\" : \\\\\\\"vs-dark\\\\\\\"\\\\r\\\\n\\\\t\\\\t) as any\\\\r\\\\n\\\\r\\\\n\\\\t\\\\treturn converted\\\\r\\\\n\\\\t} catch (e) {\\\\r\\\\n\\\\t\\\\tconsole.log(\\\\\\\"Error loading color theme: \\\\\\\", e)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\treturn undefined\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\ntype JsonObject = { [key: string]: any }\\\\r\\\\nexport function mergeJson(\\\\r\\\\n\\\\tfirst: JsonObject,\\\\r\\\\n\\\\tsecond: JsonObject,\\\\r\\\\n\\\\tmergeBehavior?: \\\\\\\"merge\\\\\\\" | \\\\\\\"overwrite\\\\\\\",\\\\r\\\\n\\\\tmergeKeys?: { [key: string]: (a: any, b: any) => boolean },\\\\r\\\\n): any {\\\\r\\\\n\\\\tconst copyOfFirst = JSON.parse(JSON.stringify(first))\\\\r\\\\n\\\\r\\\\n\\\\ttry {\\\\r\\\\n\\\\t\\\\tfor (const key in second) {\\\\r\\\\n\\\\t\\\\t\\\\tconst secondValue = second[key]\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tif (!(key in copyOfFirst) || mergeBehavior === \\\\\\\"overwrite\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// New value\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcopyOfFirst[key] = secondValue\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcontinue\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tconst firstValue = copyOfFirst[key]\\\\r\\\\n\\\\t\\\\t\\\\tif (Array.isArray(secondValue) && Array.isArray(firstValue)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Array\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (mergeKeys?.[key]) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Merge keys are used to determine whether an item form the second object should override one from the first\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst keptFromFirst: any[] = []\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tfirstValue.forEach((item: any) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (!secondValue.some((item2: any) => mergeKeys[key](item, item2))) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tkeptFromFirst.push(item)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcopyOfFirst[key] = [...keptFromFirst, ...secondValue]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcopyOfFirst[key] = [...firstValue, ...secondValue]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t} else if (typeof secondValue === \\\\\\\"object\\\\\\\" && typeof firstValue === \\\\\\\"object\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Object\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcopyOfFirst[key] = mergeJson(firstValue, secondValue, mergeBehavior)\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Other (boolean, number, string)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcopyOfFirst[key] = secondValue\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn copyOfFirst\\\\r\\\\n\\\\t} catch (e) {\\\\r\\\\n\\\\t\\\\tconsole.error(\\\\\\\"Error merging JSON\\\\\\\", e, copyOfFirst, second)\\\\r\\\\n\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\t...copyOfFirst,\\\\r\\\\n\\\\t\\\\t\\\\t...second,\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nfunction getExtensionUri(): vscode.Uri {\\\\r\\\\n\\\\treturn vscode.extensions.getExtension(\\\\\\\"saoudrizwan.claude-dev\\\\\\\")!.extensionUri\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":4348,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.960Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.960Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":142,\\\"nonEmptyLines\\\":126,\\\"commentLines\\\":6,\\\"complexity\\\":36},\\\"dependencies\\\":[\\\"vscode\\\",\\\"path\\\",\\\"fs/promises\\\",\\\"monaco-vscode-textmate-theme-converter/lib/cjs\\\"],\\\"quality\\\":{\\\"score\\\":-45,\\\"issues\\\":[],\\\"duplicateLines\\\":27,\\\"longLines\\\":5,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.380Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":142},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\workspace\\\\\\\\get-python-env.ts\\\":{\\\"content\\\":\\\"import * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\n\\\\r\\\\n/*\\\\r\\\\nUsed to get user's current python environment (unnecessary now that we use the IDE's terminal)\\\\r\\\\n${await (async () => {\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tconst pythonEnvPath = await getPythonEnvPath()\\\\r\\\\n\\\\t\\\\t\\\\tif (pythonEnvPath) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn `\\\\\\\\nPython Environment: ${pythonEnvPath}`\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t} catch {}\\\\r\\\\n\\\\t\\\\treturn \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t})()}\\\\r\\\\n*/\\\\r\\\\nexport async function getPythonEnvPath(): Promise<string | undefined> {\\\\r\\\\n\\\\tconst pythonExtension = vscode.extensions.getExtension(\\\\\\\"ms-python.python\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\tif (!pythonExtension) {\\\\r\\\\n\\\\t\\\\treturn undefined\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Ensure the Python extension is activated\\\\r\\\\n\\\\tif (!pythonExtension.isActive) {\\\\r\\\\n\\\\t\\\\t// if the python extension is not active, we can assume the project is not a python project\\\\r\\\\n\\\\t\\\\treturn undefined\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Access the Python extension API\\\\r\\\\n\\\\tconst pythonApi = pythonExtension.exports\\\\r\\\\n\\\\t// Get the active environment path for the current workspace\\\\r\\\\n\\\\tconst workspaceFolder = vscode.workspace.workspaceFolders?.[0]\\\\r\\\\n\\\\tif (!workspaceFolder) {\\\\r\\\\n\\\\t\\\\treturn undefined\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\t// Get the active python environment path for the current workspace\\\\r\\\\n\\\\tconst pythonEnv = await pythonApi?.environments?.getActiveEnvironmentPath(workspaceFolder.uri)\\\\r\\\\n\\\\tif (pythonEnv && pythonEnv.path) {\\\\r\\\\n\\\\t\\\\treturn pythonEnv.path\\\\r\\\\n\\\\t} else {\\\\r\\\\n\\\\t\\\\treturn undefined\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":1275,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.850Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.850Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":43,\\\"nonEmptyLines\\\":38,\\\"commentLines\\\":6,\\\"complexity\\\":11},\\\"dependencies\\\":[\\\"vscode\\\"],\\\"quality\\\":{\\\"score\\\":60,\\\"issues\\\":[],\\\"duplicateLines\\\":8,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.382Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":43},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\integrations\\\\\\\\workspace\\\\\\\\WorkspaceTracker.ts\\\":{\\\"content\\\":\\\"import * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\nimport * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport { listFiles } from \\\\\\\"../../services/glob/list-files\\\\\\\"\\\\r\\\\nimport { ClineProvider } from \\\\\\\"../../core/webview/ClineProvider\\\\\\\"\\\\r\\\\n\\\\r\\\\nconst cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0)\\\\r\\\\n\\\\r\\\\n// Note: this is not a drop-in replacement for listFiles at the start of tasks, since that will be done for Desktops when there is no workspace selected\\\\r\\\\nclass WorkspaceTracker {\\\\r\\\\n\\\\tprivate providerRef: WeakRef<ClineProvider>\\\\r\\\\n\\\\tprivate disposables: vscode.Disposable[] = []\\\\r\\\\n\\\\tprivate filePaths: Set<string> = new Set()\\\\r\\\\n\\\\r\\\\n\\\\tconstructor(provider: ClineProvider) {\\\\r\\\\n\\\\t\\\\tthis.providerRef = new WeakRef(provider)\\\\r\\\\n\\\\t\\\\tthis.registerListeners()\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync initializeFilePaths() {\\\\r\\\\n\\\\t\\\\t// should not auto get filepaths for desktop since it would immediately show permission popup before cline ever creates a file\\\\r\\\\n\\\\t\\\\tif (!cwd) {\\\\r\\\\n\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst [files, _] = await listFiles(cwd, true, 1_000)\\\\r\\\\n\\\\t\\\\tfiles.forEach((file) => this.filePaths.add(this.normalizeFilePath(file)))\\\\r\\\\n\\\\t\\\\tthis.workspaceDidUpdate()\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate registerListeners() {\\\\r\\\\n\\\\t\\\\t// Listen for file creation\\\\r\\\\n\\\\t\\\\t// .bind(this) ensures the callback refers to class instance when using this, not necessary when using arrow function\\\\r\\\\n\\\\t\\\\tthis.disposables.push(vscode.workspace.onDidCreateFiles(this.onFilesCreated.bind(this)))\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Listen for file deletion\\\\r\\\\n\\\\t\\\\tthis.disposables.push(vscode.workspace.onDidDeleteFiles(this.onFilesDeleted.bind(this)))\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Listen for file renaming\\\\r\\\\n\\\\t\\\\tthis.disposables.push(vscode.workspace.onDidRenameFiles(this.onFilesRenamed.bind(this)))\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t/*\\\\r\\\\n\\\\t\\\\t An event that is emitted when a workspace folder is added or removed.\\\\r\\\\n\\\\t\\\\t **Note:** this event will not fire if the first workspace folder is added, removed or changed,\\\\r\\\\n\\\\t\\\\t because in that case the currently executing extensions (including the one that listens to this\\\\r\\\\n\\\\t\\\\t event) will be terminated and restarted so that the (deprecated) `rootPath` property is updated\\\\r\\\\n\\\\t\\\\t to point to the first workspace folder.\\\\r\\\\n\\\\t\\\\t */\\\\r\\\\n\\\\t\\\\t// In other words, we don't have to worry about the root workspace folder ([0]) changing since the extension will be restarted and our cwd will be updated to reflect the new workspace folder. (We don't care about non root workspace folders, since cline will only be working within the root folder cwd)\\\\r\\\\n\\\\t\\\\t// this.disposables.push(vscode.workspace.onDidChangeWorkspaceFolders(this.onWorkspaceFoldersChanged.bind(this)))\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async onFilesCreated(event: vscode.FileCreateEvent) {\\\\r\\\\n\\\\t\\\\tawait Promise.all(\\\\r\\\\n\\\\t\\\\t\\\\tevent.files.map(async (file) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait this.addFilePath(file.fsPath)\\\\r\\\\n\\\\t\\\\t\\\\t}),\\\\r\\\\n\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\tthis.workspaceDidUpdate()\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async onFilesDeleted(event: vscode.FileDeleteEvent) {\\\\r\\\\n\\\\t\\\\tlet updated = false\\\\r\\\\n\\\\t\\\\tawait Promise.all(\\\\r\\\\n\\\\t\\\\t\\\\tevent.files.map(async (file) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (await this.removeFilePath(file.fsPath)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tupdated = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}),\\\\r\\\\n\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\tif (updated) {\\\\r\\\\n\\\\t\\\\t\\\\tthis.workspaceDidUpdate()\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async onFilesRenamed(event: vscode.FileRenameEvent) {\\\\r\\\\n\\\\t\\\\tawait Promise.all(\\\\r\\\\n\\\\t\\\\t\\\\tevent.files.map(async (file) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait this.removeFilePath(file.oldUri.fsPath)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait this.addFilePath(file.newUri.fsPath)\\\\r\\\\n\\\\t\\\\t\\\\t}),\\\\r\\\\n\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\tthis.workspaceDidUpdate()\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate workspaceDidUpdate() {\\\\r\\\\n\\\\t\\\\tif (!cwd) {\\\\r\\\\n\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tthis.providerRef.deref()?.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\ttype: \\\\\\\"workspaceUpdated\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tfilePaths: Array.from(this.filePaths).map((file) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst relativePath = path.relative(cwd, file).toPosix()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn file.endsWith(\\\\\\\"/\\\\\\\") ? relativePath + \\\\\\\"/\\\\\\\" : relativePath\\\\r\\\\n\\\\t\\\\t\\\\t}),\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate normalizeFilePath(filePath: string): string {\\\\r\\\\n\\\\t\\\\tconst resolvedPath = cwd ? path.resolve(cwd, filePath) : path.resolve(filePath)\\\\r\\\\n\\\\t\\\\treturn filePath.endsWith(\\\\\\\"/\\\\\\\") ? resolvedPath + \\\\\\\"/\\\\\\\" : resolvedPath\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async addFilePath(filePath: string): Promise<string> {\\\\r\\\\n\\\\t\\\\tconst normalizedPath = this.normalizeFilePath(filePath)\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tconst stat = await vscode.workspace.fs.stat(vscode.Uri.file(normalizedPath))\\\\r\\\\n\\\\t\\\\t\\\\tconst isDirectory = (stat.type & vscode.FileType.Directory) !== 0\\\\r\\\\n\\\\t\\\\t\\\\tconst pathWithSlash = isDirectory && !normalizedPath.endsWith(\\\\\\\"/\\\\\\\") ? normalizedPath + \\\\\\\"/\\\\\\\" : normalizedPath\\\\r\\\\n\\\\t\\\\t\\\\tthis.filePaths.add(pathWithSlash)\\\\r\\\\n\\\\t\\\\t\\\\treturn pathWithSlash\\\\r\\\\n\\\\t\\\\t} catch {\\\\r\\\\n\\\\t\\\\t\\\\t// If stat fails, assume it's a file (this can happen for newly created files)\\\\r\\\\n\\\\t\\\\t\\\\tthis.filePaths.add(normalizedPath)\\\\r\\\\n\\\\t\\\\t\\\\treturn normalizedPath\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async removeFilePath(filePath: string): Promise<boolean> {\\\\r\\\\n\\\\t\\\\tconst normalizedPath = this.normalizeFilePath(filePath)\\\\r\\\\n\\\\t\\\\treturn this.filePaths.delete(normalizedPath) || this.filePaths.delete(normalizedPath + \\\\\\\"/\\\\\\\")\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tpublic dispose() {\\\\r\\\\n\\\\t\\\\tthis.disposables.forEach((d) => d.dispose())\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport default WorkspaceTracker\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":4788,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.850Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.850Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":128,\\\"nonEmptyLines\\\":110,\\\"commentLines\\\":10,\\\"complexity\\\":14},\\\"dependencies\\\":[\\\"vscode\\\",\\\"path\\\",\\\"../../services/glob/list-files\\\",\\\"../../core/webview/ClineProvider\\\"],\\\"quality\\\":{\\\"score\\\":-67,\\\"issues\\\":[],\\\"duplicateLines\\\":31,\\\"longLines\\\":6,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.383Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":128},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\services\\\\\\\\browser\\\\\\\\BrowserSession.ts\\\":{\\\"content\\\":\\\"import * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\nimport * as fs from \\\\\\\"fs/promises\\\\\\\"\\\\r\\\\nimport * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport { Browser, Page, ScreenshotOptions, TimeoutError, launch } from \\\\\\\"puppeteer-core\\\\\\\"\\\\r\\\\n// @ts-ignore\\\\r\\\\nimport PCR from \\\\\\\"puppeteer-chromium-resolver\\\\\\\"\\\\r\\\\nimport pWaitFor from \\\\\\\"p-wait-for\\\\\\\"\\\\r\\\\nimport delay from \\\\\\\"delay\\\\\\\"\\\\r\\\\nimport { fileExistsAtPath } from \\\\\\\"../../utils/fs\\\\\\\"\\\\r\\\\nimport { BrowserActionResult } from \\\\\\\"../../shared/ExtensionMessage\\\\\\\"\\\\r\\\\n\\\\r\\\\ninterface PCRStats {\\\\r\\\\n\\\\tpuppeteer: { launch: typeof launch }\\\\r\\\\n\\\\texecutablePath: string\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport class BrowserSession {\\\\r\\\\n\\\\tprivate context: vscode.ExtensionContext\\\\r\\\\n\\\\tprivate browser?: Browser\\\\r\\\\n\\\\tprivate page?: Page\\\\r\\\\n\\\\tprivate currentMousePosition?: string\\\\r\\\\n\\\\r\\\\n\\\\tconstructor(context: vscode.ExtensionContext) {\\\\r\\\\n\\\\t\\\\tthis.context = context\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async ensureChromiumExists(): Promise<PCRStats> {\\\\r\\\\n\\\\t\\\\tconst globalStoragePath = this.context?.globalStorageUri?.fsPath\\\\r\\\\n\\\\t\\\\tif (!globalStoragePath) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Global storage uri is invalid\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst puppeteerDir = path.join(globalStoragePath, \\\\\\\"puppeteer\\\\\\\")\\\\r\\\\n\\\\t\\\\tconst dirExists = await fileExistsAtPath(puppeteerDir)\\\\r\\\\n\\\\t\\\\tif (!dirExists) {\\\\r\\\\n\\\\t\\\\t\\\\tawait fs.mkdir(puppeteerDir, { recursive: true })\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// if chromium doesn't exist, this will download it to path.join(puppeteerDir, \\\\\\\".chromium-browser-snapshots\\\\\\\")\\\\r\\\\n\\\\t\\\\t// if it does exist it will return the path to existing chromium\\\\r\\\\n\\\\t\\\\tconst stats: PCRStats = await PCR({\\\\r\\\\n\\\\t\\\\t\\\\tdownloadPath: puppeteerDir,\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\treturn stats\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync launchBrowser() {\\\\r\\\\n\\\\t\\\\tconsole.log(\\\\\\\"launch browser called\\\\\\\")\\\\r\\\\n\\\\t\\\\tif (this.browser) {\\\\r\\\\n\\\\t\\\\t\\\\t// throw new Error(\\\\\\\"Browser already launched\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tawait this.closeBrowser() // this may happen when the model launches a browser again after having used it already before\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst stats = await this.ensureChromiumExists()\\\\r\\\\n\\\\t\\\\tthis.browser = await stats.puppeteer.launch({\\\\r\\\\n\\\\t\\\\t\\\\targs: [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\texecutablePath: stats.executablePath,\\\\r\\\\n\\\\t\\\\t\\\\tdefaultViewport: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\twidth: 900,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\theight: 600,\\\\r\\\\n\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t// headless: false,\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t// (latest version of puppeteer does not add headless to user agent)\\\\r\\\\n\\\\t\\\\tthis.page = await this.browser?.newPage()\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync closeBrowser(): Promise<BrowserActionResult> {\\\\r\\\\n\\\\t\\\\tif (this.browser || this.page) {\\\\r\\\\n\\\\t\\\\t\\\\tconsole.log(\\\\\\\"closing browser...\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tawait this.browser?.close().catch(() => {})\\\\r\\\\n\\\\t\\\\t\\\\tthis.browser = undefined\\\\r\\\\n\\\\t\\\\t\\\\tthis.page = undefined\\\\r\\\\n\\\\t\\\\t\\\\tthis.currentMousePosition = undefined\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn {}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync doAction(action: (page: Page) => Promise<void>): Promise<BrowserActionResult> {\\\\r\\\\n\\\\t\\\\tif (!this.page) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"Browser is not launched. This may occur if the browser was automatically closed by a non-`browser_action` tool.\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst logs: string[] = []\\\\r\\\\n\\\\t\\\\tlet lastLogTs = Date.now()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst consoleListener = (msg: any) => {\\\\r\\\\n\\\\t\\\\t\\\\tif (msg.type() === \\\\\\\"log\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlogs.push(msg.text())\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlogs.push(`[${msg.type()}] ${msg.text()}`)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tlastLogTs = Date.now()\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tconst errorListener = (err: Error) => {\\\\r\\\\n\\\\t\\\\t\\\\tlogs.push(`[Page Error] ${err.toString()}`)\\\\r\\\\n\\\\t\\\\t\\\\tlastLogTs = Date.now()\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Add the listeners\\\\r\\\\n\\\\t\\\\tthis.page.on(\\\\\\\"console\\\\\\\", consoleListener)\\\\r\\\\n\\\\t\\\\tthis.page.on(\\\\\\\"pageerror\\\\\\\", errorListener)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tawait action(this.page)\\\\r\\\\n\\\\t\\\\t} catch (err) {\\\\r\\\\n\\\\t\\\\t\\\\tif (!(err instanceof TimeoutError)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlogs.push(`[Error] ${err.toString()}`)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Wait for console inactivity, with a timeout\\\\r\\\\n\\\\t\\\\tawait pWaitFor(() => Date.now() - lastLogTs >= 500, {\\\\r\\\\n\\\\t\\\\t\\\\ttimeout: 3_000,\\\\r\\\\n\\\\t\\\\t\\\\tinterval: 100,\\\\r\\\\n\\\\t\\\\t}).catch(() => {})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet options: ScreenshotOptions = {\\\\r\\\\n\\\\t\\\\t\\\\tencoding: \\\\\\\"base64\\\\\\\",\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// clip: {\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\tx: 0,\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\ty: 0,\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\twidth: 900,\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\theight: 600,\\\\r\\\\n\\\\t\\\\t\\\\t// },\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet screenshotBase64 = await this.page.screenshot({\\\\r\\\\n\\\\t\\\\t\\\\t...options,\\\\r\\\\n\\\\t\\\\t\\\\ttype: \\\\\\\"webp\\\\\\\",\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\tlet screenshot = `data:image/webp;base64,${screenshotBase64}`\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (!screenshotBase64) {\\\\r\\\\n\\\\t\\\\t\\\\tconsole.log(\\\\\\\"webp screenshot failed, trying png\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tscreenshotBase64 = await this.page.screenshot({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t...options,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"png\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\tscreenshot = `data:image/png;base64,${screenshotBase64}`\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tif (!screenshotBase64) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Failed to take screenshot.\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// this.page.removeAllListeners() <- causes the page to crash!\\\\r\\\\n\\\\t\\\\tthis.page.off(\\\\\\\"console\\\\\\\", consoleListener)\\\\r\\\\n\\\\t\\\\tthis.page.off(\\\\\\\"pageerror\\\\\\\", errorListener)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\treturn {\\\\r\\\\n\\\\t\\\\t\\\\tscreenshot,\\\\r\\\\n\\\\t\\\\t\\\\tlogs: logs.join(\\\\\\\"\\\\\\\\n\\\\\\\"),\\\\r\\\\n\\\\t\\\\t\\\\tcurrentUrl: this.page.url(),\\\\r\\\\n\\\\t\\\\t\\\\tcurrentMousePosition: this.currentMousePosition,\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync navigateToUrl(url: string): Promise<BrowserActionResult> {\\\\r\\\\n\\\\t\\\\treturn this.doAction(async (page) => {\\\\r\\\\n\\\\t\\\\t\\\\t// networkidle2 isn't good enough since page may take some time to load. we can assume locally running dev sites will reach networkidle0 in a reasonable amount of time\\\\r\\\\n\\\\t\\\\t\\\\tawait page.goto(url, {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttimeout: 7_000,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\twaitUntil: [\\\\\\\"domcontentloaded\\\\\\\", \\\\\\\"networkidle2\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t// await page.goto(url, { timeout: 10_000, waitUntil: \\\\\\\"load\\\\\\\" })\\\\r\\\\n\\\\t\\\\t\\\\tawait this.waitTillHTMLStable(page) // in case the page is loading more resources\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// page.goto { waitUntil: \\\\\\\"networkidle0\\\\\\\" } may not ever resolve, and not waiting could return page content too early before js has loaded\\\\r\\\\n\\\\t// https://stackoverflow.com/questions/52497252/puppeteer-wait-until-page-is-completely-loaded/61304202#61304202\\\\r\\\\n\\\\tprivate async waitTillHTMLStable(page: Page, timeout = 5_000) {\\\\r\\\\n\\\\t\\\\tconst checkDurationMsecs = 500 // 1000\\\\r\\\\n\\\\t\\\\tconst maxChecks = timeout / checkDurationMsecs\\\\r\\\\n\\\\t\\\\tlet lastHTMLSize = 0\\\\r\\\\n\\\\t\\\\tlet checkCounts = 1\\\\r\\\\n\\\\t\\\\tlet countStableSizeIterations = 0\\\\r\\\\n\\\\t\\\\tconst minStableSizeIterations = 3\\\\r\\\\n\\\\r\\\\n\\\\t\\\\twhile (checkCounts++ <= maxChecks) {\\\\r\\\\n\\\\t\\\\t\\\\tlet html = await page.content()\\\\r\\\\n\\\\t\\\\t\\\\tlet currentHTMLSize = html.length\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// let bodyHTMLSize = await page.evaluate(() => document.body.innerHTML.length)\\\\r\\\\n\\\\t\\\\t\\\\tconsole.log(\\\\\\\"last: \\\\\\\", lastHTMLSize, \\\\\\\" <> curr: \\\\\\\", currentHTMLSize)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tif (lastHTMLSize !== 0 && currentHTMLSize === lastHTMLSize) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcountStableSizeIterations++\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcountStableSizeIterations = 0 //reset the counter\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tif (countStableSizeIterations >= minStableSizeIterations) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.log(\\\\\\\"Page rendered fully...\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tlastHTMLSize = currentHTMLSize\\\\r\\\\n\\\\t\\\\t\\\\tawait delay(checkDurationMsecs)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync click(coordinate: string): Promise<BrowserActionResult> {\\\\r\\\\n\\\\t\\\\tconst [x, y] = coordinate.split(\\\\\\\",\\\\\\\").map(Number)\\\\r\\\\n\\\\t\\\\treturn this.doAction(async (page) => {\\\\r\\\\n\\\\t\\\\t\\\\t// Set up network request monitoring\\\\r\\\\n\\\\t\\\\t\\\\tlet hasNetworkActivity = false\\\\r\\\\n\\\\t\\\\t\\\\tconst requestListener = () => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\thasNetworkActivity = true\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tpage.on(\\\\\\\"request\\\\\\\", requestListener)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// Perform the click\\\\r\\\\n\\\\t\\\\t\\\\tawait page.mouse.click(x, y)\\\\r\\\\n\\\\t\\\\t\\\\tthis.currentMousePosition = coordinate\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// Small delay to check if click triggered any network activity\\\\r\\\\n\\\\t\\\\t\\\\tawait delay(100)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tif (hasNetworkActivity) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// If we detected network activity, wait for navigation/loading\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait page\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t.waitForNavigation({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\twaitUntil: [\\\\\\\"domcontentloaded\\\\\\\", \\\\\\\"networkidle2\\\\\\\"],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttimeout: 7000,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t.catch(() => {})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait this.waitTillHTMLStable(page)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// Clean up listener\\\\r\\\\n\\\\t\\\\t\\\\tpage.off(\\\\\\\"request\\\\\\\", requestListener)\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync type(text: string): Promise<BrowserActionResult> {\\\\r\\\\n\\\\t\\\\treturn this.doAction(async (page) => {\\\\r\\\\n\\\\t\\\\t\\\\tawait page.keyboard.type(text)\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync scrollDown(): Promise<BrowserActionResult> {\\\\r\\\\n\\\\t\\\\treturn this.doAction(async (page) => {\\\\r\\\\n\\\\t\\\\t\\\\tawait page.evaluate(() => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\twindow.scrollBy({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttop: 600,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbehavior: \\\\\\\"auto\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\tawait delay(300)\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync scrollUp(): Promise<BrowserActionResult> {\\\\r\\\\n\\\\t\\\\treturn this.doAction(async (page) => {\\\\r\\\\n\\\\t\\\\t\\\\tawait page.evaluate(() => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\twindow.scrollBy({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttop: -600,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbehavior: \\\\\\\"auto\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\tawait delay(300)\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":7919,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.960Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.960Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":274,\\\"nonEmptyLines\\\":234,\\\"commentLines\\\":25,\\\"complexity\\\":30},\\\"dependencies\\\":[\\\"vscode\\\",\\\"fs/promises\\\",\\\"path\\\",\\\"puppeteer-core\\\",\\\"puppeteer-chromium-resolver\\\",\\\"p-wait-for\\\",\\\"delay\\\",\\\"../../utils/fs\\\",\\\"../../shared/ExtensionMessage\\\"],\\\"quality\\\":{\\\"score\\\":-204,\\\"issues\\\":[],\\\"duplicateLines\\\":58,\\\"longLines\\\":7,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.385Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":274},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\services\\\\\\\\browser\\\\\\\\UrlContentFetcher.ts\\\":{\\\"content\\\":\\\"import * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\nimport * as fs from \\\\\\\"fs/promises\\\\\\\"\\\\r\\\\nimport * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport { Browser, Page, launch } from \\\\\\\"puppeteer-core\\\\\\\"\\\\r\\\\nimport * as cheerio from \\\\\\\"cheerio\\\\\\\"\\\\r\\\\nimport TurndownService from \\\\\\\"turndown\\\\\\\"\\\\r\\\\n// @ts-ignore\\\\r\\\\nimport PCR from \\\\\\\"puppeteer-chromium-resolver\\\\\\\"\\\\r\\\\nimport { fileExistsAtPath } from \\\\\\\"../../utils/fs\\\\\\\"\\\\r\\\\n\\\\r\\\\ninterface PCRStats {\\\\r\\\\n\\\\tpuppeteer: { launch: typeof launch }\\\\r\\\\n\\\\texecutablePath: string\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport class UrlContentFetcher {\\\\r\\\\n\\\\tprivate context: vscode.ExtensionContext\\\\r\\\\n\\\\tprivate browser?: Browser\\\\r\\\\n\\\\tprivate page?: Page\\\\r\\\\n\\\\r\\\\n\\\\tconstructor(context: vscode.ExtensionContext) {\\\\r\\\\n\\\\t\\\\tthis.context = context\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async ensureChromiumExists(): Promise<PCRStats> {\\\\r\\\\n\\\\t\\\\tconst globalStoragePath = this.context?.globalStorageUri?.fsPath\\\\r\\\\n\\\\t\\\\tif (!globalStoragePath) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Global storage uri is invalid\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst puppeteerDir = path.join(globalStoragePath, \\\\\\\"puppeteer\\\\\\\")\\\\r\\\\n\\\\t\\\\tconst dirExists = await fileExistsAtPath(puppeteerDir)\\\\r\\\\n\\\\t\\\\tif (!dirExists) {\\\\r\\\\n\\\\t\\\\t\\\\tawait fs.mkdir(puppeteerDir, { recursive: true })\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t// if chromium doesn't exist, this will download it to path.join(puppeteerDir, \\\\\\\".chromium-browser-snapshots\\\\\\\")\\\\r\\\\n\\\\t\\\\t// if it does exist it will return the path to existing chromium\\\\r\\\\n\\\\t\\\\tconst stats: PCRStats = await PCR({\\\\r\\\\n\\\\t\\\\t\\\\tdownloadPath: puppeteerDir,\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\treturn stats\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync launchBrowser(): Promise<void> {\\\\r\\\\n\\\\t\\\\tif (this.browser) {\\\\r\\\\n\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst stats = await this.ensureChromiumExists()\\\\r\\\\n\\\\t\\\\tthis.browser = await stats.puppeteer.launch({\\\\r\\\\n\\\\t\\\\t\\\\targs: [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\\\\"--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t],\\\\r\\\\n\\\\t\\\\t\\\\texecutablePath: stats.executablePath,\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t// (latest version of puppeteer does not add headless to user agent)\\\\r\\\\n\\\\t\\\\tthis.page = await this.browser?.newPage()\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync closeBrowser(): Promise<void> {\\\\r\\\\n\\\\t\\\\tawait this.browser?.close()\\\\r\\\\n\\\\t\\\\tthis.browser = undefined\\\\r\\\\n\\\\t\\\\tthis.page = undefined\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// must make sure to call launchBrowser before and closeBrowser after using this\\\\r\\\\n\\\\tasync urlToMarkdown(url: string): Promise<string> {\\\\r\\\\n\\\\t\\\\tif (!this.browser || !this.page) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Browser not initialized\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t/*\\\\r\\\\n\\\\t\\\\t- networkidle2 is equivalent to playwright's networkidle where it waits until there are no more than 2 network connections for at least 500 ms.\\\\r\\\\n\\\\t\\\\t- domcontentloaded is when the basic DOM is loaded\\\\r\\\\n\\\\t\\\\tthis should be sufficient for most doc sites\\\\r\\\\n\\\\t\\\\t*/\\\\r\\\\n\\\\t\\\\tawait this.page.goto(url, {\\\\r\\\\n\\\\t\\\\t\\\\ttimeout: 10_000,\\\\r\\\\n\\\\t\\\\t\\\\twaitUntil: [\\\\\\\"domcontentloaded\\\\\\\", \\\\\\\"networkidle2\\\\\\\"],\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\tconst content = await this.page.content()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// use cheerio to parse and clean up the HTML\\\\r\\\\n\\\\t\\\\tconst $ = cheerio.load(content)\\\\r\\\\n\\\\t\\\\t$(\\\\\\\"script, style, nav, footer, header\\\\\\\").remove()\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// convert cleaned HTML to markdown\\\\r\\\\n\\\\t\\\\tconst turndownService = new TurndownService()\\\\r\\\\n\\\\t\\\\tconst markdown = turndownService.turndown($.html())\\\\r\\\\n\\\\r\\\\n\\\\t\\\\treturn markdown\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":2941,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.961Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.961Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":91,\\\"nonEmptyLines\\\":80,\\\"commentLines\\\":8,\\\"complexity\\\":12},\\\"dependencies\\\":[\\\"vscode\\\",\\\"fs/promises\\\",\\\"path\\\",\\\"puppeteer-core\\\",\\\"cheerio\\\",\\\"turndown\\\",\\\"puppeteer-chromium-resolver\\\",\\\"../../utils/fs\\\"],\\\"quality\\\":{\\\"score\\\":34,\\\"issues\\\":[],\\\"duplicateLines\\\":12,\\\"longLines\\\":3,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.386Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":91},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\services\\\\\\\\glob\\\\\\\\list-files.ts\\\":{\\\"content\\\":\\\"import { globby, Options } from \\\\\\\"globby\\\\\\\"\\\\r\\\\nimport os from \\\\\\\"os\\\\\\\"\\\\r\\\\nimport * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport { arePathsEqual } from \\\\\\\"../../utils/path\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport async function listFiles(dirPath: string, recursive: boolean, limit: number): Promise<[string[], boolean]> {\\\\r\\\\n\\\\tconst absolutePath = path.resolve(dirPath)\\\\r\\\\n\\\\t// Do not allow listing files in root or home directory, which cline tends to want to do when the user's prompt is vague.\\\\r\\\\n\\\\tconst root = process.platform === \\\\\\\"win32\\\\\\\" ? path.parse(absolutePath).root : \\\\\\\"/\\\\\\\"\\\\r\\\\n\\\\tconst isRoot = arePathsEqual(absolutePath, root)\\\\r\\\\n\\\\tif (isRoot) {\\\\r\\\\n\\\\t\\\\treturn [[root], false]\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\tconst homeDir = os.homedir()\\\\r\\\\n\\\\tconst isHomeDir = arePathsEqual(absolutePath, homeDir)\\\\r\\\\n\\\\tif (isHomeDir) {\\\\r\\\\n\\\\t\\\\treturn [[homeDir], false]\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tconst dirsToIgnore = [\\\\r\\\\n\\\\t\\\\t\\\\\\\"node_modules\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"__pycache__\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"env\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"venv\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"target/dependency\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"build/dependencies\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"dist\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"out\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"bundle\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"vendor\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tmp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"temp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"deps\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"pkg\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"Pods\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\".*\\\\\\\", // '!**/.*' excludes hidden directories, while '!**/.*/**' excludes only their contents. This way we are at least aware of the existence of hidden directories.\\\\r\\\\n\\\\t].map((dir) => `**/${dir}/**`)\\\\r\\\\n\\\\r\\\\n\\\\tconst options = {\\\\r\\\\n\\\\t\\\\tcwd: dirPath,\\\\r\\\\n\\\\t\\\\tdot: true, // do not ignore hidden files/directories\\\\r\\\\n\\\\t\\\\tabsolute: true,\\\\r\\\\n\\\\t\\\\tmarkDirectories: true, // Append a / on any directories matched (/ is used on windows as well, so dont use path.sep)\\\\r\\\\n\\\\t\\\\tgitignore: recursive, // globby ignores any files that are gitignored\\\\r\\\\n\\\\t\\\\tignore: recursive ? dirsToIgnore : undefined, // just in case there is no gitignore, we ignore sensible defaults\\\\r\\\\n\\\\t\\\\tonlyFiles: false, // true by default, false means it will list directories on their own too\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\t// * globs all files in one dir, ** globs files in nested directories\\\\r\\\\n\\\\tconst files = recursive ? await globbyLevelByLevel(limit, options) : (await globby(\\\\\\\"*\\\\\\\", options)).slice(0, limit)\\\\r\\\\n\\\\treturn [files, files.length >= limit]\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n/*\\\\r\\\\nBreadth-first traversal of directory structure level by level up to a limit:\\\\r\\\\n - Queue-based approach ensures proper breadth-first traversal\\\\r\\\\n - Processes directory patterns level by level\\\\r\\\\n - Captures a representative sample of the directory structure up to the limit\\\\r\\\\n - Minimizes risk of missing deeply nested files\\\\r\\\\n\\\\r\\\\n- Notes:\\\\r\\\\n - Relies on globby to mark directories with /\\\\r\\\\n - Potential for loops if symbolic links reference back to parent (we could use followSymlinks: false but that may not be ideal for some projects and it's pointless if they're not using symlinks wrong)\\\\r\\\\n - Timeout mechanism prevents infinite loops\\\\r\\\\n*/\\\\r\\\\nasync function globbyLevelByLevel(limit: number, options?: Options) {\\\\r\\\\n\\\\tlet results: Set<string> = new Set()\\\\r\\\\n\\\\tlet queue: string[] = [\\\\\\\"*\\\\\\\"]\\\\r\\\\n\\\\r\\\\n\\\\tconst globbingProcess = async () => {\\\\r\\\\n\\\\t\\\\twhile (queue.length > 0 && results.size < limit) {\\\\r\\\\n\\\\t\\\\t\\\\tconst pattern = queue.shift()!\\\\r\\\\n\\\\t\\\\t\\\\tconst filesAtLevel = await globby(pattern, options)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tfor (const file of filesAtLevel) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (results.size >= limit) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tresults.add(file)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (file.endsWith(\\\\\\\"/\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tqueue.push(`${file}*`)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn Array.from(results).slice(0, limit)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Timeout after 10 seconds and return partial results\\\\r\\\\n\\\\tconst timeoutPromise = new Promise<string[]>((_, reject) => {\\\\r\\\\n\\\\t\\\\tsetTimeout(() => reject(new Error(\\\\\\\"Globbing timeout\\\\\\\")), 10_000)\\\\r\\\\n\\\\t})\\\\r\\\\n\\\\ttry {\\\\r\\\\n\\\\t\\\\treturn await Promise.race([globbingProcess(), timeoutPromise])\\\\r\\\\n\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\tconsole.warn(\\\\\\\"Globbing timed out, returning partial results\\\\\\\")\\\\r\\\\n\\\\t\\\\treturn Array.from(results)\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":3555,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.850Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.850Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":98,\\\"nonEmptyLines\\\":89,\\\"commentLines\\\":4,\\\"complexity\\\":14},\\\"dependencies\\\":[\\\"globby\\\",\\\"os\\\",\\\"path\\\",\\\"../../utils/path\\\"],\\\"quality\\\":{\\\"score\\\":36,\\\"issues\\\":[],\\\"duplicateLines\\\":10,\\\"longLines\\\":7,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.388Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":98},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\services\\\\\\\\mcp\\\\\\\\McpHub.ts\\\":{\\\"content\\\":\\\"import { Client } from \\\\\\\"@modelcontextprotocol/sdk/client/index.js\\\\\\\"\\\\r\\\\nimport { StdioClientTransport, StdioServerParameters } from \\\\\\\"@modelcontextprotocol/sdk/client/stdio.js\\\\\\\"\\\\r\\\\nimport {\\\\r\\\\n\\\\tCallToolResultSchema,\\\\r\\\\n\\\\tListResourcesResultSchema,\\\\r\\\\n\\\\tListResourceTemplatesResultSchema,\\\\r\\\\n\\\\tListToolsResultSchema,\\\\r\\\\n\\\\tReadResourceResultSchema,\\\\r\\\\n} from \\\\\\\"@modelcontextprotocol/sdk/types.js\\\\\\\"\\\\r\\\\nimport chokidar, { FSWatcher } from \\\\\\\"chokidar\\\\\\\"\\\\r\\\\nimport delay from \\\\\\\"delay\\\\\\\"\\\\r\\\\nimport deepEqual from \\\\\\\"fast-deep-equal\\\\\\\"\\\\r\\\\nimport * as fs from \\\\\\\"fs/promises\\\\\\\"\\\\r\\\\nimport * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\nimport { z } from \\\\\\\"zod\\\\\\\"\\\\r\\\\nimport { ClineProvider, GlobalFileNames } from \\\\\\\"../../core/webview/ClineProvider\\\\\\\"\\\\r\\\\nimport { McpResource, McpResourceResponse, McpResourceTemplate, McpServer, McpTool, McpToolCallResponse } from \\\\\\\"../../shared/mcp\\\\\\\"\\\\r\\\\nimport { fileExistsAtPath } from \\\\\\\"../../utils/fs\\\\\\\"\\\\r\\\\nimport { arePathsEqual } from \\\\\\\"../../utils/path\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport type McpConnection = {\\\\r\\\\n\\\\tserver: McpServer\\\\r\\\\n\\\\tclient: Client\\\\r\\\\n\\\\ttransport: StdioClientTransport\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n// StdioServerParameters\\\\r\\\\nconst StdioConfigSchema = z.object({\\\\r\\\\n\\\\tcommand: z.string(),\\\\r\\\\n\\\\targs: z.array(z.string()).optional(),\\\\r\\\\n\\\\tenv: z.record(z.string()).optional(),\\\\r\\\\n})\\\\r\\\\n\\\\r\\\\nconst McpSettingsSchema = z.object({\\\\r\\\\n\\\\tmcpServers: z.record(StdioConfigSchema),\\\\r\\\\n})\\\\r\\\\n\\\\r\\\\nexport class McpHub {\\\\r\\\\n\\\\tprivate providerRef: WeakRef<ClineProvider>\\\\r\\\\n\\\\tprivate disposables: vscode.Disposable[] = []\\\\r\\\\n\\\\tprivate settingsWatcher?: vscode.FileSystemWatcher\\\\r\\\\n\\\\tprivate fileWatchers: Map<string, FSWatcher> = new Map()\\\\r\\\\n\\\\tconnections: McpConnection[] = []\\\\r\\\\n\\\\tisConnecting: boolean = false\\\\r\\\\n\\\\r\\\\n\\\\tconstructor(provider: ClineProvider) {\\\\r\\\\n\\\\t\\\\tthis.providerRef = new WeakRef(provider)\\\\r\\\\n\\\\t\\\\tthis.watchMcpSettingsFile()\\\\r\\\\n\\\\t\\\\tthis.initializeMcpServers()\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tgetServers(): McpServer[] {\\\\r\\\\n\\\\t\\\\treturn this.connections.map((conn) => conn.server)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync getMcpServersPath(): Promise<string> {\\\\r\\\\n\\\\t\\\\tconst provider = this.providerRef.deref()\\\\r\\\\n\\\\t\\\\tif (!provider) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Provider not available\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst mcpServersPath = await provider.ensureMcpServersDirectoryExists()\\\\r\\\\n\\\\t\\\\treturn mcpServersPath\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync getMcpSettingsFilePath(): Promise<string> {\\\\r\\\\n\\\\t\\\\tconst provider = this.providerRef.deref()\\\\r\\\\n\\\\t\\\\tif (!provider) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\\\\"Provider not available\\\\\\\")\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst mcpSettingsFilePath = path.join(await provider.ensureSettingsDirectoryExists(), GlobalFileNames.mcpSettings)\\\\r\\\\n\\\\t\\\\tconst fileExists = await fileExistsAtPath(mcpSettingsFilePath)\\\\r\\\\n\\\\t\\\\tif (!fileExists) {\\\\r\\\\n\\\\t\\\\t\\\\tawait fs.writeFile(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tmcpSettingsFilePath,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t`{\\\\r\\\\n \\\\\\\"mcpServers\\\\\\\": {\\\\r\\\\n \\\\r\\\\n }\\\\r\\\\n}`,\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn mcpSettingsFilePath\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async watchMcpSettingsFile(): Promise<void> {\\\\r\\\\n\\\\t\\\\tconst settingsPath = await this.getMcpSettingsFilePath()\\\\r\\\\n\\\\t\\\\tthis.disposables.push(\\\\r\\\\n\\\\t\\\\t\\\\tvscode.workspace.onDidSaveTextDocument(async (document) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (arePathsEqual(document.uri.fsPath, settingsPath)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst content = await fs.readFile(settingsPath, \\\\\\\"utf-8\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst errorMessage =\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\\\\"Invalid MCP settings format. Please ensure your settings follow the correct JSON format.\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tlet config: any\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconfig = JSON.parse(content)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tvscode.window.showErrorMessage(errorMessage)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst result = McpSettingsSchema.safeParse(config)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (!result.success) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tvscode.window.showErrorMessage(errorMessage)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tvscode.window.showInformationMessage(\\\\\\\"Updating MCP servers...\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.updateServerConnections(result.data.mcpServers || {})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tvscode.window.showInformationMessage(\\\\\\\"MCP servers updated\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Failed to process MCP settings change:\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}),\\\\r\\\\n\\\\t\\\\t)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async initializeMcpServers(): Promise<void> {\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tconst settingsPath = await this.getMcpSettingsFilePath()\\\\r\\\\n\\\\t\\\\t\\\\tconst content = await fs.readFile(settingsPath, \\\\\\\"utf-8\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tconst config = JSON.parse(content)\\\\r\\\\n\\\\t\\\\t\\\\tawait this.updateServerConnections(config.mcpServers || {})\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Failed to initialize MCP servers:\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async connectToServer(name: string, config: StdioServerParameters): Promise<void> {\\\\r\\\\n\\\\t\\\\t// Remove existing connection if it exists (should never happen, the connection should be deleted beforehand)\\\\r\\\\n\\\\t\\\\tthis.connections = this.connections.filter((conn) => conn.server.name !== name)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t// Each MCP server requires its own transport connection and has unique capabilities, configurations, and error handling. Having separate clients also allows proper scoping of resources/tools and independent server management like reconnection.\\\\r\\\\n\\\\t\\\\t\\\\tconst client = new Client(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tname: \\\\\\\"Cline\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tversion: this.providerRef.deref()?.context.extension?.packageJSON?.version ?? \\\\\\\"1.0.0\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcapabilities: {},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tconst transport = new StdioClientTransport({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcommand: config.command,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\targs: config.args,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tenv: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t...config.env,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t...(process.env.PATH ? { PATH: process.env.PATH } : {}),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// ...(process.env.NODE_PATH ? { NODE_PATH: process.env.NODE_PATH } : {}),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tstderr: \\\\\\\"pipe\\\\\\\", // necessary for stderr to be available\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\ttransport.onerror = async (error) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.error(`Transport error for \\\\\\\"${name}\\\\\\\":`, error)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst connection = this.connections.find((conn) => conn.server.name === name)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (connection) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconnection.server.status = \\\\\\\"disconnected\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.appendErrorMessage(connection, error.message)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait this.notifyWebviewOfServerChanges()\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\ttransport.onclose = async () => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst connection = this.connections.find((conn) => conn.server.name === name)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (connection) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconnection.server.status = \\\\\\\"disconnected\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait this.notifyWebviewOfServerChanges()\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// If the config is invalid, show an error\\\\r\\\\n\\\\t\\\\t\\\\tif (!StdioConfigSchema.safeParse(config).success) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.error(`Invalid config for \\\\\\\"${name}\\\\\\\": missing or invalid parameters`)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst connection: McpConnection = {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tserver: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tname,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tconfig: JSON.stringify(config),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tstatus: \\\\\\\"disconnected\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\terror: \\\\\\\"Invalid config: missing or invalid parameters\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tclient,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttransport,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.connections.push(connection)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// valid schema\\\\r\\\\n\\\\t\\\\t\\\\tconst connection: McpConnection = {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tserver: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tname,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconfig: JSON.stringify(config),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tstatus: \\\\\\\"connecting\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tclient,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttransport,\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tthis.connections.push(connection)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// transport.stderr is only available after the process has been started. However we can't start it separately from the .connect() call because it also starts the transport. And we can't place this after the connect call since we need to capture the stderr stream before the connection is established, in order to capture errors during the connection process.\\\\r\\\\n\\\\t\\\\t\\\\t// As a workaround, we start the transport ourselves, and then monkey-patch the start method to no-op so that .connect() doesn't try to start it again.\\\\r\\\\n\\\\t\\\\t\\\\tawait transport.start()\\\\r\\\\n\\\\t\\\\t\\\\tconst stderrStream = transport.stderr\\\\r\\\\n\\\\t\\\\t\\\\tif (stderrStream) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tstderrStream.on(\\\\\\\"data\\\\\\\", async (data: Buffer) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst errorOutput = data.toString()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconsole.error(`Server \\\\\\\"${name}\\\\\\\" stderr:`, errorOutput)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst connection = this.connections.find((conn) => conn.server.name === name)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (connection) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// NOTE: we do not set server status to \\\\\\\"disconnected\\\\\\\" because stderr logs do not necessarily mean the server crashed or disconnected, it could just be informational. In fact when the server first starts up, it immediately logs \\\\\\\"<name> server running on stdio\\\\\\\" to stderr.\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tthis.appendErrorMessage(connection, errorOutput)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// Only need to update webview right away if it's already disconnected\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tif (connection.server.status === \\\\\\\"disconnected\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tawait this.notifyWebviewOfServerChanges()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.error(`No stderr stream for ${name}`)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\ttransport.start = async () => {} // No-op now, .connect() won't fail\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// // Set up notification handlers\\\\r\\\\n\\\\t\\\\t\\\\t// client.setNotificationHandler(\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\t// @ts-ignore-next-line\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\t{ method: \\\\\\\"notifications/tools/list_changed\\\\\\\" },\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\tasync () => {\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\t\\\\tconsole.log(`Tools changed for server: ${name}`)\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\t\\\\tconnection.server.tools = await this.fetchTools(name)\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\t\\\\tawait this.notifyWebviewOfServerChanges()\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t// )\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// client.setNotificationHandler(\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\t// @ts-ignore-next-line\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\t{ method: \\\\\\\"notifications/resources/list_changed\\\\\\\" },\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\tasync () => {\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\t\\\\tconsole.log(`Resources changed for server: ${name}`)\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\t\\\\tconnection.server.resources = await this.fetchResources(name)\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\t\\\\tconnection.server.resourceTemplates = await this.fetchResourceTemplates(name)\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\t\\\\tawait this.notifyWebviewOfServerChanges()\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t// )\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// Connect\\\\r\\\\n\\\\t\\\\t\\\\tawait client.connect(transport)\\\\r\\\\n\\\\t\\\\t\\\\tconnection.server.status = \\\\\\\"connected\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tconnection.server.error = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// Initial fetch of tools and resources\\\\r\\\\n\\\\t\\\\t\\\\tconnection.server.tools = await this.fetchToolsList(name)\\\\r\\\\n\\\\t\\\\t\\\\tconnection.server.resources = await this.fetchResourcesList(name)\\\\r\\\\n\\\\t\\\\t\\\\tconnection.server.resourceTemplates = await this.fetchResourceTemplatesList(name)\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t// Update status with error\\\\r\\\\n\\\\t\\\\t\\\\tconst connection = this.connections.find((conn) => conn.server.name === name)\\\\r\\\\n\\\\t\\\\t\\\\tif (connection) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconnection.server.status = \\\\\\\"disconnected\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.appendErrorMessage(connection, error instanceof Error ? error.message : String(error))\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tthrow error\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate appendErrorMessage(connection: McpConnection, error: string) {\\\\r\\\\n\\\\t\\\\tconst newError = connection.server.error ? `${connection.server.error}\\\\\\\\n${error}` : error\\\\r\\\\n\\\\t\\\\tconnection.server.error = newError //.slice(0, 800)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async fetchToolsList(serverName: string): Promise<McpTool[]> {\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tconst response = await this.connections\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t.find((conn) => conn.server.name === serverName)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t?.client.request({ method: \\\\\\\"tools/list\\\\\\\" }, ListToolsResultSchema)\\\\r\\\\n\\\\t\\\\t\\\\treturn response?.tools || []\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t// console.error(`Failed to fetch tools for ${serverName}:`, error)\\\\r\\\\n\\\\t\\\\t\\\\treturn []\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async fetchResourcesList(serverName: string): Promise<McpResource[]> {\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tconst response = await this.connections\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t.find((conn) => conn.server.name === serverName)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t?.client.request({ method: \\\\\\\"resources/list\\\\\\\" }, ListResourcesResultSchema)\\\\r\\\\n\\\\t\\\\t\\\\treturn response?.resources || []\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t// console.error(`Failed to fetch resources for ${serverName}:`, error)\\\\r\\\\n\\\\t\\\\t\\\\treturn []\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async fetchResourceTemplatesList(serverName: string): Promise<McpResourceTemplate[]> {\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tconst response = await this.connections\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t.find((conn) => conn.server.name === serverName)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t?.client.request({ method: \\\\\\\"resources/templates/list\\\\\\\" }, ListResourceTemplatesResultSchema)\\\\r\\\\n\\\\t\\\\t\\\\treturn response?.resourceTemplates || []\\\\r\\\\n\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t// console.error(`Failed to fetch resource templates for ${serverName}:`, error)\\\\r\\\\n\\\\t\\\\t\\\\treturn []\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync deleteConnection(name: string): Promise<void> {\\\\r\\\\n\\\\t\\\\tconst connection = this.connections.find((conn) => conn.server.name === name)\\\\r\\\\n\\\\t\\\\tif (connection) {\\\\r\\\\n\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// connection.client.removeNotificationHandler(\\\\\\\"notifications/tools/list_changed\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// connection.client.removeNotificationHandler(\\\\\\\"notifications/resources/list_changed\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// connection.client.removeNotificationHandler(\\\\\\\"notifications/stderr\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// connection.client.removeNotificationHandler(\\\\\\\"notifications/stderr\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait connection.transport.close()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait connection.client.close()\\\\r\\\\n\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.error(`Failed to close transport for ${name}:`, error)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\tthis.connections = this.connections.filter((conn) => conn.server.name !== name)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync updateServerConnections(newServers: Record<string, any>): Promise<void> {\\\\r\\\\n\\\\t\\\\tthis.isConnecting = true\\\\r\\\\n\\\\t\\\\tthis.removeAllFileWatchers()\\\\r\\\\n\\\\t\\\\tconst currentNames = new Set(this.connections.map((conn) => conn.server.name))\\\\r\\\\n\\\\t\\\\tconst newNames = new Set(Object.keys(newServers))\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Delete removed servers\\\\r\\\\n\\\\t\\\\tfor (const name of currentNames) {\\\\r\\\\n\\\\t\\\\t\\\\tif (!newNames.has(name)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait this.deleteConnection(name)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.log(`Deleted MCP server: ${name}`)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Update or add servers\\\\r\\\\n\\\\t\\\\tfor (const [name, config] of Object.entries(newServers)) {\\\\r\\\\n\\\\t\\\\t\\\\tconst currentConnection = this.connections.find((conn) => conn.server.name === name)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tif (!currentConnection) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// New server\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.setupFileWatcher(name, config)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.connectToServer(name, config)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconsole.error(`Failed to connect to new MCP server ${name}:`, error)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t} else if (!deepEqual(JSON.parse(currentConnection.server.config), config)) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Existing server with changed config\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tthis.setupFileWatcher(name, config)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.deleteConnection(name)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tawait this.connectToServer(name, config)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconsole.log(`Reconnected MCP server with updated config: ${name}`)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconsole.error(`Failed to reconnect MCP server ${name}:`, error)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t// If server exists with same config, do nothing\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tawait this.notifyWebviewOfServerChanges()\\\\r\\\\n\\\\t\\\\tthis.isConnecting = false\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate setupFileWatcher(name: string, config: any) {\\\\r\\\\n\\\\t\\\\tconst filePath = config.args?.find((arg: string) => arg.includes(\\\\\\\"build/index.js\\\\\\\"))\\\\r\\\\n\\\\t\\\\tif (filePath) {\\\\r\\\\n\\\\t\\\\t\\\\t// we use chokidar instead of onDidSaveTextDocument because it doesn't require the file to be open in the editor. The settings config is better suited for onDidSave since that will be manually updated by the user or Cline (and we want to detect save events, not every file change)\\\\r\\\\n\\\\t\\\\t\\\\tconst watcher = chokidar.watch(filePath, {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// persistent: true,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// ignoreInitial: true,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// awaitWriteFinish: true, // This helps with atomic writes\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\twatcher.on(\\\\\\\"change\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.log(`Detected change in ${filePath}. Restarting server ${name}...`)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthis.restartConnection(name)\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tthis.fileWatchers.set(name, watcher)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate removeAllFileWatchers() {\\\\r\\\\n\\\\t\\\\tthis.fileWatchers.forEach((watcher) => watcher.close())\\\\r\\\\n\\\\t\\\\tthis.fileWatchers.clear()\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync restartConnection(serverName: string): Promise<void> {\\\\r\\\\n\\\\t\\\\tthis.isConnecting = true\\\\r\\\\n\\\\t\\\\tconst provider = this.providerRef.deref()\\\\r\\\\n\\\\t\\\\tif (!provider) {\\\\r\\\\n\\\\t\\\\t\\\\treturn\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Get existing connection and update its status\\\\r\\\\n\\\\t\\\\tconst connection = this.connections.find((conn) => conn.server.name === serverName)\\\\r\\\\n\\\\t\\\\tconst config = connection?.server.config\\\\r\\\\n\\\\t\\\\tif (config) {\\\\r\\\\n\\\\t\\\\t\\\\tvscode.window.showInformationMessage(`Restarting ${serverName} MCP server...`)\\\\r\\\\n\\\\t\\\\t\\\\tconnection.server.status = \\\\\\\"connecting\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tconnection.server.error = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tawait this.notifyWebviewOfServerChanges()\\\\r\\\\n\\\\t\\\\t\\\\tawait delay(500) // artificial delay to show user that server is restarting\\\\r\\\\n\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait this.deleteConnection(serverName)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// Try to connect again using existing config\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait this.connectToServer(serverName, JSON.parse(config))\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tvscode.window.showInformationMessage(`${serverName} MCP server connected`)\\\\r\\\\n\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.error(`Failed to restart connection for ${serverName}:`, error)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tvscode.window.showErrorMessage(`Failed to connect to ${serverName} MCP server`)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tawait this.notifyWebviewOfServerChanges()\\\\r\\\\n\\\\t\\\\tthis.isConnecting = false\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tprivate async notifyWebviewOfServerChanges(): Promise<void> {\\\\r\\\\n\\\\t\\\\t// servers should always be sorted in the order they are defined in the settings file\\\\r\\\\n\\\\t\\\\tconst settingsPath = await this.getMcpSettingsFilePath()\\\\r\\\\n\\\\t\\\\tconst content = await fs.readFile(settingsPath, \\\\\\\"utf-8\\\\\\\")\\\\r\\\\n\\\\t\\\\tconst config = JSON.parse(content)\\\\r\\\\n\\\\t\\\\tconst serverOrder = Object.keys(config.mcpServers || {})\\\\r\\\\n\\\\t\\\\tawait this.providerRef.deref()?.postMessageToWebview({\\\\r\\\\n\\\\t\\\\t\\\\ttype: \\\\\\\"mcpServers\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\tmcpServers: [...this.connections]\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t.sort((a, b) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst indexA = serverOrder.indexOf(a.server.name)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst indexB = serverOrder.indexOf(b.server.name)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\treturn indexA - indexB\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t.map((connection) => connection.server),\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Using server\\\\r\\\\n\\\\r\\\\n\\\\tasync readResource(serverName: string, uri: string): Promise<McpResourceResponse> {\\\\r\\\\n\\\\t\\\\tconst connection = this.connections.find((conn) => conn.server.name === serverName)\\\\r\\\\n\\\\t\\\\tif (!connection) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(`No connection found for server: ${serverName}`)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn await connection.client.request(\\\\r\\\\n\\\\t\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tmethod: \\\\\\\"resources/read\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tparams: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\turi,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\tReadResourceResultSchema,\\\\r\\\\n\\\\t\\\\t)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync callTool(serverName: string, toolName: string, toolArguments?: Record<string, unknown>): Promise<McpToolCallResponse> {\\\\r\\\\n\\\\t\\\\tconst connection = this.connections.find((conn) => conn.server.name === serverName)\\\\r\\\\n\\\\t\\\\tif (!connection) {\\\\r\\\\n\\\\t\\\\t\\\\tthrow new Error(\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t`No connection found for server: ${serverName}. Please make sure to use MCP servers available under 'Connected MCP Servers'.`,\\\\r\\\\n\\\\t\\\\t\\\\t)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\treturn await connection.client.request(\\\\r\\\\n\\\\t\\\\t\\\\t{\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tmethod: \\\\\\\"tools/call\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tparams: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tname: toolName,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\targuments: toolArguments,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\t},\\\\r\\\\n\\\\t\\\\t\\\\tCallToolResultSchema,\\\\r\\\\n\\\\t\\\\t)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tasync dispose(): Promise<void> {\\\\r\\\\n\\\\t\\\\tthis.removeAllFileWatchers()\\\\r\\\\n\\\\t\\\\tfor (const connection of this.connections) {\\\\r\\\\n\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tawait this.deleteConnection(connection.server.name)\\\\r\\\\n\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.error(`Failed to close connection for ${connection.server.name}:`, error)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tthis.connections = []\\\\r\\\\n\\\\t\\\\tif (this.settingsWatcher) {\\\\r\\\\n\\\\t\\\\t\\\\tthis.settingsWatcher.dispose()\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tthis.disposables.forEach((d) => d.dispose())\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":17902,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.962Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.962Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":495,\\\"nonEmptyLines\\\":450,\\\"commentLines\\\":53,\\\"complexity\\\":67},\\\"dependencies\\\":[\\\"@modelcontextprotocol/sdk/client/index.js\\\",\\\"@modelcontextprotocol/sdk/client/stdio.js\\\",\\\"chokidar\\\",\\\"delay\\\",\\\"fast-deep-equal\\\",\\\"fs/promises\\\",\\\"path\\\",\\\"vscode\\\",\\\"zod\\\",\\\"../../core/webview/ClineProvider\\\",\\\"../../shared/mcp\\\",\\\"../../utils/fs\\\",\\\"../../utils/path\\\"],\\\"quality\\\":{\\\"score\\\":-777,\\\"issues\\\":[],\\\"duplicateLines\\\":171,\\\"longLines\\\":11,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.389Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":495},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\services\\\\\\\\ripgrep\\\\\\\\index.ts\\\":{\\\"content\\\":\\\"import * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\nimport * as childProcess from \\\\\\\"child_process\\\\\\\"\\\\r\\\\nimport * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport * as fs from \\\\\\\"fs\\\\\\\"\\\\r\\\\nimport * as readline from \\\\\\\"readline\\\\\\\"\\\\r\\\\n\\\\r\\\\n/*\\\\r\\\\nThis file provides functionality to perform regex searches on files using ripgrep.\\\\r\\\\nInspired by: https://github.com/DiscreteTom/vscode-ripgrep-utils\\\\r\\\\n\\\\r\\\\nKey components:\\\\r\\\\n1. getBinPath: Locates the ripgrep binary within the VSCode installation.\\\\r\\\\n2. execRipgrep: Executes the ripgrep command and returns the output.\\\\r\\\\n3. regexSearchFiles: The main function that performs regex searches on files.\\\\r\\\\n - Parameters:\\\\r\\\\n * cwd: The current working directory (for relative path calculation)\\\\r\\\\n * directoryPath: The directory to search in\\\\r\\\\n * regex: The regular expression to search for (Rust regex syntax)\\\\r\\\\n * filePattern: Optional glob pattern to filter files (default: '*')\\\\r\\\\n - Returns: A formatted string containing search results with context\\\\r\\\\n\\\\r\\\\nThe search results include:\\\\r\\\\n- Relative file paths\\\\r\\\\n- 2 lines of context before and after each match\\\\r\\\\n- Matches formatted with pipe characters for easy reading\\\\r\\\\n\\\\r\\\\nUsage example:\\\\r\\\\nconst results = await regexSearchFiles('/path/to/cwd', '/path/to/search', 'TODO:', '*.ts');\\\\r\\\\n\\\\r\\\\nrel/path/to/app.ts\\\\r\\\\n│----\\\\r\\\\n│function processData(data: any) {\\\\r\\\\n│ // Some processing logic here\\\\r\\\\n│ // TODO: Implement error handling\\\\r\\\\n│ return processedData;\\\\r\\\\n│}\\\\r\\\\n│----\\\\r\\\\n\\\\r\\\\nrel/path/to/helper.ts\\\\r\\\\n│----\\\\r\\\\n│ let result = 0;\\\\r\\\\n│ for (let i = 0; i < input; i++) {\\\\r\\\\n│ // TODO: Optimize this function for performance\\\\r\\\\n│ result += Math.pow(i, 2);\\\\r\\\\n│ }\\\\r\\\\n│----\\\\r\\\\n*/\\\\r\\\\n\\\\r\\\\nconst isWindows = /^win/.test(process.platform)\\\\r\\\\nconst binName = isWindows ? \\\\\\\"rg.exe\\\\\\\" : \\\\\\\"rg\\\\\\\"\\\\r\\\\n\\\\r\\\\ninterface SearchResult {\\\\r\\\\n\\\\tfile: string\\\\r\\\\n\\\\tline: number\\\\r\\\\n\\\\tcolumn: number\\\\r\\\\n\\\\tmatch: string\\\\r\\\\n\\\\tbeforeContext: string[]\\\\r\\\\n\\\\tafterContext: string[]\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nconst MAX_RESULTS = 300\\\\r\\\\n\\\\r\\\\nasync function getBinPath(vscodeAppRoot: string): Promise<string | undefined> {\\\\r\\\\n\\\\tconst checkPath = async (pkgFolder: string) => {\\\\r\\\\n\\\\t\\\\tconst fullPath = path.join(vscodeAppRoot, pkgFolder, binName)\\\\r\\\\n\\\\t\\\\treturn (await pathExists(fullPath)) ? fullPath : undefined\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\treturn (\\\\r\\\\n\\\\t\\\\t(await checkPath(\\\\\\\"node_modules/@vscode/ripgrep/bin/\\\\\\\")) ||\\\\r\\\\n\\\\t\\\\t(await checkPath(\\\\\\\"node_modules/vscode-ripgrep/bin\\\\\\\")) ||\\\\r\\\\n\\\\t\\\\t(await checkPath(\\\\\\\"node_modules.asar.unpacked/vscode-ripgrep/bin/\\\\\\\")) ||\\\\r\\\\n\\\\t\\\\t(await checkPath(\\\\\\\"node_modules.asar.unpacked/@vscode/ripgrep/bin/\\\\\\\"))\\\\r\\\\n\\\\t)\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nasync function pathExists(path: string): Promise<boolean> {\\\\r\\\\n\\\\treturn new Promise((resolve) => {\\\\r\\\\n\\\\t\\\\tfs.access(path, (err) => {\\\\r\\\\n\\\\t\\\\t\\\\tresolve(err === null)\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t})\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nasync function execRipgrep(bin: string, args: string[]): Promise<string> {\\\\r\\\\n\\\\treturn new Promise((resolve, reject) => {\\\\r\\\\n\\\\t\\\\tconst rgProcess = childProcess.spawn(bin, args)\\\\r\\\\n\\\\t\\\\t// cross-platform alternative to head, which is ripgrep author's recommendation for limiting output.\\\\r\\\\n\\\\t\\\\tconst rl = readline.createInterface({\\\\r\\\\n\\\\t\\\\t\\\\tinput: rgProcess.stdout,\\\\r\\\\n\\\\t\\\\t\\\\tcrlfDelay: Infinity, // treat \\\\\\\\r\\\\\\\\n as a single line break even if it's split across chunks. This ensures consistent behavior across different operating systems.\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet output = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\tlet lineCount = 0\\\\r\\\\n\\\\t\\\\tconst maxLines = MAX_RESULTS * 5 // limiting ripgrep output with max lines since there's no other way to limit results. it's okay that we're outputting as json, since we're parsing it line by line and ignore anything that's not part of a match. This assumes each result is at most 5 lines.\\\\r\\\\n\\\\r\\\\n\\\\t\\\\trl.on(\\\\\\\"line\\\\\\\", (line) => {\\\\r\\\\n\\\\t\\\\t\\\\tif (lineCount < maxLines) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\toutput += line + \\\\\\\"\\\\\\\\n\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlineCount++\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\trl.close()\\\\r\\\\n\\\\t\\\\t\\\\t\\\\trgProcess.kill()\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tlet errorOutput = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\trgProcess.stderr.on(\\\\\\\"data\\\\\\\", (data) => {\\\\r\\\\n\\\\t\\\\t\\\\terrorOutput += data.toString()\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\trl.on(\\\\\\\"close\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tif (errorOutput) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treject(new Error(`ripgrep process error: ${errorOutput}`))\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tresolve(output)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\trgProcess.on(\\\\\\\"error\\\\\\\", (error) => {\\\\r\\\\n\\\\t\\\\t\\\\treject(new Error(`ripgrep process error: ${error.message}`))\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t})\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport async function regexSearchFiles(cwd: string, directoryPath: string, regex: string, filePattern?: string): Promise<string> {\\\\r\\\\n\\\\tconst vscodeAppRoot = vscode.env.appRoot\\\\r\\\\n\\\\tconst rgPath = await getBinPath(vscodeAppRoot)\\\\r\\\\n\\\\r\\\\n\\\\tif (!rgPath) {\\\\r\\\\n\\\\t\\\\tthrow new Error(\\\\\\\"Could not find ripgrep binary\\\\\\\")\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tconst args = [\\\\\\\"--json\\\\\\\", \\\\\\\"-e\\\\\\\", regex, \\\\\\\"--glob\\\\\\\", filePattern || \\\\\\\"*\\\\\\\", \\\\\\\"--context\\\\\\\", \\\\\\\"1\\\\\\\", directoryPath]\\\\r\\\\n\\\\r\\\\n\\\\tlet output: string\\\\r\\\\n\\\\ttry {\\\\r\\\\n\\\\t\\\\toutput = await execRipgrep(rgPath, args)\\\\r\\\\n\\\\t} catch {\\\\r\\\\n\\\\t\\\\treturn \\\\\\\"No results found\\\\\\\"\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\tconst results: SearchResult[] = []\\\\r\\\\n\\\\tlet currentResult: Partial<SearchResult> | null = null\\\\r\\\\n\\\\r\\\\n\\\\toutput.split(\\\\\\\"\\\\\\\\n\\\\\\\").forEach((line) => {\\\\r\\\\n\\\\t\\\\tif (line) {\\\\r\\\\n\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst parsed = JSON.parse(line)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (parsed.type === \\\\\\\"match\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (currentResult) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tresults.push(currentResult as SearchResult)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcurrentResult = {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tfile: parsed.data.path.text,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tline: parsed.data.line_number,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcolumn: parsed.data.submatches[0].start,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tmatch: parsed.data.lines.text,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tbeforeContext: [],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tafterContext: [],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t} else if (parsed.type === \\\\\\\"context\\\\\\\" && currentResult) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (parsed.data.line_number < currentResult.line!) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcurrentResult.beforeContext!.push(parsed.data.lines.text)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcurrentResult.afterContext!.push(parsed.data.lines.text)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Error parsing ripgrep output:\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\tif (currentResult) {\\\\r\\\\n\\\\t\\\\tresults.push(currentResult as SearchResult)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\treturn formatResults(results, cwd)\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nfunction formatResults(results: SearchResult[], cwd: string): string {\\\\r\\\\n\\\\tconst groupedResults: { [key: string]: SearchResult[] } = {}\\\\r\\\\n\\\\r\\\\n\\\\tlet output = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\tif (results.length >= MAX_RESULTS) {\\\\r\\\\n\\\\t\\\\toutput += `Showing first ${MAX_RESULTS} of ${MAX_RESULTS}+ results. Use a more specific search if necessary.\\\\\\\\n\\\\\\\\n`\\\\r\\\\n\\\\t} else {\\\\r\\\\n\\\\t\\\\toutput += `Found ${results.length === 1 ? \\\\\\\"1 result\\\\\\\" : `${results.length.toLocaleString()} results`}.\\\\\\\\n\\\\\\\\n`\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Group results by file name\\\\r\\\\n\\\\tresults.slice(0, MAX_RESULTS).forEach((result) => {\\\\r\\\\n\\\\t\\\\tconst relativeFilePath = path.relative(cwd, result.file)\\\\r\\\\n\\\\t\\\\tif (!groupedResults[relativeFilePath]) {\\\\r\\\\n\\\\t\\\\t\\\\tgroupedResults[relativeFilePath] = []\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tgroupedResults[relativeFilePath].push(result)\\\\r\\\\n\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\tfor (const [filePath, fileResults] of Object.entries(groupedResults)) {\\\\r\\\\n\\\\t\\\\toutput += `${filePath.toPosix()}\\\\\\\\n│----\\\\\\\\n`\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tfileResults.forEach((result, index) => {\\\\r\\\\n\\\\t\\\\t\\\\tconst allLines = [...result.beforeContext, result.match, ...result.afterContext]\\\\r\\\\n\\\\t\\\\t\\\\tallLines.forEach((line) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\toutput += `│${line?.trimEnd() ?? \\\\\\\"\\\\\\\"}\\\\\\\\n`\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tif (index < fileResults.length - 1) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\toutput += \\\\\\\"│----\\\\\\\\n\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\toutput += \\\\\\\"│----\\\\\\\\n\\\\\\\\n\\\\\\\"\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\treturn output.trim()\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":6803,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.962Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.962Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":218,\\\"nonEmptyLines\\\":186,\\\"commentLines\\\":3,\\\"complexity\\\":35},\\\"dependencies\\\":[\\\"vscode\\\",\\\"child_process\\\",\\\"path\\\",\\\"fs\\\",\\\"readline\\\"],\\\"quality\\\":{\\\"score\\\":-124,\\\"issues\\\":[],\\\"duplicateLines\\\":42,\\\"longLines\\\":7,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.391Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":218},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\services\\\\\\\\tree-sitter\\\\\\\\index.ts\\\":{\\\"content\\\":\\\"import * as fs from \\\\\\\"fs/promises\\\\\\\"\\\\r\\\\nimport * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport { listFiles } from \\\\\\\"../glob/list-files\\\\\\\"\\\\r\\\\nimport { LanguageParser, loadRequiredLanguageParsers } from \\\\\\\"./languageParser\\\\\\\"\\\\r\\\\nimport { fileExistsAtPath } from \\\\\\\"../../utils/fs\\\\\\\"\\\\r\\\\n\\\\r\\\\n// TODO: implement caching behavior to avoid having to keep analyzing project for new tasks.\\\\r\\\\nexport async function parseSourceCodeForDefinitionsTopLevel(dirPath: string): Promise<string> {\\\\r\\\\n\\\\t// check if the path exists\\\\r\\\\n\\\\tconst dirExists = await fileExistsAtPath(path.resolve(dirPath))\\\\r\\\\n\\\\tif (!dirExists) {\\\\r\\\\n\\\\t\\\\treturn \\\\\\\"This directory does not exist or you do not have permission to access it.\\\\\\\"\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Get all files at top level (not gitignored)\\\\r\\\\n\\\\tconst [allFiles, _] = await listFiles(dirPath, false, 200)\\\\r\\\\n\\\\r\\\\n\\\\tlet result = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\r\\\\n\\\\t// Separate files to parse and remaining files\\\\r\\\\n\\\\tconst { filesToParse, remainingFiles } = separateFiles(allFiles)\\\\r\\\\n\\\\r\\\\n\\\\tconst languageParsers = await loadRequiredLanguageParsers(filesToParse)\\\\r\\\\n\\\\r\\\\n\\\\t// Parse specific files we have language parsers for\\\\r\\\\n\\\\t// const filesWithoutDefinitions: string[] = []\\\\r\\\\n\\\\tfor (const file of filesToParse) {\\\\r\\\\n\\\\t\\\\tconst definitions = await parseFile(file, languageParsers)\\\\r\\\\n\\\\t\\\\tif (definitions) {\\\\r\\\\n\\\\t\\\\t\\\\tresult += `${path.relative(dirPath, file).toPosix()}\\\\\\\\n${definitions}\\\\\\\\n`\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t// else {\\\\r\\\\n\\\\t\\\\t// \\\\tfilesWithoutDefinitions.push(file)\\\\r\\\\n\\\\t\\\\t// }\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// List remaining files' paths\\\\r\\\\n\\\\t// let didFindUnparsedFiles = false\\\\r\\\\n\\\\t// filesWithoutDefinitions\\\\r\\\\n\\\\t// \\\\t.concat(remainingFiles)\\\\r\\\\n\\\\t// \\\\t.sort()\\\\r\\\\n\\\\t// \\\\t.forEach((file) => {\\\\r\\\\n\\\\t// \\\\t\\\\tif (!didFindUnparsedFiles) {\\\\r\\\\n\\\\t// \\\\t\\\\t\\\\tresult += \\\\\\\"# Unparsed Files\\\\\\\\n\\\\\\\\n\\\\\\\"\\\\r\\\\n\\\\t// \\\\t\\\\t\\\\tdidFindUnparsedFiles = true\\\\r\\\\n\\\\t// \\\\t\\\\t}\\\\r\\\\n\\\\t// \\\\t\\\\tresult += `${path.relative(dirPath, file)}\\\\\\\\n`\\\\r\\\\n\\\\t// \\\\t})\\\\r\\\\n\\\\r\\\\n\\\\treturn result ? result : \\\\\\\"No source code definitions found.\\\\\\\"\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nfunction separateFiles(allFiles: string[]): {\\\\r\\\\n\\\\tfilesToParse: string[]\\\\r\\\\n\\\\tremainingFiles: string[]\\\\r\\\\n} {\\\\r\\\\n\\\\tconst extensions = [\\\\r\\\\n\\\\t\\\\t\\\\\\\"js\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"jsx\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"ts\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"tsx\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"py\\\\\\\",\\\\r\\\\n\\\\t\\\\t// Rust\\\\r\\\\n\\\\t\\\\t\\\\\\\"rs\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"go\\\\\\\",\\\\r\\\\n\\\\t\\\\t// C\\\\r\\\\n\\\\t\\\\t\\\\\\\"c\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"h\\\\\\\",\\\\r\\\\n\\\\t\\\\t// C++\\\\r\\\\n\\\\t\\\\t\\\\\\\"cpp\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"hpp\\\\\\\",\\\\r\\\\n\\\\t\\\\t// C#\\\\r\\\\n\\\\t\\\\t\\\\\\\"cs\\\\\\\",\\\\r\\\\n\\\\t\\\\t// Ruby\\\\r\\\\n\\\\t\\\\t\\\\\\\"rb\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"java\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"php\\\\\\\",\\\\r\\\\n\\\\t\\\\t\\\\\\\"swift\\\\\\\",\\\\r\\\\n\\\\t].map((e) => `.${e}`)\\\\r\\\\n\\\\tconst filesToParse = allFiles.filter((file) => extensions.includes(path.extname(file))).slice(0, 50) // 50 files max\\\\r\\\\n\\\\tconst remainingFiles = allFiles.filter((file) => !filesToParse.includes(file))\\\\r\\\\n\\\\treturn { filesToParse, remainingFiles }\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n/*\\\\r\\\\nParsing files using tree-sitter\\\\r\\\\n\\\\r\\\\n1. Parse the file content into an AST (Abstract Syntax Tree) using the appropriate language grammar (set of rules that define how the components of a language like keywords, expressions, and statements can be combined to create valid programs).\\\\r\\\\n2. Create a query using a language-specific query string, and run it against the AST's root node to capture specific syntax elements.\\\\r\\\\n - We use tag queries to identify named entities in a program, and then use a syntax capture to label the entity and its name. A notable example of this is GitHub's search-based code navigation.\\\\r\\\\n\\\\t- Our custom tag queries are based on tree-sitter's default tag queries, but modified to only capture definitions.\\\\r\\\\n3. Sort the captures by their position in the file, output the name of the definition, and format by i.e. adding \\\\\\\"|----\\\\\\\\n\\\\\\\" for gaps between captured sections.\\\\r\\\\n\\\\r\\\\nThis approach allows us to focus on the most relevant parts of the code (defined by our language-specific queries) and provides a concise yet informative view of the file's structure and key elements.\\\\r\\\\n\\\\r\\\\n- https://github.com/tree-sitter/node-tree-sitter/blob/master/test/query_test.js\\\\r\\\\n- https://github.com/tree-sitter/tree-sitter/blob/master/lib/binding_web/test/query-test.js\\\\r\\\\n- https://github.com/tree-sitter/tree-sitter/blob/master/lib/binding_web/test/helper.js\\\\r\\\\n- https://tree-sitter.github.io/tree-sitter/code-navigation-systems\\\\r\\\\n*/\\\\r\\\\nasync function parseFile(filePath: string, languageParsers: LanguageParser): Promise<string | undefined> {\\\\r\\\\n\\\\tconst fileContent = await fs.readFile(filePath, \\\\\\\"utf8\\\\\\\")\\\\r\\\\n\\\\tconst ext = path.extname(filePath).toLowerCase().slice(1)\\\\r\\\\n\\\\r\\\\n\\\\tconst { parser, query } = languageParsers[ext] || {}\\\\r\\\\n\\\\tif (!parser || !query) {\\\\r\\\\n\\\\t\\\\treturn `Unsupported file type: ${filePath}`\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tlet formattedOutput = \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\r\\\\n\\\\ttry {\\\\r\\\\n\\\\t\\\\t// Parse the file content into an Abstract Syntax Tree (AST), a tree-like representation of the code\\\\r\\\\n\\\\t\\\\tconst tree = parser.parse(fileContent)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Apply the query to the AST and get the captures\\\\r\\\\n\\\\t\\\\t// Captures are specific parts of the AST that match our query patterns, each capture represents a node in the AST that we're interested in.\\\\r\\\\n\\\\t\\\\tconst captures = query.captures(tree.rootNode)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Sort captures by their start position\\\\r\\\\n\\\\t\\\\tcaptures.sort((a, b) => a.node.startPosition.row - b.node.startPosition.row)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Split the file content into individual lines\\\\r\\\\n\\\\t\\\\tconst lines = fileContent.split(\\\\\\\"\\\\\\\\n\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t// Keep track of the last line we've processed\\\\r\\\\n\\\\t\\\\tlet lastLine = -1\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tcaptures.forEach((capture) => {\\\\r\\\\n\\\\t\\\\t\\\\tconst { node, name } = capture\\\\r\\\\n\\\\t\\\\t\\\\t// Get the start and end lines of the current AST node\\\\r\\\\n\\\\t\\\\t\\\\tconst startLine = node.startPosition.row\\\\r\\\\n\\\\t\\\\t\\\\tconst endLine = node.endPosition.row\\\\r\\\\n\\\\t\\\\t\\\\t// Once we've retrieved the nodes we care about through the language query, we filter for lines with definition names only.\\\\r\\\\n\\\\t\\\\t\\\\t// name.startsWith(\\\\\\\"name.reference.\\\\\\\") > refs can be used for ranking purposes, but we don't need them for the output\\\\r\\\\n\\\\t\\\\t\\\\t// previously we did `name.startsWith(\\\\\\\"name.definition.\\\\\\\")` but this was too strict and excluded some relevant definitions\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// Add separator if there's a gap between captures\\\\r\\\\n\\\\t\\\\t\\\\tif (lastLine !== -1 && startLine > lastLine + 1) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tformattedOutput += \\\\\\\"|----\\\\\\\\n\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t// Only add the first line of the definition\\\\r\\\\n\\\\t\\\\t\\\\t// query captures includes the definition name and the definition implementation, but we only want the name (I found discrepencies in the naming structure for various languages, i.e. javascript names would be 'name' and typescript names would be 'name.definition)\\\\r\\\\n\\\\t\\\\t\\\\tif (name.includes(\\\\\\\"name\\\\\\\") && lines[startLine]) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tformattedOutput += `│${lines[startLine]}\\\\\\\\n`\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t// Adds all the captured lines\\\\r\\\\n\\\\t\\\\t\\\\t// for (let i = startLine; i <= endLine; i++) {\\\\r\\\\n\\\\t\\\\t\\\\t// \\\\tformattedOutput += `│${lines[i]}\\\\\\\\n`\\\\r\\\\n\\\\t\\\\t\\\\t// }\\\\r\\\\n\\\\t\\\\t\\\\t//}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tlastLine = endLine\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\tconsole.log(`Error parsing file: ${error}\\\\\\\\n`)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tif (formattedOutput.length > 0) {\\\\r\\\\n\\\\t\\\\treturn `|----\\\\\\\\n${formattedOutput}|----\\\\\\\\n`\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\treturn undefined\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":6433,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.963Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.963Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":164,\\\"nonEmptyLines\\\":139,\\\"commentLines\\\":45,\\\"complexity\\\":17},\\\"dependencies\\\":[\\\"fs/promises\\\",\\\"path\\\",\\\"../glob/list-files\\\",\\\"./languageParser\\\",\\\"../../utils/fs\\\"],\\\"quality\\\":{\\\"score\\\":17,\\\"issues\\\":[],\\\"duplicateLines\\\":11,\\\"longLines\\\":14,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.392Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":164},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\services\\\\\\\\tree-sitter\\\\\\\\languageParser.ts\\\":{\\\"content\\\":\\\"import * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport Parser from \\\\\\\"web-tree-sitter\\\\\\\"\\\\r\\\\nimport {\\\\r\\\\n\\\\tjavascriptQuery,\\\\r\\\\n\\\\ttypescriptQuery,\\\\r\\\\n\\\\tpythonQuery,\\\\r\\\\n\\\\trustQuery,\\\\r\\\\n\\\\tgoQuery,\\\\r\\\\n\\\\tcppQuery,\\\\r\\\\n\\\\tcQuery,\\\\r\\\\n\\\\tcsharpQuery,\\\\r\\\\n\\\\trubyQuery,\\\\r\\\\n\\\\tjavaQuery,\\\\r\\\\n\\\\tphpQuery,\\\\r\\\\n\\\\tswiftQuery,\\\\r\\\\n} from \\\\\\\"./queries\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport interface LanguageParser {\\\\r\\\\n\\\\t[key: string]: {\\\\r\\\\n\\\\t\\\\tparser: Parser\\\\r\\\\n\\\\t\\\\tquery: Parser.Query\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nasync function loadLanguage(langName: string) {\\\\r\\\\n\\\\treturn await Parser.Language.load(path.join(__dirname, `tree-sitter-${langName}.wasm`))\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nlet isParserInitialized = false\\\\r\\\\n\\\\r\\\\nasync function initializeParser() {\\\\r\\\\n\\\\tif (!isParserInitialized) {\\\\r\\\\n\\\\t\\\\tawait Parser.init()\\\\r\\\\n\\\\t\\\\tisParserInitialized = true\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n/*\\\\r\\\\nUsing node bindings for tree-sitter is problematic in vscode extensions \\\\r\\\\nbecause of incompatibility with electron. Going the .wasm route has the \\\\r\\\\nadvantage of not having to build for multiple architectures.\\\\r\\\\n\\\\r\\\\nWe use web-tree-sitter and tree-sitter-wasms which provides auto-updating prebuilt WASM binaries for tree-sitter's language parsers.\\\\r\\\\n\\\\r\\\\nThis function loads WASM modules for relevant language parsers based on input files:\\\\r\\\\n1. Extracts unique file extensions\\\\r\\\\n2. Maps extensions to language names\\\\r\\\\n3. Loads corresponding WASM files (containing grammar rules)\\\\r\\\\n4. Uses WASM modules to initialize tree-sitter parsers\\\\r\\\\n\\\\r\\\\nThis approach optimizes performance by loading only necessary parsers once for all relevant files.\\\\r\\\\n\\\\r\\\\nSources:\\\\r\\\\n- https://github.com/tree-sitter/node-tree-sitter/issues/169\\\\r\\\\n- https://github.com/tree-sitter/node-tree-sitter/issues/168\\\\r\\\\n- https://github.com/Gregoor/tree-sitter-wasms/blob/main/README.md\\\\r\\\\n- https://github.com/tree-sitter/tree-sitter/blob/master/lib/binding_web/README.md\\\\r\\\\n- https://github.com/tree-sitter/tree-sitter/blob/master/lib/binding_web/test/query-test.js\\\\r\\\\n*/\\\\r\\\\nexport async function loadRequiredLanguageParsers(filesToParse: string[]): Promise<LanguageParser> {\\\\r\\\\n\\\\tawait initializeParser()\\\\r\\\\n\\\\tconst extensionsToLoad = new Set(filesToParse.map((file) => path.extname(file).toLowerCase().slice(1)))\\\\r\\\\n\\\\tconst parsers: LanguageParser = {}\\\\r\\\\n\\\\tfor (const ext of extensionsToLoad) {\\\\r\\\\n\\\\t\\\\tlet language: Parser.Language\\\\r\\\\n\\\\t\\\\tlet query: Parser.Query\\\\r\\\\n\\\\t\\\\tswitch (ext) {\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"js\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"jsx\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlanguage = await loadLanguage(\\\\\\\"javascript\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tquery = language.query(javascriptQuery)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"ts\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlanguage = await loadLanguage(\\\\\\\"typescript\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tquery = language.query(typescriptQuery)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"tsx\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlanguage = await loadLanguage(\\\\\\\"tsx\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tquery = language.query(typescriptQuery)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"py\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlanguage = await loadLanguage(\\\\\\\"python\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tquery = language.query(pythonQuery)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"rs\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlanguage = await loadLanguage(\\\\\\\"rust\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tquery = language.query(rustQuery)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"go\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlanguage = await loadLanguage(\\\\\\\"go\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tquery = language.query(goQuery)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"cpp\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"hpp\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlanguage = await loadLanguage(\\\\\\\"cpp\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tquery = language.query(cppQuery)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"c\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"h\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlanguage = await loadLanguage(\\\\\\\"c\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tquery = language.query(cQuery)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"cs\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlanguage = await loadLanguage(\\\\\\\"c_sharp\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tquery = language.query(csharpQuery)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"rb\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlanguage = await loadLanguage(\\\\\\\"ruby\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tquery = language.query(rubyQuery)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"java\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlanguage = await loadLanguage(\\\\\\\"java\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tquery = language.query(javaQuery)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"php\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlanguage = await loadLanguage(\\\\\\\"php\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tquery = language.query(phpQuery)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\tcase \\\\\\\"swift\\\\\\\":\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tlanguage = await loadLanguage(\\\\\\\"swift\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tquery = language.query(swiftQuery)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\tdefault:\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tthrow new Error(`Unsupported language: ${ext}`)\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\tconst parser = new Parser()\\\\r\\\\n\\\\t\\\\tparser.setLanguage(language)\\\\r\\\\n\\\\t\\\\tparsers[ext] = { parser, query }\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\treturn parsers\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":3862,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.852Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.852Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":132,\\\"nonEmptyLines\\\":122,\\\"commentLines\\\":1,\\\"complexity\\\":19},\\\"dependencies\\\":[\\\"path\\\",\\\"web-tree-sitter\\\"],\\\"quality\\\":{\\\"score\\\":-6,\\\"issues\\\":[],\\\"duplicateLines\\\":20,\\\"longLines\\\":3,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.393Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":132},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\services\\\\\\\\tree-sitter\\\\\\\\queries\\\\\\\\c-sharp.ts\\\":{\\\"content\\\":\\\"/*\\\\r\\\\n- class declarations\\\\r\\\\n- interface declarations\\\\r\\\\n- method declarations\\\\r\\\\n- namespace declarations\\\\r\\\\n*/\\\\r\\\\nexport default `\\\\r\\\\n(class_declaration\\\\r\\\\n name: (identifier) @name.definition.class\\\\r\\\\n) @definition.class\\\\r\\\\n\\\\r\\\\n(interface_declaration\\\\r\\\\n name: (identifier) @name.definition.interface\\\\r\\\\n) @definition.interface\\\\r\\\\n\\\\r\\\\n(method_declaration\\\\r\\\\n name: (identifier) @name.definition.method\\\\r\\\\n) @definition.method\\\\r\\\\n\\\\r\\\\n(namespace_declaration\\\\r\\\\n name: (identifier) @name.definition.module\\\\r\\\\n) @definition.module\\\\r\\\\n`\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":493,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.852Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.852Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":24,\\\"nonEmptyLines\\\":20,\\\"commentLines\\\":1,\\\"complexity\\\":1},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":100,\\\"issues\\\":[],\\\"duplicateLines\\\":0,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.394Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":24},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\services\\\\\\\\tree-sitter\\\\\\\\queries\\\\\\\\c.ts\\\":{\\\"content\\\":\\\"/*\\\\r\\\\n- struct declarations\\\\r\\\\n- union declarations\\\\r\\\\n- function declarations\\\\r\\\\n- typedef declarations\\\\r\\\\n*/\\\\r\\\\nexport default `\\\\r\\\\n(struct_specifier name: (type_identifier) @name.definition.class body:(_)) @definition.class\\\\r\\\\n\\\\r\\\\n(declaration type: (union_specifier name: (type_identifier) @name.definition.class)) @definition.class\\\\r\\\\n\\\\r\\\\n(function_declarator declarator: (identifier) @name.definition.function) @definition.function\\\\r\\\\n\\\\r\\\\n(type_definition declarator: (type_identifier) @name.definition.type) @definition.type\\\\r\\\\n`\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":510,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.852Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.852Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":16,\\\"nonEmptyLines\\\":12,\\\"commentLines\\\":1,\\\"complexity\\\":1},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":98,\\\"issues\\\":[],\\\"duplicateLines\\\":0,\\\"longLines\\\":1,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.394Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":16},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\services\\\\\\\\tree-sitter\\\\\\\\queries\\\\\\\\cpp.ts\\\":{\\\"content\\\":\\\"/*\\\\r\\\\n- struct declarations\\\\r\\\\n- union declarations\\\\r\\\\n- function declarations\\\\r\\\\n- method declarations (with namespace scope)\\\\r\\\\n- typedef declarations\\\\r\\\\n- class declarations\\\\r\\\\n*/\\\\r\\\\nexport default `\\\\r\\\\n(struct_specifier name: (type_identifier) @name.definition.class body:(_)) @definition.class\\\\r\\\\n\\\\r\\\\n(declaration type: (union_specifier name: (type_identifier) @name.definition.class)) @definition.class\\\\r\\\\n\\\\r\\\\n(function_declarator declarator: (identifier) @name.definition.function) @definition.function\\\\r\\\\n\\\\r\\\\n(function_declarator declarator: (field_identifier) @name.definition.function) @definition.function\\\\r\\\\n\\\\r\\\\n(function_declarator declarator: (qualified_identifier scope: (namespace_identifier) @scope name: (identifier) @name.definition.method)) @definition.method\\\\r\\\\n\\\\r\\\\n(type_definition declarator: (type_identifier) @name.definition.type) @definition.type\\\\r\\\\n\\\\r\\\\n(class_specifier name: (type_identifier) @name.definition.class) @definition.class\\\\r\\\\n`\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":926,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.852Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.852Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":24,\\\"nonEmptyLines\\\":17,\\\"commentLines\\\":1,\\\"complexity\\\":1},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":96,\\\"issues\\\":[],\\\"duplicateLines\\\":0,\\\"longLines\\\":2,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.395Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":24},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\services\\\\\\\\tree-sitter\\\\\\\\queries\\\\\\\\go.ts\\\":{\\\"content\\\":\\\"/*\\\\r\\\\n- function declarations (with associated comments)\\\\r\\\\n- method declarations (with associated comments)\\\\r\\\\n- type specifications\\\\r\\\\n*/\\\\r\\\\nexport default `\\\\r\\\\n(\\\\r\\\\n (comment)* @doc\\\\r\\\\n .\\\\r\\\\n (function_declaration\\\\r\\\\n name: (identifier) @name.definition.function) @definition.function\\\\r\\\\n (#strip! @doc \\\\\\\"^//\\\\\\\\\\\\\\\\s*\\\\\\\")\\\\r\\\\n (#set-adjacent! @doc @definition.function)\\\\r\\\\n)\\\\r\\\\n\\\\r\\\\n(\\\\r\\\\n (comment)* @doc\\\\r\\\\n .\\\\r\\\\n (method_declaration\\\\r\\\\n name: (field_identifier) @name.definition.method) @definition.method\\\\r\\\\n (#strip! @doc \\\\\\\"^//\\\\\\\\\\\\\\\\s*\\\\\\\")\\\\r\\\\n (#set-adjacent! @doc @definition.method)\\\\r\\\\n)\\\\r\\\\n\\\\r\\\\n(type_spec\\\\r\\\\n name: (type_identifier) @name.definition.type) @definition.type\\\\r\\\\n`\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":637,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.852Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.852Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":28,\\\"nonEmptyLines\\\":25,\\\"commentLines\\\":1,\\\"complexity\\\":1},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":75,\\\"issues\\\":[],\\\"duplicateLines\\\":5,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.396Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":28},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\services\\\\\\\\tree-sitter\\\\\\\\queries\\\\\\\\index.ts\\\":{\\\"content\\\":\\\"export { default as phpQuery } from \\\\\\\"./php\\\\\\\"\\\\r\\\\nexport { default as typescriptQuery } from \\\\\\\"./typescript\\\\\\\"\\\\r\\\\nexport { default as pythonQuery } from \\\\\\\"./python\\\\\\\"\\\\r\\\\nexport { default as javascriptQuery } from \\\\\\\"./javascript\\\\\\\"\\\\r\\\\nexport { default as javaQuery } from \\\\\\\"./java\\\\\\\"\\\\r\\\\nexport { default as rustQuery } from \\\\\\\"./rust\\\\\\\"\\\\r\\\\nexport { default as rubyQuery } from \\\\\\\"./ruby\\\\\\\"\\\\r\\\\nexport { default as cppQuery } from \\\\\\\"./cpp\\\\\\\"\\\\r\\\\nexport { default as cQuery } from \\\\\\\"./c\\\\\\\"\\\\r\\\\nexport { default as csharpQuery } from \\\\\\\"./c-sharp\\\\\\\"\\\\r\\\\nexport { default as goQuery } from \\\\\\\"./go\\\\\\\"\\\\r\\\\nexport { default as swiftQuery } from \\\\\\\"./swift\\\\\\\"\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":585,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.852Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.852Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":13,\\\"nonEmptyLines\\\":12,\\\"commentLines\\\":0,\\\"complexity\\\":1},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":100,\\\"issues\\\":[],\\\"duplicateLines\\\":0,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.397Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":13},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\services\\\\\\\\tree-sitter\\\\\\\\queries\\\\\\\\java.ts\\\":{\\\"content\\\":\\\"/*\\\\r\\\\n- class declarations\\\\r\\\\n- method declarations\\\\r\\\\n- interface declarations\\\\r\\\\n*/\\\\r\\\\nexport default `\\\\r\\\\n(class_declaration\\\\r\\\\n name: (identifier) @name.definition.class) @definition.class\\\\r\\\\n\\\\r\\\\n(method_declaration\\\\r\\\\n name: (identifier) @name.definition.method) @definition.method\\\\r\\\\n\\\\r\\\\n(interface_declaration\\\\r\\\\n name: (identifier) @name.definition.interface) @definition.interface\\\\r\\\\n`\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":371,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.852Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.852Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":16,\\\"nonEmptyLines\\\":13,\\\"commentLines\\\":1,\\\"complexity\\\":1},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":100,\\\"issues\\\":[],\\\"duplicateLines\\\":0,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.398Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":16},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\services\\\\\\\\tree-sitter\\\\\\\\queries\\\\\\\\javascript.ts\\\":{\\\"content\\\":\\\"/*\\\\r\\\\n- class definitions\\\\r\\\\n- method definitions\\\\r\\\\n- named function declarations\\\\r\\\\n- arrow functions and function expressions assigned to variables\\\\r\\\\n*/\\\\r\\\\nexport default `\\\\r\\\\n(\\\\r\\\\n (comment)* @doc\\\\r\\\\n .\\\\r\\\\n (method_definition\\\\r\\\\n name: (property_identifier) @name) @definition.method\\\\r\\\\n (#not-eq? @name \\\\\\\"constructor\\\\\\\")\\\\r\\\\n (#strip! @doc \\\\\\\"^[\\\\\\\\\\\\\\\\s\\\\\\\\\\\\\\\\*/]+|^[\\\\\\\\\\\\\\\\s\\\\\\\\\\\\\\\\*/]$\\\\\\\")\\\\r\\\\n (#select-adjacent! @doc @definition.method)\\\\r\\\\n)\\\\r\\\\n\\\\r\\\\n(\\\\r\\\\n (comment)* @doc\\\\r\\\\n .\\\\r\\\\n [\\\\r\\\\n (class\\\\r\\\\n name: (_) @name)\\\\r\\\\n (class_declaration\\\\r\\\\n name: (_) @name)\\\\r\\\\n ] @definition.class\\\\r\\\\n (#strip! @doc \\\\\\\"^[\\\\\\\\\\\\\\\\s\\\\\\\\\\\\\\\\*/]+|^[\\\\\\\\\\\\\\\\s\\\\\\\\\\\\\\\\*/]$\\\\\\\")\\\\r\\\\n (#select-adjacent! @doc @definition.class)\\\\r\\\\n)\\\\r\\\\n\\\\r\\\\n(\\\\r\\\\n (comment)* @doc\\\\r\\\\n .\\\\r\\\\n [\\\\r\\\\n (function_declaration\\\\r\\\\n name: (identifier) @name)\\\\r\\\\n (generator_function_declaration\\\\r\\\\n name: (identifier) @name)\\\\r\\\\n ] @definition.function\\\\r\\\\n (#strip! @doc \\\\\\\"^[\\\\\\\\\\\\\\\\s\\\\\\\\\\\\\\\\*/]+|^[\\\\\\\\\\\\\\\\s\\\\\\\\\\\\\\\\*/]$\\\\\\\")\\\\r\\\\n (#select-adjacent! @doc @definition.function)\\\\r\\\\n)\\\\r\\\\n\\\\r\\\\n(\\\\r\\\\n (comment)* @doc\\\\r\\\\n .\\\\r\\\\n (lexical_declaration\\\\r\\\\n (variable_declarator\\\\r\\\\n name: (identifier) @name\\\\r\\\\n value: [(arrow_function) (function_expression)]) @definition.function)\\\\r\\\\n (#strip! @doc \\\\\\\"^[\\\\\\\\\\\\\\\\s\\\\\\\\\\\\\\\\*/]+|^[\\\\\\\\\\\\\\\\s\\\\\\\\\\\\\\\\*/]$\\\\\\\")\\\\r\\\\n (#select-adjacent! @doc @definition.function)\\\\r\\\\n)\\\\r\\\\n\\\\r\\\\n(\\\\r\\\\n (comment)* @doc\\\\r\\\\n .\\\\r\\\\n (variable_declaration\\\\r\\\\n (variable_declarator\\\\r\\\\n name: (identifier) @name\\\\r\\\\n value: [(arrow_function) (function_expression)]) @definition.function)\\\\r\\\\n (#strip! @doc \\\\\\\"^[\\\\\\\\\\\\\\\\s\\\\\\\\\\\\\\\\*/]+|^[\\\\\\\\\\\\\\\\s\\\\\\\\\\\\\\\\*/]$\\\\\\\")\\\\r\\\\n (#select-adjacent! @doc @definition.function)\\\\r\\\\n)\\\\r\\\\n`\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":1496,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.853Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.852Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":66,\\\"nonEmptyLines\\\":61,\\\"commentLines\\\":1,\\\"complexity\\\":2},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":-40,\\\"issues\\\":[],\\\"duplicateLines\\\":28,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.399Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":66},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\services\\\\\\\\tree-sitter\\\\\\\\queries\\\\\\\\php.ts\\\":{\\\"content\\\":\\\"/*\\\\r\\\\n- class declarations\\\\r\\\\n- function definitions\\\\r\\\\n- method declarations\\\\r\\\\n*/\\\\r\\\\nexport default `\\\\r\\\\n(class_declaration\\\\r\\\\n name: (name) @name.definition.class) @definition.class\\\\r\\\\n\\\\r\\\\n(function_definition\\\\r\\\\n name: (name) @name.definition.function) @definition.function\\\\r\\\\n\\\\r\\\\n(method_declaration\\\\r\\\\n name: (name) @name.definition.function) @definition.function\\\\r\\\\n`\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":351,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.853Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.853Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":16,\\\"nonEmptyLines\\\":13,\\\"commentLines\\\":1,\\\"complexity\\\":1},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":95,\\\"issues\\\":[],\\\"duplicateLines\\\":1,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.400Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":16},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\services\\\\\\\\tree-sitter\\\\\\\\queries\\\\\\\\python.ts\\\":{\\\"content\\\":\\\"/*\\\\r\\\\n- class definitions\\\\r\\\\n- function definitions\\\\r\\\\n*/\\\\r\\\\nexport default `\\\\r\\\\n(class_definition\\\\r\\\\n name: (identifier) @name.definition.class) @definition.class\\\\r\\\\n\\\\r\\\\n(function_definition\\\\r\\\\n name: (identifier) @name.definition.function) @definition.function\\\\r\\\\n`\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":251,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.853Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.853Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":12,\\\"nonEmptyLines\\\":10,\\\"commentLines\\\":1,\\\"complexity\\\":1},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":100,\\\"issues\\\":[],\\\"duplicateLines\\\":0,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.401Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":12},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\services\\\\\\\\tree-sitter\\\\\\\\queries\\\\\\\\ruby.ts\\\":{\\\"content\\\":\\\"/*\\\\r\\\\n- method definitions (including singleton methods and aliases, with associated comments)\\\\r\\\\n- class definitions (including singleton classes, with associated comments)\\\\r\\\\n- module definitions\\\\r\\\\n*/\\\\r\\\\nexport default `\\\\r\\\\n(\\\\r\\\\n (comment)* @doc\\\\r\\\\n .\\\\r\\\\n [\\\\r\\\\n (method\\\\r\\\\n name: (_) @name.definition.method) @definition.method\\\\r\\\\n (singleton_method\\\\r\\\\n name: (_) @name.definition.method) @definition.method\\\\r\\\\n ]\\\\r\\\\n (#strip! @doc \\\\\\\"^#\\\\\\\\\\\\\\\\s*\\\\\\\")\\\\r\\\\n (#select-adjacent! @doc @definition.method)\\\\r\\\\n)\\\\r\\\\n\\\\r\\\\n(alias\\\\r\\\\n name: (_) @name.definition.method) @definition.method\\\\r\\\\n\\\\r\\\\n(\\\\r\\\\n (comment)* @doc\\\\r\\\\n .\\\\r\\\\n [\\\\r\\\\n (class\\\\r\\\\n name: [\\\\r\\\\n (constant) @name.definition.class\\\\r\\\\n (scope_resolution\\\\r\\\\n name: (_) @name.definition.class)\\\\r\\\\n ]) @definition.class\\\\r\\\\n (singleton_class\\\\r\\\\n value: [\\\\r\\\\n (constant) @name.definition.class\\\\r\\\\n (scope_resolution\\\\r\\\\n name: (_) @name.definition.class)\\\\r\\\\n ]) @definition.class\\\\r\\\\n ]\\\\r\\\\n (#strip! @doc \\\\\\\"^#\\\\\\\\\\\\\\\\s*\\\\\\\")\\\\r\\\\n (#select-adjacent! @doc @definition.class)\\\\r\\\\n)\\\\r\\\\n\\\\r\\\\n(\\\\r\\\\n (module\\\\r\\\\n name: [\\\\r\\\\n (constant) @name.definition.module\\\\r\\\\n (scope_resolution\\\\r\\\\n name: (_) @name.definition.module)\\\\r\\\\n ]) @definition.module\\\\r\\\\n)\\\\r\\\\n`\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":1193,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.854Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.853Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":53,\\\"nonEmptyLines\\\":49,\\\"commentLines\\\":1,\\\"complexity\\\":1},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":15,\\\"issues\\\":[],\\\"duplicateLines\\\":17,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.403Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":53},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\services\\\\\\\\tree-sitter\\\\\\\\queries\\\\\\\\rust.ts\\\":{\\\"content\\\":\\\"/*\\\\r\\\\n- struct definitions\\\\r\\\\n- method definitions\\\\r\\\\n- function definitions\\\\r\\\\n*/\\\\r\\\\nexport default `\\\\r\\\\n(struct_item\\\\r\\\\n name: (type_identifier) @name.definition.class) @definition.class\\\\r\\\\n\\\\r\\\\n(declaration_list\\\\r\\\\n (function_item\\\\r\\\\n name: (identifier) @name.definition.method)) @definition.method\\\\r\\\\n\\\\r\\\\n(function_item\\\\r\\\\n name: (identifier) @name.definition.function) @definition.function\\\\r\\\\n`\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":386,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.854Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.854Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":17,\\\"nonEmptyLines\\\":14,\\\"commentLines\\\":1,\\\"complexity\\\":1},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":95,\\\"issues\\\":[],\\\"duplicateLines\\\":1,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.404Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":17},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\services\\\\\\\\tree-sitter\\\\\\\\queries\\\\\\\\swift.ts\\\":{\\\"content\\\":\\\"/*\\\\r\\\\n- class declarations\\\\r\\\\n- method declarations (including initializers and deinitializers)\\\\r\\\\n- property declarations\\\\r\\\\n- function declarations\\\\r\\\\n*/\\\\r\\\\nexport default `\\\\r\\\\n(class_declaration\\\\r\\\\n name: (type_identifier) @name) @definition.class\\\\r\\\\n\\\\r\\\\n(protocol_declaration\\\\r\\\\n name: (type_identifier) @name) @definition.interface\\\\r\\\\n\\\\r\\\\n(class_declaration\\\\r\\\\n (class_body\\\\r\\\\n [\\\\r\\\\n (function_declaration\\\\r\\\\n name: (simple_identifier) @name\\\\r\\\\n )\\\\r\\\\n (subscript_declaration\\\\r\\\\n (parameter (simple_identifier) @name)\\\\r\\\\n )\\\\r\\\\n (init_declaration \\\\\\\"init\\\\\\\" @name)\\\\r\\\\n (deinit_declaration \\\\\\\"deinit\\\\\\\" @name)\\\\r\\\\n ]\\\\r\\\\n )\\\\r\\\\n) @definition.method\\\\r\\\\n\\\\r\\\\n(class_declaration\\\\r\\\\n (class_body\\\\r\\\\n [\\\\r\\\\n (property_declaration\\\\r\\\\n (pattern (simple_identifier) @name)\\\\r\\\\n )\\\\r\\\\n ]\\\\r\\\\n )\\\\r\\\\n) @definition.property\\\\r\\\\n\\\\r\\\\n(property_declaration\\\\r\\\\n (pattern (simple_identifier) @name)\\\\r\\\\n) @definition.property\\\\r\\\\n\\\\r\\\\n(function_declaration\\\\r\\\\n name: (simple_identifier) @name) @definition.function\\\\r\\\\n`\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":1079,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.854Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.854Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":46,\\\"nonEmptyLines\\\":40,\\\"commentLines\\\":1,\\\"complexity\\\":1},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":35,\\\"issues\\\":[],\\\"duplicateLines\\\":13,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.405Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":46},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\services\\\\\\\\tree-sitter\\\\\\\\queries\\\\\\\\typescript.ts\\\":{\\\"content\\\":\\\"/*\\\\r\\\\n- function signatures and declarations\\\\r\\\\n- method signatures and definitions\\\\r\\\\n- abstract method signatures\\\\r\\\\n- class declarations (including abstract classes)\\\\r\\\\n- module declarations\\\\r\\\\n*/\\\\r\\\\nexport default `\\\\r\\\\n(function_signature\\\\r\\\\n name: (identifier) @name.definition.function) @definition.function\\\\r\\\\n\\\\r\\\\n(method_signature\\\\r\\\\n name: (property_identifier) @name.definition.method) @definition.method\\\\r\\\\n\\\\r\\\\n(abstract_method_signature\\\\r\\\\n name: (property_identifier) @name.definition.method) @definition.method\\\\r\\\\n\\\\r\\\\n(abstract_class_declaration\\\\r\\\\n name: (type_identifier) @name.definition.class) @definition.class\\\\r\\\\n\\\\r\\\\n(module\\\\r\\\\n name: (identifier) @name.definition.module) @definition.module\\\\r\\\\n\\\\r\\\\n(function_declaration\\\\r\\\\n name: (identifier) @name.definition.function) @definition.function\\\\r\\\\n\\\\r\\\\n(method_definition\\\\r\\\\n name: (property_identifier) @name.definition.method) @definition.method\\\\r\\\\n\\\\r\\\\n(class_declaration\\\\r\\\\n name: (type_identifier) @name.definition.class) @definition.class\\\\r\\\\n`\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":962,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.854Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.854Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":33,\\\"nonEmptyLines\\\":25,\\\"commentLines\\\":1,\\\"complexity\\\":1},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":80,\\\"issues\\\":[],\\\"duplicateLines\\\":4,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.406Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":33},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\shared\\\\\\\\api.ts\\\":{\\\"content\\\":\\\"export type ApiProvider =\\\\r\\\\n\\\\t| \\\\\\\"anthropic\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"openrouter\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"bedrock\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"vertex\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"openai\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"ollama\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"lmstudio\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"gemini\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"openai-native\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"deepseek\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport interface ApiHandlerOptions {\\\\r\\\\n\\\\tapiModelId?: string\\\\r\\\\n\\\\tapiKey?: string // anthropic\\\\r\\\\n\\\\tanthropicBaseUrl?: string\\\\r\\\\n\\\\topenRouterApiKey?: string\\\\r\\\\n\\\\topenRouterModelId?: string\\\\r\\\\n\\\\topenRouterModelInfo?: ModelInfo\\\\r\\\\n\\\\tawsAccessKey?: string\\\\r\\\\n\\\\tawsSecretKey?: string\\\\r\\\\n\\\\tawsSessionToken?: string\\\\r\\\\n\\\\tawsRegion?: string\\\\r\\\\n\\\\tawsUseCrossRegionInference?: boolean\\\\r\\\\n\\\\tvertexProjectId?: string\\\\r\\\\n\\\\tvertexRegion?: string\\\\r\\\\n\\\\topenAiBaseUrl?: string\\\\r\\\\n\\\\topenAiApiKey?: string\\\\r\\\\n\\\\topenAiModelId?: string\\\\r\\\\n\\\\tollamaModelId?: string\\\\r\\\\n\\\\tollamaBaseUrl?: string\\\\r\\\\n\\\\tlmStudioModelId?: string\\\\r\\\\n\\\\tlmStudioBaseUrl?: string\\\\r\\\\n\\\\tgeminiApiKey?: string\\\\r\\\\n\\\\topenAiNativeApiKey?: string\\\\r\\\\n\\\\tdeepSeekApiKey?: string\\\\r\\\\n\\\\tazureApiVersion?: string\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport type ApiConfiguration = ApiHandlerOptions & {\\\\r\\\\n\\\\tapiProvider?: ApiProvider\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n// Models\\\\r\\\\n\\\\r\\\\nexport interface ModelInfo {\\\\r\\\\n\\\\tmaxTokens?: number\\\\r\\\\n\\\\tcontextWindow?: number\\\\r\\\\n\\\\tsupportsImages?: boolean\\\\r\\\\n\\\\tsupportsComputerUse?: boolean\\\\r\\\\n\\\\tsupportsPromptCache: boolean // this value is hardcoded for now\\\\r\\\\n\\\\tinputPrice?: number\\\\r\\\\n\\\\toutputPrice?: number\\\\r\\\\n\\\\tcacheWritesPrice?: number\\\\r\\\\n\\\\tcacheReadsPrice?: number\\\\r\\\\n\\\\tdescription?: string\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n// Anthropic\\\\r\\\\n// https://docs.anthropic.com/en/docs/about-claude/models // prices updated 2025-01-02\\\\r\\\\nexport type AnthropicModelId = keyof typeof anthropicModels\\\\r\\\\nexport const anthropicDefaultModelId: AnthropicModelId = \\\\\\\"claude-3-5-sonnet-20241022\\\\\\\"\\\\r\\\\nexport const anthropicModels = {\\\\r\\\\n\\\\t\\\\\\\"claude-3-5-sonnet-20241022\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 8192,\\\\r\\\\n\\\\t\\\\tcontextWindow: 200_000,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsComputerUse: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: true,\\\\r\\\\n\\\\t\\\\tinputPrice: 3.0, // $3 per million input tokens\\\\r\\\\n\\\\t\\\\toutputPrice: 15.0, // $15 per million output tokens\\\\r\\\\n\\\\t\\\\tcacheWritesPrice: 3.75, // $3.75 per million tokens\\\\r\\\\n\\\\t\\\\tcacheReadsPrice: 0.3, // $0.30 per million tokens\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"claude-3-5-haiku-20241022\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 8192,\\\\r\\\\n\\\\t\\\\tcontextWindow: 200_000,\\\\r\\\\n\\\\t\\\\tsupportsImages: false,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: true,\\\\r\\\\n\\\\t\\\\tinputPrice: 0.8,\\\\r\\\\n\\\\t\\\\toutputPrice: 4.0,\\\\r\\\\n\\\\t\\\\tcacheWritesPrice: 1.0,\\\\r\\\\n\\\\t\\\\tcacheReadsPrice: 0.08,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"claude-3-opus-20240229\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 4096,\\\\r\\\\n\\\\t\\\\tcontextWindow: 200_000,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: true,\\\\r\\\\n\\\\t\\\\tinputPrice: 15.0,\\\\r\\\\n\\\\t\\\\toutputPrice: 75.0,\\\\r\\\\n\\\\t\\\\tcacheWritesPrice: 18.75,\\\\r\\\\n\\\\t\\\\tcacheReadsPrice: 1.5,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"claude-3-haiku-20240307\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 4096,\\\\r\\\\n\\\\t\\\\tcontextWindow: 200_000,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: true,\\\\r\\\\n\\\\t\\\\tinputPrice: 0.25,\\\\r\\\\n\\\\t\\\\toutputPrice: 1.25,\\\\r\\\\n\\\\t\\\\tcacheWritesPrice: 0.3,\\\\r\\\\n\\\\t\\\\tcacheReadsPrice: 0.03,\\\\r\\\\n\\\\t},\\\\r\\\\n} as const satisfies Record<string, ModelInfo> // as const assertion makes the object deeply readonly\\\\r\\\\n\\\\r\\\\n// AWS Bedrock\\\\r\\\\n// https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference.html\\\\r\\\\nexport type BedrockModelId = keyof typeof bedrockModels\\\\r\\\\nexport const bedrockDefaultModelId: BedrockModelId = \\\\\\\"anthropic.claude-3-5-sonnet-20241022-v2:0\\\\\\\"\\\\r\\\\nexport const bedrockModels = {\\\\r\\\\n\\\\t\\\\\\\"anthropic.claude-3-5-sonnet-20241022-v2:0\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 8192,\\\\r\\\\n\\\\t\\\\tcontextWindow: 200_000,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsComputerUse: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 3.0,\\\\r\\\\n\\\\t\\\\toutputPrice: 15.0,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"anthropic.claude-3-5-haiku-20241022-v1:0\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 8192,\\\\r\\\\n\\\\t\\\\tcontextWindow: 200_000,\\\\r\\\\n\\\\t\\\\tsupportsImages: false,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 1.0,\\\\r\\\\n\\\\t\\\\toutputPrice: 5.0,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"anthropic.claude-3-5-sonnet-20240620-v1:0\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 8192,\\\\r\\\\n\\\\t\\\\tcontextWindow: 200_000,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 3.0,\\\\r\\\\n\\\\t\\\\toutputPrice: 15.0,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"anthropic.claude-3-opus-20240229-v1:0\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 4096,\\\\r\\\\n\\\\t\\\\tcontextWindow: 200_000,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 15.0,\\\\r\\\\n\\\\t\\\\toutputPrice: 75.0,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"anthropic.claude-3-sonnet-20240229-v1:0\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 4096,\\\\r\\\\n\\\\t\\\\tcontextWindow: 200_000,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 3.0,\\\\r\\\\n\\\\t\\\\toutputPrice: 15.0,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"anthropic.claude-3-haiku-20240307-v1:0\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 4096,\\\\r\\\\n\\\\t\\\\tcontextWindow: 200_000,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 0.25,\\\\r\\\\n\\\\t\\\\toutputPrice: 1.25,\\\\r\\\\n\\\\t},\\\\r\\\\n} as const satisfies Record<string, ModelInfo>\\\\r\\\\n\\\\r\\\\n// OpenRouter\\\\r\\\\n// https://openrouter.ai/models?order=newest&supported_parameters=tools\\\\r\\\\nexport const openRouterDefaultModelId = \\\\\\\"anthropic/claude-3.5-sonnet:beta\\\\\\\" // will always exist in openRouterModels\\\\r\\\\nexport const openRouterDefaultModelInfo: ModelInfo = {\\\\r\\\\n\\\\tmaxTokens: 8192,\\\\r\\\\n\\\\tcontextWindow: 200_000,\\\\r\\\\n\\\\tsupportsImages: true,\\\\r\\\\n\\\\tsupportsComputerUse: true,\\\\r\\\\n\\\\tsupportsPromptCache: true,\\\\r\\\\n\\\\tinputPrice: 3.0,\\\\r\\\\n\\\\toutputPrice: 15.0,\\\\r\\\\n\\\\tcacheWritesPrice: 3.75,\\\\r\\\\n\\\\tcacheReadsPrice: 0.3,\\\\r\\\\n\\\\tdescription:\\\\r\\\\n\\\\t\\\\t\\\\\\\"The new Claude 3.5 Sonnet delivers better-than-Opus capabilities, faster-than-Sonnet speeds, at the same Sonnet prices. Sonnet is particularly good at:\\\\\\\\n\\\\\\\\n- Coding: New Sonnet scores ~49% on SWE-Bench Verified, higher than the last best score, and without any fancy prompt scaffolding\\\\\\\\n- Data science: Augments human data science expertise; navigates unstructured data while using multiple tools for insights\\\\\\\\n- Visual processing: excelling at interpreting charts, graphs, and images, accurately transcribing text to derive insights beyond just the text alone\\\\\\\\n- Agentic tasks: exceptional tool use, making it great at agentic tasks (i.e. complex, multi-step problem solving tasks that require engaging with other systems)\\\\\\\\n\\\\\\\\n#multimodal\\\\\\\\n\\\\\\\\n_This is a faster endpoint, made available in collaboration with Anthropic, that is self-moderated: response moderation happens on the provider's side instead of OpenRouter's. For requests that pass moderation, it's identical to the [Standard](/anthropic/claude-3.5-sonnet) variant._\\\\\\\",\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n// Vertex AI\\\\r\\\\n// https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-claude\\\\r\\\\nexport type VertexModelId = keyof typeof vertexModels\\\\r\\\\nexport const vertexDefaultModelId: VertexModelId = \\\\\\\"claude-3-5-sonnet-v2@20241022\\\\\\\"\\\\r\\\\nexport const vertexModels = {\\\\r\\\\n\\\\t\\\\\\\"claude-3-5-sonnet-v2@20241022\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 8192,\\\\r\\\\n\\\\t\\\\tcontextWindow: 200_000,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsComputerUse: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 3.0,\\\\r\\\\n\\\\t\\\\toutputPrice: 15.0,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"claude-3-5-sonnet@20240620\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 8192,\\\\r\\\\n\\\\t\\\\tcontextWindow: 200_000,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 3.0,\\\\r\\\\n\\\\t\\\\toutputPrice: 15.0,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"claude-3-5-haiku@20241022\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 8192,\\\\r\\\\n\\\\t\\\\tcontextWindow: 200_000,\\\\r\\\\n\\\\t\\\\tsupportsImages: false,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 1.0,\\\\r\\\\n\\\\t\\\\toutputPrice: 5.0,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"claude-3-opus@20240229\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 4096,\\\\r\\\\n\\\\t\\\\tcontextWindow: 200_000,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 15.0,\\\\r\\\\n\\\\t\\\\toutputPrice: 75.0,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"claude-3-haiku@20240307\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 4096,\\\\r\\\\n\\\\t\\\\tcontextWindow: 200_000,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 0.25,\\\\r\\\\n\\\\t\\\\toutputPrice: 1.25,\\\\r\\\\n\\\\t},\\\\r\\\\n} as const satisfies Record<string, ModelInfo>\\\\r\\\\n\\\\r\\\\nexport const openAiModelInfoSaneDefaults: ModelInfo = {\\\\r\\\\n\\\\tmaxTokens: -1,\\\\r\\\\n\\\\tcontextWindow: 128_000,\\\\r\\\\n\\\\tsupportsImages: true,\\\\r\\\\n\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\tinputPrice: 0,\\\\r\\\\n\\\\toutputPrice: 0,\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n// Gemini\\\\r\\\\n// https://ai.google.dev/gemini-api/docs/models/gemini\\\\r\\\\nexport type GeminiModelId = keyof typeof geminiModels\\\\r\\\\nexport const geminiDefaultModelId: GeminiModelId = \\\\\\\"gemini-2.0-flash-thinking-exp-1219\\\\\\\"\\\\r\\\\nexport const geminiModels = {\\\\r\\\\n\\\\t\\\\\\\"gemini-2.0-flash-thinking-exp-1219\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 8192,\\\\r\\\\n\\\\t\\\\tcontextWindow: 32_767,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 0,\\\\r\\\\n\\\\t\\\\toutputPrice: 0,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"gemini-2.0-flash-exp\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 8192,\\\\r\\\\n\\\\t\\\\tcontextWindow: 1_048_576,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 0,\\\\r\\\\n\\\\t\\\\toutputPrice: 0,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"gemini-exp-1206\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 8192,\\\\r\\\\n\\\\t\\\\tcontextWindow: 2_097_152,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 0,\\\\r\\\\n\\\\t\\\\toutputPrice: 0,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"gemini-1.5-flash-002\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 8192,\\\\r\\\\n\\\\t\\\\tcontextWindow: 1_048_576,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 0,\\\\r\\\\n\\\\t\\\\toutputPrice: 0,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"gemini-1.5-flash-exp-0827\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 8192,\\\\r\\\\n\\\\t\\\\tcontextWindow: 1_048_576,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 0,\\\\r\\\\n\\\\t\\\\toutputPrice: 0,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"gemini-1.5-flash-8b-exp-0827\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 8192,\\\\r\\\\n\\\\t\\\\tcontextWindow: 1_048_576,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 0,\\\\r\\\\n\\\\t\\\\toutputPrice: 0,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"gemini-1.5-pro-002\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 8192,\\\\r\\\\n\\\\t\\\\tcontextWindow: 2_097_152,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 0,\\\\r\\\\n\\\\t\\\\toutputPrice: 0,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"gemini-1.5-pro-exp-0827\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 8192,\\\\r\\\\n\\\\t\\\\tcontextWindow: 2_097_152,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 0,\\\\r\\\\n\\\\t\\\\toutputPrice: 0,\\\\r\\\\n\\\\t},\\\\r\\\\n} as const satisfies Record<string, ModelInfo>\\\\r\\\\n\\\\r\\\\n// OpenAI Native\\\\r\\\\n// https://openai.com/api/pricing/\\\\r\\\\nexport type OpenAiNativeModelId = keyof typeof openAiNativeModels\\\\r\\\\nexport const openAiNativeDefaultModelId: OpenAiNativeModelId = \\\\\\\"gpt-4o\\\\\\\"\\\\r\\\\nexport const openAiNativeModels = {\\\\r\\\\n\\\\t// don't support tool use yet\\\\r\\\\n\\\\to1: {\\\\r\\\\n\\\\t\\\\tmaxTokens: 100_000,\\\\r\\\\n\\\\t\\\\tcontextWindow: 200_000,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 15,\\\\r\\\\n\\\\t\\\\toutputPrice: 60,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"o1-preview\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 32_768,\\\\r\\\\n\\\\t\\\\tcontextWindow: 128_000,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 15,\\\\r\\\\n\\\\t\\\\toutputPrice: 60,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"o1-mini\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 65_536,\\\\r\\\\n\\\\t\\\\tcontextWindow: 128_000,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 3,\\\\r\\\\n\\\\t\\\\toutputPrice: 12,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"gpt-4o\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 4_096,\\\\r\\\\n\\\\t\\\\tcontextWindow: 128_000,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 5,\\\\r\\\\n\\\\t\\\\toutputPrice: 15,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\t\\\\\\\"gpt-4o-mini\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 16_384,\\\\r\\\\n\\\\t\\\\tcontextWindow: 128_000,\\\\r\\\\n\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\tinputPrice: 0.15,\\\\r\\\\n\\\\t\\\\toutputPrice: 0.6,\\\\r\\\\n\\\\t},\\\\r\\\\n} as const satisfies Record<string, ModelInfo>\\\\r\\\\n\\\\r\\\\n// Azure OpenAI\\\\r\\\\n// https://learn.microsoft.com/en-us/azure/ai-services/openai/api-version-deprecation\\\\r\\\\n// https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#api-specs\\\\r\\\\nexport const azureOpenAiDefaultApiVersion = \\\\\\\"2024-08-01-preview\\\\\\\"\\\\r\\\\n\\\\r\\\\n// DeepSeek\\\\r\\\\n// https://api-docs.deepseek.com/quick_start/pricing\\\\r\\\\nexport type DeepSeekModelId = keyof typeof deepSeekModels\\\\r\\\\nexport const deepSeekDefaultModelId: DeepSeekModelId = \\\\\\\"deepseek-chat\\\\\\\"\\\\r\\\\nexport const deepSeekModels = {\\\\r\\\\n\\\\t\\\\\\\"deepseek-chat\\\\\\\": {\\\\r\\\\n\\\\t\\\\tmaxTokens: 8_000,\\\\r\\\\n\\\\t\\\\tcontextWindow: 64_000,\\\\r\\\\n\\\\t\\\\tsupportsImages: false,\\\\r\\\\n\\\\t\\\\tsupportsPromptCache: true, // supports context caching, but not in the way anthropic does it (deepseek reports input tokens and reads/writes in the same usage report) FIXME: we need to show users cache stats how deepseek does it\\\\r\\\\n\\\\t\\\\tinputPrice: 0, // technically there is no input price, it's all either a cache hit or miss (ApiOptions will not show this)\\\\r\\\\n\\\\t\\\\toutputPrice: 0.28,\\\\r\\\\n\\\\t\\\\tcacheWritesPrice: 0.14,\\\\r\\\\n\\\\t\\\\tcacheReadsPrice: 0.014,\\\\r\\\\n\\\\t},\\\\r\\\\n} as const satisfies Record<string, ModelInfo>\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":11221,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.964Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.964Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":377,\\\"nonEmptyLines\\\":363,\\\"commentLines\\\":19,\\\"complexity\\\":36},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":-815,\\\"issues\\\":[],\\\"duplicateLines\\\":181,\\\"longLines\\\":5,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.407Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":377},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\shared\\\\\\\\array.test.ts\\\":{\\\"content\\\":\\\"import { describe, it } from \\\\\\\"mocha\\\\\\\"\\\\r\\\\nimport \\\\\\\"should\\\\\\\"\\\\r\\\\nimport { findLastIndex, findLast } from \\\\\\\"./array\\\\\\\"\\\\r\\\\n\\\\r\\\\ndescribe(\\\\\\\"Array Utilities\\\\\\\", () => {\\\\r\\\\n\\\\tdescribe(\\\\\\\"findLastIndex\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should find last matching element's index\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst array = [1, 2, 3, 2, 1]\\\\r\\\\n\\\\t\\\\t\\\\tconst index = findLastIndex(array, (x) => x === 2)\\\\r\\\\n\\\\t\\\\t\\\\tindex.should.equal(3) // last '2' is at index 3\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should return -1 when no element matches\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst array = [1, 2, 3]\\\\r\\\\n\\\\t\\\\t\\\\tconst index = findLastIndex(array, (x) => x === 4)\\\\r\\\\n\\\\t\\\\t\\\\tindex.should.equal(-1)\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should handle empty arrays\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst array: number[] = []\\\\r\\\\n\\\\t\\\\t\\\\tconst index = findLastIndex(array, (x) => x === 1)\\\\r\\\\n\\\\t\\\\t\\\\tindex.should.equal(-1)\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should work with different types\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst array = [\\\\\\\"a\\\\\\\", \\\\\\\"b\\\\\\\", \\\\\\\"c\\\\\\\", \\\\\\\"b\\\\\\\", \\\\\\\"a\\\\\\\"]\\\\r\\\\n\\\\t\\\\t\\\\tconst index = findLastIndex(array, (x) => x === \\\\\\\"b\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tindex.should.equal(3)\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should provide correct index in predicate\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst array = [1, 2, 3]\\\\r\\\\n\\\\t\\\\t\\\\tconst indices: number[] = []\\\\r\\\\n\\\\t\\\\t\\\\tfindLastIndex(array, (_, index) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tindices.push(index)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn false\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\tindices.should.deepEqual([2, 1, 0]) // Should iterate in reverse\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should provide array reference in predicate\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst array = [1, 2, 3]\\\\r\\\\n\\\\t\\\\t\\\\tfindLastIndex(array, (_, __, arr) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tarr.should.equal(array) // Should pass original array\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn false\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\tdescribe(\\\\\\\"findLast\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should find last matching element\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst array = [1, 2, 3, 2, 1]\\\\r\\\\n\\\\t\\\\t\\\\tconst element = findLast(array, (x) => x === 2)\\\\r\\\\n\\\\t\\\\t\\\\tshould(element).not.be.undefined()\\\\r\\\\n\\\\t\\\\t\\\\telement!.should.equal(2)\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should return undefined when no element matches\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst array = [1, 2, 3]\\\\r\\\\n\\\\t\\\\t\\\\tconst element = findLast(array, (x) => x === 4)\\\\r\\\\n\\\\t\\\\t\\\\tshould(element).be.undefined()\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should handle empty arrays\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst array: number[] = []\\\\r\\\\n\\\\t\\\\t\\\\tconst element = findLast(array, (x) => x === 1)\\\\r\\\\n\\\\t\\\\t\\\\tshould(element).be.undefined()\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should work with object arrays\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst array = [\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t{ id: 1, value: \\\\\\\"a\\\\\\\" },\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t{ id: 2, value: \\\\\\\"b\\\\\\\" },\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t{ id: 3, value: \\\\\\\"a\\\\\\\" },\\\\r\\\\n\\\\t\\\\t\\\\t]\\\\r\\\\n\\\\t\\\\t\\\\tconst element = findLast(array, (x) => x.value === \\\\\\\"a\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tshould(element).not.be.undefined()\\\\r\\\\n\\\\t\\\\t\\\\telement!.should.deepEqual({ id: 3, value: \\\\\\\"a\\\\\\\" })\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should provide correct index in predicate\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst array = [1, 2, 3]\\\\r\\\\n\\\\t\\\\t\\\\tconst indices: number[] = []\\\\r\\\\n\\\\t\\\\t\\\\tfindLast(array, (_, index) => {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tindices.push(index)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn false\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\t\\\\t\\\\tindices.should.deepEqual([2, 1, 0]) // Should iterate in reverse\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t})\\\\r\\\\n})\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":2642,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.854Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.854Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":92,\\\"nonEmptyLines\\\":80,\\\"commentLines\\\":0,\\\"complexity\\\":1},\\\"dependencies\\\":[\\\"mocha\\\",\\\"./array\\\"],\\\"quality\\\":{\\\"score\\\":-60,\\\"issues\\\":[],\\\"duplicateLines\\\":32,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.409Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":92},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\shared\\\\\\\\array.ts\\\":{\\\"content\\\":\\\"/**\\\\r\\\\n * Returns the index of the last element in the array where predicate is true, and -1\\\\r\\\\n * otherwise.\\\\r\\\\n * @param array The source array to search in\\\\r\\\\n * @param predicate find calls predicate once for each element of the array, in descending\\\\r\\\\n * order, until it finds one where predicate returns true. If such an element is found,\\\\r\\\\n * findLastIndex immediately returns that element index. Otherwise, findLastIndex returns -1.\\\\r\\\\n */\\\\r\\\\nexport function findLastIndex<T>(array: Array<T>, predicate: (value: T, index: number, obj: T[]) => boolean): number {\\\\r\\\\n\\\\tlet l = array.length\\\\r\\\\n\\\\twhile (l--) {\\\\r\\\\n\\\\t\\\\tif (predicate(array[l], l, array)) {\\\\r\\\\n\\\\t\\\\t\\\\treturn l\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\treturn -1\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport function findLast<T>(array: Array<T>, predicate: (value: T, index: number, obj: T[]) => boolean): T | undefined {\\\\r\\\\n\\\\tconst index = findLastIndex(array, predicate)\\\\r\\\\n\\\\treturn index === -1 ? undefined : array[index]\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":895,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.854Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.854Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":23,\\\"nonEmptyLines\\\":21,\\\"commentLines\\\":1,\\\"complexity\\\":4},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":81,\\\"issues\\\":[],\\\"duplicateLines\\\":3,\\\"longLines\\\":2,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.410Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":23},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\shared\\\\\\\\AutoApprovalSettings.ts\\\":{\\\"content\\\":\\\"export interface AutoApprovalSettings {\\\\r\\\\n\\\\t// Whether auto-approval is enabled\\\\r\\\\n\\\\tenabled: boolean\\\\r\\\\n\\\\t// Individual action permissions\\\\r\\\\n\\\\tactions: {\\\\r\\\\n\\\\t\\\\treadFiles: boolean // Read files and directories\\\\r\\\\n\\\\t\\\\teditFiles: boolean // Edit files\\\\r\\\\n\\\\t\\\\texecuteCommands: boolean // Execute safe commands\\\\r\\\\n\\\\t\\\\tuseBrowser: boolean // Use browser\\\\r\\\\n\\\\t\\\\tuseMcp: boolean // Use MCP servers\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\t// Global settings\\\\r\\\\n\\\\tmaxRequests: number // Maximum number of auto-approved requests\\\\r\\\\n\\\\tenableNotifications: boolean // Show notifications for approval and task completion\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport const DEFAULT_AUTO_APPROVAL_SETTINGS: AutoApprovalSettings = {\\\\r\\\\n\\\\tenabled: false,\\\\r\\\\n\\\\tactions: {\\\\r\\\\n\\\\t\\\\treadFiles: false,\\\\r\\\\n\\\\t\\\\teditFiles: false,\\\\r\\\\n\\\\t\\\\texecuteCommands: false,\\\\r\\\\n\\\\t\\\\tuseBrowser: false,\\\\r\\\\n\\\\t\\\\tuseMcp: false,\\\\r\\\\n\\\\t},\\\\r\\\\n\\\\tmaxRequests: 20,\\\\r\\\\n\\\\tenableNotifications: false,\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":813,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.854Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.854Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":29,\\\"nonEmptyLines\\\":27,\\\"commentLines\\\":3,\\\"complexity\\\":1},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":85,\\\"issues\\\":[],\\\"duplicateLines\\\":3,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.411Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":29},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\shared\\\\\\\\combineApiRequests.ts\\\":{\\\"content\\\":\\\"import { ClineMessage } from \\\\\\\"./ExtensionMessage\\\\\\\"\\\\r\\\\n\\\\r\\\\n/**\\\\r\\\\n * Combines API request start and finish messages in an array of ClineMessages.\\\\r\\\\n *\\\\r\\\\n * This function looks for pairs of 'api_req_started' and 'api_req_finished' messages.\\\\r\\\\n * When it finds a pair, it combines them into a single 'api_req_combined' message.\\\\r\\\\n * The JSON data in the text fields of both messages are merged.\\\\r\\\\n *\\\\r\\\\n * @param messages - An array of ClineMessage objects to process.\\\\r\\\\n * @returns A new array of ClineMessage objects with API requests combined.\\\\r\\\\n *\\\\r\\\\n * @example\\\\r\\\\n * const messages = [\\\\r\\\\n * { type: \\\\\\\"say\\\\\\\", say: \\\\\\\"api_req_started\\\\\\\", text: '{\\\\\\\"request\\\\\\\":\\\\\\\"GET /api/data\\\\\\\"}', ts: 1000 },\\\\r\\\\n * { type: \\\\\\\"say\\\\\\\", say: \\\\\\\"api_req_finished\\\\\\\", text: '{\\\\\\\"cost\\\\\\\":0.005}', ts: 1001 }\\\\r\\\\n * ];\\\\r\\\\n * const result = combineApiRequests(messages);\\\\r\\\\n * // Result: [{ type: \\\\\\\"say\\\\\\\", say: \\\\\\\"api_req_started\\\\\\\", text: '{\\\\\\\"request\\\\\\\":\\\\\\\"GET /api/data\\\\\\\",\\\\\\\"cost\\\\\\\":0.005}', ts: 1000 }]\\\\r\\\\n */\\\\r\\\\nexport function combineApiRequests(messages: ClineMessage[]): ClineMessage[] {\\\\r\\\\n\\\\tconst combinedApiRequests: ClineMessage[] = []\\\\r\\\\n\\\\r\\\\n\\\\tfor (let i = 0; i < messages.length; i++) {\\\\r\\\\n\\\\t\\\\tif (messages[i].type === \\\\\\\"say\\\\\\\" && messages[i].say === \\\\\\\"api_req_started\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\tlet startedRequest = JSON.parse(messages[i].text || \\\\\\\"{}\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tlet j = i + 1\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\twhile (j < messages.length) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (messages[j].type === \\\\\\\"say\\\\\\\" && messages[j].say === \\\\\\\"api_req_finished\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tlet finishedRequest = JSON.parse(messages[j].text || \\\\\\\"{}\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tlet combinedRequest = {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t...startedRequest,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t...finishedRequest,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tcombinedApiRequests.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t...messages[i],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\ttext: JSON.stringify(combinedRequest),\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ti = j // Skip to the api_req_finished message\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tj++\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tif (j === messages.length) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// If no matching api_req_finished found, keep the original api_req_started\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcombinedApiRequests.push(messages[i])\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Replace original api_req_started and remove api_req_finished\\\\r\\\\n\\\\treturn messages\\\\r\\\\n\\\\t\\\\t.filter((msg) => !(msg.type === \\\\\\\"say\\\\\\\" && msg.say === \\\\\\\"api_req_finished\\\\\\\"))\\\\r\\\\n\\\\t\\\\t.map((msg) => {\\\\r\\\\n\\\\t\\\\t\\\\tif (msg.type === \\\\\\\"say\\\\\\\" && msg.say === \\\\\\\"api_req_started\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst combinedRequest = combinedApiRequests.find((req) => req.ts === msg.ts)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn combinedRequest || msg\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\treturn msg\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":2298,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.964Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.964Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":66,\\\"nonEmptyLines\\\":58,\\\"commentLines\\\":3,\\\"complexity\\\":14},\\\"dependencies\\\":[\\\"./ExtensionMessage\\\"],\\\"quality\\\":{\\\"score\\\":48,\\\"issues\\\":[],\\\"duplicateLines\\\":10,\\\"longLines\\\":1,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.412Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":66},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\shared\\\\\\\\combineCommandSequences.ts\\\":{\\\"content\\\":\\\"import { ClineMessage } from \\\\\\\"./ExtensionMessage\\\\\\\"\\\\r\\\\n\\\\r\\\\n/**\\\\r\\\\n * Combines sequences of command and command_output messages in an array of ClineMessages.\\\\r\\\\n *\\\\r\\\\n * This function processes an array of ClineMessages objects, looking for sequences\\\\r\\\\n * where a 'command' message is followed by one or more 'command_output' messages.\\\\r\\\\n * When such a sequence is found, it combines them into a single message, merging\\\\r\\\\n * their text contents.\\\\r\\\\n *\\\\r\\\\n * @param messages - An array of ClineMessage objects to process.\\\\r\\\\n * @returns A new array of ClineMessage objects with command sequences combined.\\\\r\\\\n *\\\\r\\\\n * @example\\\\r\\\\n * const messages: ClineMessage[] = [\\\\r\\\\n * { type: 'ask', ask: 'command', text: 'ls', ts: 1625097600000 },\\\\r\\\\n * { type: 'ask', ask: 'command_output', text: 'file1.txt', ts: 1625097601000 },\\\\r\\\\n * { type: 'ask', ask: 'command_output', text: 'file2.txt', ts: 1625097602000 }\\\\r\\\\n * ];\\\\r\\\\n * const result = simpleCombineCommandSequences(messages);\\\\r\\\\n * // Result: [{ type: 'ask', ask: 'command', text: 'ls\\\\\\\\nfile1.txt\\\\\\\\nfile2.txt', ts: 1625097600000 }]\\\\r\\\\n */\\\\r\\\\nexport function combineCommandSequences(messages: ClineMessage[]): ClineMessage[] {\\\\r\\\\n\\\\tconst combinedCommands: ClineMessage[] = []\\\\r\\\\n\\\\r\\\\n\\\\t// First pass: combine commands with their outputs\\\\r\\\\n\\\\tfor (let i = 0; i < messages.length; i++) {\\\\r\\\\n\\\\t\\\\tif (messages[i].type === \\\\\\\"ask\\\\\\\" && (messages[i].ask === \\\\\\\"command\\\\\\\" || messages[i].say === \\\\\\\"command\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\tlet combinedText = messages[i].text || \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tlet didAddOutput = false\\\\r\\\\n\\\\t\\\\t\\\\tlet j = i + 1\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\twhile (j < messages.length) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (messages[j].type === \\\\\\\"ask\\\\\\\" && (messages[j].ask === \\\\\\\"command\\\\\\\" || messages[j].say === \\\\\\\"command\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// Stop if we encounter the next command\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tbreak\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (messages[j].ask === \\\\\\\"command_output\\\\\\\" || messages[j].say === \\\\\\\"command_output\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (!didAddOutput) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\t// Add a newline before the first output\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcombinedText += `\\\\\\\\n${COMMAND_OUTPUT_STRING}`\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tdidAddOutput = true\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t// handle cases where we receive empty command_output (ie when extension is relinquishing control over exit command button)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tconst output = messages[j].text || \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tif (output.length > 0) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t\\\\tcombinedText += \\\\\\\"\\\\\\\\n\\\\\\\" + output\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tj++\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tcombinedCommands.push({\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t...messages[i],\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttext: combinedText,\\\\r\\\\n\\\\t\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\ti = j - 1 // Move to the index just before the next command or end of array\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Second pass: remove command_outputs and replace original commands with combined ones\\\\r\\\\n\\\\treturn messages\\\\r\\\\n\\\\t\\\\t.filter((msg) => !(msg.ask === \\\\\\\"command_output\\\\\\\" || msg.say === \\\\\\\"command_output\\\\\\\"))\\\\r\\\\n\\\\t\\\\t.map((msg) => {\\\\r\\\\n\\\\t\\\\t\\\\tif (msg.type === \\\\\\\"ask\\\\\\\" && (msg.ask === \\\\\\\"command\\\\\\\" || msg.say === \\\\\\\"command\\\\\\\")) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst combinedCommand = combinedCommands.find((cmd) => cmd.ts === msg.ts)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\treturn combinedCommand || msg\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\treturn msg\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n}\\\\r\\\\nexport const COMMAND_OUTPUT_STRING = \\\\\\\"Output:\\\\\\\"\\\\r\\\\nexport const COMMAND_REQ_APP_STRING = \\\\\\\"REQ_APP\\\\\\\"\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":2924,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.856Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.856Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":75,\\\"nonEmptyLines\\\":68,\\\"commentLines\\\":6,\\\"complexity\\\":20},\\\"dependencies\\\":[\\\"./ExtensionMessage\\\"],\\\"quality\\\":{\\\"score\\\":37,\\\"issues\\\":[],\\\"duplicateLines\\\":11,\\\"longLines\\\":4,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.413Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":75},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\shared\\\\\\\\context-mentions.ts\\\":{\\\"content\\\":\\\"/*\\\\r\\\\nMention regex:\\\\r\\\\n- **Purpose**: \\\\r\\\\n - To identify and highlight specific mentions in text that start with '@'. \\\\r\\\\n - These mentions can be file paths, URLs, or the exact word 'problems'.\\\\r\\\\n - Ensures that trailing punctuation marks (like commas, periods, etc.) are not included in the match, allowing punctuation to follow the mention without being part of it.\\\\r\\\\n\\\\r\\\\n- **Regex Breakdown**:\\\\r\\\\n - `/@`: \\\\r\\\\n - **@**: The mention must start with the '@' symbol.\\\\r\\\\n \\\\r\\\\n - `((?:\\\\\\\\/|\\\\\\\\w+:\\\\\\\\/\\\\\\\\/)[^\\\\\\\\s]+?|problems\\\\\\\\b)`:\\\\r\\\\n - **Capturing Group (`(...)`)**: Captures the part of the string that matches one of the specified patterns.\\\\r\\\\n - `(?:\\\\\\\\/|\\\\\\\\w+:\\\\\\\\/\\\\\\\\/)`: \\\\r\\\\n - **Non-Capturing Group (`(?:...)`)**: Groups the alternatives without capturing them for back-referencing.\\\\r\\\\n - `\\\\\\\\/`: \\\\r\\\\n - **Slash (`/`)**: Indicates that the mention is a file or folder path starting with a '/'.\\\\r\\\\n - `|`: Logical OR.\\\\r\\\\n - `\\\\\\\\w+:\\\\\\\\/\\\\\\\\/`: \\\\r\\\\n - **Protocol (`\\\\\\\\w+://`)**: Matches URLs that start with a word character sequence followed by '://', such as 'http://', 'https://', 'ftp://', etc.\\\\r\\\\n - `[^\\\\\\\\s]+?`: \\\\r\\\\n - **Non-Whitespace Characters (`[^\\\\\\\\s]+`)**: Matches one or more characters that are not whitespace.\\\\r\\\\n - **Non-Greedy (`+?`)**: Ensures the smallest possible match, preventing the inclusion of trailing punctuation.\\\\r\\\\n - `|`: Logical OR.\\\\r\\\\n - `problems\\\\\\\\b`: \\\\r\\\\n - **Exact Word ('problems')**: Matches the exact word 'problems'.\\\\r\\\\n - **Word Boundary (`\\\\\\\\b`)**: Ensures that 'problems' is matched as a whole word and not as part of another word (e.g., 'problematic').\\\\r\\\\n\\\\r\\\\n - `(?=[.,;:!?]?(?=[\\\\\\\\s\\\\\\\\r\\\\\\\\n]|$))`:\\\\r\\\\n - **Positive Lookahead (`(?=...)`)**: Ensures that the match is followed by specific patterns without including them in the match.\\\\r\\\\n - `[.,;:!?]?`: \\\\r\\\\n - **Optional Punctuation (`[.,;:!?]?`)**: Matches zero or one of the specified punctuation marks.\\\\r\\\\n - `(?=[\\\\\\\\s\\\\\\\\r\\\\\\\\n]|$)`: \\\\r\\\\n - **Nested Positive Lookahead (`(?=[\\\\\\\\s\\\\\\\\r\\\\\\\\n]|$)`)**: Ensures that the punctuation (if present) is followed by a whitespace character, a line break, or the end of the string.\\\\r\\\\n \\\\r\\\\n- **Summary**:\\\\r\\\\n - The regex effectively matches:\\\\r\\\\n - Mentions that are file or folder paths starting with '/' and containing any non-whitespace characters (including periods within the path).\\\\r\\\\n - URLs that start with a protocol (like 'http://') followed by any non-whitespace characters (including query parameters).\\\\r\\\\n - The exact word 'problems'.\\\\r\\\\n - It ensures that any trailing punctuation marks (such as ',', '.', '!', etc.) are not included in the matched mention, allowing the punctuation to follow the mention naturally in the text.\\\\r\\\\n\\\\r\\\\n- **Global Regex**:\\\\r\\\\n - `mentionRegexGlobal`: Creates a global version of the `mentionRegex` to find all matches within a given string.\\\\r\\\\n\\\\r\\\\n*/\\\\r\\\\nexport const mentionRegex = /@((?:\\\\\\\\/|\\\\\\\\w+:\\\\\\\\/\\\\\\\\/)[^\\\\\\\\s]+?|problems\\\\\\\\b)(?=[.,;:!?]?(?=[\\\\\\\\s\\\\\\\\r\\\\\\\\n]|$))/\\\\r\\\\nexport const mentionRegexGlobal = new RegExp(mentionRegex.source, \\\\\\\"g\\\\\\\")\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":2967,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.856Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.856Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":49,\\\"nonEmptyLines\\\":42,\\\"commentLines\\\":1,\\\"complexity\\\":24},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":67,\\\"issues\\\":[],\\\"duplicateLines\\\":1,\\\"longLines\\\":14,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.414Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":49},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\shared\\\\\\\\ExtensionMessage.ts\\\":{\\\"content\\\":\\\"// type that represents json data that is sent from extension to webview, called ExtensionMessage and has 'type' enum which can be 'plusButtonClicked' or 'settingsButtonClicked' or 'hello'\\\\r\\\\n\\\\r\\\\nimport { ApiConfiguration, ModelInfo } from \\\\\\\"./api\\\\\\\"\\\\r\\\\nimport { AutoApprovalSettings } from \\\\\\\"./AutoApprovalSettings\\\\\\\"\\\\r\\\\nimport { HistoryItem } from \\\\\\\"./HistoryItem\\\\\\\"\\\\r\\\\nimport { McpServer } from \\\\\\\"./mcp\\\\\\\"\\\\r\\\\n\\\\r\\\\n// webview will hold state\\\\r\\\\nexport interface ExtensionMessage {\\\\r\\\\n\\\\ttype:\\\\r\\\\n\\\\t\\\\t| \\\\\\\"action\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"state\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"selectedImages\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"ollamaModels\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"lmStudioModels\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"theme\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"workspaceUpdated\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"invoke\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"partialMessage\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"openRouterModels\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"mcpServers\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"relinquishControl\\\\\\\"\\\\r\\\\n\\\\ttext?: string\\\\r\\\\n\\\\taction?: \\\\\\\"chatButtonClicked\\\\\\\" | \\\\\\\"mcpButtonClicked\\\\\\\" | \\\\\\\"settingsButtonClicked\\\\\\\" | \\\\\\\"historyButtonClicked\\\\\\\" | \\\\\\\"didBecomeVisible\\\\\\\"\\\\r\\\\n\\\\tinvoke?: \\\\\\\"sendMessage\\\\\\\" | \\\\\\\"primaryButtonClick\\\\\\\" | \\\\\\\"secondaryButtonClick\\\\\\\"\\\\r\\\\n\\\\tstate?: ExtensionState\\\\r\\\\n\\\\timages?: string[]\\\\r\\\\n\\\\tollamaModels?: string[]\\\\r\\\\n\\\\tlmStudioModels?: string[]\\\\r\\\\n\\\\tfilePaths?: string[]\\\\r\\\\n\\\\tpartialMessage?: ClineMessage\\\\r\\\\n\\\\topenRouterModels?: Record<string, ModelInfo>\\\\r\\\\n\\\\tmcpServers?: McpServer[]\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport interface ExtensionState {\\\\r\\\\n\\\\tversion: string\\\\r\\\\n\\\\tapiConfiguration?: ApiConfiguration\\\\r\\\\n\\\\tcustomInstructions?: string\\\\r\\\\n\\\\turiScheme?: string\\\\r\\\\n\\\\tcurrentTaskItem?: HistoryItem\\\\r\\\\n\\\\tcheckpointTrackerErrorMessage?: string\\\\r\\\\n\\\\tclineMessages: ClineMessage[]\\\\r\\\\n\\\\ttaskHistory: HistoryItem[]\\\\r\\\\n\\\\tshouldShowAnnouncement: boolean\\\\r\\\\n\\\\tautoApprovalSettings: AutoApprovalSettings\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport interface ClineMessage {\\\\r\\\\n\\\\tts: number\\\\r\\\\n\\\\ttype: \\\\\\\"ask\\\\\\\" | \\\\\\\"say\\\\\\\"\\\\r\\\\n\\\\task?: ClineAsk\\\\r\\\\n\\\\tsay?: ClineSay\\\\r\\\\n\\\\ttext?: string\\\\r\\\\n\\\\timages?: string[]\\\\r\\\\n\\\\tpartial?: boolean\\\\r\\\\n\\\\tlastCheckpointHash?: string\\\\r\\\\n\\\\tconversationHistoryIndex?: number\\\\r\\\\n\\\\tconversationHistoryDeletedRange?: [number, number] // for when conversation history is truncated for API requests\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport type ClineAsk =\\\\r\\\\n\\\\t| \\\\\\\"followup\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"command\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"command_output\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"completion_result\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"tool\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"api_req_failed\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"resume_task\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"resume_completed_task\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"mistake_limit_reached\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"auto_approval_max_req_reached\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"browser_action_launch\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"use_mcp_server\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport type ClineSay =\\\\r\\\\n\\\\t| \\\\\\\"task\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"error\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"api_req_started\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"api_req_finished\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"text\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"completion_result\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"user_feedback\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"user_feedback_diff\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"api_req_retried\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"command\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"command_output\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"tool\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"shell_integration_warning\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"browser_action_launch\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"browser_action\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"browser_action_result\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"mcp_server_request_started\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"mcp_server_response\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"use_mcp_server\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"diff_error\\\\\\\"\\\\r\\\\n\\\\t| \\\\\\\"deleted_api_reqs\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport interface ClineSayTool {\\\\r\\\\n\\\\ttool:\\\\r\\\\n\\\\t\\\\t| \\\\\\\"editedExistingFile\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"newFileCreated\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"readFile\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"listFilesTopLevel\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"listFilesRecursive\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"listCodeDefinitionNames\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"searchFiles\\\\\\\"\\\\r\\\\n\\\\tpath?: string\\\\r\\\\n\\\\tdiff?: string\\\\r\\\\n\\\\tcontent?: string\\\\r\\\\n\\\\tregex?: string\\\\r\\\\n\\\\tfilePattern?: string\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n// must keep in sync with system prompt\\\\r\\\\nexport const browserActions = [\\\\\\\"launch\\\\\\\", \\\\\\\"click\\\\\\\", \\\\\\\"type\\\\\\\", \\\\\\\"scroll_down\\\\\\\", \\\\\\\"scroll_up\\\\\\\", \\\\\\\"close\\\\\\\"] as const\\\\r\\\\nexport type BrowserAction = (typeof browserActions)[number]\\\\r\\\\n\\\\r\\\\nexport interface ClineSayBrowserAction {\\\\r\\\\n\\\\taction: BrowserAction\\\\r\\\\n\\\\tcoordinate?: string\\\\r\\\\n\\\\ttext?: string\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport type BrowserActionResult = {\\\\r\\\\n\\\\tscreenshot?: string\\\\r\\\\n\\\\tlogs?: string\\\\r\\\\n\\\\tcurrentUrl?: string\\\\r\\\\n\\\\tcurrentMousePosition?: string\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport interface ClineAskUseMcpServer {\\\\r\\\\n\\\\tserverName: string\\\\r\\\\n\\\\ttype: \\\\\\\"use_mcp_tool\\\\\\\" | \\\\\\\"access_mcp_resource\\\\\\\"\\\\r\\\\n\\\\ttoolName?: string\\\\r\\\\n\\\\targuments?: string\\\\r\\\\n\\\\turi?: string\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport interface ClineApiReqInfo {\\\\r\\\\n\\\\trequest?: string\\\\r\\\\n\\\\ttokensIn?: number\\\\r\\\\n\\\\ttokensOut?: number\\\\r\\\\n\\\\tcacheWrites?: number\\\\r\\\\n\\\\tcacheReads?: number\\\\r\\\\n\\\\tcost?: number\\\\r\\\\n\\\\tcancelReason?: ClineApiReqCancelReason\\\\r\\\\n\\\\tstreamingFailedMessage?: string\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport type ClineApiReqCancelReason = \\\\\\\"streaming_failed\\\\\\\" | \\\\\\\"user_cancelled\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport const COMPLETION_RESULT_CHANGES_FLAG = \\\\\\\"HAS_CHANGES\\\\\\\"\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":3908,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.963Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.963Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":154,\\\"nonEmptyLines\\\":139,\\\"commentLines\\\":3,\\\"complexity\\\":47},\\\"dependencies\\\":[\\\"./api\\\",\\\"./AutoApprovalSettings\\\",\\\"./HistoryItem\\\",\\\"./mcp\\\"],\\\"quality\\\":{\\\"score\\\":12,\\\"issues\\\":[],\\\"duplicateLines\\\":16,\\\"longLines\\\":4,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.415Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":154},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\shared\\\\\\\\getApiMetrics.ts\\\":{\\\"content\\\":\\\"import { ClineMessage } from \\\\\\\"./ExtensionMessage\\\\\\\"\\\\r\\\\n\\\\r\\\\ninterface ApiMetrics {\\\\r\\\\n\\\\ttotalTokensIn: number\\\\r\\\\n\\\\ttotalTokensOut: number\\\\r\\\\n\\\\ttotalCacheWrites?: number\\\\r\\\\n\\\\ttotalCacheReads?: number\\\\r\\\\n\\\\ttotalCost: number\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n/**\\\\r\\\\n * Calculates API metrics from an array of ClineMessages.\\\\r\\\\n *\\\\r\\\\n * This function processes 'api_req_started' messages that have been combined with their\\\\r\\\\n * corresponding 'api_req_finished' messages by the combineApiRequests function. It also takes into account 'deleted_api_reqs' messages, which are aggregated from deleted messages.\\\\r\\\\n * It extracts and sums up the tokensIn, tokensOut, cacheWrites, cacheReads, and cost from these messages.\\\\r\\\\n *\\\\r\\\\n * @param messages - An array of ClineMessage objects to process.\\\\r\\\\n * @returns An ApiMetrics object containing totalTokensIn, totalTokensOut, totalCacheWrites, totalCacheReads, and totalCost.\\\\r\\\\n *\\\\r\\\\n * @example\\\\r\\\\n * const messages = [\\\\r\\\\n * { type: \\\\\\\"say\\\\\\\", say: \\\\\\\"api_req_started\\\\\\\", text: '{\\\\\\\"request\\\\\\\":\\\\\\\"GET /api/data\\\\\\\",\\\\\\\"tokensIn\\\\\\\":10,\\\\\\\"tokensOut\\\\\\\":20,\\\\\\\"cost\\\\\\\":0.005}', ts: 1000 }\\\\r\\\\n * ];\\\\r\\\\n * const { totalTokensIn, totalTokensOut, totalCost } = getApiMetrics(messages);\\\\r\\\\n * // Result: { totalTokensIn: 10, totalTokensOut: 20, totalCost: 0.005 }\\\\r\\\\n */\\\\r\\\\nexport function getApiMetrics(messages: ClineMessage[]): ApiMetrics {\\\\r\\\\n\\\\tconst result: ApiMetrics = {\\\\r\\\\n\\\\t\\\\ttotalTokensIn: 0,\\\\r\\\\n\\\\t\\\\ttotalTokensOut: 0,\\\\r\\\\n\\\\t\\\\ttotalCacheWrites: undefined,\\\\r\\\\n\\\\t\\\\ttotalCacheReads: undefined,\\\\r\\\\n\\\\t\\\\ttotalCost: 0,\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tmessages.forEach((message) => {\\\\r\\\\n\\\\t\\\\tif (message.type === \\\\\\\"say\\\\\\\" && (message.say === \\\\\\\"api_req_started\\\\\\\" || message.say === \\\\\\\"deleted_api_reqs\\\\\\\") && message.text) {\\\\r\\\\n\\\\t\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst parsedData = JSON.parse(message.text)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst { tokensIn, tokensOut, cacheWrites, cacheReads, cost } = parsedData\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (typeof tokensIn === \\\\\\\"number\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tresult.totalTokensIn += tokensIn\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (typeof tokensOut === \\\\\\\"number\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tresult.totalTokensOut += tokensOut\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (typeof cacheWrites === \\\\\\\"number\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tresult.totalCacheWrites = (result.totalCacheWrites ?? 0) + cacheWrites\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (typeof cacheReads === \\\\\\\"number\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tresult.totalCacheReads = (result.totalCacheReads ?? 0) + cacheReads\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tif (typeof cost === \\\\\\\"number\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tresult.totalCost += cost\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t\\\\t} catch (error) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconsole.error(\\\\\\\"Error parsing JSON:\\\\\\\", error)\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\treturn result\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":2338,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.964Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.964Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":66,\\\"nonEmptyLines\\\":60,\\\"commentLines\\\":1,\\\"complexity\\\":17},\\\"dependencies\\\":[\\\"./ExtensionMessage\\\"],\\\"quality\\\":{\\\"score\\\":35,\\\"issues\\\":[],\\\"duplicateLines\\\":11,\\\"longLines\\\":5,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.416Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":66},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\shared\\\\\\\\HistoryItem.ts\\\":{\\\"content\\\":\\\"export type HistoryItem = {\\\\r\\\\n\\\\tid: string\\\\r\\\\n\\\\tts: number\\\\r\\\\n\\\\ttask: string\\\\r\\\\n\\\\ttokensIn: number\\\\r\\\\n\\\\ttokensOut: number\\\\r\\\\n\\\\tcacheWrites?: number\\\\r\\\\n\\\\tcacheReads?: number\\\\r\\\\n\\\\ttotalCost: number\\\\r\\\\n\\\\r\\\\n\\\\tsize?: number\\\\r\\\\n\\\\tshadowGitConfigWorkTree?: string\\\\r\\\\n\\\\tconversationHistoryDeletedRange?: [number, number]\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":283,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.964Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.963Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":15,\\\"nonEmptyLines\\\":13,\\\"commentLines\\\":0,\\\"complexity\\\":6},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":100,\\\"issues\\\":[],\\\"duplicateLines\\\":0,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.417Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":15},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\shared\\\\\\\\mcp.ts\\\":{\\\"content\\\":\\\"export type McpServer = {\\\\r\\\\n\\\\tname: string\\\\r\\\\n\\\\tconfig: string\\\\r\\\\n\\\\tstatus: \\\\\\\"connected\\\\\\\" | \\\\\\\"connecting\\\\\\\" | \\\\\\\"disconnected\\\\\\\"\\\\r\\\\n\\\\terror?: string\\\\r\\\\n\\\\ttools?: McpTool[]\\\\r\\\\n\\\\tresources?: McpResource[]\\\\r\\\\n\\\\tresourceTemplates?: McpResourceTemplate[]\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport type McpTool = {\\\\r\\\\n\\\\tname: string\\\\r\\\\n\\\\tdescription?: string\\\\r\\\\n\\\\tinputSchema?: object\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport type McpResource = {\\\\r\\\\n\\\\turi: string\\\\r\\\\n\\\\tname: string\\\\r\\\\n\\\\tmimeType?: string\\\\r\\\\n\\\\tdescription?: string\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport type McpResourceTemplate = {\\\\r\\\\n\\\\turiTemplate: string\\\\r\\\\n\\\\tname: string\\\\r\\\\n\\\\tdescription?: string\\\\r\\\\n\\\\tmimeType?: string\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport type McpResourceResponse = {\\\\r\\\\n\\\\t_meta?: Record<string, any>\\\\r\\\\n\\\\tcontents: Array<{\\\\r\\\\n\\\\t\\\\turi: string\\\\r\\\\n\\\\t\\\\tmimeType?: string\\\\r\\\\n\\\\t\\\\ttext?: string\\\\r\\\\n\\\\t\\\\tblob?: string\\\\r\\\\n\\\\t}>\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport type McpToolCallResponse = {\\\\r\\\\n\\\\t_meta?: Record<string, any>\\\\r\\\\n\\\\tcontent: Array<\\\\r\\\\n\\\\t\\\\t| {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"text\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttext: string\\\\r\\\\n\\\\t\\\\t }\\\\r\\\\n\\\\t\\\\t| {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"image\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tdata: string\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tmimeType: string\\\\r\\\\n\\\\t\\\\t }\\\\r\\\\n\\\\t\\\\t| {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\ttype: \\\\\\\"resource\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tresource: {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\turi: string\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tmimeType?: string\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\ttext?: string\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t\\\\tblob?: string\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t }\\\\r\\\\n\\\\t>\\\\r\\\\n\\\\tisError?: boolean\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":1090,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.856Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.856Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":65,\\\"nonEmptyLines\\\":59,\\\"commentLines\\\":0,\\\"complexity\\\":20},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":-20,\\\"issues\\\":[],\\\"duplicateLines\\\":24,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.418Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":65},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\shared\\\\\\\\WebviewMessage.ts\\\":{\\\"content\\\":\\\"import { ApiConfiguration } from \\\\\\\"./api\\\\\\\"\\\\r\\\\nimport { AutoApprovalSettings } from \\\\\\\"./AutoApprovalSettings\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport interface WebviewMessage {\\\\r\\\\n\\\\ttype:\\\\r\\\\n\\\\t\\\\t| \\\\\\\"apiConfiguration\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"customInstructions\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"webviewDidLaunch\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"newTask\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"askResponse\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"clearTask\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"didShowAnnouncement\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"selectImages\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"exportCurrentTask\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"showTaskWithId\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"deleteTaskWithId\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"exportTaskWithId\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"resetState\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"requestOllamaModels\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"requestLmStudioModels\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"openImage\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"openFile\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"openMention\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"cancelTask\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"refreshOpenRouterModels\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"openMcpSettings\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"restartMcpServer\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"autoApprovalSettings\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"checkpointDiff\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"checkpointRestore\\\\\\\"\\\\r\\\\n\\\\t\\\\t| \\\\\\\"taskCompletionViewChanges\\\\\\\"\\\\r\\\\n\\\\ttext?: string\\\\r\\\\n\\\\taskResponse?: ClineAskResponse\\\\r\\\\n\\\\tapiConfiguration?: ApiConfiguration\\\\r\\\\n\\\\timages?: string[]\\\\r\\\\n\\\\tbool?: boolean\\\\r\\\\n\\\\tnumber?: number\\\\r\\\\n\\\\tautoApprovalSettings?: AutoApprovalSettings\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport type ClineAskResponse = \\\\\\\"yesButtonClicked\\\\\\\" | \\\\\\\"noButtonClicked\\\\\\\" | \\\\\\\"messageResponse\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport type ClineCheckpointRestore = \\\\\\\"task\\\\\\\" | \\\\\\\"workspace\\\\\\\" | \\\\\\\"taskAndWorkspace\\\\\\\"\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":1113,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2025-01-15T15:26:07.964Z\\\",\\\"createdTime\\\":\\\"2025-01-15T15:26:07.964Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":44,\\\"nonEmptyLines\\\":40,\\\"commentLines\\\":0,\\\"complexity\\\":8},\\\"dependencies\\\":[\\\"./api\\\",\\\"./AutoApprovalSettings\\\"],\\\"quality\\\":{\\\"score\\\":100,\\\"issues\\\":[],\\\"duplicateLines\\\":0,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.419Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":44},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\test\\\\\\\\extension.test.ts\\\":{\\\"content\\\":\\\"import { readFile } from \\\\\\\"fs/promises\\\\\\\"\\\\r\\\\nimport { describe, it, after } from \\\\\\\"mocha\\\\\\\"\\\\r\\\\nimport path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport \\\\\\\"should\\\\\\\"\\\\r\\\\nimport * as vscode from \\\\\\\"vscode\\\\\\\"\\\\r\\\\n\\\\r\\\\nconst packagePath = path.join(__dirname, \\\\\\\"..\\\\\\\", \\\\\\\"..\\\\\\\", \\\\\\\"..\\\\\\\", \\\\\\\"package.json\\\\\\\")\\\\r\\\\n\\\\r\\\\ndescribe(\\\\\\\"Cline Extension\\\\\\\", () => {\\\\r\\\\n\\\\tafter(() => {\\\\r\\\\n\\\\t\\\\tvscode.window.showInformationMessage(\\\\\\\"All tests done!\\\\\\\")\\\\r\\\\n\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\tit(\\\\\\\"should verify extension ID matches package.json\\\\\\\", async () => {\\\\r\\\\n\\\\t\\\\tconst packageJSON = JSON.parse(await readFile(packagePath, \\\\\\\"utf8\\\\\\\"))\\\\r\\\\n\\\\t\\\\tconst id = packageJSON.publisher + \\\\\\\".\\\\\\\" + packageJSON.name\\\\r\\\\n\\\\t\\\\tconst clineExtensionApi = vscode.extensions.getExtension(id)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tclineExtensionApi?.id.should.equal(id)\\\\r\\\\n\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\tit(\\\\\\\"should successfully execute the plus button command\\\\\\\", async () => {\\\\r\\\\n\\\\t\\\\tawait new Promise((resolve) => setTimeout(resolve, 400))\\\\r\\\\n\\\\t\\\\tawait vscode.commands.executeCommand(\\\\\\\"cline.plusButtonClicked\\\\\\\")\\\\r\\\\n\\\\t})\\\\r\\\\n})\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":887,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.857Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.857Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":27,\\\"nonEmptyLines\\\":21,\\\"commentLines\\\":0,\\\"complexity\\\":2},\\\"dependencies\\\":[\\\"fs/promises\\\",\\\"mocha\\\",\\\"path\\\",\\\"vscode\\\"],\\\"quality\\\":{\\\"score\\\":85,\\\"issues\\\":[],\\\"duplicateLines\\\":3,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.420Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":27},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\utils\\\\\\\\cost.test.ts\\\":{\\\"content\\\":\\\"import { describe, it } from \\\\\\\"mocha\\\\\\\"\\\\r\\\\nimport \\\\\\\"should\\\\\\\"\\\\r\\\\nimport { calculateApiCost } from \\\\\\\"./cost\\\\\\\"\\\\r\\\\nimport { ModelInfo } from \\\\\\\"../shared/api\\\\\\\"\\\\r\\\\n\\\\r\\\\ndescribe(\\\\\\\"Cost Utilities\\\\\\\", () => {\\\\r\\\\n\\\\tdescribe(\\\\\\\"calculateApiCost\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should calculate basic input/output costs\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst modelInfo: ModelInfo = {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tsupportsPromptCache: false,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tinputPrice: 3.0, // $3 per million tokens\\\\r\\\\n\\\\t\\\\t\\\\t\\\\toutputPrice: 15.0, // $15 per million tokens\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tconst cost = calculateApiCost(modelInfo, 1000, 500)\\\\r\\\\n\\\\t\\\\t\\\\t// Input: (3.0 / 1_000_000) * 1000 = 0.003\\\\r\\\\n\\\\t\\\\t\\\\t// Output: (15.0 / 1_000_000) * 500 = 0.0075\\\\r\\\\n\\\\t\\\\t\\\\t// Total: 0.003 + 0.0075 = 0.0105\\\\r\\\\n\\\\t\\\\t\\\\tcost.should.equal(0.0105)\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should handle missing prices\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst modelInfo: ModelInfo = {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tsupportsPromptCache: true,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\t// No prices specified\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tconst cost = calculateApiCost(modelInfo, 1000, 500)\\\\r\\\\n\\\\t\\\\t\\\\tcost.should.equal(0)\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should use real model configuration (Claude 3.5 Sonnet)\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst modelInfo: ModelInfo = {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tmaxTokens: 8192,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcontextWindow: 200_000,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tsupportsImages: true,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tsupportsComputerUse: true,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tsupportsPromptCache: true,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tinputPrice: 3.0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\toutputPrice: 15.0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcacheWritesPrice: 3.75,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcacheReadsPrice: 0.3,\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tconst cost = calculateApiCost(modelInfo, 2000, 1000, 1500, 500)\\\\r\\\\n\\\\t\\\\t\\\\t// Cache writes: (3.75 / 1_000_000) * 1500 = 0.005625\\\\r\\\\n\\\\t\\\\t\\\\t// Cache reads: (0.3 / 1_000_000) * 500 = 0.00015\\\\r\\\\n\\\\t\\\\t\\\\t// Input: (3.0 / 1_000_000) * 2000 = 0.006\\\\r\\\\n\\\\t\\\\t\\\\t// Output: (15.0 / 1_000_000) * 1000 = 0.015\\\\r\\\\n\\\\t\\\\t\\\\t// Total: 0.005625 + 0.00015 + 0.006 + 0.015 = 0.026775\\\\r\\\\n\\\\t\\\\t\\\\tcost.should.equal(0.026775)\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should handle zero token counts\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst modelInfo: ModelInfo = {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tsupportsPromptCache: true,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tinputPrice: 3.0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\toutputPrice: 15.0,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcacheWritesPrice: 3.75,\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tcacheReadsPrice: 0.3,\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tconst cost = calculateApiCost(modelInfo, 0, 0, 0, 0)\\\\r\\\\n\\\\t\\\\t\\\\tcost.should.equal(0)\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t})\\\\r\\\\n})\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":1975,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.857Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.857Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":68,\\\"nonEmptyLines\\\":59,\\\"commentLines\\\":9,\\\"complexity\\\":1},\\\"dependencies\\\":[\\\"mocha\\\",\\\"./cost\\\",\\\"../shared/api\\\"],\\\"quality\\\":{\\\"score\\\":5,\\\"issues\\\":[],\\\"duplicateLines\\\":19,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.421Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":68},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\utils\\\\\\\\cost.ts\\\":{\\\"content\\\":\\\"import { ModelInfo } from \\\\\\\"../shared/api\\\\\\\"\\\\r\\\\n\\\\r\\\\nexport function calculateApiCost(\\\\r\\\\n\\\\tmodelInfo: ModelInfo,\\\\r\\\\n\\\\tinputTokens: number,\\\\r\\\\n\\\\toutputTokens: number,\\\\r\\\\n\\\\tcacheCreationInputTokens?: number,\\\\r\\\\n\\\\tcacheReadInputTokens?: number,\\\\r\\\\n): number {\\\\r\\\\n\\\\tconst modelCacheWritesPrice = modelInfo.cacheWritesPrice\\\\r\\\\n\\\\tlet cacheWritesCost = 0\\\\r\\\\n\\\\tif (cacheCreationInputTokens && modelCacheWritesPrice) {\\\\r\\\\n\\\\t\\\\tcacheWritesCost = (modelCacheWritesPrice / 1_000_000) * cacheCreationInputTokens\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\tconst modelCacheReadsPrice = modelInfo.cacheReadsPrice\\\\r\\\\n\\\\tlet cacheReadsCost = 0\\\\r\\\\n\\\\tif (cacheReadInputTokens && modelCacheReadsPrice) {\\\\r\\\\n\\\\t\\\\tcacheReadsCost = (modelCacheReadsPrice / 1_000_000) * cacheReadInputTokens\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\tconst baseInputCost = ((modelInfo.inputPrice || 0) / 1_000_000) * inputTokens\\\\r\\\\n\\\\tconst outputCost = ((modelInfo.outputPrice || 0) / 1_000_000) * outputTokens\\\\r\\\\n\\\\tconst totalCost = cacheWritesCost + cacheReadsCost + baseInputCost + outputCost\\\\r\\\\n\\\\treturn totalCost\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":947,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.857Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.857Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":25,\\\"nonEmptyLines\\\":23,\\\"commentLines\\\":0,\\\"complexity\\\":9},\\\"dependencies\\\":[\\\"../shared/api\\\"],\\\"quality\\\":{\\\"score\\\":90,\\\"issues\\\":[],\\\"duplicateLines\\\":2,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.422Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":25},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\utils\\\\\\\\fs.test.ts\\\":{\\\"content\\\":\\\"import * as fs from \\\\\\\"fs/promises\\\\\\\"\\\\r\\\\nimport { after, describe, it } from \\\\\\\"mocha\\\\\\\"\\\\r\\\\nimport * as os from \\\\\\\"os\\\\\\\"\\\\r\\\\nimport * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport \\\\\\\"should\\\\\\\"\\\\r\\\\nimport { createDirectoriesForFile, fileExistsAtPath } from \\\\\\\"./fs\\\\\\\"\\\\r\\\\n\\\\r\\\\ndescribe(\\\\\\\"Filesystem Utilities\\\\\\\", () => {\\\\r\\\\n\\\\tconst tmpDir = path.join(os.tmpdir(), \\\\\\\"cline-test-\\\\\\\" + Math.random().toString(36).slice(2))\\\\r\\\\n\\\\r\\\\n\\\\t// Clean up after tests\\\\r\\\\n\\\\tafter(async () => {\\\\r\\\\n\\\\t\\\\ttry {\\\\r\\\\n\\\\t\\\\t\\\\tawait fs.rm(tmpDir, { recursive: true, force: true })\\\\r\\\\n\\\\t\\\\t} catch {\\\\r\\\\n\\\\t\\\\t\\\\t// Ignore cleanup errors\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\tdescribe(\\\\\\\"fileExistsAtPath\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should return true for existing paths\\\\\\\", async () => {\\\\r\\\\n\\\\t\\\\t\\\\tawait fs.mkdir(tmpDir, { recursive: true })\\\\r\\\\n\\\\t\\\\t\\\\tconst testFile = path.join(tmpDir, \\\\\\\"test.txt\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tawait fs.writeFile(testFile, \\\\\\\"test\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tconst exists = await fileExistsAtPath(testFile)\\\\r\\\\n\\\\t\\\\t\\\\texists.should.be.true()\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should return false for non-existing paths\\\\\\\", async () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst nonExistentPath = path.join(tmpDir, \\\\\\\"does-not-exist.txt\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tconst exists = await fileExistsAtPath(nonExistentPath)\\\\r\\\\n\\\\t\\\\t\\\\texists.should.be.false()\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\tdescribe(\\\\\\\"createDirectoriesForFile\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should create all necessary directories\\\\\\\", async () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst deepPath = path.join(tmpDir, \\\\\\\"deep\\\\\\\", \\\\\\\"nested\\\\\\\", \\\\\\\"dir\\\\\\\", \\\\\\\"file.txt\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tconst createdDirs = await createDirectoriesForFile(deepPath)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// Verify directories were created\\\\r\\\\n\\\\t\\\\t\\\\tcreatedDirs.length.should.be.greaterThan(0)\\\\r\\\\n\\\\t\\\\t\\\\tfor (const dir of createdDirs) {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tconst exists = await fileExistsAtPath(dir)\\\\r\\\\n\\\\t\\\\t\\\\t\\\\texists.should.be.true()\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should handle existing directories\\\\\\\", async () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst existingDir = path.join(tmpDir, \\\\\\\"existing\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tawait fs.mkdir(existingDir, { recursive: true })\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\tconst filePath = path.join(existingDir, \\\\\\\"file.txt\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tconst createdDirs = await createDirectoriesForFile(filePath)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// Should not create any new directories\\\\r\\\\n\\\\t\\\\t\\\\tcreatedDirs.length.should.equal(0)\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should normalize paths\\\\\\\", async () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst unnormalizedPath = path.join(tmpDir, \\\\\\\"a\\\\\\\", \\\\\\\"..\\\\\\\", \\\\\\\"b\\\\\\\", \\\\\\\".\\\\\\\", \\\\\\\"file.txt\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tconst createdDirs = await createDirectoriesForFile(unnormalizedPath)\\\\r\\\\n\\\\r\\\\n\\\\t\\\\t\\\\t// Should create only the necessary directory\\\\r\\\\n\\\\t\\\\t\\\\tcreatedDirs.length.should.equal(1)\\\\r\\\\n\\\\t\\\\t\\\\tconst exists = await fileExistsAtPath(path.join(tmpDir, \\\\\\\"b\\\\\\\"))\\\\r\\\\n\\\\t\\\\t\\\\texists.should.be.true()\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t})\\\\r\\\\n})\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":2342,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.858Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.857Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":72,\\\"nonEmptyLines\\\":59,\\\"commentLines\\\":5,\\\"complexity\\\":2},\\\"dependencies\\\":[\\\"fs/promises\\\",\\\"mocha\\\",\\\"os\\\",\\\"path\\\",\\\"./fs\\\"],\\\"quality\\\":{\\\"score\\\":45,\\\"issues\\\":[],\\\"duplicateLines\\\":11,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.423Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":72},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\utils\\\\\\\\fs.ts\\\":{\\\"content\\\":\\\"import fs from \\\\\\\"fs/promises\\\\\\\"\\\\r\\\\nimport * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\n\\\\r\\\\n/**\\\\r\\\\n * Asynchronously creates all non-existing subdirectories for a given file path\\\\r\\\\n * and collects them in an array for later deletion.\\\\r\\\\n *\\\\r\\\\n * @param filePath - The full path to a file.\\\\r\\\\n * @returns A promise that resolves to an array of newly created directories.\\\\r\\\\n */\\\\r\\\\nexport async function createDirectoriesForFile(filePath: string): Promise<string[]> {\\\\r\\\\n\\\\tconst newDirectories: string[] = []\\\\r\\\\n\\\\tconst normalizedFilePath = path.normalize(filePath) // Normalize path for cross-platform compatibility\\\\r\\\\n\\\\tconst directoryPath = path.dirname(normalizedFilePath)\\\\r\\\\n\\\\r\\\\n\\\\tlet currentPath = directoryPath\\\\r\\\\n\\\\tconst dirsToCreate: string[] = []\\\\r\\\\n\\\\r\\\\n\\\\t// Traverse up the directory tree and collect missing directories\\\\r\\\\n\\\\twhile (!(await fileExistsAtPath(currentPath))) {\\\\r\\\\n\\\\t\\\\tdirsToCreate.push(currentPath)\\\\r\\\\n\\\\t\\\\tcurrentPath = path.dirname(currentPath)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\t// Create directories from the topmost missing one down to the target directory\\\\r\\\\n\\\\tfor (let i = dirsToCreate.length - 1; i >= 0; i--) {\\\\r\\\\n\\\\t\\\\tawait fs.mkdir(dirsToCreate[i])\\\\r\\\\n\\\\t\\\\tnewDirectories.push(dirsToCreate[i])\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\treturn newDirectories\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n/**\\\\r\\\\n * Helper function to check if a path exists.\\\\r\\\\n *\\\\r\\\\n * @param path - The path to check.\\\\r\\\\n * @returns A promise that resolves to true if the path exists, false otherwise.\\\\r\\\\n */\\\\r\\\\nexport async function fileExistsAtPath(filePath: string): Promise<boolean> {\\\\r\\\\n\\\\ttry {\\\\r\\\\n\\\\t\\\\tawait fs.access(filePath)\\\\r\\\\n\\\\t\\\\treturn true\\\\r\\\\n\\\\t} catch {\\\\r\\\\n\\\\t\\\\treturn false\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":1492,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.858Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.858Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":48,\\\"nonEmptyLines\\\":41,\\\"commentLines\\\":4,\\\"complexity\\\":3},\\\"dependencies\\\":[\\\"fs/promises\\\",\\\"path\\\"],\\\"quality\\\":{\\\"score\\\":63,\\\"issues\\\":[],\\\"duplicateLines\\\":7,\\\"longLines\\\":1,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.424Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":48},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\utils\\\\\\\\path.test.ts\\\":{\\\"content\\\":\\\"import { describe, it } from \\\\\\\"mocha\\\\\\\"\\\\r\\\\nimport * as os from \\\\\\\"os\\\\\\\"\\\\r\\\\nimport * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport \\\\\\\"should\\\\\\\"\\\\r\\\\nimport { arePathsEqual, getReadablePath } from \\\\\\\"./path\\\\\\\"\\\\r\\\\n\\\\r\\\\ndescribe(\\\\\\\"Path Utilities\\\\\\\", () => {\\\\r\\\\n\\\\tdescribe(\\\\\\\"arePathsEqual\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should handle undefined paths\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tarePathsEqual(undefined, undefined).should.be.true()\\\\r\\\\n\\\\t\\\\t\\\\tarePathsEqual(\\\\\\\"foo\\\\\\\", undefined).should.be.false()\\\\r\\\\n\\\\t\\\\t\\\\tarePathsEqual(undefined, \\\\\\\"foo\\\\\\\").should.be.false()\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should handle case sensitivity based on platform\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tif (process.platform === \\\\\\\"win32\\\\\\\") {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tarePathsEqual(\\\\\\\"FOO/BAR\\\\\\\", \\\\\\\"foo/bar\\\\\\\").should.be.true()\\\\r\\\\n\\\\t\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t\\\\tarePathsEqual(\\\\\\\"FOO/BAR\\\\\\\", \\\\\\\"foo/bar\\\\\\\").should.be.false()\\\\r\\\\n\\\\t\\\\t\\\\t}\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should handle normalized paths\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tarePathsEqual(\\\\\\\"/tmp/./dir\\\\\\\", \\\\\\\"/tmp/../tmp/dir\\\\\\\").should.be.true()\\\\r\\\\n\\\\t\\\\t\\\\tarePathsEqual(\\\\\\\"/tmp/./dir\\\\\\\", \\\\\\\"/tmp/../dir\\\\\\\").should.be.false()\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\tdescribe(\\\\\\\"getReadablePath\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should handle desktop path\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst desktop = path.join(os.homedir(), \\\\\\\"Desktop\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tconst testPath = path.join(desktop, \\\\\\\"test.txt\\\\\\\")\\\\r\\\\n\\\\t\\\\t\\\\tgetReadablePath(desktop, \\\\\\\"test.txt\\\\\\\").should.equal(testPath.replace(/\\\\\\\\\\\\\\\\/g, \\\\\\\"/\\\\\\\"))\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should show relative paths within cwd\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst cwd = \\\\\\\"/home/user/project\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tconst filePath = \\\\\\\"/home/user/project/src/file.txt\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tgetReadablePath(cwd, filePath).should.equal(\\\\\\\"src/file.txt\\\\\\\")\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should show basename when path equals cwd\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst cwd = \\\\\\\"/home/user/project\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tgetReadablePath(cwd, cwd).should.equal(\\\\\\\"project\\\\\\\")\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\r\\\\n\\\\t\\\\tit(\\\\\\\"should show absolute path when outside cwd\\\\\\\", () => {\\\\r\\\\n\\\\t\\\\t\\\\tconst cwd = \\\\\\\"/home/user/project\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tconst filePath = \\\\\\\"/home/user/other/file.txt\\\\\\\"\\\\r\\\\n\\\\t\\\\t\\\\tgetReadablePath(cwd, filePath).should.equal(filePath)\\\\r\\\\n\\\\t\\\\t})\\\\r\\\\n\\\\t})\\\\r\\\\n})\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":1783,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.858Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.858Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":54,\\\"nonEmptyLines\\\":46,\\\"commentLines\\\":0,\\\"complexity\\\":4},\\\"dependencies\\\":[\\\"mocha\\\",\\\"os\\\",\\\"path\\\",\\\"./path\\\"],\\\"quality\\\":{\\\"score\\\":45,\\\"issues\\\":[],\\\"duplicateLines\\\":11,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.425Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":54},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\utils\\\\\\\\path.ts\\\":{\\\"content\\\":\\\"import * as path from \\\\\\\"path\\\\\\\"\\\\r\\\\nimport os from \\\\\\\"os\\\\\\\"\\\\r\\\\n\\\\r\\\\n/*\\\\r\\\\nThe Node.js 'path' module resolves and normalizes paths differently depending on the platform:\\\\r\\\\n- On Windows, it uses backslashes (\\\\\\\\) as the default path separator.\\\\r\\\\n- On POSIX-compliant systems (Linux, macOS), it uses forward slashes (/) as the default path separator.\\\\r\\\\n\\\\r\\\\nWhile modules like 'upath' can be used to normalize paths to use forward slashes consistently,\\\\r\\\\nthis can create inconsistencies when interfacing with other modules (like vscode.fs) that use\\\\r\\\\nbackslashes on Windows.\\\\r\\\\n\\\\r\\\\nOur approach:\\\\r\\\\n1. We present paths with forward slashes to the AI and user for consistency.\\\\r\\\\n2. We use the 'arePathsEqual' function for safe path comparisons.\\\\r\\\\n3. Internally, Node.js gracefully handles both backslashes and forward slashes.\\\\r\\\\n\\\\r\\\\nThis strategy ensures consistent path presentation while leveraging Node.js's built-in\\\\r\\\\npath handling capabilities across different platforms.\\\\r\\\\n\\\\r\\\\nNote: When interacting with the file system or VS Code APIs, we still use the native path module\\\\r\\\\nto ensure correct behavior on all platforms. The toPosixPath and arePathsEqual functions are\\\\r\\\\nprimarily used for presentation and comparison purposes, not for actual file system operations.\\\\r\\\\n\\\\r\\\\nObservations:\\\\r\\\\n- Macos isn't so flexible with mixed separators, whereas windows can handle both. (\\\\\\\"Node.js does automatically handle path separators on Windows, converting forward slashes to backslashes as needed. However, on macOS and other Unix-like systems, the path separator is always a forward slash (/), and backslashes are treated as regular characters.\\\\\\\")\\\\r\\\\n*/\\\\r\\\\n\\\\r\\\\nfunction toPosixPath(p: string) {\\\\r\\\\n\\\\t// Extended-Length Paths in Windows start with \\\\\\\"\\\\\\\\\\\\\\\\?\\\\\\\\\\\\\\\" to allow longer paths and bypass usual parsing. If detected, we return the path unmodified to maintain functionality, as altering these paths could break their special syntax.\\\\r\\\\n\\\\tconst isExtendedLengthPath = p.startsWith(\\\\\\\"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\?\\\\\\\\\\\\\\\\\\\\\\\")\\\\r\\\\n\\\\r\\\\n\\\\tif (isExtendedLengthPath) {\\\\r\\\\n\\\\t\\\\treturn p\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\treturn p.replace(/\\\\\\\\\\\\\\\\/g, \\\\\\\"/\\\\\\\")\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n// Declaration merging allows us to add a new method to the String type\\\\r\\\\n// You must import this file in your entry point (extension.ts) to have access at runtime\\\\r\\\\ndeclare global {\\\\r\\\\n\\\\tinterface String {\\\\r\\\\n\\\\t\\\\ttoPosix(): string\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nString.prototype.toPosix = function (this: string): string {\\\\r\\\\n\\\\treturn toPosixPath(this)\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n// Safe path comparison that works across different platforms\\\\r\\\\nexport function arePathsEqual(path1?: string, path2?: string): boolean {\\\\r\\\\n\\\\tif (!path1 && !path2) {\\\\r\\\\n\\\\t\\\\treturn true\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\tif (!path1 || !path2) {\\\\r\\\\n\\\\t\\\\treturn false\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\r\\\\n\\\\tpath1 = normalizePath(path1)\\\\r\\\\n\\\\tpath2 = normalizePath(path2)\\\\r\\\\n\\\\r\\\\n\\\\tif (process.platform === \\\\\\\"win32\\\\\\\") {\\\\r\\\\n\\\\t\\\\treturn path1.toLowerCase() === path2.toLowerCase()\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\treturn path1 === path2\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nfunction normalizePath(p: string): string {\\\\r\\\\n\\\\t// normalize resolve ./.. segments, removes duplicate slashes, and standardizes path separators\\\\r\\\\n\\\\tlet normalized = path.normalize(p)\\\\r\\\\n\\\\t// however it doesn't remove trailing slashes\\\\r\\\\n\\\\t// remove trailing slash, except for root paths\\\\r\\\\n\\\\tif (normalized.length > 1 && (normalized.endsWith(\\\\\\\"/\\\\\\\") || normalized.endsWith(\\\\\\\"\\\\\\\\\\\\\\\\\\\\\\\"))) {\\\\r\\\\n\\\\t\\\\tnormalized = normalized.slice(0, -1)\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\treturn normalized\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\nexport function getReadablePath(cwd: string, relPath?: string): string {\\\\r\\\\n\\\\trelPath = relPath || \\\\\\\"\\\\\\\"\\\\r\\\\n\\\\t// path.resolve is flexible in that it will resolve relative paths like '../../' to the cwd and even ignore the cwd if the relPath is actually an absolute path\\\\r\\\\n\\\\tconst absolutePath = path.resolve(cwd, relPath)\\\\r\\\\n\\\\tif (arePathsEqual(cwd, path.join(os.homedir(), \\\\\\\"Desktop\\\\\\\"))) {\\\\r\\\\n\\\\t\\\\t// User opened vscode without a workspace, so cwd is the Desktop. Show the full absolute path to keep the user aware of where files are being created\\\\r\\\\n\\\\t\\\\treturn absolutePath.toPosix()\\\\r\\\\n\\\\t}\\\\r\\\\n\\\\tif (arePathsEqual(path.normalize(absolutePath), path.normalize(cwd))) {\\\\r\\\\n\\\\t\\\\treturn path.basename(absolutePath).toPosix()\\\\r\\\\n\\\\t} else {\\\\r\\\\n\\\\t\\\\t// show the relative path to the cwd\\\\r\\\\n\\\\t\\\\tconst normalizedRelPath = path.relative(cwd, absolutePath)\\\\r\\\\n\\\\t\\\\tif (absolutePath.includes(cwd)) {\\\\r\\\\n\\\\t\\\\t\\\\treturn normalizedRelPath.toPosix()\\\\r\\\\n\\\\t\\\\t} else {\\\\r\\\\n\\\\t\\\\t\\\\t// we are outside the cwd, so show the absolute path (useful for when cline passes in '../../' for example)\\\\r\\\\n\\\\t\\\\t\\\\treturn absolutePath.toPosix()\\\\r\\\\n\\\\t\\\\t}\\\\r\\\\n\\\\t}\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":4238,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.858Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.858Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":102,\\\"nonEmptyLines\\\":85,\\\"commentLines\\\":12,\\\"complexity\\\":21},\\\"dependencies\\\":[\\\"path\\\",\\\"os\\\"],\\\"quality\\\":{\\\"score\\\":8,\\\"issues\\\":[],\\\"duplicateLines\\\":16,\\\"longLines\\\":6,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.426Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":102},\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\\\\\\utils\\\\\\\\string.ts\\\":{\\\"content\\\":\\\"/**\\\\r\\\\n * Fixes incorrectly escaped HTML entities in AI model outputs\\\\r\\\\n * @param text String potentially containing incorrectly escaped HTML entities from AI models\\\\r\\\\n * @returns String with HTML entities converted back to normal characters\\\\r\\\\n */\\\\r\\\\nexport function fixModelHtmlEscaping(text: string): string {\\\\r\\\\n\\\\treturn text\\\\r\\\\n\\\\t\\\\t.replace(/&gt;/g, \\\\\\\">\\\\\\\")\\\\r\\\\n\\\\t\\\\t.replace(/&lt;/g, \\\\\\\"<\\\\\\\")\\\\r\\\\n\\\\t\\\\t.replace(/&quot;/g, '\\\\\\\"')\\\\r\\\\n\\\\t\\\\t.replace(/&amp;/g, \\\\\\\"&\\\\\\\")\\\\r\\\\n\\\\t\\\\t.replace(/&apos;/g, \\\\\\\"'\\\\\\\")\\\\r\\\\n}\\\\r\\\\n\\\\r\\\\n/**\\\\r\\\\n * Removes invalid characters (like the replacement character �) from a string\\\\r\\\\n * @param text String potentially containing invalid characters\\\\r\\\\n * @returns String with invalid characters removed\\\\r\\\\n */\\\\r\\\\nexport function removeInvalidChars(text: string): string {\\\\r\\\\n\\\\treturn text.replace(/\\\\\\\\uFFFD/g, \\\\\\\"\\\\\\\")\\\\r\\\\n}\\\\r\\\\n\\\",\\\"metadata\\\":{\\\"size\\\":769,\\\"mimeType\\\":\\\"video/mp2t\\\",\\\"modifiedTime\\\":\\\"2024-12-29T05:57:23.859Z\\\",\\\"createdTime\\\":\\\"2024-12-29T05:57:23.859Z\\\",\\\"isDirectory\\\":false,\\\"analysis\\\":{\\\"metrics\\\":{\\\"lines\\\":23,\\\"nonEmptyLines\\\":21,\\\"commentLines\\\":2,\\\"complexity\\\":1},\\\"dependencies\\\":[],\\\"quality\\\":{\\\"score\\\":85,\\\"issues\\\":[],\\\"duplicateLines\\\":3,\\\"longLines\\\":0,\\\"complexFunctions\\\":0}},\\\"lastAnalyzed\\\":\\\"2025-01-16T17:00:55.427Z\\\"},\\\"encoding\\\":\\\"utf8\\\",\\\"truncated\\\":false,\\\"totalLines\\\":23}},\\\"metadata\\\":{\\\"totalFiles\\\":95,\\\"totalSize\\\":604006,\\\"searchPath\\\":\\\"D:\\\\\\\\Projects\\\\\\\\cline\\\\\\\\src\\\"}}\",\"metadata\":{\"size\":604006,\"mimeType\":\"application/json\",\"modifiedTime\":\"2025-01-16T17:00:55.430Z\",\"createdTime\":\"2025-01-16T17:00:55.430Z\",\"isDirectory\":false},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":0},\"lastModified\":1737046855434,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.434Z\",\"size\":853413},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\utils\\\\string.ts\",\"content\":{\"content\":\"/**\\r\\n * Fixes incorrectly escaped HTML entities in AI model outputs\\r\\n * @param text String potentially containing incorrectly escaped HTML entities from AI models\\r\\n * @returns String with HTML entities converted back to normal characters\\r\\n */\\r\\nexport function fixModelHtmlEscaping(text: string): string {\\r\\n\\treturn text\\r\\n\\t\\t.replace(/&gt;/g, \\\">\\\")\\r\\n\\t\\t.replace(/&lt;/g, \\\"<\\\")\\r\\n\\t\\t.replace(/&quot;/g, '\\\"')\\r\\n\\t\\t.replace(/&amp;/g, \\\"&\\\")\\r\\n\\t\\t.replace(/&apos;/g, \\\"'\\\")\\r\\n}\\r\\n\\r\\n/**\\r\\n * Removes invalid characters (like the replacement character �) from a string\\r\\n * @param text String potentially containing invalid characters\\r\\n * @returns String with invalid characters removed\\r\\n */\\r\\nexport function removeInvalidChars(text: string): string {\\r\\n\\treturn text.replace(/\\\\uFFFD/g, \\\"\\\")\\r\\n}\\r\\n\",\"metadata\":{\"size\":769,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.859Z\",\"createdTime\":\"2024-12-29T05:57:23.859Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":23,\"nonEmptyLines\":21,\"commentLines\":2,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":85,\"issues\":[],\"duplicateLines\":3,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.427Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":23},\"analysis\":{\"metrics\":{\"lines\":23,\"nonEmptyLines\":21,\"commentLines\":2,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":85,\"issues\":[],\"duplicateLines\":3,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855427,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.427Z\",\"size\":1290},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\utils\\\\path.ts\",\"content\":{\"content\":\"import * as path from \\\"path\\\"\\r\\nimport os from \\\"os\\\"\\r\\n\\r\\n/*\\r\\nThe Node.js 'path' module resolves and normalizes paths differently depending on the platform:\\r\\n- On Windows, it uses backslashes (\\\\) as the default path separator.\\r\\n- On POSIX-compliant systems (Linux, macOS), it uses forward slashes (/) as the default path separator.\\r\\n\\r\\nWhile modules like 'upath' can be used to normalize paths to use forward slashes consistently,\\r\\nthis can create inconsistencies when interfacing with other modules (like vscode.fs) that use\\r\\nbackslashes on Windows.\\r\\n\\r\\nOur approach:\\r\\n1. We present paths with forward slashes to the AI and user for consistency.\\r\\n2. We use the 'arePathsEqual' function for safe path comparisons.\\r\\n3. Internally, Node.js gracefully handles both backslashes and forward slashes.\\r\\n\\r\\nThis strategy ensures consistent path presentation while leveraging Node.js's built-in\\r\\npath handling capabilities across different platforms.\\r\\n\\r\\nNote: When interacting with the file system or VS Code APIs, we still use the native path module\\r\\nto ensure correct behavior on all platforms. The toPosixPath and arePathsEqual functions are\\r\\nprimarily used for presentation and comparison purposes, not for actual file system operations.\\r\\n\\r\\nObservations:\\r\\n- Macos isn't so flexible with mixed separators, whereas windows can handle both. (\\\"Node.js does automatically handle path separators on Windows, converting forward slashes to backslashes as needed. However, on macOS and other Unix-like systems, the path separator is always a forward slash (/), and backslashes are treated as regular characters.\\\")\\r\\n*/\\r\\n\\r\\nfunction toPosixPath(p: string) {\\r\\n\\t// Extended-Length Paths in Windows start with \\\"\\\\\\\\?\\\\\\\" to allow longer paths and bypass usual parsing. If detected, we return the path unmodified to maintain functionality, as altering these paths could break their special syntax.\\r\\n\\tconst isExtendedLengthPath = p.startsWith(\\\"\\\\\\\\\\\\\\\\?\\\\\\\\\\\")\\r\\n\\r\\n\\tif (isExtendedLengthPath) {\\r\\n\\t\\treturn p\\r\\n\\t}\\r\\n\\r\\n\\treturn p.replace(/\\\\\\\\/g, \\\"/\\\")\\r\\n}\\r\\n\\r\\n// Declaration merging allows us to add a new method to the String type\\r\\n// You must import this file in your entry point (extension.ts) to have access at runtime\\r\\ndeclare global {\\r\\n\\tinterface String {\\r\\n\\t\\ttoPosix(): string\\r\\n\\t}\\r\\n}\\r\\n\\r\\nString.prototype.toPosix = function (this: string): string {\\r\\n\\treturn toPosixPath(this)\\r\\n}\\r\\n\\r\\n// Safe path comparison that works across different platforms\\r\\nexport function arePathsEqual(path1?: string, path2?: string): boolean {\\r\\n\\tif (!path1 && !path2) {\\r\\n\\t\\treturn true\\r\\n\\t}\\r\\n\\tif (!path1 || !path2) {\\r\\n\\t\\treturn false\\r\\n\\t}\\r\\n\\r\\n\\tpath1 = normalizePath(path1)\\r\\n\\tpath2 = normalizePath(path2)\\r\\n\\r\\n\\tif (process.platform === \\\"win32\\\") {\\r\\n\\t\\treturn path1.toLowerCase() === path2.toLowerCase()\\r\\n\\t}\\r\\n\\treturn path1 === path2\\r\\n}\\r\\n\\r\\nfunction normalizePath(p: string): string {\\r\\n\\t// normalize resolve ./.. segments, removes duplicate slashes, and standardizes path separators\\r\\n\\tlet normalized = path.normalize(p)\\r\\n\\t// however it doesn't remove trailing slashes\\r\\n\\t// remove trailing slash, except for root paths\\r\\n\\tif (normalized.length > 1 && (normalized.endsWith(\\\"/\\\") || normalized.endsWith(\\\"\\\\\\\\\\\"))) {\\r\\n\\t\\tnormalized = normalized.slice(0, -1)\\r\\n\\t}\\r\\n\\treturn normalized\\r\\n}\\r\\n\\r\\nexport function getReadablePath(cwd: string, relPath?: string): string {\\r\\n\\trelPath = relPath || \\\"\\\"\\r\\n\\t// path.resolve is flexible in that it will resolve relative paths like '../../' to the cwd and even ignore the cwd if the relPath is actually an absolute path\\r\\n\\tconst absolutePath = path.resolve(cwd, relPath)\\r\\n\\tif (arePathsEqual(cwd, path.join(os.homedir(), \\\"Desktop\\\"))) {\\r\\n\\t\\t// User opened vscode without a workspace, so cwd is the Desktop. Show the full absolute path to keep the user aware of where files are being created\\r\\n\\t\\treturn absolutePath.toPosix()\\r\\n\\t}\\r\\n\\tif (arePathsEqual(path.normalize(absolutePath), path.normalize(cwd))) {\\r\\n\\t\\treturn path.basename(absolutePath).toPosix()\\r\\n\\t} else {\\r\\n\\t\\t// show the relative path to the cwd\\r\\n\\t\\tconst normalizedRelPath = path.relative(cwd, absolutePath)\\r\\n\\t\\tif (absolutePath.includes(cwd)) {\\r\\n\\t\\t\\treturn normalizedRelPath.toPosix()\\r\\n\\t\\t} else {\\r\\n\\t\\t\\t// we are outside the cwd, so show the absolute path (useful for when cline passes in '../../' for example)\\r\\n\\t\\t\\treturn absolutePath.toPosix()\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":4238,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.858Z\",\"createdTime\":\"2024-12-29T05:57:23.858Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":102,\"nonEmptyLines\":85,\"commentLines\":12,\"complexity\":21},\"dependencies\":[\"path\",\"os\"],\"quality\":{\"score\":8,\"issues\":[],\"duplicateLines\":16,\"longLines\":6,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.426Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":102},\"analysis\":{\"metrics\":{\"lines\":102,\"nonEmptyLines\":85,\"commentLines\":12,\"complexity\":21},\"dependencies\":[\"path\",\"os\"],\"quality\":{\"score\":8,\"issues\":[],\"duplicateLines\":16,\"longLines\":6,\"complexFunctions\":0}},\"lastModified\":1737046855427,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.427Z\",\"size\":5014},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\utils\\\\path.test.ts\",\"content\":{\"content\":\"import { describe, it } from \\\"mocha\\\"\\r\\nimport * as os from \\\"os\\\"\\r\\nimport * as path from \\\"path\\\"\\r\\nimport \\\"should\\\"\\r\\nimport { arePathsEqual, getReadablePath } from \\\"./path\\\"\\r\\n\\r\\ndescribe(\\\"Path Utilities\\\", () => {\\r\\n\\tdescribe(\\\"arePathsEqual\\\", () => {\\r\\n\\t\\tit(\\\"should handle undefined paths\\\", () => {\\r\\n\\t\\t\\tarePathsEqual(undefined, undefined).should.be.true()\\r\\n\\t\\t\\tarePathsEqual(\\\"foo\\\", undefined).should.be.false()\\r\\n\\t\\t\\tarePathsEqual(undefined, \\\"foo\\\").should.be.false()\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tit(\\\"should handle case sensitivity based on platform\\\", () => {\\r\\n\\t\\t\\tif (process.platform === \\\"win32\\\") {\\r\\n\\t\\t\\t\\tarePathsEqual(\\\"FOO/BAR\\\", \\\"foo/bar\\\").should.be.true()\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tarePathsEqual(\\\"FOO/BAR\\\", \\\"foo/bar\\\").should.be.false()\\r\\n\\t\\t\\t}\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tit(\\\"should handle normalized paths\\\", () => {\\r\\n\\t\\t\\tarePathsEqual(\\\"/tmp/./dir\\\", \\\"/tmp/../tmp/dir\\\").should.be.true()\\r\\n\\t\\t\\tarePathsEqual(\\\"/tmp/./dir\\\", \\\"/tmp/../dir\\\").should.be.false()\\r\\n\\t\\t})\\r\\n\\t})\\r\\n\\r\\n\\tdescribe(\\\"getReadablePath\\\", () => {\\r\\n\\t\\tit(\\\"should handle desktop path\\\", () => {\\r\\n\\t\\t\\tconst desktop = path.join(os.homedir(), \\\"Desktop\\\")\\r\\n\\t\\t\\tconst testPath = path.join(desktop, \\\"test.txt\\\")\\r\\n\\t\\t\\tgetReadablePath(desktop, \\\"test.txt\\\").should.equal(testPath.replace(/\\\\\\\\/g, \\\"/\\\"))\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tit(\\\"should show relative paths within cwd\\\", () => {\\r\\n\\t\\t\\tconst cwd = \\\"/home/user/project\\\"\\r\\n\\t\\t\\tconst filePath = \\\"/home/user/project/src/file.txt\\\"\\r\\n\\t\\t\\tgetReadablePath(cwd, filePath).should.equal(\\\"src/file.txt\\\")\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tit(\\\"should show basename when path equals cwd\\\", () => {\\r\\n\\t\\t\\tconst cwd = \\\"/home/user/project\\\"\\r\\n\\t\\t\\tgetReadablePath(cwd, cwd).should.equal(\\\"project\\\")\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tit(\\\"should show absolute path when outside cwd\\\", () => {\\r\\n\\t\\t\\tconst cwd = \\\"/home/user/project\\\"\\r\\n\\t\\t\\tconst filePath = \\\"/home/user/other/file.txt\\\"\\r\\n\\t\\t\\tgetReadablePath(cwd, filePath).should.equal(filePath)\\r\\n\\t\\t})\\r\\n\\t})\\r\\n})\\r\\n\",\"metadata\":{\"size\":1783,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.858Z\",\"createdTime\":\"2024-12-29T05:57:23.858Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":54,\"nonEmptyLines\":46,\"commentLines\":0,\"complexity\":4},\"dependencies\":[\"mocha\",\"os\",\"path\",\"./path\"],\"quality\":{\"score\":45,\"issues\":[],\"duplicateLines\":11,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.425Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":54},\"analysis\":{\"metrics\":{\"lines\":54,\"nonEmptyLines\":46,\"commentLines\":0,\"complexity\":4},\"dependencies\":[\"mocha\",\"os\",\"path\",\"./path\"],\"quality\":{\"score\":45,\"issues\":[],\"duplicateLines\":11,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855426,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.426Z\",\"size\":2545},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\utils\\\\fs.ts\",\"content\":{\"content\":\"import fs from \\\"fs/promises\\\"\\r\\nimport * as path from \\\"path\\\"\\r\\n\\r\\n/**\\r\\n * Asynchronously creates all non-existing subdirectories for a given file path\\r\\n * and collects them in an array for later deletion.\\r\\n *\\r\\n * @param filePath - The full path to a file.\\r\\n * @returns A promise that resolves to an array of newly created directories.\\r\\n */\\r\\nexport async function createDirectoriesForFile(filePath: string): Promise<string[]> {\\r\\n\\tconst newDirectories: string[] = []\\r\\n\\tconst normalizedFilePath = path.normalize(filePath) // Normalize path for cross-platform compatibility\\r\\n\\tconst directoryPath = path.dirname(normalizedFilePath)\\r\\n\\r\\n\\tlet currentPath = directoryPath\\r\\n\\tconst dirsToCreate: string[] = []\\r\\n\\r\\n\\t// Traverse up the directory tree and collect missing directories\\r\\n\\twhile (!(await fileExistsAtPath(currentPath))) {\\r\\n\\t\\tdirsToCreate.push(currentPath)\\r\\n\\t\\tcurrentPath = path.dirname(currentPath)\\r\\n\\t}\\r\\n\\r\\n\\t// Create directories from the topmost missing one down to the target directory\\r\\n\\tfor (let i = dirsToCreate.length - 1; i >= 0; i--) {\\r\\n\\t\\tawait fs.mkdir(dirsToCreate[i])\\r\\n\\t\\tnewDirectories.push(dirsToCreate[i])\\r\\n\\t}\\r\\n\\r\\n\\treturn newDirectories\\r\\n}\\r\\n\\r\\n/**\\r\\n * Helper function to check if a path exists.\\r\\n *\\r\\n * @param path - The path to check.\\r\\n * @returns A promise that resolves to true if the path exists, false otherwise.\\r\\n */\\r\\nexport async function fileExistsAtPath(filePath: string): Promise<boolean> {\\r\\n\\ttry {\\r\\n\\t\\tawait fs.access(filePath)\\r\\n\\t\\treturn true\\r\\n\\t} catch {\\r\\n\\t\\treturn false\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":1492,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.858Z\",\"createdTime\":\"2024-12-29T05:57:23.858Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":48,\"nonEmptyLines\":41,\"commentLines\":4,\"complexity\":3},\"dependencies\":[\"fs/promises\",\"path\"],\"quality\":{\"score\":63,\"issues\":[],\"duplicateLines\":7,\"longLines\":1,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.424Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":48},\"analysis\":{\"metrics\":{\"lines\":48,\"nonEmptyLines\":41,\"commentLines\":4,\"complexity\":3},\"dependencies\":[\"fs/promises\",\"path\"],\"quality\":{\"score\":63,\"issues\":[],\"duplicateLines\":7,\"longLines\":1,\"complexFunctions\":0}},\"lastModified\":1737046855425,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.425Z\",\"size\":2093},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\utils\\\\fs.test.ts\",\"content\":{\"content\":\"import * as fs from \\\"fs/promises\\\"\\r\\nimport { after, describe, it } from \\\"mocha\\\"\\r\\nimport * as os from \\\"os\\\"\\r\\nimport * as path from \\\"path\\\"\\r\\nimport \\\"should\\\"\\r\\nimport { createDirectoriesForFile, fileExistsAtPath } from \\\"./fs\\\"\\r\\n\\r\\ndescribe(\\\"Filesystem Utilities\\\", () => {\\r\\n\\tconst tmpDir = path.join(os.tmpdir(), \\\"cline-test-\\\" + Math.random().toString(36).slice(2))\\r\\n\\r\\n\\t// Clean up after tests\\r\\n\\tafter(async () => {\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tawait fs.rm(tmpDir, { recursive: true, force: true })\\r\\n\\t\\t} catch {\\r\\n\\t\\t\\t// Ignore cleanup errors\\r\\n\\t\\t}\\r\\n\\t})\\r\\n\\r\\n\\tdescribe(\\\"fileExistsAtPath\\\", () => {\\r\\n\\t\\tit(\\\"should return true for existing paths\\\", async () => {\\r\\n\\t\\t\\tawait fs.mkdir(tmpDir, { recursive: true })\\r\\n\\t\\t\\tconst testFile = path.join(tmpDir, \\\"test.txt\\\")\\r\\n\\t\\t\\tawait fs.writeFile(testFile, \\\"test\\\")\\r\\n\\r\\n\\t\\t\\tconst exists = await fileExistsAtPath(testFile)\\r\\n\\t\\t\\texists.should.be.true()\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tit(\\\"should return false for non-existing paths\\\", async () => {\\r\\n\\t\\t\\tconst nonExistentPath = path.join(tmpDir, \\\"does-not-exist.txt\\\")\\r\\n\\t\\t\\tconst exists = await fileExistsAtPath(nonExistentPath)\\r\\n\\t\\t\\texists.should.be.false()\\r\\n\\t\\t})\\r\\n\\t})\\r\\n\\r\\n\\tdescribe(\\\"createDirectoriesForFile\\\", () => {\\r\\n\\t\\tit(\\\"should create all necessary directories\\\", async () => {\\r\\n\\t\\t\\tconst deepPath = path.join(tmpDir, \\\"deep\\\", \\\"nested\\\", \\\"dir\\\", \\\"file.txt\\\")\\r\\n\\t\\t\\tconst createdDirs = await createDirectoriesForFile(deepPath)\\r\\n\\r\\n\\t\\t\\t// Verify directories were created\\r\\n\\t\\t\\tcreatedDirs.length.should.be.greaterThan(0)\\r\\n\\t\\t\\tfor (const dir of createdDirs) {\\r\\n\\t\\t\\t\\tconst exists = await fileExistsAtPath(dir)\\r\\n\\t\\t\\t\\texists.should.be.true()\\r\\n\\t\\t\\t}\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tit(\\\"should handle existing directories\\\", async () => {\\r\\n\\t\\t\\tconst existingDir = path.join(tmpDir, \\\"existing\\\")\\r\\n\\t\\t\\tawait fs.mkdir(existingDir, { recursive: true })\\r\\n\\r\\n\\t\\t\\tconst filePath = path.join(existingDir, \\\"file.txt\\\")\\r\\n\\t\\t\\tconst createdDirs = await createDirectoriesForFile(filePath)\\r\\n\\r\\n\\t\\t\\t// Should not create any new directories\\r\\n\\t\\t\\tcreatedDirs.length.should.equal(0)\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tit(\\\"should normalize paths\\\", async () => {\\r\\n\\t\\t\\tconst unnormalizedPath = path.join(tmpDir, \\\"a\\\", \\\"..\\\", \\\"b\\\", \\\".\\\", \\\"file.txt\\\")\\r\\n\\t\\t\\tconst createdDirs = await createDirectoriesForFile(unnormalizedPath)\\r\\n\\r\\n\\t\\t\\t// Should create only the necessary directory\\r\\n\\t\\t\\tcreatedDirs.length.should.equal(1)\\r\\n\\t\\t\\tconst exists = await fileExistsAtPath(path.join(tmpDir, \\\"b\\\"))\\r\\n\\t\\t\\texists.should.be.true()\\r\\n\\t\\t})\\r\\n\\t})\\r\\n})\\r\\n\",\"metadata\":{\"size\":2342,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.858Z\",\"createdTime\":\"2024-12-29T05:57:23.857Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":72,\"nonEmptyLines\":59,\"commentLines\":5,\"complexity\":2},\"dependencies\":[\"fs/promises\",\"mocha\",\"os\",\"path\",\"./fs\"],\"quality\":{\"score\":45,\"issues\":[],\"duplicateLines\":11,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.423Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":72},\"analysis\":{\"metrics\":{\"lines\":72,\"nonEmptyLines\":59,\"commentLines\":5,\"complexity\":2},\"dependencies\":[\"fs/promises\",\"mocha\",\"os\",\"path\",\"./fs\"],\"quality\":{\"score\":45,\"issues\":[],\"duplicateLines\":11,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855424,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.424Z\",\"size\":3165},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\utils\\\\cost.ts\",\"content\":{\"content\":\"import { ModelInfo } from \\\"../shared/api\\\"\\r\\n\\r\\nexport function calculateApiCost(\\r\\n\\tmodelInfo: ModelInfo,\\r\\n\\tinputTokens: number,\\r\\n\\toutputTokens: number,\\r\\n\\tcacheCreationInputTokens?: number,\\r\\n\\tcacheReadInputTokens?: number,\\r\\n): number {\\r\\n\\tconst modelCacheWritesPrice = modelInfo.cacheWritesPrice\\r\\n\\tlet cacheWritesCost = 0\\r\\n\\tif (cacheCreationInputTokens && modelCacheWritesPrice) {\\r\\n\\t\\tcacheWritesCost = (modelCacheWritesPrice / 1_000_000) * cacheCreationInputTokens\\r\\n\\t}\\r\\n\\tconst modelCacheReadsPrice = modelInfo.cacheReadsPrice\\r\\n\\tlet cacheReadsCost = 0\\r\\n\\tif (cacheReadInputTokens && modelCacheReadsPrice) {\\r\\n\\t\\tcacheReadsCost = (modelCacheReadsPrice / 1_000_000) * cacheReadInputTokens\\r\\n\\t}\\r\\n\\tconst baseInputCost = ((modelInfo.inputPrice || 0) / 1_000_000) * inputTokens\\r\\n\\tconst outputCost = ((modelInfo.outputPrice || 0) / 1_000_000) * outputTokens\\r\\n\\tconst totalCost = cacheWritesCost + cacheReadsCost + baseInputCost + outputCost\\r\\n\\treturn totalCost\\r\\n}\\r\\n\",\"metadata\":{\"size\":947,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.857Z\",\"createdTime\":\"2024-12-29T05:57:23.857Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":25,\"nonEmptyLines\":23,\"commentLines\":0,\"complexity\":9},\"dependencies\":[\"../shared/api\"],\"quality\":{\"score\":90,\"issues\":[],\"duplicateLines\":2,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.422Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":25},\"analysis\":{\"metrics\":{\"lines\":25,\"nonEmptyLines\":23,\"commentLines\":0,\"complexity\":9},\"dependencies\":[\"../shared/api\"],\"quality\":{\"score\":90,\"issues\":[],\"duplicateLines\":2,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855423,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.423Z\",\"size\":1486},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\utils\\\\cost.test.ts\",\"content\":{\"content\":\"import { describe, it } from \\\"mocha\\\"\\r\\nimport \\\"should\\\"\\r\\nimport { calculateApiCost } from \\\"./cost\\\"\\r\\nimport { ModelInfo } from \\\"../shared/api\\\"\\r\\n\\r\\ndescribe(\\\"Cost Utilities\\\", () => {\\r\\n\\tdescribe(\\\"calculateApiCost\\\", () => {\\r\\n\\t\\tit(\\\"should calculate basic input/output costs\\\", () => {\\r\\n\\t\\t\\tconst modelInfo: ModelInfo = {\\r\\n\\t\\t\\t\\tsupportsPromptCache: false,\\r\\n\\t\\t\\t\\tinputPrice: 3.0, // $3 per million tokens\\r\\n\\t\\t\\t\\toutputPrice: 15.0, // $15 per million tokens\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tconst cost = calculateApiCost(modelInfo, 1000, 500)\\r\\n\\t\\t\\t// Input: (3.0 / 1_000_000) * 1000 = 0.003\\r\\n\\t\\t\\t// Output: (15.0 / 1_000_000) * 500 = 0.0075\\r\\n\\t\\t\\t// Total: 0.003 + 0.0075 = 0.0105\\r\\n\\t\\t\\tcost.should.equal(0.0105)\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tit(\\\"should handle missing prices\\\", () => {\\r\\n\\t\\t\\tconst modelInfo: ModelInfo = {\\r\\n\\t\\t\\t\\tsupportsPromptCache: true,\\r\\n\\t\\t\\t\\t// No prices specified\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tconst cost = calculateApiCost(modelInfo, 1000, 500)\\r\\n\\t\\t\\tcost.should.equal(0)\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tit(\\\"should use real model configuration (Claude 3.5 Sonnet)\\\", () => {\\r\\n\\t\\t\\tconst modelInfo: ModelInfo = {\\r\\n\\t\\t\\t\\tmaxTokens: 8192,\\r\\n\\t\\t\\t\\tcontextWindow: 200_000,\\r\\n\\t\\t\\t\\tsupportsImages: true,\\r\\n\\t\\t\\t\\tsupportsComputerUse: true,\\r\\n\\t\\t\\t\\tsupportsPromptCache: true,\\r\\n\\t\\t\\t\\tinputPrice: 3.0,\\r\\n\\t\\t\\t\\toutputPrice: 15.0,\\r\\n\\t\\t\\t\\tcacheWritesPrice: 3.75,\\r\\n\\t\\t\\t\\tcacheReadsPrice: 0.3,\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tconst cost = calculateApiCost(modelInfo, 2000, 1000, 1500, 500)\\r\\n\\t\\t\\t// Cache writes: (3.75 / 1_000_000) * 1500 = 0.005625\\r\\n\\t\\t\\t// Cache reads: (0.3 / 1_000_000) * 500 = 0.00015\\r\\n\\t\\t\\t// Input: (3.0 / 1_000_000) * 2000 = 0.006\\r\\n\\t\\t\\t// Output: (15.0 / 1_000_000) * 1000 = 0.015\\r\\n\\t\\t\\t// Total: 0.005625 + 0.00015 + 0.006 + 0.015 = 0.026775\\r\\n\\t\\t\\tcost.should.equal(0.026775)\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tit(\\\"should handle zero token counts\\\", () => {\\r\\n\\t\\t\\tconst modelInfo: ModelInfo = {\\r\\n\\t\\t\\t\\tsupportsPromptCache: true,\\r\\n\\t\\t\\t\\tinputPrice: 3.0,\\r\\n\\t\\t\\t\\toutputPrice: 15.0,\\r\\n\\t\\t\\t\\tcacheWritesPrice: 3.75,\\r\\n\\t\\t\\t\\tcacheReadsPrice: 0.3,\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tconst cost = calculateApiCost(modelInfo, 0, 0, 0, 0)\\r\\n\\t\\t\\tcost.should.equal(0)\\r\\n\\t\\t})\\r\\n\\t})\\r\\n})\\r\\n\",\"metadata\":{\"size\":1975,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.857Z\",\"createdTime\":\"2024-12-29T05:57:23.857Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":68,\"nonEmptyLines\":59,\"commentLines\":9,\"complexity\":1},\"dependencies\":[\"mocha\",\"./cost\",\"../shared/api\"],\"quality\":{\"score\":5,\"issues\":[],\"duplicateLines\":19,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.421Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":68},\"analysis\":{\"metrics\":{\"lines\":68,\"nonEmptyLines\":59,\"commentLines\":9,\"complexity\":1},\"dependencies\":[\"mocha\",\"./cost\",\"../shared/api\"],\"quality\":{\"score\":5,\"issues\":[],\"duplicateLines\":19,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855422,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.422Z\",\"size\":2781},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\test\\\\extension.test.ts\",\"content\":{\"content\":\"import { readFile } from \\\"fs/promises\\\"\\r\\nimport { describe, it, after } from \\\"mocha\\\"\\r\\nimport path from \\\"path\\\"\\r\\nimport \\\"should\\\"\\r\\nimport * as vscode from \\\"vscode\\\"\\r\\n\\r\\nconst packagePath = path.join(__dirname, \\\"..\\\", \\\"..\\\", \\\"..\\\", \\\"package.json\\\")\\r\\n\\r\\ndescribe(\\\"Cline Extension\\\", () => {\\r\\n\\tafter(() => {\\r\\n\\t\\tvscode.window.showInformationMessage(\\\"All tests done!\\\")\\r\\n\\t})\\r\\n\\r\\n\\tit(\\\"should verify extension ID matches package.json\\\", async () => {\\r\\n\\t\\tconst packageJSON = JSON.parse(await readFile(packagePath, \\\"utf8\\\"))\\r\\n\\t\\tconst id = packageJSON.publisher + \\\".\\\" + packageJSON.name\\r\\n\\t\\tconst clineExtensionApi = vscode.extensions.getExtension(id)\\r\\n\\r\\n\\t\\tclineExtensionApi?.id.should.equal(id)\\r\\n\\t})\\r\\n\\r\\n\\tit(\\\"should successfully execute the plus button command\\\", async () => {\\r\\n\\t\\tawait new Promise((resolve) => setTimeout(resolve, 400))\\r\\n\\t\\tawait vscode.commands.executeCommand(\\\"cline.plusButtonClicked\\\")\\r\\n\\t})\\r\\n})\\r\\n\",\"metadata\":{\"size\":887,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.857Z\",\"createdTime\":\"2024-12-29T05:57:23.857Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":27,\"nonEmptyLines\":21,\"commentLines\":0,\"complexity\":2},\"dependencies\":[\"fs/promises\",\"mocha\",\"path\",\"vscode\"],\"quality\":{\"score\":85,\"issues\":[],\"duplicateLines\":3,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.420Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":27},\"analysis\":{\"metrics\":{\"lines\":27,\"nonEmptyLines\":21,\"commentLines\":0,\"complexity\":2},\"dependencies\":[\"fs/promises\",\"mocha\",\"path\",\"vscode\"],\"quality\":{\"score\":85,\"issues\":[],\"duplicateLines\":3,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855420,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.420Z\",\"size\":1481},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\shared\\\\WebviewMessage.ts\",\"content\":{\"content\":\"import { ApiConfiguration } from \\\"./api\\\"\\r\\nimport { AutoApprovalSettings } from \\\"./AutoApprovalSettings\\\"\\r\\n\\r\\nexport interface WebviewMessage {\\r\\n\\ttype:\\r\\n\\t\\t| \\\"apiConfiguration\\\"\\r\\n\\t\\t| \\\"customInstructions\\\"\\r\\n\\t\\t| \\\"webviewDidLaunch\\\"\\r\\n\\t\\t| \\\"newTask\\\"\\r\\n\\t\\t| \\\"askResponse\\\"\\r\\n\\t\\t| \\\"clearTask\\\"\\r\\n\\t\\t| \\\"didShowAnnouncement\\\"\\r\\n\\t\\t| \\\"selectImages\\\"\\r\\n\\t\\t| \\\"exportCurrentTask\\\"\\r\\n\\t\\t| \\\"showTaskWithId\\\"\\r\\n\\t\\t| \\\"deleteTaskWithId\\\"\\r\\n\\t\\t| \\\"exportTaskWithId\\\"\\r\\n\\t\\t| \\\"resetState\\\"\\r\\n\\t\\t| \\\"requestOllamaModels\\\"\\r\\n\\t\\t| \\\"requestLmStudioModels\\\"\\r\\n\\t\\t| \\\"openImage\\\"\\r\\n\\t\\t| \\\"openFile\\\"\\r\\n\\t\\t| \\\"openMention\\\"\\r\\n\\t\\t| \\\"cancelTask\\\"\\r\\n\\t\\t| \\\"refreshOpenRouterModels\\\"\\r\\n\\t\\t| \\\"openMcpSettings\\\"\\r\\n\\t\\t| \\\"restartMcpServer\\\"\\r\\n\\t\\t| \\\"autoApprovalSettings\\\"\\r\\n\\t\\t| \\\"checkpointDiff\\\"\\r\\n\\t\\t| \\\"checkpointRestore\\\"\\r\\n\\t\\t| \\\"taskCompletionViewChanges\\\"\\r\\n\\ttext?: string\\r\\n\\taskResponse?: ClineAskResponse\\r\\n\\tapiConfiguration?: ApiConfiguration\\r\\n\\timages?: string[]\\r\\n\\tbool?: boolean\\r\\n\\tnumber?: number\\r\\n\\tautoApprovalSettings?: AutoApprovalSettings\\r\\n}\\r\\n\\r\\nexport type ClineAskResponse = \\\"yesButtonClicked\\\" | \\\"noButtonClicked\\\" | \\\"messageResponse\\\"\\r\\n\\r\\nexport type ClineCheckpointRestore = \\\"task\\\" | \\\"workspace\\\" | \\\"taskAndWorkspace\\\"\\r\\n\",\"metadata\":{\"size\":1113,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.964Z\",\"createdTime\":\"2025-01-15T15:26:07.964Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":44,\"nonEmptyLines\":40,\"commentLines\":0,\"complexity\":8},\"dependencies\":[\"./api\",\"./AutoApprovalSettings\"],\"quality\":{\"score\":100,\"issues\":[],\"duplicateLines\":0,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.419Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":44},\"analysis\":{\"metrics\":{\"lines\":44,\"nonEmptyLines\":40,\"commentLines\":0,\"complexity\":8},\"dependencies\":[\"./api\",\"./AutoApprovalSettings\"],\"quality\":{\"score\":100,\"issues\":[],\"duplicateLines\":0,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855419,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.419Z\",\"size\":1814},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\shared\\\\mcp.ts\",\"content\":{\"content\":\"export type McpServer = {\\r\\n\\tname: string\\r\\n\\tconfig: string\\r\\n\\tstatus: \\\"connected\\\" | \\\"connecting\\\" | \\\"disconnected\\\"\\r\\n\\terror?: string\\r\\n\\ttools?: McpTool[]\\r\\n\\tresources?: McpResource[]\\r\\n\\tresourceTemplates?: McpResourceTemplate[]\\r\\n}\\r\\n\\r\\nexport type McpTool = {\\r\\n\\tname: string\\r\\n\\tdescription?: string\\r\\n\\tinputSchema?: object\\r\\n}\\r\\n\\r\\nexport type McpResource = {\\r\\n\\turi: string\\r\\n\\tname: string\\r\\n\\tmimeType?: string\\r\\n\\tdescription?: string\\r\\n}\\r\\n\\r\\nexport type McpResourceTemplate = {\\r\\n\\turiTemplate: string\\r\\n\\tname: string\\r\\n\\tdescription?: string\\r\\n\\tmimeType?: string\\r\\n}\\r\\n\\r\\nexport type McpResourceResponse = {\\r\\n\\t_meta?: Record<string, any>\\r\\n\\tcontents: Array<{\\r\\n\\t\\turi: string\\r\\n\\t\\tmimeType?: string\\r\\n\\t\\ttext?: string\\r\\n\\t\\tblob?: string\\r\\n\\t}>\\r\\n}\\r\\n\\r\\nexport type McpToolCallResponse = {\\r\\n\\t_meta?: Record<string, any>\\r\\n\\tcontent: Array<\\r\\n\\t\\t| {\\r\\n\\t\\t\\t\\ttype: \\\"text\\\"\\r\\n\\t\\t\\t\\ttext: string\\r\\n\\t\\t }\\r\\n\\t\\t| {\\r\\n\\t\\t\\t\\ttype: \\\"image\\\"\\r\\n\\t\\t\\t\\tdata: string\\r\\n\\t\\t\\t\\tmimeType: string\\r\\n\\t\\t }\\r\\n\\t\\t| {\\r\\n\\t\\t\\t\\ttype: \\\"resource\\\"\\r\\n\\t\\t\\t\\tresource: {\\r\\n\\t\\t\\t\\t\\turi: string\\r\\n\\t\\t\\t\\t\\tmimeType?: string\\r\\n\\t\\t\\t\\t\\ttext?: string\\r\\n\\t\\t\\t\\t\\tblob?: string\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t }\\r\\n\\t>\\r\\n\\tisError?: boolean\\r\\n}\\r\\n\",\"metadata\":{\"size\":1090,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.856Z\",\"createdTime\":\"2024-12-29T05:57:23.856Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":65,\"nonEmptyLines\":59,\"commentLines\":0,\"complexity\":20},\"dependencies\":[],\"quality\":{\"score\":-20,\"issues\":[],\"duplicateLines\":24,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.418Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":65},\"analysis\":{\"metrics\":{\"lines\":65,\"nonEmptyLines\":59,\"commentLines\":0,\"complexity\":20},\"dependencies\":[],\"quality\":{\"score\":-20,\"issues\":[],\"duplicateLines\":24,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855419,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.419Z\",\"size\":1784},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\shared\\\\HistoryItem.ts\",\"content\":{\"content\":\"export type HistoryItem = {\\r\\n\\tid: string\\r\\n\\tts: number\\r\\n\\ttask: string\\r\\n\\ttokensIn: number\\r\\n\\ttokensOut: number\\r\\n\\tcacheWrites?: number\\r\\n\\tcacheReads?: number\\r\\n\\ttotalCost: number\\r\\n\\r\\n\\tsize?: number\\r\\n\\tshadowGitConfigWorkTree?: string\\r\\n\\tconversationHistoryDeletedRange?: [number, number]\\r\\n}\\r\\n\",\"metadata\":{\"size\":283,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.964Z\",\"createdTime\":\"2025-01-15T15:26:07.963Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":15,\"nonEmptyLines\":13,\"commentLines\":0,\"complexity\":6},\"dependencies\":[],\"quality\":{\"score\":100,\"issues\":[],\"duplicateLines\":0,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.417Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":15},\"analysis\":{\"metrics\":{\"lines\":15,\"nonEmptyLines\":13,\"commentLines\":0,\"complexity\":6},\"dependencies\":[],\"quality\":{\"score\":100,\"issues\":[],\"duplicateLines\":0,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855417,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.417Z\",\"size\":776},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\shared\\\\getApiMetrics.ts\",\"content\":{\"content\":\"import { ClineMessage } from \\\"./ExtensionMessage\\\"\\r\\n\\r\\ninterface ApiMetrics {\\r\\n\\ttotalTokensIn: number\\r\\n\\ttotalTokensOut: number\\r\\n\\ttotalCacheWrites?: number\\r\\n\\ttotalCacheReads?: number\\r\\n\\ttotalCost: number\\r\\n}\\r\\n\\r\\n/**\\r\\n * Calculates API metrics from an array of ClineMessages.\\r\\n *\\r\\n * This function processes 'api_req_started' messages that have been combined with their\\r\\n * corresponding 'api_req_finished' messages by the combineApiRequests function. It also takes into account 'deleted_api_reqs' messages, which are aggregated from deleted messages.\\r\\n * It extracts and sums up the tokensIn, tokensOut, cacheWrites, cacheReads, and cost from these messages.\\r\\n *\\r\\n * @param messages - An array of ClineMessage objects to process.\\r\\n * @returns An ApiMetrics object containing totalTokensIn, totalTokensOut, totalCacheWrites, totalCacheReads, and totalCost.\\r\\n *\\r\\n * @example\\r\\n * const messages = [\\r\\n * { type: \\\"say\\\", say: \\\"api_req_started\\\", text: '{\\\"request\\\":\\\"GET /api/data\\\",\\\"tokensIn\\\":10,\\\"tokensOut\\\":20,\\\"cost\\\":0.005}', ts: 1000 }\\r\\n * ];\\r\\n * const { totalTokensIn, totalTokensOut, totalCost } = getApiMetrics(messages);\\r\\n * // Result: { totalTokensIn: 10, totalTokensOut: 20, totalCost: 0.005 }\\r\\n */\\r\\nexport function getApiMetrics(messages: ClineMessage[]): ApiMetrics {\\r\\n\\tconst result: ApiMetrics = {\\r\\n\\t\\ttotalTokensIn: 0,\\r\\n\\t\\ttotalTokensOut: 0,\\r\\n\\t\\ttotalCacheWrites: undefined,\\r\\n\\t\\ttotalCacheReads: undefined,\\r\\n\\t\\ttotalCost: 0,\\r\\n\\t}\\r\\n\\r\\n\\tmessages.forEach((message) => {\\r\\n\\t\\tif (message.type === \\\"say\\\" && (message.say === \\\"api_req_started\\\" || message.say === \\\"deleted_api_reqs\\\") && message.text) {\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\tconst parsedData = JSON.parse(message.text)\\r\\n\\t\\t\\t\\tconst { tokensIn, tokensOut, cacheWrites, cacheReads, cost } = parsedData\\r\\n\\r\\n\\t\\t\\t\\tif (typeof tokensIn === \\\"number\\\") {\\r\\n\\t\\t\\t\\t\\tresult.totalTokensIn += tokensIn\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tif (typeof tokensOut === \\\"number\\\") {\\r\\n\\t\\t\\t\\t\\tresult.totalTokensOut += tokensOut\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tif (typeof cacheWrites === \\\"number\\\") {\\r\\n\\t\\t\\t\\t\\tresult.totalCacheWrites = (result.totalCacheWrites ?? 0) + cacheWrites\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tif (typeof cacheReads === \\\"number\\\") {\\r\\n\\t\\t\\t\\t\\tresult.totalCacheReads = (result.totalCacheReads ?? 0) + cacheReads\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tif (typeof cost === \\\"number\\\") {\\r\\n\\t\\t\\t\\t\\tresult.totalCost += cost\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\tconsole.error(\\\"Error parsing JSON:\\\", error)\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t})\\r\\n\\r\\n\\treturn result\\r\\n}\\r\\n\",\"metadata\":{\"size\":2338,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.964Z\",\"createdTime\":\"2025-01-15T15:26:07.964Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":66,\"nonEmptyLines\":60,\"commentLines\":1,\"complexity\":17},\"dependencies\":[\"./ExtensionMessage\"],\"quality\":{\"score\":35,\"issues\":[],\"duplicateLines\":11,\"longLines\":5,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.416Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":66},\"analysis\":{\"metrics\":{\"lines\":66,\"nonEmptyLines\":60,\"commentLines\":1,\"complexity\":17},\"dependencies\":[\"./ExtensionMessage\"],\"quality\":{\"score\":35,\"issues\":[],\"duplicateLines\":11,\"longLines\":5,\"complexFunctions\":0}},\"lastModified\":1737046855417,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.417Z\",\"size\":3088},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\shared\\\\ExtensionMessage.ts\",\"content\":{\"content\":\"// type that represents json data that is sent from extension to webview, called ExtensionMessage and has 'type' enum which can be 'plusButtonClicked' or 'settingsButtonClicked' or 'hello'\\r\\n\\r\\nimport { ApiConfiguration, ModelInfo } from \\\"./api\\\"\\r\\nimport { AutoApprovalSettings } from \\\"./AutoApprovalSettings\\\"\\r\\nimport { HistoryItem } from \\\"./HistoryItem\\\"\\r\\nimport { McpServer } from \\\"./mcp\\\"\\r\\n\\r\\n// webview will hold state\\r\\nexport interface ExtensionMessage {\\r\\n\\ttype:\\r\\n\\t\\t| \\\"action\\\"\\r\\n\\t\\t| \\\"state\\\"\\r\\n\\t\\t| \\\"selectedImages\\\"\\r\\n\\t\\t| \\\"ollamaModels\\\"\\r\\n\\t\\t| \\\"lmStudioModels\\\"\\r\\n\\t\\t| \\\"theme\\\"\\r\\n\\t\\t| \\\"workspaceUpdated\\\"\\r\\n\\t\\t| \\\"invoke\\\"\\r\\n\\t\\t| \\\"partialMessage\\\"\\r\\n\\t\\t| \\\"openRouterModels\\\"\\r\\n\\t\\t| \\\"mcpServers\\\"\\r\\n\\t\\t| \\\"relinquishControl\\\"\\r\\n\\ttext?: string\\r\\n\\taction?: \\\"chatButtonClicked\\\" | \\\"mcpButtonClicked\\\" | \\\"settingsButtonClicked\\\" | \\\"historyButtonClicked\\\" | \\\"didBecomeVisible\\\"\\r\\n\\tinvoke?: \\\"sendMessage\\\" | \\\"primaryButtonClick\\\" | \\\"secondaryButtonClick\\\"\\r\\n\\tstate?: ExtensionState\\r\\n\\timages?: string[]\\r\\n\\tollamaModels?: string[]\\r\\n\\tlmStudioModels?: string[]\\r\\n\\tfilePaths?: string[]\\r\\n\\tpartialMessage?: ClineMessage\\r\\n\\topenRouterModels?: Record<string, ModelInfo>\\r\\n\\tmcpServers?: McpServer[]\\r\\n}\\r\\n\\r\\nexport interface ExtensionState {\\r\\n\\tversion: string\\r\\n\\tapiConfiguration?: ApiConfiguration\\r\\n\\tcustomInstructions?: string\\r\\n\\turiScheme?: string\\r\\n\\tcurrentTaskItem?: HistoryItem\\r\\n\\tcheckpointTrackerErrorMessage?: string\\r\\n\\tclineMessages: ClineMessage[]\\r\\n\\ttaskHistory: HistoryItem[]\\r\\n\\tshouldShowAnnouncement: boolean\\r\\n\\tautoApprovalSettings: AutoApprovalSettings\\r\\n}\\r\\n\\r\\nexport interface ClineMessage {\\r\\n\\tts: number\\r\\n\\ttype: \\\"ask\\\" | \\\"say\\\"\\r\\n\\task?: ClineAsk\\r\\n\\tsay?: ClineSay\\r\\n\\ttext?: string\\r\\n\\timages?: string[]\\r\\n\\tpartial?: boolean\\r\\n\\tlastCheckpointHash?: string\\r\\n\\tconversationHistoryIndex?: number\\r\\n\\tconversationHistoryDeletedRange?: [number, number] // for when conversation history is truncated for API requests\\r\\n}\\r\\n\\r\\nexport type ClineAsk =\\r\\n\\t| \\\"followup\\\"\\r\\n\\t| \\\"command\\\"\\r\\n\\t| \\\"command_output\\\"\\r\\n\\t| \\\"completion_result\\\"\\r\\n\\t| \\\"tool\\\"\\r\\n\\t| \\\"api_req_failed\\\"\\r\\n\\t| \\\"resume_task\\\"\\r\\n\\t| \\\"resume_completed_task\\\"\\r\\n\\t| \\\"mistake_limit_reached\\\"\\r\\n\\t| \\\"auto_approval_max_req_reached\\\"\\r\\n\\t| \\\"browser_action_launch\\\"\\r\\n\\t| \\\"use_mcp_server\\\"\\r\\n\\r\\nexport type ClineSay =\\r\\n\\t| \\\"task\\\"\\r\\n\\t| \\\"error\\\"\\r\\n\\t| \\\"api_req_started\\\"\\r\\n\\t| \\\"api_req_finished\\\"\\r\\n\\t| \\\"text\\\"\\r\\n\\t| \\\"completion_result\\\"\\r\\n\\t| \\\"user_feedback\\\"\\r\\n\\t| \\\"user_feedback_diff\\\"\\r\\n\\t| \\\"api_req_retried\\\"\\r\\n\\t| \\\"command\\\"\\r\\n\\t| \\\"command_output\\\"\\r\\n\\t| \\\"tool\\\"\\r\\n\\t| \\\"shell_integration_warning\\\"\\r\\n\\t| \\\"browser_action_launch\\\"\\r\\n\\t| \\\"browser_action\\\"\\r\\n\\t| \\\"browser_action_result\\\"\\r\\n\\t| \\\"mcp_server_request_started\\\"\\r\\n\\t| \\\"mcp_server_response\\\"\\r\\n\\t| \\\"use_mcp_server\\\"\\r\\n\\t| \\\"diff_error\\\"\\r\\n\\t| \\\"deleted_api_reqs\\\"\\r\\n\\r\\nexport interface ClineSayTool {\\r\\n\\ttool:\\r\\n\\t\\t| \\\"editedExistingFile\\\"\\r\\n\\t\\t| \\\"newFileCreated\\\"\\r\\n\\t\\t| \\\"readFile\\\"\\r\\n\\t\\t| \\\"listFilesTopLevel\\\"\\r\\n\\t\\t| \\\"listFilesRecursive\\\"\\r\\n\\t\\t| \\\"listCodeDefinitionNames\\\"\\r\\n\\t\\t| \\\"searchFiles\\\"\\r\\n\\tpath?: string\\r\\n\\tdiff?: string\\r\\n\\tcontent?: string\\r\\n\\tregex?: string\\r\\n\\tfilePattern?: string\\r\\n}\\r\\n\\r\\n// must keep in sync with system prompt\\r\\nexport const browserActions = [\\\"launch\\\", \\\"click\\\", \\\"type\\\", \\\"scroll_down\\\", \\\"scroll_up\\\", \\\"close\\\"] as const\\r\\nexport type BrowserAction = (typeof browserActions)[number]\\r\\n\\r\\nexport interface ClineSayBrowserAction {\\r\\n\\taction: BrowserAction\\r\\n\\tcoordinate?: string\\r\\n\\ttext?: string\\r\\n}\\r\\n\\r\\nexport type BrowserActionResult = {\\r\\n\\tscreenshot?: string\\r\\n\\tlogs?: string\\r\\n\\tcurrentUrl?: string\\r\\n\\tcurrentMousePosition?: string\\r\\n}\\r\\n\\r\\nexport interface ClineAskUseMcpServer {\\r\\n\\tserverName: string\\r\\n\\ttype: \\\"use_mcp_tool\\\" | \\\"access_mcp_resource\\\"\\r\\n\\ttoolName?: string\\r\\n\\targuments?: string\\r\\n\\turi?: string\\r\\n}\\r\\n\\r\\nexport interface ClineApiReqInfo {\\r\\n\\trequest?: string\\r\\n\\ttokensIn?: number\\r\\n\\ttokensOut?: number\\r\\n\\tcacheWrites?: number\\r\\n\\tcacheReads?: number\\r\\n\\tcost?: number\\r\\n\\tcancelReason?: ClineApiReqCancelReason\\r\\n\\tstreamingFailedMessage?: string\\r\\n}\\r\\n\\r\\nexport type ClineApiReqCancelReason = \\\"streaming_failed\\\" | \\\"user_cancelled\\\"\\r\\n\\r\\nexport const COMPLETION_RESULT_CHANGES_FLAG = \\\"HAS_CHANGES\\\"\\r\\n\",\"metadata\":{\"size\":3908,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.963Z\",\"createdTime\":\"2025-01-15T15:26:07.963Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":154,\"nonEmptyLines\":139,\"commentLines\":3,\"complexity\":47},\"dependencies\":[\"./api\",\"./AutoApprovalSettings\",\"./HistoryItem\",\"./mcp\"],\"quality\":{\"score\":12,\"issues\":[],\"duplicateLines\":16,\"longLines\":4,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.415Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":154},\"analysis\":{\"metrics\":{\"lines\":154,\"nonEmptyLines\":139,\"commentLines\":3,\"complexity\":47},\"dependencies\":[\"./api\",\"./AutoApprovalSettings\",\"./HistoryItem\",\"./mcp\"],\"quality\":{\"score\":12,\"issues\":[],\"duplicateLines\":16,\"longLines\":4,\"complexFunctions\":0}},\"lastModified\":1737046855415,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.415Z\",\"size\":5012},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\shared\\\\context-mentions.ts\",\"content\":{\"content\":\"/*\\r\\nMention regex:\\r\\n- **Purpose**: \\r\\n - To identify and highlight specific mentions in text that start with '@'. \\r\\n - These mentions can be file paths, URLs, or the exact word 'problems'.\\r\\n - Ensures that trailing punctuation marks (like commas, periods, etc.) are not included in the match, allowing punctuation to follow the mention without being part of it.\\r\\n\\r\\n- **Regex Breakdown**:\\r\\n - `/@`: \\r\\n - **@**: The mention must start with the '@' symbol.\\r\\n \\r\\n - `((?:\\\\/|\\\\w+:\\\\/\\\\/)[^\\\\s]+?|problems\\\\b)`:\\r\\n - **Capturing Group (`(...)`)**: Captures the part of the string that matches one of the specified patterns.\\r\\n - `(?:\\\\/|\\\\w+:\\\\/\\\\/)`: \\r\\n - **Non-Capturing Group (`(?:...)`)**: Groups the alternatives without capturing them for back-referencing.\\r\\n - `\\\\/`: \\r\\n - **Slash (`/`)**: Indicates that the mention is a file or folder path starting with a '/'.\\r\\n - `|`: Logical OR.\\r\\n - `\\\\w+:\\\\/\\\\/`: \\r\\n - **Protocol (`\\\\w+://`)**: Matches URLs that start with a word character sequence followed by '://', such as 'http://', 'https://', 'ftp://', etc.\\r\\n - `[^\\\\s]+?`: \\r\\n - **Non-Whitespace Characters (`[^\\\\s]+`)**: Matches one or more characters that are not whitespace.\\r\\n - **Non-Greedy (`+?`)**: Ensures the smallest possible match, preventing the inclusion of trailing punctuation.\\r\\n - `|`: Logical OR.\\r\\n - `problems\\\\b`: \\r\\n - **Exact Word ('problems')**: Matches the exact word 'problems'.\\r\\n - **Word Boundary (`\\\\b`)**: Ensures that 'problems' is matched as a whole word and not as part of another word (e.g., 'problematic').\\r\\n\\r\\n - `(?=[.,;:!?]?(?=[\\\\s\\\\r\\\\n]|$))`:\\r\\n - **Positive Lookahead (`(?=...)`)**: Ensures that the match is followed by specific patterns without including them in the match.\\r\\n - `[.,;:!?]?`: \\r\\n - **Optional Punctuation (`[.,;:!?]?`)**: Matches zero or one of the specified punctuation marks.\\r\\n - `(?=[\\\\s\\\\r\\\\n]|$)`: \\r\\n - **Nested Positive Lookahead (`(?=[\\\\s\\\\r\\\\n]|$)`)**: Ensures that the punctuation (if present) is followed by a whitespace character, a line break, or the end of the string.\\r\\n \\r\\n- **Summary**:\\r\\n - The regex effectively matches:\\r\\n - Mentions that are file or folder paths starting with '/' and containing any non-whitespace characters (including periods within the path).\\r\\n - URLs that start with a protocol (like 'http://') followed by any non-whitespace characters (including query parameters).\\r\\n - The exact word 'problems'.\\r\\n - It ensures that any trailing punctuation marks (such as ',', '.', '!', etc.) are not included in the matched mention, allowing the punctuation to follow the mention naturally in the text.\\r\\n\\r\\n- **Global Regex**:\\r\\n - `mentionRegexGlobal`: Creates a global version of the `mentionRegex` to find all matches within a given string.\\r\\n\\r\\n*/\\r\\nexport const mentionRegex = /@((?:\\\\/|\\\\w+:\\\\/\\\\/)[^\\\\s]+?|problems\\\\b)(?=[.,;:!?]?(?=[\\\\s\\\\r\\\\n]|$))/\\r\\nexport const mentionRegexGlobal = new RegExp(mentionRegex.source, \\\"g\\\")\\r\\n\",\"metadata\":{\"size\":2967,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.856Z\",\"createdTime\":\"2024-12-29T05:57:23.856Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":49,\"nonEmptyLines\":42,\"commentLines\":1,\"complexity\":24},\"dependencies\":[],\"quality\":{\"score\":67,\"issues\":[],\"duplicateLines\":1,\"longLines\":14,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.414Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":49},\"analysis\":{\"metrics\":{\"lines\":49,\"nonEmptyLines\":42,\"commentLines\":1,\"complexity\":24},\"dependencies\":[],\"quality\":{\"score\":67,\"issues\":[],\"duplicateLines\":1,\"longLines\":14,\"complexFunctions\":0}},\"lastModified\":1737046855414,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.414Z\",\"size\":3558},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\shared\\\\combineCommandSequences.ts\",\"content\":{\"content\":\"import { ClineMessage } from \\\"./ExtensionMessage\\\"\\r\\n\\r\\n/**\\r\\n * Combines sequences of command and command_output messages in an array of ClineMessages.\\r\\n *\\r\\n * This function processes an array of ClineMessages objects, looking for sequences\\r\\n * where a 'command' message is followed by one or more 'command_output' messages.\\r\\n * When such a sequence is found, it combines them into a single message, merging\\r\\n * their text contents.\\r\\n *\\r\\n * @param messages - An array of ClineMessage objects to process.\\r\\n * @returns A new array of ClineMessage objects with command sequences combined.\\r\\n *\\r\\n * @example\\r\\n * const messages: ClineMessage[] = [\\r\\n * { type: 'ask', ask: 'command', text: 'ls', ts: 1625097600000 },\\r\\n * { type: 'ask', ask: 'command_output', text: 'file1.txt', ts: 1625097601000 },\\r\\n * { type: 'ask', ask: 'command_output', text: 'file2.txt', ts: 1625097602000 }\\r\\n * ];\\r\\n * const result = simpleCombineCommandSequences(messages);\\r\\n * // Result: [{ type: 'ask', ask: 'command', text: 'ls\\\\nfile1.txt\\\\nfile2.txt', ts: 1625097600000 }]\\r\\n */\\r\\nexport function combineCommandSequences(messages: ClineMessage[]): ClineMessage[] {\\r\\n\\tconst combinedCommands: ClineMessage[] = []\\r\\n\\r\\n\\t// First pass: combine commands with their outputs\\r\\n\\tfor (let i = 0; i < messages.length; i++) {\\r\\n\\t\\tif (messages[i].type === \\\"ask\\\" && (messages[i].ask === \\\"command\\\" || messages[i].say === \\\"command\\\")) {\\r\\n\\t\\t\\tlet combinedText = messages[i].text || \\\"\\\"\\r\\n\\t\\t\\tlet didAddOutput = false\\r\\n\\t\\t\\tlet j = i + 1\\r\\n\\r\\n\\t\\t\\twhile (j < messages.length) {\\r\\n\\t\\t\\t\\tif (messages[j].type === \\\"ask\\\" && (messages[j].ask === \\\"command\\\" || messages[j].say === \\\"command\\\")) {\\r\\n\\t\\t\\t\\t\\t// Stop if we encounter the next command\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tif (messages[j].ask === \\\"command_output\\\" || messages[j].say === \\\"command_output\\\") {\\r\\n\\t\\t\\t\\t\\tif (!didAddOutput) {\\r\\n\\t\\t\\t\\t\\t\\t// Add a newline before the first output\\r\\n\\t\\t\\t\\t\\t\\tcombinedText += `\\\\n${COMMAND_OUTPUT_STRING}`\\r\\n\\t\\t\\t\\t\\t\\tdidAddOutput = true\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t// handle cases where we receive empty command_output (ie when extension is relinquishing control over exit command button)\\r\\n\\t\\t\\t\\t\\tconst output = messages[j].text || \\\"\\\"\\r\\n\\t\\t\\t\\t\\tif (output.length > 0) {\\r\\n\\t\\t\\t\\t\\t\\tcombinedText += \\\"\\\\n\\\" + output\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tj++\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tcombinedCommands.push({\\r\\n\\t\\t\\t\\t...messages[i],\\r\\n\\t\\t\\t\\ttext: combinedText,\\r\\n\\t\\t\\t})\\r\\n\\r\\n\\t\\t\\ti = j - 1 // Move to the index just before the next command or end of array\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t// Second pass: remove command_outputs and replace original commands with combined ones\\r\\n\\treturn messages\\r\\n\\t\\t.filter((msg) => !(msg.ask === \\\"command_output\\\" || msg.say === \\\"command_output\\\"))\\r\\n\\t\\t.map((msg) => {\\r\\n\\t\\t\\tif (msg.type === \\\"ask\\\" && (msg.ask === \\\"command\\\" || msg.say === \\\"command\\\")) {\\r\\n\\t\\t\\t\\tconst combinedCommand = combinedCommands.find((cmd) => cmd.ts === msg.ts)\\r\\n\\t\\t\\t\\treturn combinedCommand || msg\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\treturn msg\\r\\n\\t\\t})\\r\\n}\\r\\nexport const COMMAND_OUTPUT_STRING = \\\"Output:\\\"\\r\\nexport const COMMAND_REQ_APP_STRING = \\\"REQ_APP\\\"\\r\\n\",\"metadata\":{\"size\":2924,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.856Z\",\"createdTime\":\"2024-12-29T05:57:23.856Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":75,\"nonEmptyLines\":68,\"commentLines\":6,\"complexity\":20},\"dependencies\":[\"./ExtensionMessage\"],\"quality\":{\"score\":37,\"issues\":[],\"duplicateLines\":11,\"longLines\":4,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.413Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":75},\"analysis\":{\"metrics\":{\"lines\":75,\"nonEmptyLines\":68,\"commentLines\":6,\"complexity\":20},\"dependencies\":[\"./ExtensionMessage\"],\"quality\":{\"score\":37,\"issues\":[],\"duplicateLines\":11,\"longLines\":4,\"complexFunctions\":0}},\"lastModified\":1737046855413,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.413Z\",\"size\":3739},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\shared\\\\combineApiRequests.ts\",\"content\":{\"content\":\"import { ClineMessage } from \\\"./ExtensionMessage\\\"\\r\\n\\r\\n/**\\r\\n * Combines API request start and finish messages in an array of ClineMessages.\\r\\n *\\r\\n * This function looks for pairs of 'api_req_started' and 'api_req_finished' messages.\\r\\n * When it finds a pair, it combines them into a single 'api_req_combined' message.\\r\\n * The JSON data in the text fields of both messages are merged.\\r\\n *\\r\\n * @param messages - An array of ClineMessage objects to process.\\r\\n * @returns A new array of ClineMessage objects with API requests combined.\\r\\n *\\r\\n * @example\\r\\n * const messages = [\\r\\n * { type: \\\"say\\\", say: \\\"api_req_started\\\", text: '{\\\"request\\\":\\\"GET /api/data\\\"}', ts: 1000 },\\r\\n * { type: \\\"say\\\", say: \\\"api_req_finished\\\", text: '{\\\"cost\\\":0.005}', ts: 1001 }\\r\\n * ];\\r\\n * const result = combineApiRequests(messages);\\r\\n * // Result: [{ type: \\\"say\\\", say: \\\"api_req_started\\\", text: '{\\\"request\\\":\\\"GET /api/data\\\",\\\"cost\\\":0.005}', ts: 1000 }]\\r\\n */\\r\\nexport function combineApiRequests(messages: ClineMessage[]): ClineMessage[] {\\r\\n\\tconst combinedApiRequests: ClineMessage[] = []\\r\\n\\r\\n\\tfor (let i = 0; i < messages.length; i++) {\\r\\n\\t\\tif (messages[i].type === \\\"say\\\" && messages[i].say === \\\"api_req_started\\\") {\\r\\n\\t\\t\\tlet startedRequest = JSON.parse(messages[i].text || \\\"{}\\\")\\r\\n\\t\\t\\tlet j = i + 1\\r\\n\\r\\n\\t\\t\\twhile (j < messages.length) {\\r\\n\\t\\t\\t\\tif (messages[j].type === \\\"say\\\" && messages[j].say === \\\"api_req_finished\\\") {\\r\\n\\t\\t\\t\\t\\tlet finishedRequest = JSON.parse(messages[j].text || \\\"{}\\\")\\r\\n\\t\\t\\t\\t\\tlet combinedRequest = {\\r\\n\\t\\t\\t\\t\\t\\t...startedRequest,\\r\\n\\t\\t\\t\\t\\t\\t...finishedRequest,\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\tcombinedApiRequests.push({\\r\\n\\t\\t\\t\\t\\t\\t...messages[i],\\r\\n\\t\\t\\t\\t\\t\\ttext: JSON.stringify(combinedRequest),\\r\\n\\t\\t\\t\\t\\t})\\r\\n\\r\\n\\t\\t\\t\\t\\ti = j // Skip to the api_req_finished message\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tj++\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif (j === messages.length) {\\r\\n\\t\\t\\t\\t// If no matching api_req_finished found, keep the original api_req_started\\r\\n\\t\\t\\t\\tcombinedApiRequests.push(messages[i])\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t// Replace original api_req_started and remove api_req_finished\\r\\n\\treturn messages\\r\\n\\t\\t.filter((msg) => !(msg.type === \\\"say\\\" && msg.say === \\\"api_req_finished\\\"))\\r\\n\\t\\t.map((msg) => {\\r\\n\\t\\t\\tif (msg.type === \\\"say\\\" && msg.say === \\\"api_req_started\\\") {\\r\\n\\t\\t\\t\\tconst combinedRequest = combinedApiRequests.find((req) => req.ts === msg.ts)\\r\\n\\t\\t\\t\\treturn combinedRequest || msg\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\treturn msg\\r\\n\\t\\t})\\r\\n}\\r\\n\",\"metadata\":{\"size\":2298,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.964Z\",\"createdTime\":\"2025-01-15T15:26:07.964Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":66,\"nonEmptyLines\":58,\"commentLines\":3,\"complexity\":14},\"dependencies\":[\"./ExtensionMessage\"],\"quality\":{\"score\":48,\"issues\":[],\"duplicateLines\":10,\"longLines\":1,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.412Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":66},\"analysis\":{\"metrics\":{\"lines\":66,\"nonEmptyLines\":58,\"commentLines\":3,\"complexity\":14},\"dependencies\":[\"./ExtensionMessage\"],\"quality\":{\"score\":48,\"issues\":[],\"duplicateLines\":10,\"longLines\":1,\"complexFunctions\":0}},\"lastModified\":1737046855412,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.412Z\",\"size\":3079},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\shared\\\\AutoApprovalSettings.ts\",\"content\":{\"content\":\"export interface AutoApprovalSettings {\\r\\n\\t// Whether auto-approval is enabled\\r\\n\\tenabled: boolean\\r\\n\\t// Individual action permissions\\r\\n\\tactions: {\\r\\n\\t\\treadFiles: boolean // Read files and directories\\r\\n\\t\\teditFiles: boolean // Edit files\\r\\n\\t\\texecuteCommands: boolean // Execute safe commands\\r\\n\\t\\tuseBrowser: boolean // Use browser\\r\\n\\t\\tuseMcp: boolean // Use MCP servers\\r\\n\\t}\\r\\n\\t// Global settings\\r\\n\\tmaxRequests: number // Maximum number of auto-approved requests\\r\\n\\tenableNotifications: boolean // Show notifications for approval and task completion\\r\\n}\\r\\n\\r\\nexport const DEFAULT_AUTO_APPROVAL_SETTINGS: AutoApprovalSettings = {\\r\\n\\tenabled: false,\\r\\n\\tactions: {\\r\\n\\t\\treadFiles: false,\\r\\n\\t\\teditFiles: false,\\r\\n\\t\\texecuteCommands: false,\\r\\n\\t\\tuseBrowser: false,\\r\\n\\t\\tuseMcp: false,\\r\\n\\t},\\r\\n\\tmaxRequests: 20,\\r\\n\\tenableNotifications: false,\\r\\n}\\r\\n\",\"metadata\":{\"size\":813,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.854Z\",\"createdTime\":\"2024-12-29T05:57:23.854Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":29,\"nonEmptyLines\":27,\"commentLines\":3,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":85,\"issues\":[],\"duplicateLines\":3,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.411Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":29},\"analysis\":{\"metrics\":{\"lines\":29,\"nonEmptyLines\":27,\"commentLines\":3,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":85,\"issues\":[],\"duplicateLines\":3,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855411,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.411Z\",\"size\":1355},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\shared\\\\array.ts\",\"content\":{\"content\":\"/**\\r\\n * Returns the index of the last element in the array where predicate is true, and -1\\r\\n * otherwise.\\r\\n * @param array The source array to search in\\r\\n * @param predicate find calls predicate once for each element of the array, in descending\\r\\n * order, until it finds one where predicate returns true. If such an element is found,\\r\\n * findLastIndex immediately returns that element index. Otherwise, findLastIndex returns -1.\\r\\n */\\r\\nexport function findLastIndex<T>(array: Array<T>, predicate: (value: T, index: number, obj: T[]) => boolean): number {\\r\\n\\tlet l = array.length\\r\\n\\twhile (l--) {\\r\\n\\t\\tif (predicate(array[l], l, array)) {\\r\\n\\t\\t\\treturn l\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\treturn -1\\r\\n}\\r\\n\\r\\nexport function findLast<T>(array: Array<T>, predicate: (value: T, index: number, obj: T[]) => boolean): T | undefined {\\r\\n\\tconst index = findLastIndex(array, predicate)\\r\\n\\treturn index === -1 ? undefined : array[index]\\r\\n}\\r\\n\",\"metadata\":{\"size\":895,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.854Z\",\"createdTime\":\"2024-12-29T05:57:23.854Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":23,\"nonEmptyLines\":21,\"commentLines\":1,\"complexity\":4},\"dependencies\":[],\"quality\":{\"score\":81,\"issues\":[],\"duplicateLines\":3,\"longLines\":2,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.410Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":23},\"analysis\":{\"metrics\":{\"lines\":23,\"nonEmptyLines\":21,\"commentLines\":1,\"complexity\":4},\"dependencies\":[],\"quality\":{\"score\":81,\"issues\":[],\"duplicateLines\":3,\"longLines\":2,\"complexFunctions\":0}},\"lastModified\":1737046855410,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.410Z\",\"size\":1405},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\shared\\\\array.test.ts\",\"content\":{\"content\":\"import { describe, it } from \\\"mocha\\\"\\r\\nimport \\\"should\\\"\\r\\nimport { findLastIndex, findLast } from \\\"./array\\\"\\r\\n\\r\\ndescribe(\\\"Array Utilities\\\", () => {\\r\\n\\tdescribe(\\\"findLastIndex\\\", () => {\\r\\n\\t\\tit(\\\"should find last matching element's index\\\", () => {\\r\\n\\t\\t\\tconst array = [1, 2, 3, 2, 1]\\r\\n\\t\\t\\tconst index = findLastIndex(array, (x) => x === 2)\\r\\n\\t\\t\\tindex.should.equal(3) // last '2' is at index 3\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tit(\\\"should return -1 when no element matches\\\", () => {\\r\\n\\t\\t\\tconst array = [1, 2, 3]\\r\\n\\t\\t\\tconst index = findLastIndex(array, (x) => x === 4)\\r\\n\\t\\t\\tindex.should.equal(-1)\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tit(\\\"should handle empty arrays\\\", () => {\\r\\n\\t\\t\\tconst array: number[] = []\\r\\n\\t\\t\\tconst index = findLastIndex(array, (x) => x === 1)\\r\\n\\t\\t\\tindex.should.equal(-1)\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tit(\\\"should work with different types\\\", () => {\\r\\n\\t\\t\\tconst array = [\\\"a\\\", \\\"b\\\", \\\"c\\\", \\\"b\\\", \\\"a\\\"]\\r\\n\\t\\t\\tconst index = findLastIndex(array, (x) => x === \\\"b\\\")\\r\\n\\t\\t\\tindex.should.equal(3)\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tit(\\\"should provide correct index in predicate\\\", () => {\\r\\n\\t\\t\\tconst array = [1, 2, 3]\\r\\n\\t\\t\\tconst indices: number[] = []\\r\\n\\t\\t\\tfindLastIndex(array, (_, index) => {\\r\\n\\t\\t\\t\\tindices.push(index)\\r\\n\\t\\t\\t\\treturn false\\r\\n\\t\\t\\t})\\r\\n\\t\\t\\tindices.should.deepEqual([2, 1, 0]) // Should iterate in reverse\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tit(\\\"should provide array reference in predicate\\\", () => {\\r\\n\\t\\t\\tconst array = [1, 2, 3]\\r\\n\\t\\t\\tfindLastIndex(array, (_, __, arr) => {\\r\\n\\t\\t\\t\\tarr.should.equal(array) // Should pass original array\\r\\n\\t\\t\\t\\treturn false\\r\\n\\t\\t\\t})\\r\\n\\t\\t})\\r\\n\\t})\\r\\n\\r\\n\\tdescribe(\\\"findLast\\\", () => {\\r\\n\\t\\tit(\\\"should find last matching element\\\", () => {\\r\\n\\t\\t\\tconst array = [1, 2, 3, 2, 1]\\r\\n\\t\\t\\tconst element = findLast(array, (x) => x === 2)\\r\\n\\t\\t\\tshould(element).not.be.undefined()\\r\\n\\t\\t\\telement!.should.equal(2)\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tit(\\\"should return undefined when no element matches\\\", () => {\\r\\n\\t\\t\\tconst array = [1, 2, 3]\\r\\n\\t\\t\\tconst element = findLast(array, (x) => x === 4)\\r\\n\\t\\t\\tshould(element).be.undefined()\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tit(\\\"should handle empty arrays\\\", () => {\\r\\n\\t\\t\\tconst array: number[] = []\\r\\n\\t\\t\\tconst element = findLast(array, (x) => x === 1)\\r\\n\\t\\t\\tshould(element).be.undefined()\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tit(\\\"should work with object arrays\\\", () => {\\r\\n\\t\\t\\tconst array = [\\r\\n\\t\\t\\t\\t{ id: 1, value: \\\"a\\\" },\\r\\n\\t\\t\\t\\t{ id: 2, value: \\\"b\\\" },\\r\\n\\t\\t\\t\\t{ id: 3, value: \\\"a\\\" },\\r\\n\\t\\t\\t]\\r\\n\\t\\t\\tconst element = findLast(array, (x) => x.value === \\\"a\\\")\\r\\n\\t\\t\\tshould(element).not.be.undefined()\\r\\n\\t\\t\\telement!.should.deepEqual({ id: 3, value: \\\"a\\\" })\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tit(\\\"should provide correct index in predicate\\\", () => {\\r\\n\\t\\t\\tconst array = [1, 2, 3]\\r\\n\\t\\t\\tconst indices: number[] = []\\r\\n\\t\\t\\tfindLast(array, (_, index) => {\\r\\n\\t\\t\\t\\tindices.push(index)\\r\\n\\t\\t\\t\\treturn false\\r\\n\\t\\t\\t})\\r\\n\\t\\t\\tindices.should.deepEqual([2, 1, 0]) // Should iterate in reverse\\r\\n\\t\\t})\\r\\n\\t})\\r\\n})\\r\\n\",\"metadata\":{\"size\":2642,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.854Z\",\"createdTime\":\"2024-12-29T05:57:23.854Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":92,\"nonEmptyLines\":80,\"commentLines\":0,\"complexity\":1},\"dependencies\":[\"mocha\",\"./array\"],\"quality\":{\"score\":-60,\"issues\":[],\"duplicateLines\":32,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.409Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":92},\"analysis\":{\"metrics\":{\"lines\":92,\"nonEmptyLines\":80,\"commentLines\":0,\"complexity\":1},\"dependencies\":[\"mocha\",\"./array\"],\"quality\":{\"score\":-60,\"issues\":[],\"duplicateLines\":32,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855409,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.409Z\",\"size\":3557},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\shared\\\\api.ts\",\"content\":{\"content\":\"export type ApiProvider =\\r\\n\\t| \\\"anthropic\\\"\\r\\n\\t| \\\"openrouter\\\"\\r\\n\\t| \\\"bedrock\\\"\\r\\n\\t| \\\"vertex\\\"\\r\\n\\t| \\\"openai\\\"\\r\\n\\t| \\\"ollama\\\"\\r\\n\\t| \\\"lmstudio\\\"\\r\\n\\t| \\\"gemini\\\"\\r\\n\\t| \\\"openai-native\\\"\\r\\n\\t| \\\"deepseek\\\"\\r\\n\\r\\nexport interface ApiHandlerOptions {\\r\\n\\tapiModelId?: string\\r\\n\\tapiKey?: string // anthropic\\r\\n\\tanthropicBaseUrl?: string\\r\\n\\topenRouterApiKey?: string\\r\\n\\topenRouterModelId?: string\\r\\n\\topenRouterModelInfo?: ModelInfo\\r\\n\\tawsAccessKey?: string\\r\\n\\tawsSecretKey?: string\\r\\n\\tawsSessionToken?: string\\r\\n\\tawsRegion?: string\\r\\n\\tawsUseCrossRegionInference?: boolean\\r\\n\\tvertexProjectId?: string\\r\\n\\tvertexRegion?: string\\r\\n\\topenAiBaseUrl?: string\\r\\n\\topenAiApiKey?: string\\r\\n\\topenAiModelId?: string\\r\\n\\tollamaModelId?: string\\r\\n\\tollamaBaseUrl?: string\\r\\n\\tlmStudioModelId?: string\\r\\n\\tlmStudioBaseUrl?: string\\r\\n\\tgeminiApiKey?: string\\r\\n\\topenAiNativeApiKey?: string\\r\\n\\tdeepSeekApiKey?: string\\r\\n\\tazureApiVersion?: string\\r\\n}\\r\\n\\r\\nexport type ApiConfiguration = ApiHandlerOptions & {\\r\\n\\tapiProvider?: ApiProvider\\r\\n}\\r\\n\\r\\n// Models\\r\\n\\r\\nexport interface ModelInfo {\\r\\n\\tmaxTokens?: number\\r\\n\\tcontextWindow?: number\\r\\n\\tsupportsImages?: boolean\\r\\n\\tsupportsComputerUse?: boolean\\r\\n\\tsupportsPromptCache: boolean // this value is hardcoded for now\\r\\n\\tinputPrice?: number\\r\\n\\toutputPrice?: number\\r\\n\\tcacheWritesPrice?: number\\r\\n\\tcacheReadsPrice?: number\\r\\n\\tdescription?: string\\r\\n}\\r\\n\\r\\n// Anthropic\\r\\n// https://docs.anthropic.com/en/docs/about-claude/models // prices updated 2025-01-02\\r\\nexport type AnthropicModelId = keyof typeof anthropicModels\\r\\nexport const anthropicDefaultModelId: AnthropicModelId = \\\"claude-3-5-sonnet-20241022\\\"\\r\\nexport const anthropicModels = {\\r\\n\\t\\\"claude-3-5-sonnet-20241022\\\": {\\r\\n\\t\\tmaxTokens: 8192,\\r\\n\\t\\tcontextWindow: 200_000,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsComputerUse: true,\\r\\n\\t\\tsupportsPromptCache: true,\\r\\n\\t\\tinputPrice: 3.0, // $3 per million input tokens\\r\\n\\t\\toutputPrice: 15.0, // $15 per million output tokens\\r\\n\\t\\tcacheWritesPrice: 3.75, // $3.75 per million tokens\\r\\n\\t\\tcacheReadsPrice: 0.3, // $0.30 per million tokens\\r\\n\\t},\\r\\n\\t\\\"claude-3-5-haiku-20241022\\\": {\\r\\n\\t\\tmaxTokens: 8192,\\r\\n\\t\\tcontextWindow: 200_000,\\r\\n\\t\\tsupportsImages: false,\\r\\n\\t\\tsupportsPromptCache: true,\\r\\n\\t\\tinputPrice: 0.8,\\r\\n\\t\\toutputPrice: 4.0,\\r\\n\\t\\tcacheWritesPrice: 1.0,\\r\\n\\t\\tcacheReadsPrice: 0.08,\\r\\n\\t},\\r\\n\\t\\\"claude-3-opus-20240229\\\": {\\r\\n\\t\\tmaxTokens: 4096,\\r\\n\\t\\tcontextWindow: 200_000,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: true,\\r\\n\\t\\tinputPrice: 15.0,\\r\\n\\t\\toutputPrice: 75.0,\\r\\n\\t\\tcacheWritesPrice: 18.75,\\r\\n\\t\\tcacheReadsPrice: 1.5,\\r\\n\\t},\\r\\n\\t\\\"claude-3-haiku-20240307\\\": {\\r\\n\\t\\tmaxTokens: 4096,\\r\\n\\t\\tcontextWindow: 200_000,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: true,\\r\\n\\t\\tinputPrice: 0.25,\\r\\n\\t\\toutputPrice: 1.25,\\r\\n\\t\\tcacheWritesPrice: 0.3,\\r\\n\\t\\tcacheReadsPrice: 0.03,\\r\\n\\t},\\r\\n} as const satisfies Record<string, ModelInfo> // as const assertion makes the object deeply readonly\\r\\n\\r\\n// AWS Bedrock\\r\\n// https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference.html\\r\\nexport type BedrockModelId = keyof typeof bedrockModels\\r\\nexport const bedrockDefaultModelId: BedrockModelId = \\\"anthropic.claude-3-5-sonnet-20241022-v2:0\\\"\\r\\nexport const bedrockModels = {\\r\\n\\t\\\"anthropic.claude-3-5-sonnet-20241022-v2:0\\\": {\\r\\n\\t\\tmaxTokens: 8192,\\r\\n\\t\\tcontextWindow: 200_000,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsComputerUse: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 3.0,\\r\\n\\t\\toutputPrice: 15.0,\\r\\n\\t},\\r\\n\\t\\\"anthropic.claude-3-5-haiku-20241022-v1:0\\\": {\\r\\n\\t\\tmaxTokens: 8192,\\r\\n\\t\\tcontextWindow: 200_000,\\r\\n\\t\\tsupportsImages: false,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 1.0,\\r\\n\\t\\toutputPrice: 5.0,\\r\\n\\t},\\r\\n\\t\\\"anthropic.claude-3-5-sonnet-20240620-v1:0\\\": {\\r\\n\\t\\tmaxTokens: 8192,\\r\\n\\t\\tcontextWindow: 200_000,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 3.0,\\r\\n\\t\\toutputPrice: 15.0,\\r\\n\\t},\\r\\n\\t\\\"anthropic.claude-3-opus-20240229-v1:0\\\": {\\r\\n\\t\\tmaxTokens: 4096,\\r\\n\\t\\tcontextWindow: 200_000,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 15.0,\\r\\n\\t\\toutputPrice: 75.0,\\r\\n\\t},\\r\\n\\t\\\"anthropic.claude-3-sonnet-20240229-v1:0\\\": {\\r\\n\\t\\tmaxTokens: 4096,\\r\\n\\t\\tcontextWindow: 200_000,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 3.0,\\r\\n\\t\\toutputPrice: 15.0,\\r\\n\\t},\\r\\n\\t\\\"anthropic.claude-3-haiku-20240307-v1:0\\\": {\\r\\n\\t\\tmaxTokens: 4096,\\r\\n\\t\\tcontextWindow: 200_000,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 0.25,\\r\\n\\t\\toutputPrice: 1.25,\\r\\n\\t},\\r\\n} as const satisfies Record<string, ModelInfo>\\r\\n\\r\\n// OpenRouter\\r\\n// https://openrouter.ai/models?order=newest&supported_parameters=tools\\r\\nexport const openRouterDefaultModelId = \\\"anthropic/claude-3.5-sonnet:beta\\\" // will always exist in openRouterModels\\r\\nexport const openRouterDefaultModelInfo: ModelInfo = {\\r\\n\\tmaxTokens: 8192,\\r\\n\\tcontextWindow: 200_000,\\r\\n\\tsupportsImages: true,\\r\\n\\tsupportsComputerUse: true,\\r\\n\\tsupportsPromptCache: true,\\r\\n\\tinputPrice: 3.0,\\r\\n\\toutputPrice: 15.0,\\r\\n\\tcacheWritesPrice: 3.75,\\r\\n\\tcacheReadsPrice: 0.3,\\r\\n\\tdescription:\\r\\n\\t\\t\\\"The new Claude 3.5 Sonnet delivers better-than-Opus capabilities, faster-than-Sonnet speeds, at the same Sonnet prices. Sonnet is particularly good at:\\\\n\\\\n- Coding: New Sonnet scores ~49% on SWE-Bench Verified, higher than the last best score, and without any fancy prompt scaffolding\\\\n- Data science: Augments human data science expertise; navigates unstructured data while using multiple tools for insights\\\\n- Visual processing: excelling at interpreting charts, graphs, and images, accurately transcribing text to derive insights beyond just the text alone\\\\n- Agentic tasks: exceptional tool use, making it great at agentic tasks (i.e. complex, multi-step problem solving tasks that require engaging with other systems)\\\\n\\\\n#multimodal\\\\n\\\\n_This is a faster endpoint, made available in collaboration with Anthropic, that is self-moderated: response moderation happens on the provider's side instead of OpenRouter's. For requests that pass moderation, it's identical to the [Standard](/anthropic/claude-3.5-sonnet) variant._\\\",\\r\\n}\\r\\n\\r\\n// Vertex AI\\r\\n// https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-claude\\r\\nexport type VertexModelId = keyof typeof vertexModels\\r\\nexport const vertexDefaultModelId: VertexModelId = \\\"claude-3-5-sonnet-v2@20241022\\\"\\r\\nexport const vertexModels = {\\r\\n\\t\\\"claude-3-5-sonnet-v2@20241022\\\": {\\r\\n\\t\\tmaxTokens: 8192,\\r\\n\\t\\tcontextWindow: 200_000,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsComputerUse: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 3.0,\\r\\n\\t\\toutputPrice: 15.0,\\r\\n\\t},\\r\\n\\t\\\"claude-3-5-sonnet@20240620\\\": {\\r\\n\\t\\tmaxTokens: 8192,\\r\\n\\t\\tcontextWindow: 200_000,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 3.0,\\r\\n\\t\\toutputPrice: 15.0,\\r\\n\\t},\\r\\n\\t\\\"claude-3-5-haiku@20241022\\\": {\\r\\n\\t\\tmaxTokens: 8192,\\r\\n\\t\\tcontextWindow: 200_000,\\r\\n\\t\\tsupportsImages: false,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 1.0,\\r\\n\\t\\toutputPrice: 5.0,\\r\\n\\t},\\r\\n\\t\\\"claude-3-opus@20240229\\\": {\\r\\n\\t\\tmaxTokens: 4096,\\r\\n\\t\\tcontextWindow: 200_000,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 15.0,\\r\\n\\t\\toutputPrice: 75.0,\\r\\n\\t},\\r\\n\\t\\\"claude-3-haiku@20240307\\\": {\\r\\n\\t\\tmaxTokens: 4096,\\r\\n\\t\\tcontextWindow: 200_000,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 0.25,\\r\\n\\t\\toutputPrice: 1.25,\\r\\n\\t},\\r\\n} as const satisfies Record<string, ModelInfo>\\r\\n\\r\\nexport const openAiModelInfoSaneDefaults: ModelInfo = {\\r\\n\\tmaxTokens: -1,\\r\\n\\tcontextWindow: 128_000,\\r\\n\\tsupportsImages: true,\\r\\n\\tsupportsPromptCache: false,\\r\\n\\tinputPrice: 0,\\r\\n\\toutputPrice: 0,\\r\\n}\\r\\n\\r\\n// Gemini\\r\\n// https://ai.google.dev/gemini-api/docs/models/gemini\\r\\nexport type GeminiModelId = keyof typeof geminiModels\\r\\nexport const geminiDefaultModelId: GeminiModelId = \\\"gemini-2.0-flash-thinking-exp-1219\\\"\\r\\nexport const geminiModels = {\\r\\n\\t\\\"gemini-2.0-flash-thinking-exp-1219\\\": {\\r\\n\\t\\tmaxTokens: 8192,\\r\\n\\t\\tcontextWindow: 32_767,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 0,\\r\\n\\t\\toutputPrice: 0,\\r\\n\\t},\\r\\n\\t\\\"gemini-2.0-flash-exp\\\": {\\r\\n\\t\\tmaxTokens: 8192,\\r\\n\\t\\tcontextWindow: 1_048_576,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 0,\\r\\n\\t\\toutputPrice: 0,\\r\\n\\t},\\r\\n\\t\\\"gemini-exp-1206\\\": {\\r\\n\\t\\tmaxTokens: 8192,\\r\\n\\t\\tcontextWindow: 2_097_152,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 0,\\r\\n\\t\\toutputPrice: 0,\\r\\n\\t},\\r\\n\\t\\\"gemini-1.5-flash-002\\\": {\\r\\n\\t\\tmaxTokens: 8192,\\r\\n\\t\\tcontextWindow: 1_048_576,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 0,\\r\\n\\t\\toutputPrice: 0,\\r\\n\\t},\\r\\n\\t\\\"gemini-1.5-flash-exp-0827\\\": {\\r\\n\\t\\tmaxTokens: 8192,\\r\\n\\t\\tcontextWindow: 1_048_576,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 0,\\r\\n\\t\\toutputPrice: 0,\\r\\n\\t},\\r\\n\\t\\\"gemini-1.5-flash-8b-exp-0827\\\": {\\r\\n\\t\\tmaxTokens: 8192,\\r\\n\\t\\tcontextWindow: 1_048_576,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 0,\\r\\n\\t\\toutputPrice: 0,\\r\\n\\t},\\r\\n\\t\\\"gemini-1.5-pro-002\\\": {\\r\\n\\t\\tmaxTokens: 8192,\\r\\n\\t\\tcontextWindow: 2_097_152,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 0,\\r\\n\\t\\toutputPrice: 0,\\r\\n\\t},\\r\\n\\t\\\"gemini-1.5-pro-exp-0827\\\": {\\r\\n\\t\\tmaxTokens: 8192,\\r\\n\\t\\tcontextWindow: 2_097_152,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 0,\\r\\n\\t\\toutputPrice: 0,\\r\\n\\t},\\r\\n} as const satisfies Record<string, ModelInfo>\\r\\n\\r\\n// OpenAI Native\\r\\n// https://openai.com/api/pricing/\\r\\nexport type OpenAiNativeModelId = keyof typeof openAiNativeModels\\r\\nexport const openAiNativeDefaultModelId: OpenAiNativeModelId = \\\"gpt-4o\\\"\\r\\nexport const openAiNativeModels = {\\r\\n\\t// don't support tool use yet\\r\\n\\to1: {\\r\\n\\t\\tmaxTokens: 100_000,\\r\\n\\t\\tcontextWindow: 200_000,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 15,\\r\\n\\t\\toutputPrice: 60,\\r\\n\\t},\\r\\n\\t\\\"o1-preview\\\": {\\r\\n\\t\\tmaxTokens: 32_768,\\r\\n\\t\\tcontextWindow: 128_000,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 15,\\r\\n\\t\\toutputPrice: 60,\\r\\n\\t},\\r\\n\\t\\\"o1-mini\\\": {\\r\\n\\t\\tmaxTokens: 65_536,\\r\\n\\t\\tcontextWindow: 128_000,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 3,\\r\\n\\t\\toutputPrice: 12,\\r\\n\\t},\\r\\n\\t\\\"gpt-4o\\\": {\\r\\n\\t\\tmaxTokens: 4_096,\\r\\n\\t\\tcontextWindow: 128_000,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 5,\\r\\n\\t\\toutputPrice: 15,\\r\\n\\t},\\r\\n\\t\\\"gpt-4o-mini\\\": {\\r\\n\\t\\tmaxTokens: 16_384,\\r\\n\\t\\tcontextWindow: 128_000,\\r\\n\\t\\tsupportsImages: true,\\r\\n\\t\\tsupportsPromptCache: false,\\r\\n\\t\\tinputPrice: 0.15,\\r\\n\\t\\toutputPrice: 0.6,\\r\\n\\t},\\r\\n} as const satisfies Record<string, ModelInfo>\\r\\n\\r\\n// Azure OpenAI\\r\\n// https://learn.microsoft.com/en-us/azure/ai-services/openai/api-version-deprecation\\r\\n// https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#api-specs\\r\\nexport const azureOpenAiDefaultApiVersion = \\\"2024-08-01-preview\\\"\\r\\n\\r\\n// DeepSeek\\r\\n// https://api-docs.deepseek.com/quick_start/pricing\\r\\nexport type DeepSeekModelId = keyof typeof deepSeekModels\\r\\nexport const deepSeekDefaultModelId: DeepSeekModelId = \\\"deepseek-chat\\\"\\r\\nexport const deepSeekModels = {\\r\\n\\t\\\"deepseek-chat\\\": {\\r\\n\\t\\tmaxTokens: 8_000,\\r\\n\\t\\tcontextWindow: 64_000,\\r\\n\\t\\tsupportsImages: false,\\r\\n\\t\\tsupportsPromptCache: true, // supports context caching, but not in the way anthropic does it (deepseek reports input tokens and reads/writes in the same usage report) FIXME: we need to show users cache stats how deepseek does it\\r\\n\\t\\tinputPrice: 0, // technically there is no input price, it's all either a cache hit or miss (ApiOptions will not show this)\\r\\n\\t\\toutputPrice: 0.28,\\r\\n\\t\\tcacheWritesPrice: 0.14,\\r\\n\\t\\tcacheReadsPrice: 0.014,\\r\\n\\t},\\r\\n} as const satisfies Record<string, ModelInfo>\\r\\n\",\"metadata\":{\"size\":11221,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.964Z\",\"createdTime\":\"2025-01-15T15:26:07.964Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":377,\"nonEmptyLines\":363,\"commentLines\":19,\"complexity\":36},\"dependencies\":[],\"quality\":{\"score\":-815,\"issues\":[],\"duplicateLines\":181,\"longLines\":5,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.407Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":377},\"analysis\":{\"metrics\":{\"lines\":377,\"nonEmptyLines\":363,\"commentLines\":19,\"complexity\":36},\"dependencies\":[],\"quality\":{\"score\":-815,\"issues\":[],\"duplicateLines\":181,\"longLines\":5,\"complexFunctions\":0}},\"lastModified\":1737046855408,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.408Z\",\"size\":13036},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\services\\\\tree-sitter\\\\queries\\\\typescript.ts\",\"content\":{\"content\":\"/*\\r\\n- function signatures and declarations\\r\\n- method signatures and definitions\\r\\n- abstract method signatures\\r\\n- class declarations (including abstract classes)\\r\\n- module declarations\\r\\n*/\\r\\nexport default `\\r\\n(function_signature\\r\\n name: (identifier) @name.definition.function) @definition.function\\r\\n\\r\\n(method_signature\\r\\n name: (property_identifier) @name.definition.method) @definition.method\\r\\n\\r\\n(abstract_method_signature\\r\\n name: (property_identifier) @name.definition.method) @definition.method\\r\\n\\r\\n(abstract_class_declaration\\r\\n name: (type_identifier) @name.definition.class) @definition.class\\r\\n\\r\\n(module\\r\\n name: (identifier) @name.definition.module) @definition.module\\r\\n\\r\\n(function_declaration\\r\\n name: (identifier) @name.definition.function) @definition.function\\r\\n\\r\\n(method_definition\\r\\n name: (property_identifier) @name.definition.method) @definition.method\\r\\n\\r\\n(class_declaration\\r\\n name: (type_identifier) @name.definition.class) @definition.class\\r\\n`\\r\\n\",\"metadata\":{\"size\":962,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.854Z\",\"createdTime\":\"2024-12-29T05:57:23.854Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":33,\"nonEmptyLines\":25,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":80,\"issues\":[],\"duplicateLines\":4,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.406Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":33},\"analysis\":{\"metrics\":{\"lines\":33,\"nonEmptyLines\":25,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":80,\"issues\":[],\"duplicateLines\":4,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855406,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.406Z\",\"size\":1479},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\services\\\\tree-sitter\\\\queries\\\\swift.ts\",\"content\":{\"content\":\"/*\\r\\n- class declarations\\r\\n- method declarations (including initializers and deinitializers)\\r\\n- property declarations\\r\\n- function declarations\\r\\n*/\\r\\nexport default `\\r\\n(class_declaration\\r\\n name: (type_identifier) @name) @definition.class\\r\\n\\r\\n(protocol_declaration\\r\\n name: (type_identifier) @name) @definition.interface\\r\\n\\r\\n(class_declaration\\r\\n (class_body\\r\\n [\\r\\n (function_declaration\\r\\n name: (simple_identifier) @name\\r\\n )\\r\\n (subscript_declaration\\r\\n (parameter (simple_identifier) @name)\\r\\n )\\r\\n (init_declaration \\\"init\\\" @name)\\r\\n (deinit_declaration \\\"deinit\\\" @name)\\r\\n ]\\r\\n )\\r\\n) @definition.method\\r\\n\\r\\n(class_declaration\\r\\n (class_body\\r\\n [\\r\\n (property_declaration\\r\\n (pattern (simple_identifier) @name)\\r\\n )\\r\\n ]\\r\\n )\\r\\n) @definition.property\\r\\n\\r\\n(property_declaration\\r\\n (pattern (simple_identifier) @name)\\r\\n) @definition.property\\r\\n\\r\\n(function_declaration\\r\\n name: (simple_identifier) @name) @definition.function\\r\\n`\\r\\n\",\"metadata\":{\"size\":1079,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.854Z\",\"createdTime\":\"2024-12-29T05:57:23.854Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":46,\"nonEmptyLines\":40,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":35,\"issues\":[],\"duplicateLines\":13,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.405Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":46},\"analysis\":{\"metrics\":{\"lines\":46,\"nonEmptyLines\":40,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":35,\"issues\":[],\"duplicateLines\":13,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855405,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.405Z\",\"size\":1628},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\services\\\\tree-sitter\\\\queries\\\\rust.ts\",\"content\":{\"content\":\"/*\\r\\n- struct definitions\\r\\n- method definitions\\r\\n- function definitions\\r\\n*/\\r\\nexport default `\\r\\n(struct_item\\r\\n name: (type_identifier) @name.definition.class) @definition.class\\r\\n\\r\\n(declaration_list\\r\\n (function_item\\r\\n name: (identifier) @name.definition.method)) @definition.method\\r\\n\\r\\n(function_item\\r\\n name: (identifier) @name.definition.function) @definition.function\\r\\n`\\r\\n\",\"metadata\":{\"size\":386,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.854Z\",\"createdTime\":\"2024-12-29T05:57:23.854Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":17,\"nonEmptyLines\":14,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":95,\"issues\":[],\"duplicateLines\":1,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.404Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":17},\"analysis\":{\"metrics\":{\"lines\":17,\"nonEmptyLines\":14,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":95,\"issues\":[],\"duplicateLines\":1,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855404,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.404Z\",\"size\":871},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\services\\\\tree-sitter\\\\queries\\\\ruby.ts\",\"content\":{\"content\":\"/*\\r\\n- method definitions (including singleton methods and aliases, with associated comments)\\r\\n- class definitions (including singleton classes, with associated comments)\\r\\n- module definitions\\r\\n*/\\r\\nexport default `\\r\\n(\\r\\n (comment)* @doc\\r\\n .\\r\\n [\\r\\n (method\\r\\n name: (_) @name.definition.method) @definition.method\\r\\n (singleton_method\\r\\n name: (_) @name.definition.method) @definition.method\\r\\n ]\\r\\n (#strip! @doc \\\"^#\\\\\\\\s*\\\")\\r\\n (#select-adjacent! @doc @definition.method)\\r\\n)\\r\\n\\r\\n(alias\\r\\n name: (_) @name.definition.method) @definition.method\\r\\n\\r\\n(\\r\\n (comment)* @doc\\r\\n .\\r\\n [\\r\\n (class\\r\\n name: [\\r\\n (constant) @name.definition.class\\r\\n (scope_resolution\\r\\n name: (_) @name.definition.class)\\r\\n ]) @definition.class\\r\\n (singleton_class\\r\\n value: [\\r\\n (constant) @name.definition.class\\r\\n (scope_resolution\\r\\n name: (_) @name.definition.class)\\r\\n ]) @definition.class\\r\\n ]\\r\\n (#strip! @doc \\\"^#\\\\\\\\s*\\\")\\r\\n (#select-adjacent! @doc @definition.class)\\r\\n)\\r\\n\\r\\n(\\r\\n (module\\r\\n name: [\\r\\n (constant) @name.definition.module\\r\\n (scope_resolution\\r\\n name: (_) @name.definition.module)\\r\\n ]) @definition.module\\r\\n)\\r\\n`\\r\\n\",\"metadata\":{\"size\":1193,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.854Z\",\"createdTime\":\"2024-12-29T05:57:23.853Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":53,\"nonEmptyLines\":49,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":15,\"issues\":[],\"duplicateLines\":17,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.403Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":53},\"analysis\":{\"metrics\":{\"lines\":53,\"nonEmptyLines\":49,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":15,\"issues\":[],\"duplicateLines\":17,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855403,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.403Z\",\"size\":1760},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\services\\\\tree-sitter\\\\queries\\\\python.ts\",\"content\":{\"content\":\"/*\\r\\n- class definitions\\r\\n- function definitions\\r\\n*/\\r\\nexport default `\\r\\n(class_definition\\r\\n name: (identifier) @name.definition.class) @definition.class\\r\\n\\r\\n(function_definition\\r\\n name: (identifier) @name.definition.function) @definition.function\\r\\n`\\r\\n\",\"metadata\":{\"size\":251,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.853Z\",\"createdTime\":\"2024-12-29T05:57:23.853Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":12,\"nonEmptyLines\":10,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":100,\"issues\":[],\"duplicateLines\":0,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.401Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":12},\"analysis\":{\"metrics\":{\"lines\":12,\"nonEmptyLines\":10,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":100,\"issues\":[],\"duplicateLines\":0,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855402,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.402Z\",\"size\":727},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\services\\\\tree-sitter\\\\queries\\\\php.ts\",\"content\":{\"content\":\"/*\\r\\n- class declarations\\r\\n- function definitions\\r\\n- method declarations\\r\\n*/\\r\\nexport default `\\r\\n(class_declaration\\r\\n name: (name) @name.definition.class) @definition.class\\r\\n\\r\\n(function_definition\\r\\n name: (name) @name.definition.function) @definition.function\\r\\n\\r\\n(method_declaration\\r\\n name: (name) @name.definition.function) @definition.function\\r\\n`\\r\\n\",\"metadata\":{\"size\":351,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.853Z\",\"createdTime\":\"2024-12-29T05:57:23.853Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":16,\"nonEmptyLines\":13,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":95,\"issues\":[],\"duplicateLines\":1,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.400Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":16},\"analysis\":{\"metrics\":{\"lines\":16,\"nonEmptyLines\":13,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":95,\"issues\":[],\"duplicateLines\":1,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855400,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.400Z\",\"size\":834},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\services\\\\tree-sitter\\\\queries\\\\javascript.ts\",\"content\":{\"content\":\"/*\\r\\n- class definitions\\r\\n- method definitions\\r\\n- named function declarations\\r\\n- arrow functions and function expressions assigned to variables\\r\\n*/\\r\\nexport default `\\r\\n(\\r\\n (comment)* @doc\\r\\n .\\r\\n (method_definition\\r\\n name: (property_identifier) @name) @definition.method\\r\\n (#not-eq? @name \\\"constructor\\\")\\r\\n (#strip! @doc \\\"^[\\\\\\\\s\\\\\\\\*/]+|^[\\\\\\\\s\\\\\\\\*/]$\\\")\\r\\n (#select-adjacent! @doc @definition.method)\\r\\n)\\r\\n\\r\\n(\\r\\n (comment)* @doc\\r\\n .\\r\\n [\\r\\n (class\\r\\n name: (_) @name)\\r\\n (class_declaration\\r\\n name: (_) @name)\\r\\n ] @definition.class\\r\\n (#strip! @doc \\\"^[\\\\\\\\s\\\\\\\\*/]+|^[\\\\\\\\s\\\\\\\\*/]$\\\")\\r\\n (#select-adjacent! @doc @definition.class)\\r\\n)\\r\\n\\r\\n(\\r\\n (comment)* @doc\\r\\n .\\r\\n [\\r\\n (function_declaration\\r\\n name: (identifier) @name)\\r\\n (generator_function_declaration\\r\\n name: (identifier) @name)\\r\\n ] @definition.function\\r\\n (#strip! @doc \\\"^[\\\\\\\\s\\\\\\\\*/]+|^[\\\\\\\\s\\\\\\\\*/]$\\\")\\r\\n (#select-adjacent! @doc @definition.function)\\r\\n)\\r\\n\\r\\n(\\r\\n (comment)* @doc\\r\\n .\\r\\n (lexical_declaration\\r\\n (variable_declarator\\r\\n name: (identifier) @name\\r\\n value: [(arrow_function) (function_expression)]) @definition.function)\\r\\n (#strip! @doc \\\"^[\\\\\\\\s\\\\\\\\*/]+|^[\\\\\\\\s\\\\\\\\*/]$\\\")\\r\\n (#select-adjacent! @doc @definition.function)\\r\\n)\\r\\n\\r\\n(\\r\\n (comment)* @doc\\r\\n .\\r\\n (variable_declaration\\r\\n (variable_declarator\\r\\n name: (identifier) @name\\r\\n value: [(arrow_function) (function_expression)]) @definition.function)\\r\\n (#strip! @doc \\\"^[\\\\\\\\s\\\\\\\\*/]+|^[\\\\\\\\s\\\\\\\\*/]$\\\")\\r\\n (#select-adjacent! @doc @definition.function)\\r\\n)\\r\\n`\\r\\n\",\"metadata\":{\"size\":1496,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.853Z\",\"createdTime\":\"2024-12-29T05:57:23.852Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":66,\"nonEmptyLines\":61,\"commentLines\":1,\"complexity\":2},\"dependencies\":[],\"quality\":{\"score\":-40,\"issues\":[],\"duplicateLines\":28,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.399Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":66},\"analysis\":{\"metrics\":{\"lines\":66,\"nonEmptyLines\":61,\"commentLines\":1,\"complexity\":2},\"dependencies\":[],\"quality\":{\"score\":-40,\"issues\":[],\"duplicateLines\":28,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855399,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.399Z\",\"size\":2134},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\services\\\\tree-sitter\\\\queries\\\\java.ts\",\"content\":{\"content\":\"/*\\r\\n- class declarations\\r\\n- method declarations\\r\\n- interface declarations\\r\\n*/\\r\\nexport default `\\r\\n(class_declaration\\r\\n name: (identifier) @name.definition.class) @definition.class\\r\\n\\r\\n(method_declaration\\r\\n name: (identifier) @name.definition.method) @definition.method\\r\\n\\r\\n(interface_declaration\\r\\n name: (identifier) @name.definition.interface) @definition.interface\\r\\n`\\r\\n\",\"metadata\":{\"size\":371,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.852Z\",\"createdTime\":\"2024-12-29T05:57:23.852Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":16,\"nonEmptyLines\":13,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":100,\"issues\":[],\"duplicateLines\":0,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.398Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":16},\"analysis\":{\"metrics\":{\"lines\":16,\"nonEmptyLines\":13,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":100,\"issues\":[],\"duplicateLines\":0,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855398,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.398Z\",\"size\":855},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\services\\\\tree-sitter\\\\queries\\\\index.ts\",\"content\":{\"content\":\"export { default as phpQuery } from \\\"./php\\\"\\r\\nexport { default as typescriptQuery } from \\\"./typescript\\\"\\r\\nexport { default as pythonQuery } from \\\"./python\\\"\\r\\nexport { default as javascriptQuery } from \\\"./javascript\\\"\\r\\nexport { default as javaQuery } from \\\"./java\\\"\\r\\nexport { default as rustQuery } from \\\"./rust\\\"\\r\\nexport { default as rubyQuery } from \\\"./ruby\\\"\\r\\nexport { default as cppQuery } from \\\"./cpp\\\"\\r\\nexport { default as cQuery } from \\\"./c\\\"\\r\\nexport { default as csharpQuery } from \\\"./c-sharp\\\"\\r\\nexport { default as goQuery } from \\\"./go\\\"\\r\\nexport { default as swiftQuery } from \\\"./swift\\\"\\r\\n\",\"metadata\":{\"size\":585,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.852Z\",\"createdTime\":\"2024-12-29T05:57:23.852Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":13,\"nonEmptyLines\":12,\"commentLines\":0,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":100,\"issues\":[],\"duplicateLines\":0,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.397Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":13},\"analysis\":{\"metrics\":{\"lines\":13,\"nonEmptyLines\":12,\"commentLines\":0,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":100,\"issues\":[],\"duplicateLines\":0,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855397,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.397Z\",\"size\":1087},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\services\\\\tree-sitter\\\\queries\\\\go.ts\",\"content\":{\"content\":\"/*\\r\\n- function declarations (with associated comments)\\r\\n- method declarations (with associated comments)\\r\\n- type specifications\\r\\n*/\\r\\nexport default `\\r\\n(\\r\\n (comment)* @doc\\r\\n .\\r\\n (function_declaration\\r\\n name: (identifier) @name.definition.function) @definition.function\\r\\n (#strip! @doc \\\"^//\\\\\\\\s*\\\")\\r\\n (#set-adjacent! @doc @definition.function)\\r\\n)\\r\\n\\r\\n(\\r\\n (comment)* @doc\\r\\n .\\r\\n (method_declaration\\r\\n name: (field_identifier) @name.definition.method) @definition.method\\r\\n (#strip! @doc \\\"^//\\\\\\\\s*\\\")\\r\\n (#set-adjacent! @doc @definition.method)\\r\\n)\\r\\n\\r\\n(type_spec\\r\\n name: (type_identifier) @name.definition.type) @definition.type\\r\\n`\\r\\n\",\"metadata\":{\"size\":637,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.852Z\",\"createdTime\":\"2024-12-29T05:57:23.852Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":28,\"nonEmptyLines\":25,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":75,\"issues\":[],\"duplicateLines\":5,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.396Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":28},\"analysis\":{\"metrics\":{\"lines\":28,\"nonEmptyLines\":25,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":75,\"issues\":[],\"duplicateLines\":5,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855396,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.396Z\",\"size\":1152},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\services\\\\tree-sitter\\\\queries\\\\cpp.ts\",\"content\":{\"content\":\"/*\\r\\n- struct declarations\\r\\n- union declarations\\r\\n- function declarations\\r\\n- method declarations (with namespace scope)\\r\\n- typedef declarations\\r\\n- class declarations\\r\\n*/\\r\\nexport default `\\r\\n(struct_specifier name: (type_identifier) @name.definition.class body:(_)) @definition.class\\r\\n\\r\\n(declaration type: (union_specifier name: (type_identifier) @name.definition.class)) @definition.class\\r\\n\\r\\n(function_declarator declarator: (identifier) @name.definition.function) @definition.function\\r\\n\\r\\n(function_declarator declarator: (field_identifier) @name.definition.function) @definition.function\\r\\n\\r\\n(function_declarator declarator: (qualified_identifier scope: (namespace_identifier) @scope name: (identifier) @name.definition.method)) @definition.method\\r\\n\\r\\n(type_definition declarator: (type_identifier) @name.definition.type) @definition.type\\r\\n\\r\\n(class_specifier name: (type_identifier) @name.definition.class) @definition.class\\r\\n`\\r\\n\",\"metadata\":{\"size\":926,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.852Z\",\"createdTime\":\"2024-12-29T05:57:23.852Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":24,\"nonEmptyLines\":17,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":96,\"issues\":[],\"duplicateLines\":0,\"longLines\":2,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.395Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":24},\"analysis\":{\"metrics\":{\"lines\":24,\"nonEmptyLines\":17,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":96,\"issues\":[],\"duplicateLines\":0,\"longLines\":2,\"complexFunctions\":0}},\"lastModified\":1737046855395,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.395Z\",\"size\":1425},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\services\\\\tree-sitter\\\\queries\\\\c.ts\",\"content\":{\"content\":\"/*\\r\\n- struct declarations\\r\\n- union declarations\\r\\n- function declarations\\r\\n- typedef declarations\\r\\n*/\\r\\nexport default `\\r\\n(struct_specifier name: (type_identifier) @name.definition.class body:(_)) @definition.class\\r\\n\\r\\n(declaration type: (union_specifier name: (type_identifier) @name.definition.class)) @definition.class\\r\\n\\r\\n(function_declarator declarator: (identifier) @name.definition.function) @definition.function\\r\\n\\r\\n(type_definition declarator: (type_identifier) @name.definition.type) @definition.type\\r\\n`\\r\\n\",\"metadata\":{\"size\":510,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.852Z\",\"createdTime\":\"2024-12-29T05:57:23.852Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":16,\"nonEmptyLines\":12,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":98,\"issues\":[],\"duplicateLines\":0,\"longLines\":1,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.394Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":16},\"analysis\":{\"metrics\":{\"lines\":16,\"nonEmptyLines\":12,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":98,\"issues\":[],\"duplicateLines\":0,\"longLines\":1,\"complexFunctions\":0}},\"lastModified\":1737046855395,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.395Z\",\"size\":993},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\services\\\\tree-sitter\\\\queries\\\\c-sharp.ts\",\"content\":{\"content\":\"/*\\r\\n- class declarations\\r\\n- interface declarations\\r\\n- method declarations\\r\\n- namespace declarations\\r\\n*/\\r\\nexport default `\\r\\n(class_declaration\\r\\n name: (identifier) @name.definition.class\\r\\n) @definition.class\\r\\n\\r\\n(interface_declaration\\r\\n name: (identifier) @name.definition.interface\\r\\n) @definition.interface\\r\\n\\r\\n(method_declaration\\r\\n name: (identifier) @name.definition.method\\r\\n) @definition.method\\r\\n\\r\\n(namespace_declaration\\r\\n name: (identifier) @name.definition.module\\r\\n) @definition.module\\r\\n`\\r\\n\",\"metadata\":{\"size\":493,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.852Z\",\"createdTime\":\"2024-12-29T05:57:23.852Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":24,\"nonEmptyLines\":20,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":100,\"issues\":[],\"duplicateLines\":0,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.394Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":24},\"analysis\":{\"metrics\":{\"lines\":24,\"nonEmptyLines\":20,\"commentLines\":1,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":100,\"issues\":[],\"duplicateLines\":0,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855394,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.394Z\",\"size\":993},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\services\\\\tree-sitter\\\\languageParser.ts\",\"content\":{\"content\":\"import * as path from \\\"path\\\"\\r\\nimport Parser from \\\"web-tree-sitter\\\"\\r\\nimport {\\r\\n\\tjavascriptQuery,\\r\\n\\ttypescriptQuery,\\r\\n\\tpythonQuery,\\r\\n\\trustQuery,\\r\\n\\tgoQuery,\\r\\n\\tcppQuery,\\r\\n\\tcQuery,\\r\\n\\tcsharpQuery,\\r\\n\\trubyQuery,\\r\\n\\tjavaQuery,\\r\\n\\tphpQuery,\\r\\n\\tswiftQuery,\\r\\n} from \\\"./queries\\\"\\r\\n\\r\\nexport interface LanguageParser {\\r\\n\\t[key: string]: {\\r\\n\\t\\tparser: Parser\\r\\n\\t\\tquery: Parser.Query\\r\\n\\t}\\r\\n}\\r\\n\\r\\nasync function loadLanguage(langName: string) {\\r\\n\\treturn await Parser.Language.load(path.join(__dirname, `tree-sitter-${langName}.wasm`))\\r\\n}\\r\\n\\r\\nlet isParserInitialized = false\\r\\n\\r\\nasync function initializeParser() {\\r\\n\\tif (!isParserInitialized) {\\r\\n\\t\\tawait Parser.init()\\r\\n\\t\\tisParserInitialized = true\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/*\\r\\nUsing node bindings for tree-sitter is problematic in vscode extensions \\r\\nbecause of incompatibility with electron. Going the .wasm route has the \\r\\nadvantage of not having to build for multiple architectures.\\r\\n\\r\\nWe use web-tree-sitter and tree-sitter-wasms which provides auto-updating prebuilt WASM binaries for tree-sitter's language parsers.\\r\\n\\r\\nThis function loads WASM modules for relevant language parsers based on input files:\\r\\n1. Extracts unique file extensions\\r\\n2. Maps extensions to language names\\r\\n3. Loads corresponding WASM files (containing grammar rules)\\r\\n4. Uses WASM modules to initialize tree-sitter parsers\\r\\n\\r\\nThis approach optimizes performance by loading only necessary parsers once for all relevant files.\\r\\n\\r\\nSources:\\r\\n- https://github.com/tree-sitter/node-tree-sitter/issues/169\\r\\n- https://github.com/tree-sitter/node-tree-sitter/issues/168\\r\\n- https://github.com/Gregoor/tree-sitter-wasms/blob/main/README.md\\r\\n- https://github.com/tree-sitter/tree-sitter/blob/master/lib/binding_web/README.md\\r\\n- https://github.com/tree-sitter/tree-sitter/blob/master/lib/binding_web/test/query-test.js\\r\\n*/\\r\\nexport async function loadRequiredLanguageParsers(filesToParse: string[]): Promise<LanguageParser> {\\r\\n\\tawait initializeParser()\\r\\n\\tconst extensionsToLoad = new Set(filesToParse.map((file) => path.extname(file).toLowerCase().slice(1)))\\r\\n\\tconst parsers: LanguageParser = {}\\r\\n\\tfor (const ext of extensionsToLoad) {\\r\\n\\t\\tlet language: Parser.Language\\r\\n\\t\\tlet query: Parser.Query\\r\\n\\t\\tswitch (ext) {\\r\\n\\t\\t\\tcase \\\"js\\\":\\r\\n\\t\\t\\tcase \\\"jsx\\\":\\r\\n\\t\\t\\t\\tlanguage = await loadLanguage(\\\"javascript\\\")\\r\\n\\t\\t\\t\\tquery = language.query(javascriptQuery)\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\tcase \\\"ts\\\":\\r\\n\\t\\t\\t\\tlanguage = await loadLanguage(\\\"typescript\\\")\\r\\n\\t\\t\\t\\tquery = language.query(typescriptQuery)\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\tcase \\\"tsx\\\":\\r\\n\\t\\t\\t\\tlanguage = await loadLanguage(\\\"tsx\\\")\\r\\n\\t\\t\\t\\tquery = language.query(typescriptQuery)\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\tcase \\\"py\\\":\\r\\n\\t\\t\\t\\tlanguage = await loadLanguage(\\\"python\\\")\\r\\n\\t\\t\\t\\tquery = language.query(pythonQuery)\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\tcase \\\"rs\\\":\\r\\n\\t\\t\\t\\tlanguage = await loadLanguage(\\\"rust\\\")\\r\\n\\t\\t\\t\\tquery = language.query(rustQuery)\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\tcase \\\"go\\\":\\r\\n\\t\\t\\t\\tlanguage = await loadLanguage(\\\"go\\\")\\r\\n\\t\\t\\t\\tquery = language.query(goQuery)\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\tcase \\\"cpp\\\":\\r\\n\\t\\t\\tcase \\\"hpp\\\":\\r\\n\\t\\t\\t\\tlanguage = await loadLanguage(\\\"cpp\\\")\\r\\n\\t\\t\\t\\tquery = language.query(cppQuery)\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\tcase \\\"c\\\":\\r\\n\\t\\t\\tcase \\\"h\\\":\\r\\n\\t\\t\\t\\tlanguage = await loadLanguage(\\\"c\\\")\\r\\n\\t\\t\\t\\tquery = language.query(cQuery)\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\tcase \\\"cs\\\":\\r\\n\\t\\t\\t\\tlanguage = await loadLanguage(\\\"c_sharp\\\")\\r\\n\\t\\t\\t\\tquery = language.query(csharpQuery)\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\tcase \\\"rb\\\":\\r\\n\\t\\t\\t\\tlanguage = await loadLanguage(\\\"ruby\\\")\\r\\n\\t\\t\\t\\tquery = language.query(rubyQuery)\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\tcase \\\"java\\\":\\r\\n\\t\\t\\t\\tlanguage = await loadLanguage(\\\"java\\\")\\r\\n\\t\\t\\t\\tquery = language.query(javaQuery)\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\tcase \\\"php\\\":\\r\\n\\t\\t\\t\\tlanguage = await loadLanguage(\\\"php\\\")\\r\\n\\t\\t\\t\\tquery = language.query(phpQuery)\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\tcase \\\"swift\\\":\\r\\n\\t\\t\\t\\tlanguage = await loadLanguage(\\\"swift\\\")\\r\\n\\t\\t\\t\\tquery = language.query(swiftQuery)\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\tthrow new Error(`Unsupported language: ${ext}`)\\r\\n\\t\\t}\\r\\n\\t\\tconst parser = new Parser()\\r\\n\\t\\tparser.setLanguage(language)\\r\\n\\t\\tparsers[ext] = { parser, query }\\r\\n\\t}\\r\\n\\treturn parsers\\r\\n}\\r\\n\",\"metadata\":{\"size\":3862,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.852Z\",\"createdTime\":\"2024-12-29T05:57:23.852Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":132,\"nonEmptyLines\":122,\"commentLines\":1,\"complexity\":19},\"dependencies\":[\"path\",\"web-tree-sitter\"],\"quality\":{\"score\":-6,\"issues\":[],\"duplicateLines\":20,\"longLines\":3,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.393Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":132},\"analysis\":{\"metrics\":{\"lines\":132,\"nonEmptyLines\":122,\"commentLines\":1,\"complexity\":19},\"dependencies\":[\"path\",\"web-tree-sitter\"],\"quality\":{\"score\":-6,\"issues\":[],\"duplicateLines\":20,\"longLines\":3,\"complexFunctions\":0}},\"lastModified\":1737046855393,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.393Z\",\"size\":4927},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\services\\\\tree-sitter\\\\index.ts\",\"content\":{\"content\":\"import * as fs from \\\"fs/promises\\\"\\r\\nimport * as path from \\\"path\\\"\\r\\nimport { listFiles } from \\\"../glob/list-files\\\"\\r\\nimport { LanguageParser, loadRequiredLanguageParsers } from \\\"./languageParser\\\"\\r\\nimport { fileExistsAtPath } from \\\"../../utils/fs\\\"\\r\\n\\r\\n// TODO: implement caching behavior to avoid having to keep analyzing project for new tasks.\\r\\nexport async function parseSourceCodeForDefinitionsTopLevel(dirPath: string): Promise<string> {\\r\\n\\t// check if the path exists\\r\\n\\tconst dirExists = await fileExistsAtPath(path.resolve(dirPath))\\r\\n\\tif (!dirExists) {\\r\\n\\t\\treturn \\\"This directory does not exist or you do not have permission to access it.\\\"\\r\\n\\t}\\r\\n\\r\\n\\t// Get all files at top level (not gitignored)\\r\\n\\tconst [allFiles, _] = await listFiles(dirPath, false, 200)\\r\\n\\r\\n\\tlet result = \\\"\\\"\\r\\n\\r\\n\\t// Separate files to parse and remaining files\\r\\n\\tconst { filesToParse, remainingFiles } = separateFiles(allFiles)\\r\\n\\r\\n\\tconst languageParsers = await loadRequiredLanguageParsers(filesToParse)\\r\\n\\r\\n\\t// Parse specific files we have language parsers for\\r\\n\\t// const filesWithoutDefinitions: string[] = []\\r\\n\\tfor (const file of filesToParse) {\\r\\n\\t\\tconst definitions = await parseFile(file, languageParsers)\\r\\n\\t\\tif (definitions) {\\r\\n\\t\\t\\tresult += `${path.relative(dirPath, file).toPosix()}\\\\n${definitions}\\\\n`\\r\\n\\t\\t}\\r\\n\\t\\t// else {\\r\\n\\t\\t// \\tfilesWithoutDefinitions.push(file)\\r\\n\\t\\t// }\\r\\n\\t}\\r\\n\\r\\n\\t// List remaining files' paths\\r\\n\\t// let didFindUnparsedFiles = false\\r\\n\\t// filesWithoutDefinitions\\r\\n\\t// \\t.concat(remainingFiles)\\r\\n\\t// \\t.sort()\\r\\n\\t// \\t.forEach((file) => {\\r\\n\\t// \\t\\tif (!didFindUnparsedFiles) {\\r\\n\\t// \\t\\t\\tresult += \\\"# Unparsed Files\\\\n\\\\n\\\"\\r\\n\\t// \\t\\t\\tdidFindUnparsedFiles = true\\r\\n\\t// \\t\\t}\\r\\n\\t// \\t\\tresult += `${path.relative(dirPath, file)}\\\\n`\\r\\n\\t// \\t})\\r\\n\\r\\n\\treturn result ? result : \\\"No source code definitions found.\\\"\\r\\n}\\r\\n\\r\\nfunction separateFiles(allFiles: string[]): {\\r\\n\\tfilesToParse: string[]\\r\\n\\tremainingFiles: string[]\\r\\n} {\\r\\n\\tconst extensions = [\\r\\n\\t\\t\\\"js\\\",\\r\\n\\t\\t\\\"jsx\\\",\\r\\n\\t\\t\\\"ts\\\",\\r\\n\\t\\t\\\"tsx\\\",\\r\\n\\t\\t\\\"py\\\",\\r\\n\\t\\t// Rust\\r\\n\\t\\t\\\"rs\\\",\\r\\n\\t\\t\\\"go\\\",\\r\\n\\t\\t// C\\r\\n\\t\\t\\\"c\\\",\\r\\n\\t\\t\\\"h\\\",\\r\\n\\t\\t// C++\\r\\n\\t\\t\\\"cpp\\\",\\r\\n\\t\\t\\\"hpp\\\",\\r\\n\\t\\t// C#\\r\\n\\t\\t\\\"cs\\\",\\r\\n\\t\\t// Ruby\\r\\n\\t\\t\\\"rb\\\",\\r\\n\\t\\t\\\"java\\\",\\r\\n\\t\\t\\\"php\\\",\\r\\n\\t\\t\\\"swift\\\",\\r\\n\\t].map((e) => `.${e}`)\\r\\n\\tconst filesToParse = allFiles.filter((file) => extensions.includes(path.extname(file))).slice(0, 50) // 50 files max\\r\\n\\tconst remainingFiles = allFiles.filter((file) => !filesToParse.includes(file))\\r\\n\\treturn { filesToParse, remainingFiles }\\r\\n}\\r\\n\\r\\n/*\\r\\nParsing files using tree-sitter\\r\\n\\r\\n1. Parse the file content into an AST (Abstract Syntax Tree) using the appropriate language grammar (set of rules that define how the components of a language like keywords, expressions, and statements can be combined to create valid programs).\\r\\n2. Create a query using a language-specific query string, and run it against the AST's root node to capture specific syntax elements.\\r\\n - We use tag queries to identify named entities in a program, and then use a syntax capture to label the entity and its name. A notable example of this is GitHub's search-based code navigation.\\r\\n\\t- Our custom tag queries are based on tree-sitter's default tag queries, but modified to only capture definitions.\\r\\n3. Sort the captures by their position in the file, output the name of the definition, and format by i.e. adding \\\"|----\\\\n\\\" for gaps between captured sections.\\r\\n\\r\\nThis approach allows us to focus on the most relevant parts of the code (defined by our language-specific queries) and provides a concise yet informative view of the file's structure and key elements.\\r\\n\\r\\n- https://github.com/tree-sitter/node-tree-sitter/blob/master/test/query_test.js\\r\\n- https://github.com/tree-sitter/tree-sitter/blob/master/lib/binding_web/test/query-test.js\\r\\n- https://github.com/tree-sitter/tree-sitter/blob/master/lib/binding_web/test/helper.js\\r\\n- https://tree-sitter.github.io/tree-sitter/code-navigation-systems\\r\\n*/\\r\\nasync function parseFile(filePath: string, languageParsers: LanguageParser): Promise<string | undefined> {\\r\\n\\tconst fileContent = await fs.readFile(filePath, \\\"utf8\\\")\\r\\n\\tconst ext = path.extname(filePath).toLowerCase().slice(1)\\r\\n\\r\\n\\tconst { parser, query } = languageParsers[ext] || {}\\r\\n\\tif (!parser || !query) {\\r\\n\\t\\treturn `Unsupported file type: ${filePath}`\\r\\n\\t}\\r\\n\\r\\n\\tlet formattedOutput = \\\"\\\"\\r\\n\\r\\n\\ttry {\\r\\n\\t\\t// Parse the file content into an Abstract Syntax Tree (AST), a tree-like representation of the code\\r\\n\\t\\tconst tree = parser.parse(fileContent)\\r\\n\\r\\n\\t\\t// Apply the query to the AST and get the captures\\r\\n\\t\\t// Captures are specific parts of the AST that match our query patterns, each capture represents a node in the AST that we're interested in.\\r\\n\\t\\tconst captures = query.captures(tree.rootNode)\\r\\n\\r\\n\\t\\t// Sort captures by their start position\\r\\n\\t\\tcaptures.sort((a, b) => a.node.startPosition.row - b.node.startPosition.row)\\r\\n\\r\\n\\t\\t// Split the file content into individual lines\\r\\n\\t\\tconst lines = fileContent.split(\\\"\\\\n\\\")\\r\\n\\r\\n\\t\\t// Keep track of the last line we've processed\\r\\n\\t\\tlet lastLine = -1\\r\\n\\r\\n\\t\\tcaptures.forEach((capture) => {\\r\\n\\t\\t\\tconst { node, name } = capture\\r\\n\\t\\t\\t// Get the start and end lines of the current AST node\\r\\n\\t\\t\\tconst startLine = node.startPosition.row\\r\\n\\t\\t\\tconst endLine = node.endPosition.row\\r\\n\\t\\t\\t// Once we've retrieved the nodes we care about through the language query, we filter for lines with definition names only.\\r\\n\\t\\t\\t// name.startsWith(\\\"name.reference.\\\") > refs can be used for ranking purposes, but we don't need them for the output\\r\\n\\t\\t\\t// previously we did `name.startsWith(\\\"name.definition.\\\")` but this was too strict and excluded some relevant definitions\\r\\n\\r\\n\\t\\t\\t// Add separator if there's a gap between captures\\r\\n\\t\\t\\tif (lastLine !== -1 && startLine > lastLine + 1) {\\r\\n\\t\\t\\t\\tformattedOutput += \\\"|----\\\\n\\\"\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t// Only add the first line of the definition\\r\\n\\t\\t\\t// query captures includes the definition name and the definition implementation, but we only want the name (I found discrepencies in the naming structure for various languages, i.e. javascript names would be 'name' and typescript names would be 'name.definition)\\r\\n\\t\\t\\tif (name.includes(\\\"name\\\") && lines[startLine]) {\\r\\n\\t\\t\\t\\tformattedOutput += `│${lines[startLine]}\\\\n`\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t// Adds all the captured lines\\r\\n\\t\\t\\t// for (let i = startLine; i <= endLine; i++) {\\r\\n\\t\\t\\t// \\tformattedOutput += `│${lines[i]}\\\\n`\\r\\n\\t\\t\\t// }\\r\\n\\t\\t\\t//}\\r\\n\\r\\n\\t\\t\\tlastLine = endLine\\r\\n\\t\\t})\\r\\n\\t} catch (error) {\\r\\n\\t\\tconsole.log(`Error parsing file: ${error}\\\\n`)\\r\\n\\t}\\r\\n\\r\\n\\tif (formattedOutput.length > 0) {\\r\\n\\t\\treturn `|----\\\\n${formattedOutput}|----\\\\n`\\r\\n\\t}\\r\\n\\treturn undefined\\r\\n}\\r\\n\",\"metadata\":{\"size\":6433,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.963Z\",\"createdTime\":\"2025-01-15T15:26:07.963Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":164,\"nonEmptyLines\":139,\"commentLines\":45,\"complexity\":17},\"dependencies\":[\"fs/promises\",\"path\",\"../glob/list-files\",\"./languageParser\",\"../../utils/fs\"],\"quality\":{\"score\":17,\"issues\":[],\"duplicateLines\":11,\"longLines\":14,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.392Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":164},\"analysis\":{\"metrics\":{\"lines\":164,\"nonEmptyLines\":139,\"commentLines\":45,\"complexity\":17},\"dependencies\":[\"fs/promises\",\"path\",\"../glob/list-files\",\"./languageParser\",\"../../utils/fs\"],\"quality\":{\"score\":17,\"issues\":[],\"duplicateLines\":11,\"longLines\":14,\"complexFunctions\":0}},\"lastModified\":1737046855392,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.392Z\",\"size\":7600},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\services\\\\ripgrep\\\\index.ts\",\"content\":{\"content\":\"import * as vscode from \\\"vscode\\\"\\r\\nimport * as childProcess from \\\"child_process\\\"\\r\\nimport * as path from \\\"path\\\"\\r\\nimport * as fs from \\\"fs\\\"\\r\\nimport * as readline from \\\"readline\\\"\\r\\n\\r\\n/*\\r\\nThis file provides functionality to perform regex searches on files using ripgrep.\\r\\nInspired by: https://github.com/DiscreteTom/vscode-ripgrep-utils\\r\\n\\r\\nKey components:\\r\\n1. getBinPath: Locates the ripgrep binary within the VSCode installation.\\r\\n2. execRipgrep: Executes the ripgrep command and returns the output.\\r\\n3. regexSearchFiles: The main function that performs regex searches on files.\\r\\n - Parameters:\\r\\n * cwd: The current working directory (for relative path calculation)\\r\\n * directoryPath: The directory to search in\\r\\n * regex: The regular expression to search for (Rust regex syntax)\\r\\n * filePattern: Optional glob pattern to filter files (default: '*')\\r\\n - Returns: A formatted string containing search results with context\\r\\n\\r\\nThe search results include:\\r\\n- Relative file paths\\r\\n- 2 lines of context before and after each match\\r\\n- Matches formatted with pipe characters for easy reading\\r\\n\\r\\nUsage example:\\r\\nconst results = await regexSearchFiles('/path/to/cwd', '/path/to/search', 'TODO:', '*.ts');\\r\\n\\r\\nrel/path/to/app.ts\\r\\n│----\\r\\n│function processData(data: any) {\\r\\n│ // Some processing logic here\\r\\n│ // TODO: Implement error handling\\r\\n│ return processedData;\\r\\n│}\\r\\n│----\\r\\n\\r\\nrel/path/to/helper.ts\\r\\n│----\\r\\n│ let result = 0;\\r\\n│ for (let i = 0; i < input; i++) {\\r\\n│ // TODO: Optimize this function for performance\\r\\n│ result += Math.pow(i, 2);\\r\\n│ }\\r\\n│----\\r\\n*/\\r\\n\\r\\nconst isWindows = /^win/.test(process.platform)\\r\\nconst binName = isWindows ? \\\"rg.exe\\\" : \\\"rg\\\"\\r\\n\\r\\ninterface SearchResult {\\r\\n\\tfile: string\\r\\n\\tline: number\\r\\n\\tcolumn: number\\r\\n\\tmatch: string\\r\\n\\tbeforeContext: string[]\\r\\n\\tafterContext: string[]\\r\\n}\\r\\n\\r\\nconst MAX_RESULTS = 300\\r\\n\\r\\nasync function getBinPath(vscodeAppRoot: string): Promise<string | undefined> {\\r\\n\\tconst checkPath = async (pkgFolder: string) => {\\r\\n\\t\\tconst fullPath = path.join(vscodeAppRoot, pkgFolder, binName)\\r\\n\\t\\treturn (await pathExists(fullPath)) ? fullPath : undefined\\r\\n\\t}\\r\\n\\r\\n\\treturn (\\r\\n\\t\\t(await checkPath(\\\"node_modules/@vscode/ripgrep/bin/\\\")) ||\\r\\n\\t\\t(await checkPath(\\\"node_modules/vscode-ripgrep/bin\\\")) ||\\r\\n\\t\\t(await checkPath(\\\"node_modules.asar.unpacked/vscode-ripgrep/bin/\\\")) ||\\r\\n\\t\\t(await checkPath(\\\"node_modules.asar.unpacked/@vscode/ripgrep/bin/\\\"))\\r\\n\\t)\\r\\n}\\r\\n\\r\\nasync function pathExists(path: string): Promise<boolean> {\\r\\n\\treturn new Promise((resolve) => {\\r\\n\\t\\tfs.access(path, (err) => {\\r\\n\\t\\t\\tresolve(err === null)\\r\\n\\t\\t})\\r\\n\\t})\\r\\n}\\r\\n\\r\\nasync function execRipgrep(bin: string, args: string[]): Promise<string> {\\r\\n\\treturn new Promise((resolve, reject) => {\\r\\n\\t\\tconst rgProcess = childProcess.spawn(bin, args)\\r\\n\\t\\t// cross-platform alternative to head, which is ripgrep author's recommendation for limiting output.\\r\\n\\t\\tconst rl = readline.createInterface({\\r\\n\\t\\t\\tinput: rgProcess.stdout,\\r\\n\\t\\t\\tcrlfDelay: Infinity, // treat \\\\r\\\\n as a single line break even if it's split across chunks. This ensures consistent behavior across different operating systems.\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tlet output = \\\"\\\"\\r\\n\\t\\tlet lineCount = 0\\r\\n\\t\\tconst maxLines = MAX_RESULTS * 5 // limiting ripgrep output with max lines since there's no other way to limit results. it's okay that we're outputting as json, since we're parsing it line by line and ignore anything that's not part of a match. This assumes each result is at most 5 lines.\\r\\n\\r\\n\\t\\trl.on(\\\"line\\\", (line) => {\\r\\n\\t\\t\\tif (lineCount < maxLines) {\\r\\n\\t\\t\\t\\toutput += line + \\\"\\\\n\\\"\\r\\n\\t\\t\\t\\tlineCount++\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\trl.close()\\r\\n\\t\\t\\t\\trgProcess.kill()\\r\\n\\t\\t\\t}\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tlet errorOutput = \\\"\\\"\\r\\n\\t\\trgProcess.stderr.on(\\\"data\\\", (data) => {\\r\\n\\t\\t\\terrorOutput += data.toString()\\r\\n\\t\\t})\\r\\n\\t\\trl.on(\\\"close\\\", () => {\\r\\n\\t\\t\\tif (errorOutput) {\\r\\n\\t\\t\\t\\treject(new Error(`ripgrep process error: ${errorOutput}`))\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tresolve(output)\\r\\n\\t\\t\\t}\\r\\n\\t\\t})\\r\\n\\t\\trgProcess.on(\\\"error\\\", (error) => {\\r\\n\\t\\t\\treject(new Error(`ripgrep process error: ${error.message}`))\\r\\n\\t\\t})\\r\\n\\t})\\r\\n}\\r\\n\\r\\nexport async function regexSearchFiles(cwd: string, directoryPath: string, regex: string, filePattern?: string): Promise<string> {\\r\\n\\tconst vscodeAppRoot = vscode.env.appRoot\\r\\n\\tconst rgPath = await getBinPath(vscodeAppRoot)\\r\\n\\r\\n\\tif (!rgPath) {\\r\\n\\t\\tthrow new Error(\\\"Could not find ripgrep binary\\\")\\r\\n\\t}\\r\\n\\r\\n\\tconst args = [\\\"--json\\\", \\\"-e\\\", regex, \\\"--glob\\\", filePattern || \\\"*\\\", \\\"--context\\\", \\\"1\\\", directoryPath]\\r\\n\\r\\n\\tlet output: string\\r\\n\\ttry {\\r\\n\\t\\toutput = await execRipgrep(rgPath, args)\\r\\n\\t} catch {\\r\\n\\t\\treturn \\\"No results found\\\"\\r\\n\\t}\\r\\n\\tconst results: SearchResult[] = []\\r\\n\\tlet currentResult: Partial<SearchResult> | null = null\\r\\n\\r\\n\\toutput.split(\\\"\\\\n\\\").forEach((line) => {\\r\\n\\t\\tif (line) {\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\tconst parsed = JSON.parse(line)\\r\\n\\t\\t\\t\\tif (parsed.type === \\\"match\\\") {\\r\\n\\t\\t\\t\\t\\tif (currentResult) {\\r\\n\\t\\t\\t\\t\\t\\tresults.push(currentResult as SearchResult)\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tcurrentResult = {\\r\\n\\t\\t\\t\\t\\t\\tfile: parsed.data.path.text,\\r\\n\\t\\t\\t\\t\\t\\tline: parsed.data.line_number,\\r\\n\\t\\t\\t\\t\\t\\tcolumn: parsed.data.submatches[0].start,\\r\\n\\t\\t\\t\\t\\t\\tmatch: parsed.data.lines.text,\\r\\n\\t\\t\\t\\t\\t\\tbeforeContext: [],\\r\\n\\t\\t\\t\\t\\t\\tafterContext: [],\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t} else if (parsed.type === \\\"context\\\" && currentResult) {\\r\\n\\t\\t\\t\\t\\tif (parsed.data.line_number < currentResult.line!) {\\r\\n\\t\\t\\t\\t\\t\\tcurrentResult.beforeContext!.push(parsed.data.lines.text)\\r\\n\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\tcurrentResult.afterContext!.push(parsed.data.lines.text)\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\tconsole.error(\\\"Error parsing ripgrep output:\\\", error)\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t})\\r\\n\\r\\n\\tif (currentResult) {\\r\\n\\t\\tresults.push(currentResult as SearchResult)\\r\\n\\t}\\r\\n\\r\\n\\treturn formatResults(results, cwd)\\r\\n}\\r\\n\\r\\nfunction formatResults(results: SearchResult[], cwd: string): string {\\r\\n\\tconst groupedResults: { [key: string]: SearchResult[] } = {}\\r\\n\\r\\n\\tlet output = \\\"\\\"\\r\\n\\tif (results.length >= MAX_RESULTS) {\\r\\n\\t\\toutput += `Showing first ${MAX_RESULTS} of ${MAX_RESULTS}+ results. Use a more specific search if necessary.\\\\n\\\\n`\\r\\n\\t} else {\\r\\n\\t\\toutput += `Found ${results.length === 1 ? \\\"1 result\\\" : `${results.length.toLocaleString()} results`}.\\\\n\\\\n`\\r\\n\\t}\\r\\n\\r\\n\\t// Group results by file name\\r\\n\\tresults.slice(0, MAX_RESULTS).forEach((result) => {\\r\\n\\t\\tconst relativeFilePath = path.relative(cwd, result.file)\\r\\n\\t\\tif (!groupedResults[relativeFilePath]) {\\r\\n\\t\\t\\tgroupedResults[relativeFilePath] = []\\r\\n\\t\\t}\\r\\n\\t\\tgroupedResults[relativeFilePath].push(result)\\r\\n\\t})\\r\\n\\r\\n\\tfor (const [filePath, fileResults] of Object.entries(groupedResults)) {\\r\\n\\t\\toutput += `${filePath.toPosix()}\\\\n│----\\\\n`\\r\\n\\r\\n\\t\\tfileResults.forEach((result, index) => {\\r\\n\\t\\t\\tconst allLines = [...result.beforeContext, result.match, ...result.afterContext]\\r\\n\\t\\t\\tallLines.forEach((line) => {\\r\\n\\t\\t\\t\\toutput += `│${line?.trimEnd() ?? \\\"\\\"}\\\\n`\\r\\n\\t\\t\\t})\\r\\n\\r\\n\\t\\t\\tif (index < fileResults.length - 1) {\\r\\n\\t\\t\\t\\toutput += \\\"│----\\\\n\\\"\\r\\n\\t\\t\\t}\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\toutput += \\\"│----\\\\n\\\\n\\\"\\r\\n\\t}\\r\\n\\r\\n\\treturn output.trim()\\r\\n}\\r\\n\",\"metadata\":{\"size\":6803,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.962Z\",\"createdTime\":\"2025-01-15T15:26:07.962Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":218,\"nonEmptyLines\":186,\"commentLines\":3,\"complexity\":35},\"dependencies\":[\"vscode\",\"child_process\",\"path\",\"fs\",\"readline\"],\"quality\":{\"score\":-124,\"issues\":[],\"duplicateLines\":42,\"longLines\":7,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.391Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":218},\"analysis\":{\"metrics\":{\"lines\":218,\"nonEmptyLines\":186,\"commentLines\":3,\"complexity\":35},\"dependencies\":[\"vscode\",\"child_process\",\"path\",\"fs\",\"readline\"],\"quality\":{\"score\":-124,\"issues\":[],\"duplicateLines\":42,\"longLines\":7,\"complexFunctions\":0}},\"lastModified\":1737046855391,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.391Z\",\"size\":8151},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\services\\\\mcp\\\\McpHub.ts\",\"content\":{\"content\":\"import { Client } from \\\"@modelcontextprotocol/sdk/client/index.js\\\"\\r\\nimport { StdioClientTransport, StdioServerParameters } from \\\"@modelcontextprotocol/sdk/client/stdio.js\\\"\\r\\nimport {\\r\\n\\tCallToolResultSchema,\\r\\n\\tListResourcesResultSchema,\\r\\n\\tListResourceTemplatesResultSchema,\\r\\n\\tListToolsResultSchema,\\r\\n\\tReadResourceResultSchema,\\r\\n} from \\\"@modelcontextprotocol/sdk/types.js\\\"\\r\\nimport chokidar, { FSWatcher } from \\\"chokidar\\\"\\r\\nimport delay from \\\"delay\\\"\\r\\nimport deepEqual from \\\"fast-deep-equal\\\"\\r\\nimport * as fs from \\\"fs/promises\\\"\\r\\nimport * as path from \\\"path\\\"\\r\\nimport * as vscode from \\\"vscode\\\"\\r\\nimport { z } from \\\"zod\\\"\\r\\nimport { ClineProvider, GlobalFileNames } from \\\"../../core/webview/ClineProvider\\\"\\r\\nimport { McpResource, McpResourceResponse, McpResourceTemplate, McpServer, McpTool, McpToolCallResponse } from \\\"../../shared/mcp\\\"\\r\\nimport { fileExistsAtPath } from \\\"../../utils/fs\\\"\\r\\nimport { arePathsEqual } from \\\"../../utils/path\\\"\\r\\n\\r\\nexport type McpConnection = {\\r\\n\\tserver: McpServer\\r\\n\\tclient: Client\\r\\n\\ttransport: StdioClientTransport\\r\\n}\\r\\n\\r\\n// StdioServerParameters\\r\\nconst StdioConfigSchema = z.object({\\r\\n\\tcommand: z.string(),\\r\\n\\targs: z.array(z.string()).optional(),\\r\\n\\tenv: z.record(z.string()).optional(),\\r\\n})\\r\\n\\r\\nconst McpSettingsSchema = z.object({\\r\\n\\tmcpServers: z.record(StdioConfigSchema),\\r\\n})\\r\\n\\r\\nexport class McpHub {\\r\\n\\tprivate providerRef: WeakRef<ClineProvider>\\r\\n\\tprivate disposables: vscode.Disposable[] = []\\r\\n\\tprivate settingsWatcher?: vscode.FileSystemWatcher\\r\\n\\tprivate fileWatchers: Map<string, FSWatcher> = new Map()\\r\\n\\tconnections: McpConnection[] = []\\r\\n\\tisConnecting: boolean = false\\r\\n\\r\\n\\tconstructor(provider: ClineProvider) {\\r\\n\\t\\tthis.providerRef = new WeakRef(provider)\\r\\n\\t\\tthis.watchMcpSettingsFile()\\r\\n\\t\\tthis.initializeMcpServers()\\r\\n\\t}\\r\\n\\r\\n\\tgetServers(): McpServer[] {\\r\\n\\t\\treturn this.connections.map((conn) => conn.server)\\r\\n\\t}\\r\\n\\r\\n\\tasync getMcpServersPath(): Promise<string> {\\r\\n\\t\\tconst provider = this.providerRef.deref()\\r\\n\\t\\tif (!provider) {\\r\\n\\t\\t\\tthrow new Error(\\\"Provider not available\\\")\\r\\n\\t\\t}\\r\\n\\t\\tconst mcpServersPath = await provider.ensureMcpServersDirectoryExists()\\r\\n\\t\\treturn mcpServersPath\\r\\n\\t}\\r\\n\\r\\n\\tasync getMcpSettingsFilePath(): Promise<string> {\\r\\n\\t\\tconst provider = this.providerRef.deref()\\r\\n\\t\\tif (!provider) {\\r\\n\\t\\t\\tthrow new Error(\\\"Provider not available\\\")\\r\\n\\t\\t}\\r\\n\\t\\tconst mcpSettingsFilePath = path.join(await provider.ensureSettingsDirectoryExists(), GlobalFileNames.mcpSettings)\\r\\n\\t\\tconst fileExists = await fileExistsAtPath(mcpSettingsFilePath)\\r\\n\\t\\tif (!fileExists) {\\r\\n\\t\\t\\tawait fs.writeFile(\\r\\n\\t\\t\\t\\tmcpSettingsFilePath,\\r\\n\\t\\t\\t\\t`{\\r\\n \\\"mcpServers\\\": {\\r\\n \\r\\n }\\r\\n}`,\\r\\n\\t\\t\\t)\\r\\n\\t\\t}\\r\\n\\t\\treturn mcpSettingsFilePath\\r\\n\\t}\\r\\n\\r\\n\\tprivate async watchMcpSettingsFile(): Promise<void> {\\r\\n\\t\\tconst settingsPath = await this.getMcpSettingsFilePath()\\r\\n\\t\\tthis.disposables.push(\\r\\n\\t\\t\\tvscode.workspace.onDidSaveTextDocument(async (document) => {\\r\\n\\t\\t\\t\\tif (arePathsEqual(document.uri.fsPath, settingsPath)) {\\r\\n\\t\\t\\t\\t\\tconst content = await fs.readFile(settingsPath, \\\"utf-8\\\")\\r\\n\\t\\t\\t\\t\\tconst errorMessage =\\r\\n\\t\\t\\t\\t\\t\\t\\\"Invalid MCP settings format. Please ensure your settings follow the correct JSON format.\\\"\\r\\n\\t\\t\\t\\t\\tlet config: any\\r\\n\\t\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\t\\tconfig = JSON.parse(content)\\r\\n\\t\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\t\\tvscode.window.showErrorMessage(errorMessage)\\r\\n\\t\\t\\t\\t\\t\\treturn\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tconst result = McpSettingsSchema.safeParse(config)\\r\\n\\t\\t\\t\\t\\tif (!result.success) {\\r\\n\\t\\t\\t\\t\\t\\tvscode.window.showErrorMessage(errorMessage)\\r\\n\\t\\t\\t\\t\\t\\treturn\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\t\\tvscode.window.showInformationMessage(\\\"Updating MCP servers...\\\")\\r\\n\\t\\t\\t\\t\\t\\tawait this.updateServerConnections(result.data.mcpServers || {})\\r\\n\\t\\t\\t\\t\\t\\tvscode.window.showInformationMessage(\\\"MCP servers updated\\\")\\r\\n\\t\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\t\\tconsole.error(\\\"Failed to process MCP settings change:\\\", error)\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}),\\r\\n\\t\\t)\\r\\n\\t}\\r\\n\\r\\n\\tprivate async initializeMcpServers(): Promise<void> {\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tconst settingsPath = await this.getMcpSettingsFilePath()\\r\\n\\t\\t\\tconst content = await fs.readFile(settingsPath, \\\"utf-8\\\")\\r\\n\\t\\t\\tconst config = JSON.parse(content)\\r\\n\\t\\t\\tawait this.updateServerConnections(config.mcpServers || {})\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\tconsole.error(\\\"Failed to initialize MCP servers:\\\", error)\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tprivate async connectToServer(name: string, config: StdioServerParameters): Promise<void> {\\r\\n\\t\\t// Remove existing connection if it exists (should never happen, the connection should be deleted beforehand)\\r\\n\\t\\tthis.connections = this.connections.filter((conn) => conn.server.name !== name)\\r\\n\\r\\n\\t\\ttry {\\r\\n\\t\\t\\t// Each MCP server requires its own transport connection and has unique capabilities, configurations, and error handling. Having separate clients also allows proper scoping of resources/tools and independent server management like reconnection.\\r\\n\\t\\t\\tconst client = new Client(\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tname: \\\"Cline\\\",\\r\\n\\t\\t\\t\\t\\tversion: this.providerRef.deref()?.context.extension?.packageJSON?.version ?? \\\"1.0.0\\\",\\r\\n\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\tcapabilities: {},\\r\\n\\t\\t\\t\\t},\\r\\n\\t\\t\\t)\\r\\n\\r\\n\\t\\t\\tconst transport = new StdioClientTransport({\\r\\n\\t\\t\\t\\tcommand: config.command,\\r\\n\\t\\t\\t\\targs: config.args,\\r\\n\\t\\t\\t\\tenv: {\\r\\n\\t\\t\\t\\t\\t...config.env,\\r\\n\\t\\t\\t\\t\\t...(process.env.PATH ? { PATH: process.env.PATH } : {}),\\r\\n\\t\\t\\t\\t\\t// ...(process.env.NODE_PATH ? { NODE_PATH: process.env.NODE_PATH } : {}),\\r\\n\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\tstderr: \\\"pipe\\\", // necessary for stderr to be available\\r\\n\\t\\t\\t})\\r\\n\\r\\n\\t\\t\\ttransport.onerror = async (error) => {\\r\\n\\t\\t\\t\\tconsole.error(`Transport error for \\\"${name}\\\":`, error)\\r\\n\\t\\t\\t\\tconst connection = this.connections.find((conn) => conn.server.name === name)\\r\\n\\t\\t\\t\\tif (connection) {\\r\\n\\t\\t\\t\\t\\tconnection.server.status = \\\"disconnected\\\"\\r\\n\\t\\t\\t\\t\\tthis.appendErrorMessage(connection, error.message)\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tawait this.notifyWebviewOfServerChanges()\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\ttransport.onclose = async () => {\\r\\n\\t\\t\\t\\tconst connection = this.connections.find((conn) => conn.server.name === name)\\r\\n\\t\\t\\t\\tif (connection) {\\r\\n\\t\\t\\t\\t\\tconnection.server.status = \\\"disconnected\\\"\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tawait this.notifyWebviewOfServerChanges()\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t// If the config is invalid, show an error\\r\\n\\t\\t\\tif (!StdioConfigSchema.safeParse(config).success) {\\r\\n\\t\\t\\t\\tconsole.error(`Invalid config for \\\"${name}\\\": missing or invalid parameters`)\\r\\n\\t\\t\\t\\tconst connection: McpConnection = {\\r\\n\\t\\t\\t\\t\\tserver: {\\r\\n\\t\\t\\t\\t\\t\\tname,\\r\\n\\t\\t\\t\\t\\t\\tconfig: JSON.stringify(config),\\r\\n\\t\\t\\t\\t\\t\\tstatus: \\\"disconnected\\\",\\r\\n\\t\\t\\t\\t\\t\\terror: \\\"Invalid config: missing or invalid parameters\\\",\\r\\n\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\tclient,\\r\\n\\t\\t\\t\\t\\ttransport,\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tthis.connections.push(connection)\\r\\n\\t\\t\\t\\treturn\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t// valid schema\\r\\n\\t\\t\\tconst connection: McpConnection = {\\r\\n\\t\\t\\t\\tserver: {\\r\\n\\t\\t\\t\\t\\tname,\\r\\n\\t\\t\\t\\t\\tconfig: JSON.stringify(config),\\r\\n\\t\\t\\t\\t\\tstatus: \\\"connecting\\\",\\r\\n\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\tclient,\\r\\n\\t\\t\\t\\ttransport,\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tthis.connections.push(connection)\\r\\n\\r\\n\\t\\t\\t// transport.stderr is only available after the process has been started. However we can't start it separately from the .connect() call because it also starts the transport. And we can't place this after the connect call since we need to capture the stderr stream before the connection is established, in order to capture errors during the connection process.\\r\\n\\t\\t\\t// As a workaround, we start the transport ourselves, and then monkey-patch the start method to no-op so that .connect() doesn't try to start it again.\\r\\n\\t\\t\\tawait transport.start()\\r\\n\\t\\t\\tconst stderrStream = transport.stderr\\r\\n\\t\\t\\tif (stderrStream) {\\r\\n\\t\\t\\t\\tstderrStream.on(\\\"data\\\", async (data: Buffer) => {\\r\\n\\t\\t\\t\\t\\tconst errorOutput = data.toString()\\r\\n\\t\\t\\t\\t\\tconsole.error(`Server \\\"${name}\\\" stderr:`, errorOutput)\\r\\n\\t\\t\\t\\t\\tconst connection = this.connections.find((conn) => conn.server.name === name)\\r\\n\\t\\t\\t\\t\\tif (connection) {\\r\\n\\t\\t\\t\\t\\t\\t// NOTE: we do not set server status to \\\"disconnected\\\" because stderr logs do not necessarily mean the server crashed or disconnected, it could just be informational. In fact when the server first starts up, it immediately logs \\\"<name> server running on stdio\\\" to stderr.\\r\\n\\t\\t\\t\\t\\t\\tthis.appendErrorMessage(connection, errorOutput)\\r\\n\\t\\t\\t\\t\\t\\t// Only need to update webview right away if it's already disconnected\\r\\n\\t\\t\\t\\t\\t\\tif (connection.server.status === \\\"disconnected\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.notifyWebviewOfServerChanges()\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t})\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tconsole.error(`No stderr stream for ${name}`)\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\ttransport.start = async () => {} // No-op now, .connect() won't fail\\r\\n\\r\\n\\t\\t\\t// // Set up notification handlers\\r\\n\\t\\t\\t// client.setNotificationHandler(\\r\\n\\t\\t\\t// \\t// @ts-ignore-next-line\\r\\n\\t\\t\\t// \\t{ method: \\\"notifications/tools/list_changed\\\" },\\r\\n\\t\\t\\t// \\tasync () => {\\r\\n\\t\\t\\t// \\t\\tconsole.log(`Tools changed for server: ${name}`)\\r\\n\\t\\t\\t// \\t\\tconnection.server.tools = await this.fetchTools(name)\\r\\n\\t\\t\\t// \\t\\tawait this.notifyWebviewOfServerChanges()\\r\\n\\t\\t\\t// \\t},\\r\\n\\t\\t\\t// )\\r\\n\\r\\n\\t\\t\\t// client.setNotificationHandler(\\r\\n\\t\\t\\t// \\t// @ts-ignore-next-line\\r\\n\\t\\t\\t// \\t{ method: \\\"notifications/resources/list_changed\\\" },\\r\\n\\t\\t\\t// \\tasync () => {\\r\\n\\t\\t\\t// \\t\\tconsole.log(`Resources changed for server: ${name}`)\\r\\n\\t\\t\\t// \\t\\tconnection.server.resources = await this.fetchResources(name)\\r\\n\\t\\t\\t// \\t\\tconnection.server.resourceTemplates = await this.fetchResourceTemplates(name)\\r\\n\\t\\t\\t// \\t\\tawait this.notifyWebviewOfServerChanges()\\r\\n\\t\\t\\t// \\t},\\r\\n\\t\\t\\t// )\\r\\n\\r\\n\\t\\t\\t// Connect\\r\\n\\t\\t\\tawait client.connect(transport)\\r\\n\\t\\t\\tconnection.server.status = \\\"connected\\\"\\r\\n\\t\\t\\tconnection.server.error = \\\"\\\"\\r\\n\\r\\n\\t\\t\\t// Initial fetch of tools and resources\\r\\n\\t\\t\\tconnection.server.tools = await this.fetchToolsList(name)\\r\\n\\t\\t\\tconnection.server.resources = await this.fetchResourcesList(name)\\r\\n\\t\\t\\tconnection.server.resourceTemplates = await this.fetchResourceTemplatesList(name)\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\t// Update status with error\\r\\n\\t\\t\\tconst connection = this.connections.find((conn) => conn.server.name === name)\\r\\n\\t\\t\\tif (connection) {\\r\\n\\t\\t\\t\\tconnection.server.status = \\\"disconnected\\\"\\r\\n\\t\\t\\t\\tthis.appendErrorMessage(connection, error instanceof Error ? error.message : String(error))\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tthrow error\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tprivate appendErrorMessage(connection: McpConnection, error: string) {\\r\\n\\t\\tconst newError = connection.server.error ? `${connection.server.error}\\\\n${error}` : error\\r\\n\\t\\tconnection.server.error = newError //.slice(0, 800)\\r\\n\\t}\\r\\n\\r\\n\\tprivate async fetchToolsList(serverName: string): Promise<McpTool[]> {\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tconst response = await this.connections\\r\\n\\t\\t\\t\\t.find((conn) => conn.server.name === serverName)\\r\\n\\t\\t\\t\\t?.client.request({ method: \\\"tools/list\\\" }, ListToolsResultSchema)\\r\\n\\t\\t\\treturn response?.tools || []\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\t// console.error(`Failed to fetch tools for ${serverName}:`, error)\\r\\n\\t\\t\\treturn []\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tprivate async fetchResourcesList(serverName: string): Promise<McpResource[]> {\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tconst response = await this.connections\\r\\n\\t\\t\\t\\t.find((conn) => conn.server.name === serverName)\\r\\n\\t\\t\\t\\t?.client.request({ method: \\\"resources/list\\\" }, ListResourcesResultSchema)\\r\\n\\t\\t\\treturn response?.resources || []\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\t// console.error(`Failed to fetch resources for ${serverName}:`, error)\\r\\n\\t\\t\\treturn []\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tprivate async fetchResourceTemplatesList(serverName: string): Promise<McpResourceTemplate[]> {\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tconst response = await this.connections\\r\\n\\t\\t\\t\\t.find((conn) => conn.server.name === serverName)\\r\\n\\t\\t\\t\\t?.client.request({ method: \\\"resources/templates/list\\\" }, ListResourceTemplatesResultSchema)\\r\\n\\t\\t\\treturn response?.resourceTemplates || []\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\t// console.error(`Failed to fetch resource templates for ${serverName}:`, error)\\r\\n\\t\\t\\treturn []\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tasync deleteConnection(name: string): Promise<void> {\\r\\n\\t\\tconst connection = this.connections.find((conn) => conn.server.name === name)\\r\\n\\t\\tif (connection) {\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t// connection.client.removeNotificationHandler(\\\"notifications/tools/list_changed\\\")\\r\\n\\t\\t\\t\\t// connection.client.removeNotificationHandler(\\\"notifications/resources/list_changed\\\")\\r\\n\\t\\t\\t\\t// connection.client.removeNotificationHandler(\\\"notifications/stderr\\\")\\r\\n\\t\\t\\t\\t// connection.client.removeNotificationHandler(\\\"notifications/stderr\\\")\\r\\n\\t\\t\\t\\tawait connection.transport.close()\\r\\n\\t\\t\\t\\tawait connection.client.close()\\r\\n\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\tconsole.error(`Failed to close transport for ${name}:`, error)\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tthis.connections = this.connections.filter((conn) => conn.server.name !== name)\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tasync updateServerConnections(newServers: Record<string, any>): Promise<void> {\\r\\n\\t\\tthis.isConnecting = true\\r\\n\\t\\tthis.removeAllFileWatchers()\\r\\n\\t\\tconst currentNames = new Set(this.connections.map((conn) => conn.server.name))\\r\\n\\t\\tconst newNames = new Set(Object.keys(newServers))\\r\\n\\r\\n\\t\\t// Delete removed servers\\r\\n\\t\\tfor (const name of currentNames) {\\r\\n\\t\\t\\tif (!newNames.has(name)) {\\r\\n\\t\\t\\t\\tawait this.deleteConnection(name)\\r\\n\\t\\t\\t\\tconsole.log(`Deleted MCP server: ${name}`)\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// Update or add servers\\r\\n\\t\\tfor (const [name, config] of Object.entries(newServers)) {\\r\\n\\t\\t\\tconst currentConnection = this.connections.find((conn) => conn.server.name === name)\\r\\n\\r\\n\\t\\t\\tif (!currentConnection) {\\r\\n\\t\\t\\t\\t// New server\\r\\n\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\tthis.setupFileWatcher(name, config)\\r\\n\\t\\t\\t\\t\\tawait this.connectToServer(name, config)\\r\\n\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\tconsole.error(`Failed to connect to new MCP server ${name}:`, error)\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} else if (!deepEqual(JSON.parse(currentConnection.server.config), config)) {\\r\\n\\t\\t\\t\\t// Existing server with changed config\\r\\n\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\tthis.setupFileWatcher(name, config)\\r\\n\\t\\t\\t\\t\\tawait this.deleteConnection(name)\\r\\n\\t\\t\\t\\t\\tawait this.connectToServer(name, config)\\r\\n\\t\\t\\t\\t\\tconsole.log(`Reconnected MCP server with updated config: ${name}`)\\r\\n\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\tconsole.error(`Failed to reconnect MCP server ${name}:`, error)\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t// If server exists with same config, do nothing\\r\\n\\t\\t}\\r\\n\\t\\tawait this.notifyWebviewOfServerChanges()\\r\\n\\t\\tthis.isConnecting = false\\r\\n\\t}\\r\\n\\r\\n\\tprivate setupFileWatcher(name: string, config: any) {\\r\\n\\t\\tconst filePath = config.args?.find((arg: string) => arg.includes(\\\"build/index.js\\\"))\\r\\n\\t\\tif (filePath) {\\r\\n\\t\\t\\t// we use chokidar instead of onDidSaveTextDocument because it doesn't require the file to be open in the editor. The settings config is better suited for onDidSave since that will be manually updated by the user or Cline (and we want to detect save events, not every file change)\\r\\n\\t\\t\\tconst watcher = chokidar.watch(filePath, {\\r\\n\\t\\t\\t\\t// persistent: true,\\r\\n\\t\\t\\t\\t// ignoreInitial: true,\\r\\n\\t\\t\\t\\t// awaitWriteFinish: true, // This helps with atomic writes\\r\\n\\t\\t\\t})\\r\\n\\r\\n\\t\\t\\twatcher.on(\\\"change\\\", () => {\\r\\n\\t\\t\\t\\tconsole.log(`Detected change in ${filePath}. Restarting server ${name}...`)\\r\\n\\t\\t\\t\\tthis.restartConnection(name)\\r\\n\\t\\t\\t})\\r\\n\\r\\n\\t\\t\\tthis.fileWatchers.set(name, watcher)\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tprivate removeAllFileWatchers() {\\r\\n\\t\\tthis.fileWatchers.forEach((watcher) => watcher.close())\\r\\n\\t\\tthis.fileWatchers.clear()\\r\\n\\t}\\r\\n\\r\\n\\tasync restartConnection(serverName: string): Promise<void> {\\r\\n\\t\\tthis.isConnecting = true\\r\\n\\t\\tconst provider = this.providerRef.deref()\\r\\n\\t\\tif (!provider) {\\r\\n\\t\\t\\treturn\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// Get existing connection and update its status\\r\\n\\t\\tconst connection = this.connections.find((conn) => conn.server.name === serverName)\\r\\n\\t\\tconst config = connection?.server.config\\r\\n\\t\\tif (config) {\\r\\n\\t\\t\\tvscode.window.showInformationMessage(`Restarting ${serverName} MCP server...`)\\r\\n\\t\\t\\tconnection.server.status = \\\"connecting\\\"\\r\\n\\t\\t\\tconnection.server.error = \\\"\\\"\\r\\n\\t\\t\\tawait this.notifyWebviewOfServerChanges()\\r\\n\\t\\t\\tawait delay(500) // artificial delay to show user that server is restarting\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\tawait this.deleteConnection(serverName)\\r\\n\\t\\t\\t\\t// Try to connect again using existing config\\r\\n\\t\\t\\t\\tawait this.connectToServer(serverName, JSON.parse(config))\\r\\n\\t\\t\\t\\tvscode.window.showInformationMessage(`${serverName} MCP server connected`)\\r\\n\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\tconsole.error(`Failed to restart connection for ${serverName}:`, error)\\r\\n\\t\\t\\t\\tvscode.window.showErrorMessage(`Failed to connect to ${serverName} MCP server`)\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tawait this.notifyWebviewOfServerChanges()\\r\\n\\t\\tthis.isConnecting = false\\r\\n\\t}\\r\\n\\r\\n\\tprivate async notifyWebviewOfServerChanges(): Promise<void> {\\r\\n\\t\\t// servers should always be sorted in the order they are defined in the settings file\\r\\n\\t\\tconst settingsPath = await this.getMcpSettingsFilePath()\\r\\n\\t\\tconst content = await fs.readFile(settingsPath, \\\"utf-8\\\")\\r\\n\\t\\tconst config = JSON.parse(content)\\r\\n\\t\\tconst serverOrder = Object.keys(config.mcpServers || {})\\r\\n\\t\\tawait this.providerRef.deref()?.postMessageToWebview({\\r\\n\\t\\t\\ttype: \\\"mcpServers\\\",\\r\\n\\t\\t\\tmcpServers: [...this.connections]\\r\\n\\t\\t\\t\\t.sort((a, b) => {\\r\\n\\t\\t\\t\\t\\tconst indexA = serverOrder.indexOf(a.server.name)\\r\\n\\t\\t\\t\\t\\tconst indexB = serverOrder.indexOf(b.server.name)\\r\\n\\t\\t\\t\\t\\treturn indexA - indexB\\r\\n\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t.map((connection) => connection.server),\\r\\n\\t\\t})\\r\\n\\t}\\r\\n\\r\\n\\t// Using server\\r\\n\\r\\n\\tasync readResource(serverName: string, uri: string): Promise<McpResourceResponse> {\\r\\n\\t\\tconst connection = this.connections.find((conn) => conn.server.name === serverName)\\r\\n\\t\\tif (!connection) {\\r\\n\\t\\t\\tthrow new Error(`No connection found for server: ${serverName}`)\\r\\n\\t\\t}\\r\\n\\t\\treturn await connection.client.request(\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tmethod: \\\"resources/read\\\",\\r\\n\\t\\t\\t\\tparams: {\\r\\n\\t\\t\\t\\t\\turi,\\r\\n\\t\\t\\t\\t},\\r\\n\\t\\t\\t},\\r\\n\\t\\t\\tReadResourceResultSchema,\\r\\n\\t\\t)\\r\\n\\t}\\r\\n\\r\\n\\tasync callTool(serverName: string, toolName: string, toolArguments?: Record<string, unknown>): Promise<McpToolCallResponse> {\\r\\n\\t\\tconst connection = this.connections.find((conn) => conn.server.name === serverName)\\r\\n\\t\\tif (!connection) {\\r\\n\\t\\t\\tthrow new Error(\\r\\n\\t\\t\\t\\t`No connection found for server: ${serverName}. Please make sure to use MCP servers available under 'Connected MCP Servers'.`,\\r\\n\\t\\t\\t)\\r\\n\\t\\t}\\r\\n\\t\\treturn await connection.client.request(\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\tmethod: \\\"tools/call\\\",\\r\\n\\t\\t\\t\\tparams: {\\r\\n\\t\\t\\t\\t\\tname: toolName,\\r\\n\\t\\t\\t\\t\\targuments: toolArguments,\\r\\n\\t\\t\\t\\t},\\r\\n\\t\\t\\t},\\r\\n\\t\\t\\tCallToolResultSchema,\\r\\n\\t\\t)\\r\\n\\t}\\r\\n\\r\\n\\tasync dispose(): Promise<void> {\\r\\n\\t\\tthis.removeAllFileWatchers()\\r\\n\\t\\tfor (const connection of this.connections) {\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\tawait this.deleteConnection(connection.server.name)\\r\\n\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\tconsole.error(`Failed to close connection for ${connection.server.name}:`, error)\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\tthis.connections = []\\r\\n\\t\\tif (this.settingsWatcher) {\\r\\n\\t\\t\\tthis.settingsWatcher.dispose()\\r\\n\\t\\t}\\r\\n\\t\\tthis.disposables.forEach((d) => d.dispose())\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":17902,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.962Z\",\"createdTime\":\"2025-01-15T15:26:07.962Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":495,\"nonEmptyLines\":450,\"commentLines\":53,\"complexity\":67},\"dependencies\":[\"@modelcontextprotocol/sdk/client/index.js\",\"@modelcontextprotocol/sdk/client/stdio.js\",\"chokidar\",\"delay\",\"fast-deep-equal\",\"fs/promises\",\"path\",\"vscode\",\"zod\",\"../../core/webview/ClineProvider\",\"../../shared/mcp\",\"../../utils/fs\",\"../../utils/path\"],\"quality\":{\"score\":-777,\"issues\":[],\"duplicateLines\":171,\"longLines\":11,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.389Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":495},\"analysis\":{\"metrics\":{\"lines\":495,\"nonEmptyLines\":450,\"commentLines\":53,\"complexity\":67},\"dependencies\":[\"@modelcontextprotocol/sdk/client/index.js\",\"@modelcontextprotocol/sdk/client/stdio.js\",\"chokidar\",\"delay\",\"fast-deep-equal\",\"fs/promises\",\"path\",\"vscode\",\"zod\",\"../../core/webview/ClineProvider\",\"../../shared/mcp\",\"../../utils/fs\",\"../../utils/path\"],\"quality\":{\"score\":-777,\"issues\":[],\"duplicateLines\":171,\"longLines\":11,\"complexFunctions\":0}},\"lastModified\":1737046855390,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.390Z\",\"size\":21040},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\services\\\\glob\\\\list-files.ts\",\"content\":{\"content\":\"import { globby, Options } from \\\"globby\\\"\\r\\nimport os from \\\"os\\\"\\r\\nimport * as path from \\\"path\\\"\\r\\nimport { arePathsEqual } from \\\"../../utils/path\\\"\\r\\n\\r\\nexport async function listFiles(dirPath: string, recursive: boolean, limit: number): Promise<[string[], boolean]> {\\r\\n\\tconst absolutePath = path.resolve(dirPath)\\r\\n\\t// Do not allow listing files in root or home directory, which cline tends to want to do when the user's prompt is vague.\\r\\n\\tconst root = process.platform === \\\"win32\\\" ? path.parse(absolutePath).root : \\\"/\\\"\\r\\n\\tconst isRoot = arePathsEqual(absolutePath, root)\\r\\n\\tif (isRoot) {\\r\\n\\t\\treturn [[root], false]\\r\\n\\t}\\r\\n\\tconst homeDir = os.homedir()\\r\\n\\tconst isHomeDir = arePathsEqual(absolutePath, homeDir)\\r\\n\\tif (isHomeDir) {\\r\\n\\t\\treturn [[homeDir], false]\\r\\n\\t}\\r\\n\\r\\n\\tconst dirsToIgnore = [\\r\\n\\t\\t\\\"node_modules\\\",\\r\\n\\t\\t\\\"__pycache__\\\",\\r\\n\\t\\t\\\"env\\\",\\r\\n\\t\\t\\\"venv\\\",\\r\\n\\t\\t\\\"target/dependency\\\",\\r\\n\\t\\t\\\"build/dependencies\\\",\\r\\n\\t\\t\\\"dist\\\",\\r\\n\\t\\t\\\"out\\\",\\r\\n\\t\\t\\\"bundle\\\",\\r\\n\\t\\t\\\"vendor\\\",\\r\\n\\t\\t\\\"tmp\\\",\\r\\n\\t\\t\\\"temp\\\",\\r\\n\\t\\t\\\"deps\\\",\\r\\n\\t\\t\\\"pkg\\\",\\r\\n\\t\\t\\\"Pods\\\",\\r\\n\\t\\t\\\".*\\\", // '!**/.*' excludes hidden directories, while '!**/.*/**' excludes only their contents. This way we are at least aware of the existence of hidden directories.\\r\\n\\t].map((dir) => `**/${dir}/**`)\\r\\n\\r\\n\\tconst options = {\\r\\n\\t\\tcwd: dirPath,\\r\\n\\t\\tdot: true, // do not ignore hidden files/directories\\r\\n\\t\\tabsolute: true,\\r\\n\\t\\tmarkDirectories: true, // Append a / on any directories matched (/ is used on windows as well, so dont use path.sep)\\r\\n\\t\\tgitignore: recursive, // globby ignores any files that are gitignored\\r\\n\\t\\tignore: recursive ? dirsToIgnore : undefined, // just in case there is no gitignore, we ignore sensible defaults\\r\\n\\t\\tonlyFiles: false, // true by default, false means it will list directories on their own too\\r\\n\\t}\\r\\n\\t// * globs all files in one dir, ** globs files in nested directories\\r\\n\\tconst files = recursive ? await globbyLevelByLevel(limit, options) : (await globby(\\\"*\\\", options)).slice(0, limit)\\r\\n\\treturn [files, files.length >= limit]\\r\\n}\\r\\n\\r\\n/*\\r\\nBreadth-first traversal of directory structure level by level up to a limit:\\r\\n - Queue-based approach ensures proper breadth-first traversal\\r\\n - Processes directory patterns level by level\\r\\n - Captures a representative sample of the directory structure up to the limit\\r\\n - Minimizes risk of missing deeply nested files\\r\\n\\r\\n- Notes:\\r\\n - Relies on globby to mark directories with /\\r\\n - Potential for loops if symbolic links reference back to parent (we could use followSymlinks: false but that may not be ideal for some projects and it's pointless if they're not using symlinks wrong)\\r\\n - Timeout mechanism prevents infinite loops\\r\\n*/\\r\\nasync function globbyLevelByLevel(limit: number, options?: Options) {\\r\\n\\tlet results: Set<string> = new Set()\\r\\n\\tlet queue: string[] = [\\\"*\\\"]\\r\\n\\r\\n\\tconst globbingProcess = async () => {\\r\\n\\t\\twhile (queue.length > 0 && results.size < limit) {\\r\\n\\t\\t\\tconst pattern = queue.shift()!\\r\\n\\t\\t\\tconst filesAtLevel = await globby(pattern, options)\\r\\n\\r\\n\\t\\t\\tfor (const file of filesAtLevel) {\\r\\n\\t\\t\\t\\tif (results.size >= limit) {\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tresults.add(file)\\r\\n\\t\\t\\t\\tif (file.endsWith(\\\"/\\\")) {\\r\\n\\t\\t\\t\\t\\tqueue.push(`${file}*`)\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\treturn Array.from(results).slice(0, limit)\\r\\n\\t}\\r\\n\\r\\n\\t// Timeout after 10 seconds and return partial results\\r\\n\\tconst timeoutPromise = new Promise<string[]>((_, reject) => {\\r\\n\\t\\tsetTimeout(() => reject(new Error(\\\"Globbing timeout\\\")), 10_000)\\r\\n\\t})\\r\\n\\ttry {\\r\\n\\t\\treturn await Promise.race([globbingProcess(), timeoutPromise])\\r\\n\\t} catch (error) {\\r\\n\\t\\tconsole.warn(\\\"Globbing timed out, returning partial results\\\")\\r\\n\\t\\treturn Array.from(results)\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":3555,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.850Z\",\"createdTime\":\"2024-12-29T05:57:23.850Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":98,\"nonEmptyLines\":89,\"commentLines\":4,\"complexity\":14},\"dependencies\":[\"globby\",\"os\",\"path\",\"../../utils/path\"],\"quality\":{\"score\":36,\"issues\":[],\"duplicateLines\":10,\"longLines\":7,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.388Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":98},\"analysis\":{\"metrics\":{\"lines\":98,\"nonEmptyLines\":89,\"commentLines\":4,\"complexity\":14},\"dependencies\":[\"globby\",\"os\",\"path\",\"../../utils/path\"],\"quality\":{\"score\":36,\"issues\":[],\"duplicateLines\":10,\"longLines\":7,\"complexFunctions\":0}},\"lastModified\":1737046855388,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.388Z\",\"size\":4431},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\services\\\\browser\\\\UrlContentFetcher.ts\",\"content\":{\"content\":\"import * as vscode from \\\"vscode\\\"\\r\\nimport * as fs from \\\"fs/promises\\\"\\r\\nimport * as path from \\\"path\\\"\\r\\nimport { Browser, Page, launch } from \\\"puppeteer-core\\\"\\r\\nimport * as cheerio from \\\"cheerio\\\"\\r\\nimport TurndownService from \\\"turndown\\\"\\r\\n// @ts-ignore\\r\\nimport PCR from \\\"puppeteer-chromium-resolver\\\"\\r\\nimport { fileExistsAtPath } from \\\"../../utils/fs\\\"\\r\\n\\r\\ninterface PCRStats {\\r\\n\\tpuppeteer: { launch: typeof launch }\\r\\n\\texecutablePath: string\\r\\n}\\r\\n\\r\\nexport class UrlContentFetcher {\\r\\n\\tprivate context: vscode.ExtensionContext\\r\\n\\tprivate browser?: Browser\\r\\n\\tprivate page?: Page\\r\\n\\r\\n\\tconstructor(context: vscode.ExtensionContext) {\\r\\n\\t\\tthis.context = context\\r\\n\\t}\\r\\n\\r\\n\\tprivate async ensureChromiumExists(): Promise<PCRStats> {\\r\\n\\t\\tconst globalStoragePath = this.context?.globalStorageUri?.fsPath\\r\\n\\t\\tif (!globalStoragePath) {\\r\\n\\t\\t\\tthrow new Error(\\\"Global storage uri is invalid\\\")\\r\\n\\t\\t}\\r\\n\\t\\tconst puppeteerDir = path.join(globalStoragePath, \\\"puppeteer\\\")\\r\\n\\t\\tconst dirExists = await fileExistsAtPath(puppeteerDir)\\r\\n\\t\\tif (!dirExists) {\\r\\n\\t\\t\\tawait fs.mkdir(puppeteerDir, { recursive: true })\\r\\n\\t\\t}\\r\\n\\t\\t// if chromium doesn't exist, this will download it to path.join(puppeteerDir, \\\".chromium-browser-snapshots\\\")\\r\\n\\t\\t// if it does exist it will return the path to existing chromium\\r\\n\\t\\tconst stats: PCRStats = await PCR({\\r\\n\\t\\t\\tdownloadPath: puppeteerDir,\\r\\n\\t\\t})\\r\\n\\t\\treturn stats\\r\\n\\t}\\r\\n\\r\\n\\tasync launchBrowser(): Promise<void> {\\r\\n\\t\\tif (this.browser) {\\r\\n\\t\\t\\treturn\\r\\n\\t\\t}\\r\\n\\t\\tconst stats = await this.ensureChromiumExists()\\r\\n\\t\\tthis.browser = await stats.puppeteer.launch({\\r\\n\\t\\t\\targs: [\\r\\n\\t\\t\\t\\t\\\"--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36\\\",\\r\\n\\t\\t\\t],\\r\\n\\t\\t\\texecutablePath: stats.executablePath,\\r\\n\\t\\t})\\r\\n\\t\\t// (latest version of puppeteer does not add headless to user agent)\\r\\n\\t\\tthis.page = await this.browser?.newPage()\\r\\n\\t}\\r\\n\\r\\n\\tasync closeBrowser(): Promise<void> {\\r\\n\\t\\tawait this.browser?.close()\\r\\n\\t\\tthis.browser = undefined\\r\\n\\t\\tthis.page = undefined\\r\\n\\t}\\r\\n\\r\\n\\t// must make sure to call launchBrowser before and closeBrowser after using this\\r\\n\\tasync urlToMarkdown(url: string): Promise<string> {\\r\\n\\t\\tif (!this.browser || !this.page) {\\r\\n\\t\\t\\tthrow new Error(\\\"Browser not initialized\\\")\\r\\n\\t\\t}\\r\\n\\t\\t/*\\r\\n\\t\\t- networkidle2 is equivalent to playwright's networkidle where it waits until there are no more than 2 network connections for at least 500 ms.\\r\\n\\t\\t- domcontentloaded is when the basic DOM is loaded\\r\\n\\t\\tthis should be sufficient for most doc sites\\r\\n\\t\\t*/\\r\\n\\t\\tawait this.page.goto(url, {\\r\\n\\t\\t\\ttimeout: 10_000,\\r\\n\\t\\t\\twaitUntil: [\\\"domcontentloaded\\\", \\\"networkidle2\\\"],\\r\\n\\t\\t})\\r\\n\\t\\tconst content = await this.page.content()\\r\\n\\r\\n\\t\\t// use cheerio to parse and clean up the HTML\\r\\n\\t\\tconst $ = cheerio.load(content)\\r\\n\\t\\t$(\\\"script, style, nav, footer, header\\\").remove()\\r\\n\\r\\n\\t\\t// convert cleaned HTML to markdown\\r\\n\\t\\tconst turndownService = new TurndownService()\\r\\n\\t\\tconst markdown = turndownService.turndown($.html())\\r\\n\\r\\n\\t\\treturn markdown\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":2941,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.961Z\",\"createdTime\":\"2025-01-15T15:26:07.961Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":91,\"nonEmptyLines\":80,\"commentLines\":8,\"complexity\":12},\"dependencies\":[\"vscode\",\"fs/promises\",\"path\",\"puppeteer-core\",\"cheerio\",\"turndown\",\"puppeteer-chromium-resolver\",\"../../utils/fs\"],\"quality\":{\"score\":34,\"issues\":[],\"duplicateLines\":12,\"longLines\":3,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.386Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":91},\"analysis\":{\"metrics\":{\"lines\":91,\"nonEmptyLines\":80,\"commentLines\":8,\"complexity\":12},\"dependencies\":[\"vscode\",\"fs/promises\",\"path\",\"puppeteer-core\",\"cheerio\",\"turndown\",\"puppeteer-chromium-resolver\",\"../../utils/fs\"],\"quality\":{\"score\":34,\"issues\":[],\"duplicateLines\":12,\"longLines\":3,\"complexFunctions\":0}},\"lastModified\":1737046855387,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.387Z\",\"size\":3853},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\services\\\\browser\\\\BrowserSession.ts\",\"content\":{\"content\":\"import * as vscode from \\\"vscode\\\"\\r\\nimport * as fs from \\\"fs/promises\\\"\\r\\nimport * as path from \\\"path\\\"\\r\\nimport { Browser, Page, ScreenshotOptions, TimeoutError, launch } from \\\"puppeteer-core\\\"\\r\\n// @ts-ignore\\r\\nimport PCR from \\\"puppeteer-chromium-resolver\\\"\\r\\nimport pWaitFor from \\\"p-wait-for\\\"\\r\\nimport delay from \\\"delay\\\"\\r\\nimport { fileExistsAtPath } from \\\"../../utils/fs\\\"\\r\\nimport { BrowserActionResult } from \\\"../../shared/ExtensionMessage\\\"\\r\\n\\r\\ninterface PCRStats {\\r\\n\\tpuppeteer: { launch: typeof launch }\\r\\n\\texecutablePath: string\\r\\n}\\r\\n\\r\\nexport class BrowserSession {\\r\\n\\tprivate context: vscode.ExtensionContext\\r\\n\\tprivate browser?: Browser\\r\\n\\tprivate page?: Page\\r\\n\\tprivate currentMousePosition?: string\\r\\n\\r\\n\\tconstructor(context: vscode.ExtensionContext) {\\r\\n\\t\\tthis.context = context\\r\\n\\t}\\r\\n\\r\\n\\tprivate async ensureChromiumExists(): Promise<PCRStats> {\\r\\n\\t\\tconst globalStoragePath = this.context?.globalStorageUri?.fsPath\\r\\n\\t\\tif (!globalStoragePath) {\\r\\n\\t\\t\\tthrow new Error(\\\"Global storage uri is invalid\\\")\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tconst puppeteerDir = path.join(globalStoragePath, \\\"puppeteer\\\")\\r\\n\\t\\tconst dirExists = await fileExistsAtPath(puppeteerDir)\\r\\n\\t\\tif (!dirExists) {\\r\\n\\t\\t\\tawait fs.mkdir(puppeteerDir, { recursive: true })\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// if chromium doesn't exist, this will download it to path.join(puppeteerDir, \\\".chromium-browser-snapshots\\\")\\r\\n\\t\\t// if it does exist it will return the path to existing chromium\\r\\n\\t\\tconst stats: PCRStats = await PCR({\\r\\n\\t\\t\\tdownloadPath: puppeteerDir,\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\treturn stats\\r\\n\\t}\\r\\n\\r\\n\\tasync launchBrowser() {\\r\\n\\t\\tconsole.log(\\\"launch browser called\\\")\\r\\n\\t\\tif (this.browser) {\\r\\n\\t\\t\\t// throw new Error(\\\"Browser already launched\\\")\\r\\n\\t\\t\\tawait this.closeBrowser() // this may happen when the model launches a browser again after having used it already before\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tconst stats = await this.ensureChromiumExists()\\r\\n\\t\\tthis.browser = await stats.puppeteer.launch({\\r\\n\\t\\t\\targs: [\\r\\n\\t\\t\\t\\t\\\"--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36\\\",\\r\\n\\t\\t\\t],\\r\\n\\t\\t\\texecutablePath: stats.executablePath,\\r\\n\\t\\t\\tdefaultViewport: {\\r\\n\\t\\t\\t\\twidth: 900,\\r\\n\\t\\t\\t\\theight: 600,\\r\\n\\t\\t\\t},\\r\\n\\t\\t\\t// headless: false,\\r\\n\\t\\t})\\r\\n\\t\\t// (latest version of puppeteer does not add headless to user agent)\\r\\n\\t\\tthis.page = await this.browser?.newPage()\\r\\n\\t}\\r\\n\\r\\n\\tasync closeBrowser(): Promise<BrowserActionResult> {\\r\\n\\t\\tif (this.browser || this.page) {\\r\\n\\t\\t\\tconsole.log(\\\"closing browser...\\\")\\r\\n\\t\\t\\tawait this.browser?.close().catch(() => {})\\r\\n\\t\\t\\tthis.browser = undefined\\r\\n\\t\\t\\tthis.page = undefined\\r\\n\\t\\t\\tthis.currentMousePosition = undefined\\r\\n\\t\\t}\\r\\n\\t\\treturn {}\\r\\n\\t}\\r\\n\\r\\n\\tasync doAction(action: (page: Page) => Promise<void>): Promise<BrowserActionResult> {\\r\\n\\t\\tif (!this.page) {\\r\\n\\t\\t\\tthrow new Error(\\r\\n\\t\\t\\t\\t\\\"Browser is not launched. This may occur if the browser was automatically closed by a non-`browser_action` tool.\\\",\\r\\n\\t\\t\\t)\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tconst logs: string[] = []\\r\\n\\t\\tlet lastLogTs = Date.now()\\r\\n\\r\\n\\t\\tconst consoleListener = (msg: any) => {\\r\\n\\t\\t\\tif (msg.type() === \\\"log\\\") {\\r\\n\\t\\t\\t\\tlogs.push(msg.text())\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tlogs.push(`[${msg.type()}] ${msg.text()}`)\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tlastLogTs = Date.now()\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tconst errorListener = (err: Error) => {\\r\\n\\t\\t\\tlogs.push(`[Page Error] ${err.toString()}`)\\r\\n\\t\\t\\tlastLogTs = Date.now()\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// Add the listeners\\r\\n\\t\\tthis.page.on(\\\"console\\\", consoleListener)\\r\\n\\t\\tthis.page.on(\\\"pageerror\\\", errorListener)\\r\\n\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tawait action(this.page)\\r\\n\\t\\t} catch (err) {\\r\\n\\t\\t\\tif (!(err instanceof TimeoutError)) {\\r\\n\\t\\t\\t\\tlogs.push(`[Error] ${err.toString()}`)\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// Wait for console inactivity, with a timeout\\r\\n\\t\\tawait pWaitFor(() => Date.now() - lastLogTs >= 500, {\\r\\n\\t\\t\\ttimeout: 3_000,\\r\\n\\t\\t\\tinterval: 100,\\r\\n\\t\\t}).catch(() => {})\\r\\n\\r\\n\\t\\tlet options: ScreenshotOptions = {\\r\\n\\t\\t\\tencoding: \\\"base64\\\",\\r\\n\\r\\n\\t\\t\\t// clip: {\\r\\n\\t\\t\\t// \\tx: 0,\\r\\n\\t\\t\\t// \\ty: 0,\\r\\n\\t\\t\\t// \\twidth: 900,\\r\\n\\t\\t\\t// \\theight: 600,\\r\\n\\t\\t\\t// },\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tlet screenshotBase64 = await this.page.screenshot({\\r\\n\\t\\t\\t...options,\\r\\n\\t\\t\\ttype: \\\"webp\\\",\\r\\n\\t\\t})\\r\\n\\t\\tlet screenshot = `data:image/webp;base64,${screenshotBase64}`\\r\\n\\r\\n\\t\\tif (!screenshotBase64) {\\r\\n\\t\\t\\tconsole.log(\\\"webp screenshot failed, trying png\\\")\\r\\n\\t\\t\\tscreenshotBase64 = await this.page.screenshot({\\r\\n\\t\\t\\t\\t...options,\\r\\n\\t\\t\\t\\ttype: \\\"png\\\",\\r\\n\\t\\t\\t})\\r\\n\\t\\t\\tscreenshot = `data:image/png;base64,${screenshotBase64}`\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!screenshotBase64) {\\r\\n\\t\\t\\tthrow new Error(\\\"Failed to take screenshot.\\\")\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// this.page.removeAllListeners() <- causes the page to crash!\\r\\n\\t\\tthis.page.off(\\\"console\\\", consoleListener)\\r\\n\\t\\tthis.page.off(\\\"pageerror\\\", errorListener)\\r\\n\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tscreenshot,\\r\\n\\t\\t\\tlogs: logs.join(\\\"\\\\n\\\"),\\r\\n\\t\\t\\tcurrentUrl: this.page.url(),\\r\\n\\t\\t\\tcurrentMousePosition: this.currentMousePosition,\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tasync navigateToUrl(url: string): Promise<BrowserActionResult> {\\r\\n\\t\\treturn this.doAction(async (page) => {\\r\\n\\t\\t\\t// networkidle2 isn't good enough since page may take some time to load. we can assume locally running dev sites will reach networkidle0 in a reasonable amount of time\\r\\n\\t\\t\\tawait page.goto(url, {\\r\\n\\t\\t\\t\\ttimeout: 7_000,\\r\\n\\t\\t\\t\\twaitUntil: [\\\"domcontentloaded\\\", \\\"networkidle2\\\"],\\r\\n\\t\\t\\t})\\r\\n\\t\\t\\t// await page.goto(url, { timeout: 10_000, waitUntil: \\\"load\\\" })\\r\\n\\t\\t\\tawait this.waitTillHTMLStable(page) // in case the page is loading more resources\\r\\n\\t\\t})\\r\\n\\t}\\r\\n\\r\\n\\t// page.goto { waitUntil: \\\"networkidle0\\\" } may not ever resolve, and not waiting could return page content too early before js has loaded\\r\\n\\t// https://stackoverflow.com/questions/52497252/puppeteer-wait-until-page-is-completely-loaded/61304202#61304202\\r\\n\\tprivate async waitTillHTMLStable(page: Page, timeout = 5_000) {\\r\\n\\t\\tconst checkDurationMsecs = 500 // 1000\\r\\n\\t\\tconst maxChecks = timeout / checkDurationMsecs\\r\\n\\t\\tlet lastHTMLSize = 0\\r\\n\\t\\tlet checkCounts = 1\\r\\n\\t\\tlet countStableSizeIterations = 0\\r\\n\\t\\tconst minStableSizeIterations = 3\\r\\n\\r\\n\\t\\twhile (checkCounts++ <= maxChecks) {\\r\\n\\t\\t\\tlet html = await page.content()\\r\\n\\t\\t\\tlet currentHTMLSize = html.length\\r\\n\\r\\n\\t\\t\\t// let bodyHTMLSize = await page.evaluate(() => document.body.innerHTML.length)\\r\\n\\t\\t\\tconsole.log(\\\"last: \\\", lastHTMLSize, \\\" <> curr: \\\", currentHTMLSize)\\r\\n\\r\\n\\t\\t\\tif (lastHTMLSize !== 0 && currentHTMLSize === lastHTMLSize) {\\r\\n\\t\\t\\t\\tcountStableSizeIterations++\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tcountStableSizeIterations = 0 //reset the counter\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif (countStableSizeIterations >= minStableSizeIterations) {\\r\\n\\t\\t\\t\\tconsole.log(\\\"Page rendered fully...\\\")\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tlastHTMLSize = currentHTMLSize\\r\\n\\t\\t\\tawait delay(checkDurationMsecs)\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tasync click(coordinate: string): Promise<BrowserActionResult> {\\r\\n\\t\\tconst [x, y] = coordinate.split(\\\",\\\").map(Number)\\r\\n\\t\\treturn this.doAction(async (page) => {\\r\\n\\t\\t\\t// Set up network request monitoring\\r\\n\\t\\t\\tlet hasNetworkActivity = false\\r\\n\\t\\t\\tconst requestListener = () => {\\r\\n\\t\\t\\t\\thasNetworkActivity = true\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tpage.on(\\\"request\\\", requestListener)\\r\\n\\r\\n\\t\\t\\t// Perform the click\\r\\n\\t\\t\\tawait page.mouse.click(x, y)\\r\\n\\t\\t\\tthis.currentMousePosition = coordinate\\r\\n\\r\\n\\t\\t\\t// Small delay to check if click triggered any network activity\\r\\n\\t\\t\\tawait delay(100)\\r\\n\\r\\n\\t\\t\\tif (hasNetworkActivity) {\\r\\n\\t\\t\\t\\t// If we detected network activity, wait for navigation/loading\\r\\n\\t\\t\\t\\tawait page\\r\\n\\t\\t\\t\\t\\t.waitForNavigation({\\r\\n\\t\\t\\t\\t\\t\\twaitUntil: [\\\"domcontentloaded\\\", \\\"networkidle2\\\"],\\r\\n\\t\\t\\t\\t\\t\\ttimeout: 7000,\\r\\n\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t.catch(() => {})\\r\\n\\t\\t\\t\\tawait this.waitTillHTMLStable(page)\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t// Clean up listener\\r\\n\\t\\t\\tpage.off(\\\"request\\\", requestListener)\\r\\n\\t\\t})\\r\\n\\t}\\r\\n\\r\\n\\tasync type(text: string): Promise<BrowserActionResult> {\\r\\n\\t\\treturn this.doAction(async (page) => {\\r\\n\\t\\t\\tawait page.keyboard.type(text)\\r\\n\\t\\t})\\r\\n\\t}\\r\\n\\r\\n\\tasync scrollDown(): Promise<BrowserActionResult> {\\r\\n\\t\\treturn this.doAction(async (page) => {\\r\\n\\t\\t\\tawait page.evaluate(() => {\\r\\n\\t\\t\\t\\twindow.scrollBy({\\r\\n\\t\\t\\t\\t\\ttop: 600,\\r\\n\\t\\t\\t\\t\\tbehavior: \\\"auto\\\",\\r\\n\\t\\t\\t\\t})\\r\\n\\t\\t\\t})\\r\\n\\t\\t\\tawait delay(300)\\r\\n\\t\\t})\\r\\n\\t}\\r\\n\\r\\n\\tasync scrollUp(): Promise<BrowserActionResult> {\\r\\n\\t\\treturn this.doAction(async (page) => {\\r\\n\\t\\t\\tawait page.evaluate(() => {\\r\\n\\t\\t\\t\\twindow.scrollBy({\\r\\n\\t\\t\\t\\t\\ttop: -600,\\r\\n\\t\\t\\t\\t\\tbehavior: \\\"auto\\\",\\r\\n\\t\\t\\t\\t})\\r\\n\\t\\t\\t})\\r\\n\\t\\t\\tawait delay(300)\\r\\n\\t\\t})\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":7919,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.960Z\",\"createdTime\":\"2025-01-15T15:26:07.960Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":274,\"nonEmptyLines\":234,\"commentLines\":25,\"complexity\":30},\"dependencies\":[\"vscode\",\"fs/promises\",\"path\",\"puppeteer-core\",\"puppeteer-chromium-resolver\",\"p-wait-for\",\"delay\",\"../../utils/fs\",\"../../shared/ExtensionMessage\"],\"quality\":{\"score\":-204,\"issues\":[],\"duplicateLines\":58,\"longLines\":7,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.385Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":274},\"analysis\":{\"metrics\":{\"lines\":274,\"nonEmptyLines\":234,\"commentLines\":25,\"complexity\":30},\"dependencies\":[\"vscode\",\"fs/promises\",\"path\",\"puppeteer-core\",\"puppeteer-chromium-resolver\",\"p-wait-for\",\"delay\",\"../../utils/fs\",\"../../shared/ExtensionMessage\"],\"quality\":{\"score\":-204,\"issues\":[],\"duplicateLines\":58,\"longLines\":7,\"complexFunctions\":0}},\"lastModified\":1737046855385,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.385Z\",\"size\":9731},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\integrations\\\\workspace\\\\WorkspaceTracker.ts\",\"content\":{\"content\":\"import * as vscode from \\\"vscode\\\"\\r\\nimport * as path from \\\"path\\\"\\r\\nimport { listFiles } from \\\"../../services/glob/list-files\\\"\\r\\nimport { ClineProvider } from \\\"../../core/webview/ClineProvider\\\"\\r\\n\\r\\nconst cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0)\\r\\n\\r\\n// Note: this is not a drop-in replacement for listFiles at the start of tasks, since that will be done for Desktops when there is no workspace selected\\r\\nclass WorkspaceTracker {\\r\\n\\tprivate providerRef: WeakRef<ClineProvider>\\r\\n\\tprivate disposables: vscode.Disposable[] = []\\r\\n\\tprivate filePaths: Set<string> = new Set()\\r\\n\\r\\n\\tconstructor(provider: ClineProvider) {\\r\\n\\t\\tthis.providerRef = new WeakRef(provider)\\r\\n\\t\\tthis.registerListeners()\\r\\n\\t}\\r\\n\\r\\n\\tasync initializeFilePaths() {\\r\\n\\t\\t// should not auto get filepaths for desktop since it would immediately show permission popup before cline ever creates a file\\r\\n\\t\\tif (!cwd) {\\r\\n\\t\\t\\treturn\\r\\n\\t\\t}\\r\\n\\t\\tconst [files, _] = await listFiles(cwd, true, 1_000)\\r\\n\\t\\tfiles.forEach((file) => this.filePaths.add(this.normalizeFilePath(file)))\\r\\n\\t\\tthis.workspaceDidUpdate()\\r\\n\\t}\\r\\n\\r\\n\\tprivate registerListeners() {\\r\\n\\t\\t// Listen for file creation\\r\\n\\t\\t// .bind(this) ensures the callback refers to class instance when using this, not necessary when using arrow function\\r\\n\\t\\tthis.disposables.push(vscode.workspace.onDidCreateFiles(this.onFilesCreated.bind(this)))\\r\\n\\r\\n\\t\\t// Listen for file deletion\\r\\n\\t\\tthis.disposables.push(vscode.workspace.onDidDeleteFiles(this.onFilesDeleted.bind(this)))\\r\\n\\r\\n\\t\\t// Listen for file renaming\\r\\n\\t\\tthis.disposables.push(vscode.workspace.onDidRenameFiles(this.onFilesRenamed.bind(this)))\\r\\n\\r\\n\\t\\t/*\\r\\n\\t\\t An event that is emitted when a workspace folder is added or removed.\\r\\n\\t\\t **Note:** this event will not fire if the first workspace folder is added, removed or changed,\\r\\n\\t\\t because in that case the currently executing extensions (including the one that listens to this\\r\\n\\t\\t event) will be terminated and restarted so that the (deprecated) `rootPath` property is updated\\r\\n\\t\\t to point to the first workspace folder.\\r\\n\\t\\t */\\r\\n\\t\\t// In other words, we don't have to worry about the root workspace folder ([0]) changing since the extension will be restarted and our cwd will be updated to reflect the new workspace folder. (We don't care about non root workspace folders, since cline will only be working within the root folder cwd)\\r\\n\\t\\t// this.disposables.push(vscode.workspace.onDidChangeWorkspaceFolders(this.onWorkspaceFoldersChanged.bind(this)))\\r\\n\\t}\\r\\n\\r\\n\\tprivate async onFilesCreated(event: vscode.FileCreateEvent) {\\r\\n\\t\\tawait Promise.all(\\r\\n\\t\\t\\tevent.files.map(async (file) => {\\r\\n\\t\\t\\t\\tawait this.addFilePath(file.fsPath)\\r\\n\\t\\t\\t}),\\r\\n\\t\\t)\\r\\n\\t\\tthis.workspaceDidUpdate()\\r\\n\\t}\\r\\n\\r\\n\\tprivate async onFilesDeleted(event: vscode.FileDeleteEvent) {\\r\\n\\t\\tlet updated = false\\r\\n\\t\\tawait Promise.all(\\r\\n\\t\\t\\tevent.files.map(async (file) => {\\r\\n\\t\\t\\t\\tif (await this.removeFilePath(file.fsPath)) {\\r\\n\\t\\t\\t\\t\\tupdated = true\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}),\\r\\n\\t\\t)\\r\\n\\t\\tif (updated) {\\r\\n\\t\\t\\tthis.workspaceDidUpdate()\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tprivate async onFilesRenamed(event: vscode.FileRenameEvent) {\\r\\n\\t\\tawait Promise.all(\\r\\n\\t\\t\\tevent.files.map(async (file) => {\\r\\n\\t\\t\\t\\tawait this.removeFilePath(file.oldUri.fsPath)\\r\\n\\t\\t\\t\\tawait this.addFilePath(file.newUri.fsPath)\\r\\n\\t\\t\\t}),\\r\\n\\t\\t)\\r\\n\\t\\tthis.workspaceDidUpdate()\\r\\n\\t}\\r\\n\\r\\n\\tprivate workspaceDidUpdate() {\\r\\n\\t\\tif (!cwd) {\\r\\n\\t\\t\\treturn\\r\\n\\t\\t}\\r\\n\\t\\tthis.providerRef.deref()?.postMessageToWebview({\\r\\n\\t\\t\\ttype: \\\"workspaceUpdated\\\",\\r\\n\\t\\t\\tfilePaths: Array.from(this.filePaths).map((file) => {\\r\\n\\t\\t\\t\\tconst relativePath = path.relative(cwd, file).toPosix()\\r\\n\\t\\t\\t\\treturn file.endsWith(\\\"/\\\") ? relativePath + \\\"/\\\" : relativePath\\r\\n\\t\\t\\t}),\\r\\n\\t\\t})\\r\\n\\t}\\r\\n\\r\\n\\tprivate normalizeFilePath(filePath: string): string {\\r\\n\\t\\tconst resolvedPath = cwd ? path.resolve(cwd, filePath) : path.resolve(filePath)\\r\\n\\t\\treturn filePath.endsWith(\\\"/\\\") ? resolvedPath + \\\"/\\\" : resolvedPath\\r\\n\\t}\\r\\n\\r\\n\\tprivate async addFilePath(filePath: string): Promise<string> {\\r\\n\\t\\tconst normalizedPath = this.normalizeFilePath(filePath)\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tconst stat = await vscode.workspace.fs.stat(vscode.Uri.file(normalizedPath))\\r\\n\\t\\t\\tconst isDirectory = (stat.type & vscode.FileType.Directory) !== 0\\r\\n\\t\\t\\tconst pathWithSlash = isDirectory && !normalizedPath.endsWith(\\\"/\\\") ? normalizedPath + \\\"/\\\" : normalizedPath\\r\\n\\t\\t\\tthis.filePaths.add(pathWithSlash)\\r\\n\\t\\t\\treturn pathWithSlash\\r\\n\\t\\t} catch {\\r\\n\\t\\t\\t// If stat fails, assume it's a file (this can happen for newly created files)\\r\\n\\t\\t\\tthis.filePaths.add(normalizedPath)\\r\\n\\t\\t\\treturn normalizedPath\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tprivate async removeFilePath(filePath: string): Promise<boolean> {\\r\\n\\t\\tconst normalizedPath = this.normalizeFilePath(filePath)\\r\\n\\t\\treturn this.filePaths.delete(normalizedPath) || this.filePaths.delete(normalizedPath + \\\"/\\\")\\r\\n\\t}\\r\\n\\r\\n\\tpublic dispose() {\\r\\n\\t\\tthis.disposables.forEach((d) => d.dispose())\\r\\n\\t}\\r\\n}\\r\\n\\r\\nexport default WorkspaceTracker\\r\\n\",\"metadata\":{\"size\":4788,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.850Z\",\"createdTime\":\"2024-12-29T05:57:23.850Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":128,\"nonEmptyLines\":110,\"commentLines\":10,\"complexity\":14},\"dependencies\":[\"vscode\",\"path\",\"../../services/glob/list-files\",\"../../core/webview/ClineProvider\"],\"quality\":{\"score\":-67,\"issues\":[],\"duplicateLines\":31,\"longLines\":6,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.383Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":128},\"analysis\":{\"metrics\":{\"lines\":128,\"nonEmptyLines\":110,\"commentLines\":10,\"complexity\":14},\"dependencies\":[\"vscode\",\"path\",\"../../services/glob/list-files\",\"../../core/webview/ClineProvider\"],\"quality\":{\"score\":-67,\"issues\":[],\"duplicateLines\":31,\"longLines\":6,\"complexFunctions\":0}},\"lastModified\":1737046855384,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.384Z\",\"size\":5824},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\integrations\\\\workspace\\\\get-python-env.ts\",\"content\":{\"content\":\"import * as vscode from \\\"vscode\\\"\\r\\n\\r\\n/*\\r\\nUsed to get user's current python environment (unnecessary now that we use the IDE's terminal)\\r\\n${await (async () => {\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tconst pythonEnvPath = await getPythonEnvPath()\\r\\n\\t\\t\\tif (pythonEnvPath) {\\r\\n\\t\\t\\t\\treturn `\\\\nPython Environment: ${pythonEnvPath}`\\r\\n\\t\\t\\t}\\r\\n\\t\\t} catch {}\\r\\n\\t\\treturn \\\"\\\"\\r\\n\\t})()}\\r\\n*/\\r\\nexport async function getPythonEnvPath(): Promise<string | undefined> {\\r\\n\\tconst pythonExtension = vscode.extensions.getExtension(\\\"ms-python.python\\\")\\r\\n\\r\\n\\tif (!pythonExtension) {\\r\\n\\t\\treturn undefined\\r\\n\\t}\\r\\n\\r\\n\\t// Ensure the Python extension is activated\\r\\n\\tif (!pythonExtension.isActive) {\\r\\n\\t\\t// if the python extension is not active, we can assume the project is not a python project\\r\\n\\t\\treturn undefined\\r\\n\\t}\\r\\n\\r\\n\\t// Access the Python extension API\\r\\n\\tconst pythonApi = pythonExtension.exports\\r\\n\\t// Get the active environment path for the current workspace\\r\\n\\tconst workspaceFolder = vscode.workspace.workspaceFolders?.[0]\\r\\n\\tif (!workspaceFolder) {\\r\\n\\t\\treturn undefined\\r\\n\\t}\\r\\n\\t// Get the active python environment path for the current workspace\\r\\n\\tconst pythonEnv = await pythonApi?.environments?.getActiveEnvironmentPath(workspaceFolder.uri)\\r\\n\\tif (pythonEnv && pythonEnv.path) {\\r\\n\\t\\treturn pythonEnv.path\\r\\n\\t} else {\\r\\n\\t\\treturn undefined\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":1275,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.850Z\",\"createdTime\":\"2024-12-29T05:57:23.850Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":43,\"nonEmptyLines\":38,\"commentLines\":6,\"complexity\":11},\"dependencies\":[\"vscode\"],\"quality\":{\"score\":60,\"issues\":[],\"duplicateLines\":8,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.382Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":43},\"analysis\":{\"metrics\":{\"lines\":43,\"nonEmptyLines\":38,\"commentLines\":6,\"complexity\":11},\"dependencies\":[\"vscode\"],\"quality\":{\"score\":60,\"issues\":[],\"duplicateLines\":8,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855382,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.382Z\",\"size\":1878},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\integrations\\\\theme\\\\getTheme.ts\",\"content\":{\"content\":\"import * as vscode from \\\"vscode\\\"\\r\\nimport * as path from \\\"path\\\"\\r\\nimport * as fs from \\\"fs/promises\\\"\\r\\nimport { convertTheme } from \\\"monaco-vscode-textmate-theme-converter/lib/cjs\\\"\\r\\n\\r\\nconst defaultThemes: Record<string, string> = {\\r\\n\\t\\\"Default Dark Modern\\\": \\\"dark_modern\\\",\\r\\n\\t\\\"Dark+\\\": \\\"dark_plus\\\",\\r\\n\\t\\\"Default Dark+\\\": \\\"dark_plus\\\",\\r\\n\\t\\\"Dark (Visual Studio)\\\": \\\"dark_vs\\\",\\r\\n\\t\\\"Visual Studio Dark\\\": \\\"dark_vs\\\",\\r\\n\\t\\\"Dark High Contrast\\\": \\\"hc_black\\\",\\r\\n\\t\\\"Default High Contrast\\\": \\\"hc_black\\\",\\r\\n\\t\\\"Light High Contrast\\\": \\\"hc_light\\\",\\r\\n\\t\\\"Default High Contrast Light\\\": \\\"hc_light\\\",\\r\\n\\t\\\"Default Light Modern\\\": \\\"light_modern\\\",\\r\\n\\t\\\"Light+\\\": \\\"light_plus\\\",\\r\\n\\t\\\"Default Light+\\\": \\\"light_plus\\\",\\r\\n\\t\\\"Light (Visual Studio)\\\": \\\"light_vs\\\",\\r\\n\\t\\\"Visual Studio Light\\\": \\\"light_vs\\\",\\r\\n}\\r\\n\\r\\nfunction parseThemeString(themeString: string | undefined): any {\\r\\n\\tthemeString = themeString\\r\\n\\t\\t?.split(\\\"\\\\n\\\")\\r\\n\\t\\t.filter((line) => {\\r\\n\\t\\t\\treturn !line.trim().startsWith(\\\"//\\\")\\r\\n\\t\\t})\\r\\n\\t\\t.join(\\\"\\\\n\\\")\\r\\n\\treturn JSON.parse(themeString ?? \\\"{}\\\")\\r\\n}\\r\\n\\r\\nexport async function getTheme() {\\r\\n\\tlet currentTheme = undefined\\r\\n\\tconst colorTheme = vscode.workspace.getConfiguration(\\\"workbench\\\").get<string>(\\\"colorTheme\\\") || \\\"Default Dark Modern\\\"\\r\\n\\r\\n\\ttry {\\r\\n\\t\\tfor (let i = vscode.extensions.all.length - 1; i >= 0; i--) {\\r\\n\\t\\t\\tif (currentTheme) {\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tconst extension = vscode.extensions.all[i]\\r\\n\\t\\t\\tif (extension.packageJSON?.contributes?.themes?.length > 0) {\\r\\n\\t\\t\\t\\tfor (const theme of extension.packageJSON.contributes.themes) {\\r\\n\\t\\t\\t\\t\\tif (theme.label === colorTheme) {\\r\\n\\t\\t\\t\\t\\t\\tconst themePath = path.join(extension.extensionPath, theme.path)\\r\\n\\t\\t\\t\\t\\t\\tcurrentTheme = await fs.readFile(themePath, \\\"utf-8\\\")\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (currentTheme === undefined && defaultThemes[colorTheme]) {\\r\\n\\t\\t\\tconst filename = `${defaultThemes[colorTheme]}.json`\\r\\n\\t\\t\\tcurrentTheme = await fs.readFile(\\r\\n\\t\\t\\t\\tpath.join(getExtensionUri().fsPath, \\\"src\\\", \\\"integrations\\\", \\\"theme\\\", \\\"default-themes\\\", filename),\\r\\n\\t\\t\\t\\t\\\"utf-8\\\",\\r\\n\\t\\t\\t)\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// Strip comments from theme\\r\\n\\t\\tlet parsed = parseThemeString(currentTheme)\\r\\n\\r\\n\\t\\tif (parsed.include) {\\r\\n\\t\\t\\tconst includeThemeString = await fs.readFile(\\r\\n\\t\\t\\t\\tpath.join(getExtensionUri().fsPath, \\\"src\\\", \\\"integrations\\\", \\\"theme\\\", \\\"default-themes\\\", parsed.include),\\r\\n\\t\\t\\t\\t\\\"utf-8\\\",\\r\\n\\t\\t\\t)\\r\\n\\t\\t\\tconst includeTheme = parseThemeString(includeThemeString)\\r\\n\\t\\t\\tparsed = mergeJson(parsed, includeTheme)\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tconst converted = convertTheme(parsed)\\r\\n\\r\\n\\t\\tconverted.base = (\\r\\n\\t\\t\\t[\\\"vs\\\", \\\"hc-black\\\"].includes(converted.base) ? converted.base : colorTheme.includes(\\\"Light\\\") ? \\\"vs\\\" : \\\"vs-dark\\\"\\r\\n\\t\\t) as any\\r\\n\\r\\n\\t\\treturn converted\\r\\n\\t} catch (e) {\\r\\n\\t\\tconsole.log(\\\"Error loading color theme: \\\", e)\\r\\n\\t}\\r\\n\\treturn undefined\\r\\n}\\r\\n\\r\\ntype JsonObject = { [key: string]: any }\\r\\nexport function mergeJson(\\r\\n\\tfirst: JsonObject,\\r\\n\\tsecond: JsonObject,\\r\\n\\tmergeBehavior?: \\\"merge\\\" | \\\"overwrite\\\",\\r\\n\\tmergeKeys?: { [key: string]: (a: any, b: any) => boolean },\\r\\n): any {\\r\\n\\tconst copyOfFirst = JSON.parse(JSON.stringify(first))\\r\\n\\r\\n\\ttry {\\r\\n\\t\\tfor (const key in second) {\\r\\n\\t\\t\\tconst secondValue = second[key]\\r\\n\\r\\n\\t\\t\\tif (!(key in copyOfFirst) || mergeBehavior === \\\"overwrite\\\") {\\r\\n\\t\\t\\t\\t// New value\\r\\n\\t\\t\\t\\tcopyOfFirst[key] = secondValue\\r\\n\\t\\t\\t\\tcontinue\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tconst firstValue = copyOfFirst[key]\\r\\n\\t\\t\\tif (Array.isArray(secondValue) && Array.isArray(firstValue)) {\\r\\n\\t\\t\\t\\t// Array\\r\\n\\t\\t\\t\\tif (mergeKeys?.[key]) {\\r\\n\\t\\t\\t\\t\\t// Merge keys are used to determine whether an item form the second object should override one from the first\\r\\n\\t\\t\\t\\t\\tconst keptFromFirst: any[] = []\\r\\n\\t\\t\\t\\t\\tfirstValue.forEach((item: any) => {\\r\\n\\t\\t\\t\\t\\t\\tif (!secondValue.some((item2: any) => mergeKeys[key](item, item2))) {\\r\\n\\t\\t\\t\\t\\t\\t\\tkeptFromFirst.push(item)\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\tcopyOfFirst[key] = [...keptFromFirst, ...secondValue]\\r\\n\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\tcopyOfFirst[key] = [...firstValue, ...secondValue]\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} else if (typeof secondValue === \\\"object\\\" && typeof firstValue === \\\"object\\\") {\\r\\n\\t\\t\\t\\t// Object\\r\\n\\t\\t\\t\\tcopyOfFirst[key] = mergeJson(firstValue, secondValue, mergeBehavior)\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t// Other (boolean, number, string)\\r\\n\\t\\t\\t\\tcopyOfFirst[key] = secondValue\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\treturn copyOfFirst\\r\\n\\t} catch (e) {\\r\\n\\t\\tconsole.error(\\\"Error merging JSON\\\", e, copyOfFirst, second)\\r\\n\\t\\treturn {\\r\\n\\t\\t\\t...copyOfFirst,\\r\\n\\t\\t\\t...second,\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\nfunction getExtensionUri(): vscode.Uri {\\r\\n\\treturn vscode.extensions.getExtension(\\\"saoudrizwan.claude-dev\\\")!.extensionUri\\r\\n}\\r\\n\",\"metadata\":{\"size\":4348,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.960Z\",\"createdTime\":\"2025-01-15T15:26:07.960Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":142,\"nonEmptyLines\":126,\"commentLines\":6,\"complexity\":36},\"dependencies\":[\"vscode\",\"path\",\"fs/promises\",\"monaco-vscode-textmate-theme-converter/lib/cjs\"],\"quality\":{\"score\":-45,\"issues\":[],\"duplicateLines\":27,\"longLines\":5,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.380Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":142},\"analysis\":{\"metrics\":{\"lines\":142,\"nonEmptyLines\":126,\"commentLines\":6,\"complexity\":36},\"dependencies\":[\"vscode\",\"path\",\"fs/promises\",\"monaco-vscode-textmate-theme-converter/lib/cjs\"],\"quality\":{\"score\":-45,\"issues\":[],\"duplicateLines\":27,\"longLines\":5,\"complexFunctions\":0}},\"lastModified\":1737046855381,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.381Z\",\"size\":5594},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\integrations\\\\terminal\\\\TerminalRegistry.ts\",\"content\":{\"content\":\"import * as vscode from \\\"vscode\\\"\\r\\n\\r\\nexport interface TerminalInfo {\\r\\n\\tterminal: vscode.Terminal\\r\\n\\tbusy: boolean\\r\\n\\tlastCommand: string\\r\\n\\tid: number\\r\\n}\\r\\n\\r\\n// Although vscode.window.terminals provides a list of all open terminals, there's no way to know whether they're busy or not (exitStatus does not provide useful information for most commands). In order to prevent creating too many terminals, we need to keep track of terminals through the life of the extension, as well as session specific terminals for the life of a task (to get latest unretrieved output).\\r\\n// Since we have promises keeping track of terminal processes, we get the added benefit of keep track of busy terminals even after a task is closed.\\r\\nexport class TerminalRegistry {\\r\\n\\tprivate static terminals: TerminalInfo[] = []\\r\\n\\tprivate static nextTerminalId = 1\\r\\n\\r\\n\\tstatic createTerminal(cwd?: string | vscode.Uri | undefined): TerminalInfo {\\r\\n\\t\\tconst terminal = vscode.window.createTerminal({\\r\\n\\t\\t\\tcwd,\\r\\n\\t\\t\\tname: \\\"Cline\\\",\\r\\n\\t\\t\\ticonPath: new vscode.ThemeIcon(\\\"robot\\\"),\\r\\n\\t\\t})\\r\\n\\t\\tconst newInfo: TerminalInfo = {\\r\\n\\t\\t\\tterminal,\\r\\n\\t\\t\\tbusy: false,\\r\\n\\t\\t\\tlastCommand: \\\"\\\",\\r\\n\\t\\t\\tid: this.nextTerminalId++,\\r\\n\\t\\t}\\r\\n\\t\\tthis.terminals.push(newInfo)\\r\\n\\t\\treturn newInfo\\r\\n\\t}\\r\\n\\r\\n\\tstatic getTerminal(id: number): TerminalInfo | undefined {\\r\\n\\t\\tconst terminalInfo = this.terminals.find((t) => t.id === id)\\r\\n\\t\\tif (terminalInfo && this.isTerminalClosed(terminalInfo.terminal)) {\\r\\n\\t\\t\\tthis.removeTerminal(id)\\r\\n\\t\\t\\treturn undefined\\r\\n\\t\\t}\\r\\n\\t\\treturn terminalInfo\\r\\n\\t}\\r\\n\\r\\n\\tstatic updateTerminal(id: number, updates: Partial<TerminalInfo>) {\\r\\n\\t\\tconst terminal = this.getTerminal(id)\\r\\n\\t\\tif (terminal) {\\r\\n\\t\\t\\tObject.assign(terminal, updates)\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tstatic removeTerminal(id: number) {\\r\\n\\t\\tthis.terminals = this.terminals.filter((t) => t.id !== id)\\r\\n\\t}\\r\\n\\r\\n\\tstatic getAllTerminals(): TerminalInfo[] {\\r\\n\\t\\tthis.terminals = this.terminals.filter((t) => !this.isTerminalClosed(t.terminal))\\r\\n\\t\\treturn this.terminals\\r\\n\\t}\\r\\n\\r\\n\\t// The exit status of the terminal will be undefined while the terminal is active. (This value is set when onDidCloseTerminal is fired.)\\r\\n\\tprivate static isTerminalClosed(terminal: vscode.Terminal): boolean {\\r\\n\\t\\treturn terminal.exitStatus !== undefined\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":2207,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.846Z\",\"createdTime\":\"2024-12-29T05:57:23.846Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":62,\"nonEmptyLines\":53,\"commentLines\":3,\"complexity\":5},\"dependencies\":[\"vscode\"],\"quality\":{\"score\":44,\"issues\":[],\"duplicateLines\":10,\"longLines\":3,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.376Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":62},\"analysis\":{\"metrics\":{\"lines\":62,\"nonEmptyLines\":53,\"commentLines\":3,\"complexity\":5},\"dependencies\":[\"vscode\"],\"quality\":{\"score\":44,\"issues\":[],\"duplicateLines\":10,\"longLines\":3,\"complexFunctions\":0}},\"lastModified\":1737046855377,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.377Z\",\"size\":2883},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\integrations\\\\terminal\\\\TerminalProcess.ts\",\"content\":{\"content\":\"import { EventEmitter } from \\\"events\\\"\\r\\nimport stripAnsi from \\\"strip-ansi\\\"\\r\\nimport * as vscode from \\\"vscode\\\"\\r\\n\\r\\nexport interface TerminalProcessEvents {\\r\\n\\tline: [line: string]\\r\\n\\tcontinue: []\\r\\n\\tcompleted: []\\r\\n\\terror: [error: Error]\\r\\n\\tno_shell_integration: []\\r\\n}\\r\\n\\r\\n// how long to wait after a process outputs anything before we consider it \\\"cool\\\" again\\r\\nconst PROCESS_HOT_TIMEOUT_NORMAL = 2_000\\r\\nconst PROCESS_HOT_TIMEOUT_COMPILING = 15_000\\r\\n\\r\\nexport class TerminalProcess extends EventEmitter<TerminalProcessEvents> {\\r\\n\\twaitForShellIntegration: boolean = true\\r\\n\\tprivate isListening: boolean = true\\r\\n\\tprivate buffer: string = \\\"\\\"\\r\\n\\tprivate fullOutput: string = \\\"\\\"\\r\\n\\tprivate lastRetrievedIndex: number = 0\\r\\n\\tisHot: boolean = false\\r\\n\\tprivate hotTimer: NodeJS.Timeout | null = null\\r\\n\\r\\n\\t// constructor() {\\r\\n\\t// \\tsuper()\\r\\n\\r\\n\\tasync run(terminal: vscode.Terminal, command: string) {\\r\\n\\t\\tif (terminal.shellIntegration && terminal.shellIntegration.executeCommand) {\\r\\n\\t\\t\\tconst execution = terminal.shellIntegration.executeCommand(command)\\r\\n\\t\\t\\tconst stream = execution.read()\\r\\n\\t\\t\\t// todo: need to handle errors\\r\\n\\t\\t\\tlet isFirstChunk = true\\r\\n\\t\\t\\tlet didOutputNonCommand = false\\r\\n\\t\\t\\tlet didEmitEmptyLine = false\\r\\n\\t\\t\\tfor await (let data of stream) {\\r\\n\\t\\t\\t\\t// 1. Process chunk and remove artifacts\\r\\n\\t\\t\\t\\tif (isFirstChunk) {\\r\\n\\t\\t\\t\\t\\t/*\\r\\n\\t\\t\\t\\t\\tThe first chunk we get from this stream needs to be processed to be more human readable, ie remove vscode's custom escape sequences and identifiers, removing duplicate first char bug, etc.\\r\\n\\t\\t\\t\\t\\t*/\\r\\n\\r\\n\\t\\t\\t\\t\\t// bug where sometimes the command output makes its way into vscode shell integration metadata\\r\\n\\t\\t\\t\\t\\t/*\\r\\n\\t\\t\\t\\t\\t]633 is a custom sequence number used by VSCode shell integration:\\r\\n\\t\\t\\t\\t\\t- OSC 633 ; A ST - Mark prompt start\\r\\n\\t\\t\\t\\t\\t- OSC 633 ; B ST - Mark prompt end\\r\\n\\t\\t\\t\\t\\t- OSC 633 ; C ST - Mark pre-execution (start of command output)\\r\\n\\t\\t\\t\\t\\t- OSC 633 ; D [; <exitcode>] ST - Mark execution finished with optional exit code\\r\\n\\t\\t\\t\\t\\t- OSC 633 ; E ; <commandline> [; <nonce>] ST - Explicitly set command line with optional nonce\\r\\n\\t\\t\\t\\t\\t*/\\r\\n\\t\\t\\t\\t\\t// if you print this data you might see something like \\\"eecho hello worldo hello world;5ba85d14-e92a-40c4-b2fd-71525581eeb0]633;C\\\" but this is actually just a bunch of escape sequences, ignore up to the first ;C\\r\\n\\t\\t\\t\\t\\t/* ddateb15026-6a64-40db-b21f-2a621a9830f0]633;CTue Sep 17 06:37:04 EDT 2024 % ]633;D;0]633;P;Cwd=/Users/saoud/Repositories/test */\\r\\n\\t\\t\\t\\t\\t// Gets output between ]633;C (command start) and ]633;D (command end)\\r\\n\\t\\t\\t\\t\\tconst outputBetweenSequences = this.removeLastLineArtifacts(\\r\\n\\t\\t\\t\\t\\t\\tdata.match(/\\\\]633;C([\\\\s\\\\S]*?)\\\\]633;D/)?.[1] || \\\"\\\",\\r\\n\\t\\t\\t\\t\\t).trim()\\r\\n\\r\\n\\t\\t\\t\\t\\t// Once we've retrieved any potential output between sequences, we can remove everything up to end of the last sequence\\r\\n\\t\\t\\t\\t\\t// https://code.visualstudio.com/docs/terminal/shell-integration#_vs-code-custom-sequences-osc-633-st\\r\\n\\t\\t\\t\\t\\tconst vscodeSequenceRegex = /\\\\x1b\\\\]633;.[^\\\\x07]*\\\\x07/g\\r\\n\\t\\t\\t\\t\\tconst lastMatch = [...data.matchAll(vscodeSequenceRegex)].pop()\\r\\n\\t\\t\\t\\t\\tif (lastMatch && lastMatch.index !== undefined) {\\r\\n\\t\\t\\t\\t\\t\\tdata = data.slice(lastMatch.index + lastMatch[0].length)\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t// Place output back after removing vscode sequences\\r\\n\\t\\t\\t\\t\\tif (outputBetweenSequences) {\\r\\n\\t\\t\\t\\t\\t\\tdata = outputBetweenSequences + \\\"\\\\n\\\" + data\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t// remove ansi\\r\\n\\t\\t\\t\\t\\tdata = stripAnsi(data)\\r\\n\\t\\t\\t\\t\\t// Split data by newlines\\r\\n\\t\\t\\t\\t\\tlet lines = data ? data.split(\\\"\\\\n\\\") : []\\r\\n\\t\\t\\t\\t\\t// Remove non-human readable characters from the first line\\r\\n\\t\\t\\t\\t\\tif (lines.length > 0) {\\r\\n\\t\\t\\t\\t\\t\\tlines[0] = lines[0].replace(/[^\\\\x20-\\\\x7E]/g, \\\"\\\")\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t// Check if first two characters are the same, if so remove the first character\\r\\n\\t\\t\\t\\t\\tif (lines.length > 0 && lines[0].length >= 2 && lines[0][0] === lines[0][1]) {\\r\\n\\t\\t\\t\\t\\t\\tlines[0] = lines[0].slice(1)\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t// Remove everything up to the first alphanumeric character for first two lines\\r\\n\\t\\t\\t\\t\\tif (lines.length > 0) {\\r\\n\\t\\t\\t\\t\\t\\tlines[0] = lines[0].replace(/^[^a-zA-Z0-9]*/, \\\"\\\")\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tif (lines.length > 1) {\\r\\n\\t\\t\\t\\t\\t\\tlines[1] = lines[1].replace(/^[^a-zA-Z0-9]*/, \\\"\\\")\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t// Join lines back\\r\\n\\t\\t\\t\\t\\tdata = lines.join(\\\"\\\\n\\\")\\r\\n\\t\\t\\t\\t\\tisFirstChunk = false\\r\\n\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\tdata = stripAnsi(data)\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t// first few chunks could be the command being echoed back, so we must ignore\\r\\n\\t\\t\\t\\t// note this means that 'echo' commands wont work\\r\\n\\t\\t\\t\\tif (!didOutputNonCommand) {\\r\\n\\t\\t\\t\\t\\tconst lines = data.split(\\\"\\\\n\\\")\\r\\n\\t\\t\\t\\t\\tfor (let i = 0; i < lines.length; i++) {\\r\\n\\t\\t\\t\\t\\t\\tif (command.includes(lines[i].trim())) {\\r\\n\\t\\t\\t\\t\\t\\t\\tlines.splice(i, 1)\\r\\n\\t\\t\\t\\t\\t\\t\\ti-- // Adjust index after removal\\r\\n\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\tdidOutputNonCommand = true\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tdata = lines.join(\\\"\\\\n\\\")\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t// FIXME: right now it seems that data chunks returned to us from the shell integration stream contains random commas, which from what I can tell is not the expected behavior. There has to be a better solution here than just removing all commas.\\r\\n\\t\\t\\t\\tdata = data.replace(/,/g, \\\"\\\")\\r\\n\\r\\n\\t\\t\\t\\t// 2. Set isHot depending on the command\\r\\n\\t\\t\\t\\t// Set to hot to stall API requests until terminal is cool again\\r\\n\\t\\t\\t\\tthis.isHot = true\\r\\n\\t\\t\\t\\tif (this.hotTimer) {\\r\\n\\t\\t\\t\\t\\tclearTimeout(this.hotTimer)\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t// these markers indicate the command is some kind of local dev server recompiling the app, which we want to wait for output of before sending request to cline\\r\\n\\t\\t\\t\\tconst compilingMarkers = [\\\"compiling\\\", \\\"building\\\", \\\"bundling\\\", \\\"transpiling\\\", \\\"generating\\\", \\\"starting\\\"]\\r\\n\\t\\t\\t\\tconst markerNullifiers = [\\r\\n\\t\\t\\t\\t\\t\\\"compiled\\\",\\r\\n\\t\\t\\t\\t\\t\\\"success\\\",\\r\\n\\t\\t\\t\\t\\t\\\"finish\\\",\\r\\n\\t\\t\\t\\t\\t\\\"complete\\\",\\r\\n\\t\\t\\t\\t\\t\\\"succeed\\\",\\r\\n\\t\\t\\t\\t\\t\\\"done\\\",\\r\\n\\t\\t\\t\\t\\t\\\"end\\\",\\r\\n\\t\\t\\t\\t\\t\\\"stop\\\",\\r\\n\\t\\t\\t\\t\\t\\\"exit\\\",\\r\\n\\t\\t\\t\\t\\t\\\"terminate\\\",\\r\\n\\t\\t\\t\\t\\t\\\"error\\\",\\r\\n\\t\\t\\t\\t\\t\\\"fail\\\",\\r\\n\\t\\t\\t\\t]\\r\\n\\t\\t\\t\\tconst isCompiling =\\r\\n\\t\\t\\t\\t\\tcompilingMarkers.some((marker) => data.toLowerCase().includes(marker.toLowerCase())) &&\\r\\n\\t\\t\\t\\t\\t!markerNullifiers.some((nullifier) => data.toLowerCase().includes(nullifier.toLowerCase()))\\r\\n\\t\\t\\t\\tthis.hotTimer = setTimeout(\\r\\n\\t\\t\\t\\t\\t() => {\\r\\n\\t\\t\\t\\t\\t\\tthis.isHot = false\\r\\n\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\tisCompiling ? PROCESS_HOT_TIMEOUT_COMPILING : PROCESS_HOT_TIMEOUT_NORMAL,\\r\\n\\t\\t\\t\\t)\\r\\n\\r\\n\\t\\t\\t\\t// For non-immediately returning commands we want to show loading spinner right away but this wouldnt happen until it emits a line break, so as soon as we get any output we emit \\\"\\\" to let webview know to show spinner\\r\\n\\t\\t\\t\\tif (!didEmitEmptyLine && !this.fullOutput && data) {\\r\\n\\t\\t\\t\\t\\tthis.emit(\\\"line\\\", \\\"\\\") // empty line to indicate start of command output stream\\r\\n\\t\\t\\t\\t\\tdidEmitEmptyLine = true\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tthis.fullOutput += data\\r\\n\\t\\t\\t\\tif (this.isListening) {\\r\\n\\t\\t\\t\\t\\tthis.emitIfEol(data)\\r\\n\\t\\t\\t\\t\\tthis.lastRetrievedIndex = this.fullOutput.length - this.buffer.length\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tthis.emitRemainingBufferIfListening()\\r\\n\\r\\n\\t\\t\\t// for now we don't want this delaying requests since we don't send diagnostics automatically anymore (previous: \\\"even though the command is finished, we still want to consider it 'hot' in case so that api request stalls to let diagnostics catch up\\\")\\r\\n\\t\\t\\tif (this.hotTimer) {\\r\\n\\t\\t\\t\\tclearTimeout(this.hotTimer)\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tthis.isHot = false\\r\\n\\r\\n\\t\\t\\tthis.emit(\\\"completed\\\")\\r\\n\\t\\t\\tthis.emit(\\\"continue\\\")\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tterminal.sendText(command, true)\\r\\n\\t\\t\\t// For terminals without shell integration, we can't know when the command completes\\r\\n\\t\\t\\t// So we'll just emit the continue event after a delay\\r\\n\\t\\t\\tthis.emit(\\\"completed\\\")\\r\\n\\t\\t\\tthis.emit(\\\"continue\\\")\\r\\n\\t\\t\\tthis.emit(\\\"no_shell_integration\\\")\\r\\n\\t\\t\\t// setTimeout(() => {\\r\\n\\t\\t\\t// \\tconsole.log(`Emitting continue after delay for terminal`)\\r\\n\\t\\t\\t// \\t// can't emit completed since we don't if the command actually completed, it could still be running server\\r\\n\\t\\t\\t// }, 500) // Adjust this delay as needed\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t// Inspired by https://github.com/sindresorhus/execa/blob/main/lib/transform/split.js\\r\\n\\tprivate emitIfEol(chunk: string) {\\r\\n\\t\\tthis.buffer += chunk\\r\\n\\t\\tlet lineEndIndex: number\\r\\n\\t\\twhile ((lineEndIndex = this.buffer.indexOf(\\\"\\\\n\\\")) !== -1) {\\r\\n\\t\\t\\tlet line = this.buffer.slice(0, lineEndIndex).trimEnd() // removes trailing \\\\r\\r\\n\\t\\t\\t// Remove \\\\r if present (for Windows-style line endings)\\r\\n\\t\\t\\t// if (line.endsWith(\\\"\\\\r\\\")) {\\r\\n\\t\\t\\t// \\tline = line.slice(0, -1)\\r\\n\\t\\t\\t// }\\r\\n\\t\\t\\tthis.emit(\\\"line\\\", line)\\r\\n\\t\\t\\tthis.buffer = this.buffer.slice(lineEndIndex + 1)\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tprivate emitRemainingBufferIfListening() {\\r\\n\\t\\tif (this.buffer && this.isListening) {\\r\\n\\t\\t\\tconst remainingBuffer = this.removeLastLineArtifacts(this.buffer)\\r\\n\\t\\t\\tif (remainingBuffer) {\\r\\n\\t\\t\\t\\tthis.emit(\\\"line\\\", remainingBuffer)\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tthis.buffer = \\\"\\\"\\r\\n\\t\\t\\tthis.lastRetrievedIndex = this.fullOutput.length\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tcontinue() {\\r\\n\\t\\tthis.emitRemainingBufferIfListening()\\r\\n\\t\\tthis.isListening = false\\r\\n\\t\\tthis.removeAllListeners(\\\"line\\\")\\r\\n\\t\\tthis.emit(\\\"continue\\\")\\r\\n\\t}\\r\\n\\r\\n\\tgetUnretrievedOutput(): string {\\r\\n\\t\\tconst unretrieved = this.fullOutput.slice(this.lastRetrievedIndex)\\r\\n\\t\\tthis.lastRetrievedIndex = this.fullOutput.length\\r\\n\\t\\treturn this.removeLastLineArtifacts(unretrieved)\\r\\n\\t}\\r\\n\\r\\n\\t// some processing to remove artifacts like '%' at the end of the buffer (it seems that since vsode uses % at the beginning of newlines in terminal, it makes its way into the stream)\\r\\n\\t// This modification will remove '%', '$', '#', or '>' followed by optional whitespace\\r\\n\\tremoveLastLineArtifacts(output: string) {\\r\\n\\t\\tconst lines = output.trimEnd().split(\\\"\\\\n\\\")\\r\\n\\t\\tif (lines.length > 0) {\\r\\n\\t\\t\\tconst lastLine = lines[lines.length - 1]\\r\\n\\t\\t\\t// Remove prompt characters and trailing whitespace from the last line\\r\\n\\t\\t\\tlines[lines.length - 1] = lastLine.replace(/[%$#>]\\\\s*$/, \\\"\\\")\\r\\n\\t\\t}\\r\\n\\t\\treturn lines.join(\\\"\\\\n\\\").trimEnd()\\r\\n\\t}\\r\\n}\\r\\n\\r\\nexport type TerminalProcessResultPromise = TerminalProcess & Promise<void>\\r\\n\\r\\n// Similar to execa's ResultPromise, this lets us create a mixin of both a TerminalProcess and a Promise: https://github.com/sindresorhus/execa/blob/main/lib/methods/promise.js\\r\\nexport function mergePromise(process: TerminalProcess, promise: Promise<void>): TerminalProcessResultPromise {\\r\\n\\tconst nativePromisePrototype = (async () => {})().constructor.prototype\\r\\n\\tconst descriptors = [\\\"then\\\", \\\"catch\\\", \\\"finally\\\"].map(\\r\\n\\t\\t(property) => [property, Reflect.getOwnPropertyDescriptor(nativePromisePrototype, property)] as const,\\r\\n\\t)\\r\\n\\tfor (const [property, descriptor] of descriptors) {\\r\\n\\t\\tif (descriptor) {\\r\\n\\t\\t\\tconst value = descriptor.value.bind(promise)\\r\\n\\t\\t\\tReflect.defineProperty(process, property, { ...descriptor, value })\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\treturn process as TerminalProcessResultPromise\\r\\n}\\r\\n\",\"metadata\":{\"size\":10363,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.846Z\",\"createdTime\":\"2024-12-29T05:57:23.846Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":253,\"nonEmptyLines\":230,\"commentLines\":43,\"complexity\":40},\"dependencies\":[\"events\",\"strip-ansi\",\"vscode\"],\"quality\":{\"score\":-165,\"issues\":[],\"duplicateLines\":47,\"longLines\":15,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.374Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":253},\"analysis\":{\"metrics\":{\"lines\":253,\"nonEmptyLines\":230,\"commentLines\":43,\"complexity\":40},\"dependencies\":[\"events\",\"strip-ansi\",\"vscode\"],\"quality\":{\"score\":-165,\"issues\":[],\"duplicateLines\":47,\"longLines\":15,\"complexFunctions\":0}},\"lastModified\":1737046855375,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.375Z\",\"size\":12277},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\integrations\\\\terminal\\\\TerminalManager.ts\",\"content\":{\"content\":\"import pWaitFor from \\\"p-wait-for\\\"\\r\\nimport * as vscode from \\\"vscode\\\"\\r\\nimport { arePathsEqual } from \\\"../../utils/path\\\"\\r\\nimport { mergePromise, TerminalProcess, TerminalProcessResultPromise } from \\\"./TerminalProcess\\\"\\r\\nimport { TerminalInfo, TerminalRegistry } from \\\"./TerminalRegistry\\\"\\r\\n\\r\\n/*\\r\\nTerminalManager:\\r\\n- Creates/reuses terminals\\r\\n- Runs commands via runCommand(), returning a TerminalProcess\\r\\n- Handles shell integration events\\r\\n\\r\\nTerminalProcess extends EventEmitter and implements Promise:\\r\\n- Emits 'line' events with output while promise is pending\\r\\n- process.continue() resolves promise and stops event emission\\r\\n- Allows real-time output handling or background execution\\r\\n\\r\\ngetUnretrievedOutput() fetches latest output for ongoing commands\\r\\n\\r\\nEnables flexible command execution:\\r\\n- Await for completion\\r\\n- Listen to real-time events\\r\\n- Continue execution in background\\r\\n- Retrieve missed output later\\r\\n\\r\\nNotes:\\r\\n- it turns out some shellIntegration APIs are available on cursor, although not on older versions of vscode\\r\\n- \\\"By default, the shell integration script should automatically activate on supported shells launched from VS Code.\\\"\\r\\nSupported shells:\\r\\nLinux/macOS: bash, fish, pwsh, zsh\\r\\nWindows: pwsh\\r\\n\\r\\n\\r\\nExample:\\r\\n\\r\\nconst terminalManager = new TerminalManager(context);\\r\\n\\r\\n// Run a command\\r\\nconst process = terminalManager.runCommand('npm install', '/path/to/project');\\r\\n\\r\\nprocess.on('line', (line) => {\\r\\n console.log(line);\\r\\n});\\r\\n\\r\\n// To wait for the process to complete naturally:\\r\\nawait process;\\r\\n\\r\\n// Or to continue execution even if the command is still running:\\r\\nprocess.continue();\\r\\n\\r\\n// Later, if you need to get the unretrieved output:\\r\\nconst unretrievedOutput = terminalManager.getUnretrievedOutput(terminalId);\\r\\nconsole.log('Unretrieved output:', unretrievedOutput);\\r\\n\\r\\nResources:\\r\\n- https://github.com/microsoft/vscode/issues/226655\\r\\n- https://code.visualstudio.com/updates/v1_93#_terminal-shell-integration-api\\r\\n- https://code.visualstudio.com/docs/terminal/shell-integration\\r\\n- https://code.visualstudio.com/api/references/vscode-api#Terminal\\r\\n- https://github.com/microsoft/vscode-extension-samples/blob/main/terminal-sample/src/extension.ts\\r\\n- https://github.com/microsoft/vscode-extension-samples/blob/main/shell-integration-sample/src/extension.ts\\r\\n*/\\r\\n\\r\\n/*\\r\\nThe new shellIntegration API gives us access to terminal command execution output handling.\\r\\nHowever, we don't update our VSCode type definitions or engine requirements to maintain compatibility\\r\\nwith older VSCode versions. Users on older versions will automatically fall back to using sendText\\r\\nfor terminal command execution.\\r\\nInterestingly, some environments like Cursor enable these APIs even without the latest VSCode engine.\\r\\nThis approach allows us to leverage advanced features when available while ensuring broad compatibility.\\r\\n*/\\r\\ndeclare module \\\"vscode\\\" {\\r\\n\\t// https://github.com/microsoft/vscode/blob/f0417069c62e20f3667506f4b7e53ca0004b4e3e/src/vscode-dts/vscode.d.ts#L7442\\r\\n\\tinterface Terminal {\\r\\n\\t\\tshellIntegration?: {\\r\\n\\t\\t\\tcwd?: vscode.Uri\\r\\n\\t\\t\\texecuteCommand?: (command: string) => {\\r\\n\\t\\t\\t\\tread: () => AsyncIterable<string>\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\t// https://github.com/microsoft/vscode/blob/f0417069c62e20f3667506f4b7e53ca0004b4e3e/src/vscode-dts/vscode.d.ts#L10794\\r\\n\\tinterface Window {\\r\\n\\t\\tonDidStartTerminalShellExecution?: (\\r\\n\\t\\t\\tlistener: (e: any) => any,\\r\\n\\t\\t\\tthisArgs?: any,\\r\\n\\t\\t\\tdisposables?: vscode.Disposable[],\\r\\n\\t\\t) => vscode.Disposable\\r\\n\\t}\\r\\n}\\r\\n\\r\\nexport class TerminalManager {\\r\\n\\tprivate terminalIds: Set<number> = new Set()\\r\\n\\tprivate processes: Map<number, TerminalProcess> = new Map()\\r\\n\\tprivate disposables: vscode.Disposable[] = []\\r\\n\\r\\n\\tconstructor() {\\r\\n\\t\\tlet disposable: vscode.Disposable | undefined\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tdisposable = (vscode.window as vscode.Window).onDidStartTerminalShellExecution?.(async (e) => {\\r\\n\\t\\t\\t\\t// Creating a read stream here results in a more consistent output. This is most obvious when running the `date` command.\\r\\n\\t\\t\\t\\te?.execution?.read()\\r\\n\\t\\t\\t})\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\t// console.error(\\\"Error setting up onDidEndTerminalShellExecution\\\", error)\\r\\n\\t\\t}\\r\\n\\t\\tif (disposable) {\\r\\n\\t\\t\\tthis.disposables.push(disposable)\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\trunCommand(terminalInfo: TerminalInfo, command: string): TerminalProcessResultPromise {\\r\\n\\t\\tterminalInfo.busy = true\\r\\n\\t\\tterminalInfo.lastCommand = command\\r\\n\\t\\tconst process = new TerminalProcess()\\r\\n\\t\\tthis.processes.set(terminalInfo.id, process)\\r\\n\\r\\n\\t\\tprocess.once(\\\"completed\\\", () => {\\r\\n\\t\\t\\tterminalInfo.busy = false\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\t// if shell integration is not available, remove terminal so it does not get reused as it may be running a long-running process\\r\\n\\t\\tprocess.once(\\\"no_shell_integration\\\", () => {\\r\\n\\t\\t\\tconsole.log(`no_shell_integration received for terminal ${terminalInfo.id}`)\\r\\n\\t\\t\\t// Remove the terminal so we can't reuse it (in case it's running a long-running process)\\r\\n\\t\\t\\tTerminalRegistry.removeTerminal(terminalInfo.id)\\r\\n\\t\\t\\tthis.terminalIds.delete(terminalInfo.id)\\r\\n\\t\\t\\tthis.processes.delete(terminalInfo.id)\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tconst promise = new Promise<void>((resolve, reject) => {\\r\\n\\t\\t\\tprocess.once(\\\"continue\\\", () => {\\r\\n\\t\\t\\t\\tresolve()\\r\\n\\t\\t\\t})\\r\\n\\t\\t\\tprocess.once(\\\"error\\\", (error) => {\\r\\n\\t\\t\\t\\tconsole.error(`Error in terminal ${terminalInfo.id}:`, error)\\r\\n\\t\\t\\t\\treject(error)\\r\\n\\t\\t\\t})\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\t// if shell integration is already active, run the command immediately\\r\\n\\t\\tif (terminalInfo.terminal.shellIntegration) {\\r\\n\\t\\t\\tprocess.waitForShellIntegration = false\\r\\n\\t\\t\\tprocess.run(terminalInfo.terminal, command)\\r\\n\\t\\t} else {\\r\\n\\t\\t\\t// docs recommend waiting 3s for shell integration to activate\\r\\n\\t\\t\\tpWaitFor(() => terminalInfo.terminal.shellIntegration !== undefined, { timeout: 4000 }).finally(() => {\\r\\n\\t\\t\\t\\tconst existingProcess = this.processes.get(terminalInfo.id)\\r\\n\\t\\t\\t\\tif (existingProcess && existingProcess.waitForShellIntegration) {\\r\\n\\t\\t\\t\\t\\texistingProcess.waitForShellIntegration = false\\r\\n\\t\\t\\t\\t\\texistingProcess.run(terminalInfo.terminal, command)\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t})\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn mergePromise(process, promise)\\r\\n\\t}\\r\\n\\r\\n\\tasync getOrCreateTerminal(cwd: string): Promise<TerminalInfo> {\\r\\n\\t\\t// Find available terminal from our pool first (created for this task)\\r\\n\\t\\tconst availableTerminal = TerminalRegistry.getAllTerminals().find((t) => {\\r\\n\\t\\t\\tif (t.busy) {\\r\\n\\t\\t\\t\\treturn false\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tconst terminalCwd = t.terminal.shellIntegration?.cwd // one of cline's commands could have changed the cwd of the terminal\\r\\n\\t\\t\\tif (!terminalCwd) {\\r\\n\\t\\t\\t\\treturn false\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\treturn arePathsEqual(vscode.Uri.file(cwd).fsPath, terminalCwd.fsPath)\\r\\n\\t\\t})\\r\\n\\t\\tif (availableTerminal) {\\r\\n\\t\\t\\tthis.terminalIds.add(availableTerminal.id)\\r\\n\\t\\t\\treturn availableTerminal\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tconst newTerminalInfo = TerminalRegistry.createTerminal(cwd)\\r\\n\\t\\tthis.terminalIds.add(newTerminalInfo.id)\\r\\n\\t\\treturn newTerminalInfo\\r\\n\\t}\\r\\n\\r\\n\\tgetTerminals(busy: boolean): { id: number; lastCommand: string }[] {\\r\\n\\t\\treturn Array.from(this.terminalIds)\\r\\n\\t\\t\\t.map((id) => TerminalRegistry.getTerminal(id))\\r\\n\\t\\t\\t.filter((t): t is TerminalInfo => t !== undefined && t.busy === busy)\\r\\n\\t\\t\\t.map((t) => ({ id: t.id, lastCommand: t.lastCommand }))\\r\\n\\t}\\r\\n\\r\\n\\tgetUnretrievedOutput(terminalId: number): string {\\r\\n\\t\\tif (!this.terminalIds.has(terminalId)) {\\r\\n\\t\\t\\treturn \\\"\\\"\\r\\n\\t\\t}\\r\\n\\t\\tconst process = this.processes.get(terminalId)\\r\\n\\t\\treturn process ? process.getUnretrievedOutput() : \\\"\\\"\\r\\n\\t}\\r\\n\\r\\n\\tisProcessHot(terminalId: number): boolean {\\r\\n\\t\\tconst process = this.processes.get(terminalId)\\r\\n\\t\\treturn process ? process.isHot : false\\r\\n\\t}\\r\\n\\r\\n\\tdisposeAll() {\\r\\n\\t\\t// for (const info of this.terminals) {\\r\\n\\t\\t// \\t//info.terminal.dispose() // dont want to dispose terminals when task is aborted\\r\\n\\t\\t// }\\r\\n\\t\\tthis.terminalIds.clear()\\r\\n\\t\\tthis.processes.clear()\\r\\n\\t\\tthis.disposables.forEach((disposable) => disposable.dispose())\\r\\n\\t\\tthis.disposables = []\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":7715,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.846Z\",\"createdTime\":\"2024-12-29T05:57:23.846Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":211,\"nonEmptyLines\":181,\"commentLines\":18,\"complexity\":26},\"dependencies\":[\"p-wait-for\",\"vscode\",\"../../utils/path\",\"./TerminalProcess\",\"./TerminalRegistry\"],\"quality\":{\"score\":-79,\"issues\":[],\"duplicateLines\":31,\"longLines\":12,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.372Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":211},\"analysis\":{\"metrics\":{\"lines\":211,\"nonEmptyLines\":181,\"commentLines\":18,\"complexity\":26},\"dependencies\":[\"p-wait-for\",\"vscode\",\"../../utils/path\",\"./TerminalProcess\",\"./TerminalRegistry\"],\"quality\":{\"score\":-79,\"issues\":[],\"duplicateLines\":31,\"longLines\":12,\"complexFunctions\":0}},\"lastModified\":1737046855373,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.373Z\",\"size\":8991},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\integrations\\\\notifications\\\\index.ts\",\"content\":{\"content\":\"import { execa } from \\\"execa\\\"\\r\\nimport { platform } from \\\"os\\\"\\r\\n\\r\\ninterface NotificationOptions {\\r\\n\\ttitle?: string\\r\\n\\tsubtitle?: string\\r\\n\\tmessage: string\\r\\n}\\r\\n\\r\\nasync function showMacOSNotification(options: NotificationOptions): Promise<void> {\\r\\n\\tconst { title, subtitle = \\\"\\\", message } = options\\r\\n\\r\\n\\tconst script = `display notification \\\"${message}\\\" with title \\\"${title}\\\" subtitle \\\"${subtitle}\\\" sound name \\\"Tink\\\"`\\r\\n\\r\\n\\ttry {\\r\\n\\t\\tawait execa(\\\"osascript\\\", [\\\"-e\\\", script])\\r\\n\\t} catch (error) {\\r\\n\\t\\tthrow new Error(`Failed to show macOS notification: ${error}`)\\r\\n\\t}\\r\\n}\\r\\n\\r\\nasync function showWindowsNotification(options: NotificationOptions): Promise<void> {\\r\\n\\tconst { subtitle, message } = options\\r\\n\\r\\n\\tconst script = `\\r\\n [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null\\r\\n [Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] | Out-Null\\r\\n\\r\\n $template = @\\\"\\r\\n <toast>\\r\\n <visual>\\r\\n <binding template=\\\"ToastText02\\\">\\r\\n <text id=\\\"1\\\">${subtitle}</text>\\r\\n <text id=\\\"2\\\">${message}</text>\\r\\n </binding>\\r\\n </visual>\\r\\n </toast>\\r\\n\\\"@\\r\\n\\r\\n $xml = New-Object Windows.Data.Xml.Dom.XmlDocument\\r\\n $xml.LoadXml($template)\\r\\n $toast = [Windows.UI.Notifications.ToastNotification]::new($xml)\\r\\n [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier(\\\"Cline\\\").Show($toast)\\r\\n `\\r\\n\\r\\n\\ttry {\\r\\n\\t\\tawait execa(\\\"powershell\\\", [\\\"-Command\\\", script])\\r\\n\\t} catch (error) {\\r\\n\\t\\tthrow new Error(`Failed to show Windows notification: ${error}`)\\r\\n\\t}\\r\\n}\\r\\n\\r\\nasync function showLinuxNotification(options: NotificationOptions): Promise<void> {\\r\\n\\tconst { title = \\\"\\\", subtitle = \\\"\\\", message } = options\\r\\n\\r\\n\\t// Combine subtitle and message if subtitle exists\\r\\n\\tconst fullMessage = subtitle ? `${subtitle}\\\\n${message}` : message\\r\\n\\r\\n\\ttry {\\r\\n\\t\\tawait execa(\\\"notify-send\\\", [title, fullMessage])\\r\\n\\t} catch (error) {\\r\\n\\t\\tthrow new Error(`Failed to show Linux notification: ${error}`)\\r\\n\\t}\\r\\n}\\r\\n\\r\\nexport async function showSystemNotification(options: NotificationOptions): Promise<void> {\\r\\n\\ttry {\\r\\n\\t\\tconst { title = \\\"Cline\\\", message } = options\\r\\n\\r\\n\\t\\tif (!message) {\\r\\n\\t\\t\\tthrow new Error(\\\"Message is required\\\")\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tconst escapedOptions = {\\r\\n\\t\\t\\t...options,\\r\\n\\t\\t\\ttitle: title.replace(/\\\"/g, '\\\\\\\\\\\"'),\\r\\n\\t\\t\\tmessage: message.replace(/\\\"/g, '\\\\\\\\\\\"'),\\r\\n\\t\\t\\tsubtitle: options.subtitle?.replace(/\\\"/g, '\\\\\\\\\\\"') || \\\"\\\",\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tswitch (platform()) {\\r\\n\\t\\t\\tcase \\\"darwin\\\":\\r\\n\\t\\t\\t\\tawait showMacOSNotification(escapedOptions)\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\tcase \\\"win32\\\":\\r\\n\\t\\t\\t\\tawait showWindowsNotification(escapedOptions)\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\tcase \\\"linux\\\":\\r\\n\\t\\t\\t\\tawait showLinuxNotification(escapedOptions)\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\tthrow new Error(\\\"Unsupported platform\\\")\\r\\n\\t\\t}\\r\\n\\t} catch (error) {\\r\\n\\t\\tconsole.error(\\\"Could not show system notification\\\", error)\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":2877,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.958Z\",\"createdTime\":\"2025-01-15T15:26:07.958Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":98,\"nonEmptyLines\":81,\"commentLines\":1,\"complexity\":14},\"dependencies\":[\"execa\",\"os\"],\"quality\":{\"score\":-1,\"issues\":[],\"duplicateLines\":19,\"longLines\":3,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.371Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":98},\"analysis\":{\"metrics\":{\"lines\":98,\"nonEmptyLines\":81,\"commentLines\":1,\"complexity\":14},\"dependencies\":[\"execa\",\"os\"],\"quality\":{\"score\":-1,\"issues\":[],\"duplicateLines\":19,\"longLines\":3,\"complexFunctions\":0}},\"lastModified\":1737046855372,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.372Z\",\"size\":3711},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\integrations\\\\misc\\\\process-images.ts\",\"content\":{\"content\":\"import * as vscode from \\\"vscode\\\"\\r\\nimport fs from \\\"fs/promises\\\"\\r\\nimport * as path from \\\"path\\\"\\r\\n\\r\\nexport async function selectImages(): Promise<string[]> {\\r\\n\\tconst options: vscode.OpenDialogOptions = {\\r\\n\\t\\tcanSelectMany: true,\\r\\n\\t\\topenLabel: \\\"Select\\\",\\r\\n\\t\\tfilters: {\\r\\n\\t\\t\\tImages: [\\\"png\\\", \\\"jpg\\\", \\\"jpeg\\\", \\\"webp\\\"], // supported by anthropic and openrouter\\r\\n\\t\\t},\\r\\n\\t}\\r\\n\\r\\n\\tconst fileUris = await vscode.window.showOpenDialog(options)\\r\\n\\r\\n\\tif (!fileUris || fileUris.length === 0) {\\r\\n\\t\\treturn []\\r\\n\\t}\\r\\n\\r\\n\\treturn await Promise.all(\\r\\n\\t\\tfileUris.map(async (uri) => {\\r\\n\\t\\t\\tconst imagePath = uri.fsPath\\r\\n\\t\\t\\tconst buffer = await fs.readFile(imagePath)\\r\\n\\t\\t\\tconst base64 = buffer.toString(\\\"base64\\\")\\r\\n\\t\\t\\tconst mimeType = getMimeType(imagePath)\\r\\n\\t\\t\\tconst dataUrl = `data:${mimeType};base64,${base64}`\\r\\n\\t\\t\\treturn dataUrl\\r\\n\\t\\t}),\\r\\n\\t)\\r\\n}\\r\\n\\r\\nfunction getMimeType(filePath: string): string {\\r\\n\\tconst ext = path.extname(filePath).toLowerCase()\\r\\n\\tswitch (ext) {\\r\\n\\t\\tcase \\\".png\\\":\\r\\n\\t\\t\\treturn \\\"image/png\\\"\\r\\n\\t\\tcase \\\".jpeg\\\":\\r\\n\\t\\tcase \\\".jpg\\\":\\r\\n\\t\\t\\treturn \\\"image/jpeg\\\"\\r\\n\\t\\tcase \\\".webp\\\":\\r\\n\\t\\t\\treturn \\\"image/webp\\\"\\r\\n\\t\\tdefault:\\r\\n\\t\\t\\tthrow new Error(`Unsupported file type: ${ext}`)\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":1137,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.846Z\",\"createdTime\":\"2024-12-29T05:57:23.846Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":46,\"nonEmptyLines\":40,\"commentLines\":0,\"complexity\":7},\"dependencies\":[\"vscode\",\"fs/promises\",\"path\"],\"quality\":{\"score\":80,\"issues\":[],\"duplicateLines\":4,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.369Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":46},\"analysis\":{\"metrics\":{\"lines\":46,\"nonEmptyLines\":40,\"commentLines\":0,\"complexity\":7},\"dependencies\":[\"vscode\",\"fs/promises\",\"path\"],\"quality\":{\"score\":80,\"issues\":[],\"duplicateLines\":4,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855370,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.370Z\",\"size\":1809},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\integrations\\\\misc\\\\open-file.ts\",\"content\":{\"content\":\"import * as path from \\\"path\\\"\\r\\nimport * as os from \\\"os\\\"\\r\\nimport * as vscode from \\\"vscode\\\"\\r\\nimport { arePathsEqual } from \\\"../../utils/path\\\"\\r\\n\\r\\nexport async function openImage(dataUri: string) {\\r\\n\\tconst matches = dataUri.match(/^data:image\\\\/([a-zA-Z]+);base64,(.+)$/)\\r\\n\\tif (!matches) {\\r\\n\\t\\tvscode.window.showErrorMessage(\\\"Invalid data URI format\\\")\\r\\n\\t\\treturn\\r\\n\\t}\\r\\n\\tconst [, format, base64Data] = matches\\r\\n\\tconst imageBuffer = Buffer.from(base64Data, \\\"base64\\\")\\r\\n\\tconst tempFilePath = path.join(os.tmpdir(), `temp_image_${Date.now()}.${format}`)\\r\\n\\ttry {\\r\\n\\t\\tawait vscode.workspace.fs.writeFile(vscode.Uri.file(tempFilePath), imageBuffer)\\r\\n\\t\\tawait vscode.commands.executeCommand(\\\"vscode.open\\\", vscode.Uri.file(tempFilePath))\\r\\n\\t} catch (error) {\\r\\n\\t\\tvscode.window.showErrorMessage(`Error opening image: ${error}`)\\r\\n\\t}\\r\\n}\\r\\n\\r\\nexport async function openFile(absolutePath: string) {\\r\\n\\ttry {\\r\\n\\t\\tconst uri = vscode.Uri.file(absolutePath)\\r\\n\\r\\n\\t\\t// Check if the document is already open in a tab group that's not in the active editor's column. If it is, then close it (if not dirty) so that we don't duplicate tabs\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tfor (const group of vscode.window.tabGroups.all) {\\r\\n\\t\\t\\t\\tconst existingTab = group.tabs.find(\\r\\n\\t\\t\\t\\t\\t(tab) => tab.input instanceof vscode.TabInputText && arePathsEqual(tab.input.uri.fsPath, uri.fsPath),\\r\\n\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\tif (existingTab) {\\r\\n\\t\\t\\t\\t\\tconst activeColumn = vscode.window.activeTextEditor?.viewColumn\\r\\n\\t\\t\\t\\t\\tconst tabColumn = vscode.window.tabGroups.all.find((group) => group.tabs.includes(existingTab))?.viewColumn\\r\\n\\t\\t\\t\\t\\tif (activeColumn && activeColumn !== tabColumn && !existingTab.isDirty) {\\r\\n\\t\\t\\t\\t\\t\\tawait vscode.window.tabGroups.close(existingTab)\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t} catch {} // not essential, sometimes tab operations fail\\r\\n\\r\\n\\t\\tconst document = await vscode.workspace.openTextDocument(uri)\\r\\n\\t\\tawait vscode.window.showTextDocument(document, { preview: false })\\r\\n\\t} catch (error) {\\r\\n\\t\\tvscode.window.showErrorMessage(`Could not open file!`)\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":1979,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.957Z\",\"createdTime\":\"2025-01-15T15:26:07.957Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":50,\"nonEmptyLines\":45,\"commentLines\":1,\"complexity\":12},\"dependencies\":[\"path\",\"os\",\"vscode\",\"../../utils/path\"],\"quality\":{\"score\":44,\"issues\":[],\"duplicateLines\":10,\"longLines\":3,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.368Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":50},\"analysis\":{\"metrics\":{\"lines\":50,\"nonEmptyLines\":45,\"commentLines\":1,\"complexity\":12},\"dependencies\":[\"path\",\"os\",\"vscode\",\"../../utils/path\"],\"quality\":{\"score\":44,\"issues\":[],\"duplicateLines\":10,\"longLines\":3,\"complexFunctions\":0}},\"lastModified\":1737046855369,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.369Z\",\"size\":2681},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\integrations\\\\misc\\\\extract-text.ts\",\"content\":{\"content\":\"import * as path from \\\"path\\\"\\r\\n// @ts-ignore-next-line\\r\\nimport pdf from \\\"pdf-parse/lib/pdf-parse\\\"\\r\\nimport mammoth from \\\"mammoth\\\"\\r\\nimport fs from \\\"fs/promises\\\"\\r\\nimport { isBinaryFile } from \\\"isbinaryfile\\\"\\r\\n\\r\\nexport async function extractTextFromFile(filePath: string): Promise<string> {\\r\\n\\ttry {\\r\\n\\t\\tawait fs.access(filePath)\\r\\n\\t} catch (error) {\\r\\n\\t\\tthrow new Error(`File not found: ${filePath}`)\\r\\n\\t}\\r\\n\\tconst fileExtension = path.extname(filePath).toLowerCase()\\r\\n\\tswitch (fileExtension) {\\r\\n\\t\\tcase \\\".pdf\\\":\\r\\n\\t\\t\\treturn extractTextFromPDF(filePath)\\r\\n\\t\\tcase \\\".docx\\\":\\r\\n\\t\\t\\treturn extractTextFromDOCX(filePath)\\r\\n\\t\\tcase \\\".ipynb\\\":\\r\\n\\t\\t\\treturn extractTextFromIPYNB(filePath)\\r\\n\\t\\tdefault:\\r\\n\\t\\t\\tconst isBinary = await isBinaryFile(filePath).catch(() => false)\\r\\n\\t\\t\\tif (!isBinary) {\\r\\n\\t\\t\\t\\treturn await fs.readFile(filePath, \\\"utf8\\\")\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tthrow new Error(`Cannot read text for file type: ${fileExtension}`)\\r\\n\\t\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\\r\\nasync function extractTextFromPDF(filePath: string): Promise<string> {\\r\\n\\tconst dataBuffer = await fs.readFile(filePath)\\r\\n\\tconst data = await pdf(dataBuffer)\\r\\n\\treturn data.text\\r\\n}\\r\\n\\r\\nasync function extractTextFromDOCX(filePath: string): Promise<string> {\\r\\n\\tconst result = await mammoth.extractRawText({ path: filePath })\\r\\n\\treturn result.value\\r\\n}\\r\\n\\r\\nasync function extractTextFromIPYNB(filePath: string): Promise<string> {\\r\\n\\tconst data = await fs.readFile(filePath, \\\"utf8\\\")\\r\\n\\tconst notebook = JSON.parse(data)\\r\\n\\tlet extractedText = \\\"\\\"\\r\\n\\r\\n\\tfor (const cell of notebook.cells) {\\r\\n\\t\\tif ((cell.cell_type === \\\"markdown\\\" || cell.cell_type === \\\"code\\\") && cell.source) {\\r\\n\\t\\t\\textractedText += cell.source.join(\\\"\\\\n\\\") + \\\"\\\\n\\\"\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\treturn extractedText\\r\\n}\\r\\n\",\"metadata\":{\"size\":1659,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.846Z\",\"createdTime\":\"2024-12-29T05:57:23.846Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":56,\"nonEmptyLines\":49,\"commentLines\":1,\"complexity\":12},\"dependencies\":[\"path\",\"pdf-parse/lib/pdf-parse\",\"mammoth\",\"fs/promises\",\"isbinaryfile\"],\"quality\":{\"score\":60,\"issues\":[],\"duplicateLines\":8,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.366Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":56},\"analysis\":{\"metrics\":{\"lines\":56,\"nonEmptyLines\":49,\"commentLines\":1,\"complexity\":12},\"dependencies\":[\"path\",\"pdf-parse/lib/pdf-parse\",\"mammoth\",\"fs/promises\",\"isbinaryfile\"],\"quality\":{\"score\":60,\"issues\":[],\"duplicateLines\":8,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855367,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.367Z\",\"size\":2392},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\integrations\\\\misc\\\\export-markdown.ts\",\"content\":{\"content\":\"import { Anthropic } from \\\"@anthropic-ai/sdk\\\"\\r\\nimport os from \\\"os\\\"\\r\\nimport * as path from \\\"path\\\"\\r\\nimport * as vscode from \\\"vscode\\\"\\r\\n\\r\\nexport async function downloadTask(dateTs: number, conversationHistory: Anthropic.MessageParam[]) {\\r\\n\\t// File name\\r\\n\\tconst date = new Date(dateTs)\\r\\n\\tconst month = date.toLocaleString(\\\"en-US\\\", { month: \\\"short\\\" }).toLowerCase()\\r\\n\\tconst day = date.getDate()\\r\\n\\tconst year = date.getFullYear()\\r\\n\\tlet hours = date.getHours()\\r\\n\\tconst minutes = date.getMinutes().toString().padStart(2, \\\"0\\\")\\r\\n\\tconst seconds = date.getSeconds().toString().padStart(2, \\\"0\\\")\\r\\n\\tconst ampm = hours >= 12 ? \\\"pm\\\" : \\\"am\\\"\\r\\n\\thours = hours % 12\\r\\n\\thours = hours ? hours : 12 // the hour '0' should be '12'\\r\\n\\tconst fileName = `cline_task_${month}-${day}-${year}_${hours}-${minutes}-${seconds}-${ampm}.md`\\r\\n\\r\\n\\t// Generate markdown\\r\\n\\tconst markdownContent = conversationHistory\\r\\n\\t\\t.map((message) => {\\r\\n\\t\\t\\tconst role = message.role === \\\"user\\\" ? \\\"**User:**\\\" : \\\"**Assistant:**\\\"\\r\\n\\t\\t\\tconst content = Array.isArray(message.content)\\r\\n\\t\\t\\t\\t? message.content.map((block) => formatContentBlockToMarkdown(block)).join(\\\"\\\\n\\\")\\r\\n\\t\\t\\t\\t: message.content\\r\\n\\t\\t\\treturn `${role}\\\\n\\\\n${content}\\\\n\\\\n`\\r\\n\\t\\t})\\r\\n\\t\\t.join(\\\"---\\\\n\\\\n\\\")\\r\\n\\r\\n\\t// Prompt user for save location\\r\\n\\tconst saveUri = await vscode.window.showSaveDialog({\\r\\n\\t\\tfilters: { Markdown: [\\\"md\\\"] },\\r\\n\\t\\tdefaultUri: vscode.Uri.file(path.join(os.homedir(), \\\"Downloads\\\", fileName)),\\r\\n\\t})\\r\\n\\r\\n\\tif (saveUri) {\\r\\n\\t\\t// Write content to the selected location\\r\\n\\t\\tawait vscode.workspace.fs.writeFile(saveUri, Buffer.from(markdownContent))\\r\\n\\t\\tvscode.window.showTextDocument(saveUri, { preview: true })\\r\\n\\t}\\r\\n}\\r\\n\\r\\nexport function formatContentBlockToMarkdown(\\r\\n\\tblock: Anthropic.TextBlockParam | Anthropic.ImageBlockParam | Anthropic.ToolUseBlockParam | Anthropic.ToolResultBlockParam,\\r\\n\\t// messages: Anthropic.MessageParam[]\\r\\n): string {\\r\\n\\tswitch (block.type) {\\r\\n\\t\\tcase \\\"text\\\":\\r\\n\\t\\t\\treturn block.text\\r\\n\\t\\tcase \\\"image\\\":\\r\\n\\t\\t\\treturn `[Image]`\\r\\n\\t\\tcase \\\"tool_use\\\":\\r\\n\\t\\t\\tlet input: string\\r\\n\\t\\t\\tif (typeof block.input === \\\"object\\\" && block.input !== null) {\\r\\n\\t\\t\\t\\tinput = Object.entries(block.input)\\r\\n\\t\\t\\t\\t\\t.map(([key, value]) => `${key.charAt(0).toUpperCase() + key.slice(1)}: ${value}`)\\r\\n\\t\\t\\t\\t\\t.join(\\\"\\\\n\\\")\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tinput = String(block.input)\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\treturn `[Tool Use: ${block.name}]\\\\n${input}`\\r\\n\\t\\tcase \\\"tool_result\\\":\\r\\n\\t\\t\\t// For now we're not doing tool name lookup since we don't use tools anymore\\r\\n\\t\\t\\t// const toolName = findToolName(block.tool_use_id, messages)\\r\\n\\t\\t\\tconst toolName = \\\"Tool\\\"\\r\\n\\t\\t\\tif (typeof block.content === \\\"string\\\") {\\r\\n\\t\\t\\t\\treturn `[${toolName}${block.is_error ? \\\" (Error)\\\" : \\\"\\\"}]\\\\n${block.content}`\\r\\n\\t\\t\\t} else if (Array.isArray(block.content)) {\\r\\n\\t\\t\\t\\treturn `[${toolName}${block.is_error ? \\\" (Error)\\\" : \\\"\\\"}]\\\\n${block.content\\r\\n\\t\\t\\t\\t\\t.map((contentBlock) => formatContentBlockToMarkdown(contentBlock))\\r\\n\\t\\t\\t\\t\\t.join(\\\"\\\\n\\\")}`\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\treturn `[${toolName}${block.is_error ? \\\" (Error)\\\" : \\\"\\\"}]`\\r\\n\\t\\t\\t}\\r\\n\\t\\tdefault:\\r\\n\\t\\t\\treturn \\\"[Unexpected content type]\\\"\\r\\n\\t}\\r\\n}\\r\\n\\r\\nexport function findToolName(toolCallId: string, messages: Anthropic.MessageParam[]): string {\\r\\n\\tfor (const message of messages) {\\r\\n\\t\\tif (Array.isArray(message.content)) {\\r\\n\\t\\t\\tfor (const block of message.content) {\\r\\n\\t\\t\\t\\tif (block.type === \\\"tool_use\\\" && block.id === toolCallId) {\\r\\n\\t\\t\\t\\t\\treturn block.name\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\treturn \\\"Unknown Tool\\\"\\r\\n}\\r\\n\",\"metadata\":{\"size\":3341,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.957Z\",\"createdTime\":\"2025-01-15T15:26:07.957Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":93,\"nonEmptyLines\":86,\"commentLines\":7,\"complexity\":26},\"dependencies\":[\"@anthropic-ai/sdk\",\"os\",\"path\",\"vscode\"],\"quality\":{\"score\":38,\"issues\":[],\"duplicateLines\":12,\"longLines\":1,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.365Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":93},\"analysis\":{\"metrics\":{\"lines\":93,\"nonEmptyLines\":86,\"commentLines\":7,\"complexity\":26},\"dependencies\":[\"@anthropic-ai/sdk\",\"os\",\"path\",\"vscode\"],\"quality\":{\"score\":38,\"issues\":[],\"duplicateLines\":12,\"longLines\":1,\"complexFunctions\":0}},\"lastModified\":1737046855366,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.366Z\",\"size\":4280},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\integrations\\\\editor\\\\DiffViewProvider.ts\",\"content\":{\"content\":\"import * as vscode from \\\"vscode\\\"\\r\\nimport * as path from \\\"path\\\"\\r\\nimport * as fs from \\\"fs/promises\\\"\\r\\nimport { createDirectoriesForFile } from \\\"../../utils/fs\\\"\\r\\nimport { arePathsEqual } from \\\"../../utils/path\\\"\\r\\nimport { formatResponse } from \\\"../../core/prompts/responses\\\"\\r\\nimport { DecorationController } from \\\"./DecorationController\\\"\\r\\nimport * as diff from \\\"diff\\\"\\r\\nimport { diagnosticsToProblemsString, getNewDiagnostics } from \\\"../diagnostics\\\"\\r\\n\\r\\nexport const DIFF_VIEW_URI_SCHEME = \\\"cline-diff\\\"\\r\\n\\r\\nexport class DiffViewProvider {\\r\\n\\teditType?: \\\"create\\\" | \\\"modify\\\"\\r\\n\\tisEditing = false\\r\\n\\toriginalContent: string | undefined\\r\\n\\tprivate createdDirs: string[] = []\\r\\n\\tprivate documentWasOpen = false\\r\\n\\tprivate relPath?: string\\r\\n\\tprivate newContent?: string\\r\\n\\tprivate activeDiffEditor?: vscode.TextEditor\\r\\n\\tprivate fadedOverlayController?: DecorationController\\r\\n\\tprivate activeLineController?: DecorationController\\r\\n\\tprivate streamedLines: string[] = []\\r\\n\\tprivate preDiagnostics: [vscode.Uri, vscode.Diagnostic[]][] = []\\r\\n\\r\\n\\tconstructor(private cwd: string) {}\\r\\n\\r\\n\\tasync open(relPath: string): Promise<void> {\\r\\n\\t\\tthis.relPath = relPath\\r\\n\\t\\tconst fileExists = this.editType === \\\"modify\\\"\\r\\n\\t\\tconst absolutePath = path.resolve(this.cwd, relPath)\\r\\n\\t\\tthis.isEditing = true\\r\\n\\t\\t// if the file is already open, ensure it's not dirty before getting its contents\\r\\n\\t\\tif (fileExists) {\\r\\n\\t\\t\\tconst existingDocument = vscode.workspace.textDocuments.find((doc) => arePathsEqual(doc.uri.fsPath, absolutePath))\\r\\n\\t\\t\\tif (existingDocument && existingDocument.isDirty) {\\r\\n\\t\\t\\t\\tawait existingDocument.save()\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// get diagnostics before editing the file, we'll compare to diagnostics after editing to see if cline needs to fix anything\\r\\n\\t\\tthis.preDiagnostics = vscode.languages.getDiagnostics()\\r\\n\\r\\n\\t\\tif (fileExists) {\\r\\n\\t\\t\\tthis.originalContent = await fs.readFile(absolutePath, \\\"utf-8\\\")\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tthis.originalContent = \\\"\\\"\\r\\n\\t\\t}\\r\\n\\t\\t// for new files, create any necessary directories and keep track of new directories to delete if the user denies the operation\\r\\n\\t\\tthis.createdDirs = await createDirectoriesForFile(absolutePath)\\r\\n\\t\\t// make sure the file exists before we open it\\r\\n\\t\\tif (!fileExists) {\\r\\n\\t\\t\\tawait fs.writeFile(absolutePath, \\\"\\\")\\r\\n\\t\\t}\\r\\n\\t\\t// if the file was already open, close it (must happen after showing the diff view since if it's the only tab the column will close)\\r\\n\\t\\tthis.documentWasOpen = false\\r\\n\\t\\t// close the tab if it's open (it's already saved above)\\r\\n\\t\\tconst tabs = vscode.window.tabGroups.all\\r\\n\\t\\t\\t.map((tg) => tg.tabs)\\r\\n\\t\\t\\t.flat()\\r\\n\\t\\t\\t.filter((tab) => tab.input instanceof vscode.TabInputText && arePathsEqual(tab.input.uri.fsPath, absolutePath))\\r\\n\\t\\tfor (const tab of tabs) {\\r\\n\\t\\t\\tif (!tab.isDirty) {\\r\\n\\t\\t\\t\\tawait vscode.window.tabGroups.close(tab)\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tthis.documentWasOpen = true\\r\\n\\t\\t}\\r\\n\\t\\tthis.activeDiffEditor = await this.openDiffEditor()\\r\\n\\t\\tthis.fadedOverlayController = new DecorationController(\\\"fadedOverlay\\\", this.activeDiffEditor)\\r\\n\\t\\tthis.activeLineController = new DecorationController(\\\"activeLine\\\", this.activeDiffEditor)\\r\\n\\t\\t// Apply faded overlay to all lines initially\\r\\n\\t\\tthis.fadedOverlayController.addLines(0, this.activeDiffEditor.document.lineCount)\\r\\n\\t\\tthis.scrollEditorToLine(0) // will this crash for new files?\\r\\n\\t\\tthis.streamedLines = []\\r\\n\\t}\\r\\n\\r\\n\\tasync update(accumulatedContent: string, isFinal: boolean) {\\r\\n\\t\\tif (!this.relPath || !this.activeLineController || !this.fadedOverlayController) {\\r\\n\\t\\t\\tthrow new Error(\\\"Required values not set\\\")\\r\\n\\t\\t}\\r\\n\\t\\tthis.newContent = accumulatedContent\\r\\n\\t\\tconst accumulatedLines = accumulatedContent.split(\\\"\\\\n\\\")\\r\\n\\t\\tif (!isFinal) {\\r\\n\\t\\t\\taccumulatedLines.pop() // remove the last partial line only if it's not the final update\\r\\n\\t\\t}\\r\\n\\t\\tconst diffLines = accumulatedLines.slice(this.streamedLines.length)\\r\\n\\r\\n\\t\\tconst diffEditor = this.activeDiffEditor\\r\\n\\t\\tconst document = diffEditor?.document\\r\\n\\t\\tif (!diffEditor || !document) {\\r\\n\\t\\t\\tthrow new Error(\\\"User closed text editor, unable to edit file...\\\")\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// Place cursor at the beginning of the diff editor to keep it out of the way of the stream animation\\r\\n\\t\\tconst beginningOfDocument = new vscode.Position(0, 0)\\r\\n\\t\\tdiffEditor.selection = new vscode.Selection(beginningOfDocument, beginningOfDocument)\\r\\n\\r\\n\\t\\tfor (let i = 0; i < diffLines.length; i++) {\\r\\n\\t\\t\\tconst currentLine = this.streamedLines.length + i\\r\\n\\t\\t\\t// Replace all content up to the current line with accumulated lines\\r\\n\\t\\t\\t// This is necessary (as compared to inserting one line at a time) to handle cases where html tags on previous lines are auto closed for example\\r\\n\\t\\t\\tconst edit = new vscode.WorkspaceEdit()\\r\\n\\t\\t\\tconst rangeToReplace = new vscode.Range(0, 0, currentLine + 1, 0)\\r\\n\\t\\t\\tconst contentToReplace = accumulatedLines.slice(0, currentLine + 1).join(\\\"\\\\n\\\") + \\\"\\\\n\\\"\\r\\n\\t\\t\\tedit.replace(document.uri, rangeToReplace, contentToReplace)\\r\\n\\t\\t\\tawait vscode.workspace.applyEdit(edit)\\r\\n\\t\\t\\t// Update decorations\\r\\n\\t\\t\\tthis.activeLineController.setActiveLine(currentLine)\\r\\n\\t\\t\\tthis.fadedOverlayController.updateOverlayAfterLine(currentLine, document.lineCount)\\r\\n\\t\\t\\t// Scroll to the current line\\r\\n\\t\\t\\tthis.scrollEditorToLine(currentLine)\\r\\n\\t\\t}\\r\\n\\t\\t// Update the streamedLines with the new accumulated content\\r\\n\\t\\tthis.streamedLines = accumulatedLines\\r\\n\\t\\tif (isFinal) {\\r\\n\\t\\t\\t// Handle any remaining lines if the new content is shorter than the original\\r\\n\\t\\t\\tif (this.streamedLines.length < document.lineCount) {\\r\\n\\t\\t\\t\\tconst edit = new vscode.WorkspaceEdit()\\r\\n\\t\\t\\t\\tedit.delete(document.uri, new vscode.Range(this.streamedLines.length, 0, document.lineCount, 0))\\r\\n\\t\\t\\t\\tawait vscode.workspace.applyEdit(edit)\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t// Add empty last line if original content had one\\r\\n\\t\\t\\tconst hasEmptyLastLine = this.originalContent?.endsWith(\\\"\\\\n\\\")\\r\\n\\t\\t\\tif (hasEmptyLastLine) {\\r\\n\\t\\t\\t\\tconst accumulatedLines = accumulatedContent.split(\\\"\\\\n\\\")\\r\\n\\t\\t\\t\\tif (accumulatedLines[accumulatedLines.length - 1] !== \\\"\\\") {\\r\\n\\t\\t\\t\\t\\taccumulatedContent += \\\"\\\\n\\\"\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t// Clear all decorations at the end (before applying final edit)\\r\\n\\t\\t\\tthis.fadedOverlayController.clear()\\r\\n\\t\\t\\tthis.activeLineController.clear()\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tasync saveChanges(): Promise<{\\r\\n\\t\\tnewProblemsMessage: string | undefined\\r\\n\\t\\tuserEdits: string | undefined\\r\\n\\t\\tautoFormattingEdits: string | undefined\\r\\n\\t\\tfinalContent: string | undefined\\r\\n\\t}> {\\r\\n\\t\\tif (!this.relPath || !this.newContent || !this.activeDiffEditor) {\\r\\n\\t\\t\\treturn {\\r\\n\\t\\t\\t\\tnewProblemsMessage: undefined,\\r\\n\\t\\t\\t\\tuserEdits: undefined,\\r\\n\\t\\t\\t\\tautoFormattingEdits: undefined,\\r\\n\\t\\t\\t\\tfinalContent: undefined,\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\tconst absolutePath = path.resolve(this.cwd, this.relPath)\\r\\n\\t\\tconst updatedDocument = this.activeDiffEditor.document\\r\\n\\r\\n\\t\\t// get the contents before save operation which may do auto-formatting\\r\\n\\t\\tconst preSaveContent = updatedDocument.getText()\\r\\n\\r\\n\\t\\tif (updatedDocument.isDirty) {\\r\\n\\t\\t\\tawait updatedDocument.save()\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// await delay(100)\\r\\n\\t\\t// get text after save in case there is any auto-formatting done by the editor\\r\\n\\t\\tconst postSaveContent = updatedDocument.getText()\\r\\n\\r\\n\\t\\tawait vscode.window.showTextDocument(vscode.Uri.file(absolutePath), {\\r\\n\\t\\t\\tpreview: false,\\r\\n\\t\\t})\\r\\n\\t\\tawait this.closeAllDiffViews()\\r\\n\\r\\n\\t\\t/*\\r\\n\\t\\tGetting diagnostics before and after the file edit is a better approach than\\r\\n\\t\\tautomatically tracking problems in real-time. This method ensures we only\\r\\n\\t\\treport new problems that are a direct result of this specific edit.\\r\\n\\t\\tSince these are new problems resulting from Cline's edit, we know they're\\r\\n\\t\\tdirectly related to the work he's doing. This eliminates the risk of Cline\\r\\n\\t\\tgoing off-task or getting distracted by unrelated issues, which was a problem\\r\\n\\t\\twith the previous auto-debug approach. Some users' machines may be slow to\\r\\n\\t\\tupdate diagnostics, so this approach provides a good balance between automation\\r\\n\\t\\tand avoiding potential issues where Cline might get stuck in loops due to\\r\\n\\t\\toutdated problem information. If no new problems show up by the time the user\\r\\n\\t\\taccepts the changes, they can always debug later using the '@problems' mention.\\r\\n\\t\\tThis way, Cline only becomes aware of new problems resulting from his edits\\r\\n\\t\\tand can address them accordingly. If problems don't change immediately after\\r\\n\\t\\tapplying a fix, Cline won't be notified, which is generally fine since the\\r\\n\\t\\tinitial fix is usually correct and it may just take time for linters to catch up.\\r\\n\\t\\t*/\\r\\n\\t\\tconst postDiagnostics = vscode.languages.getDiagnostics()\\r\\n\\t\\tconst newProblems = diagnosticsToProblemsString(\\r\\n\\t\\t\\tgetNewDiagnostics(this.preDiagnostics, postDiagnostics),\\r\\n\\t\\t\\t[\\r\\n\\t\\t\\t\\tvscode.DiagnosticSeverity.Error, // only including errors since warnings can be distracting (if user wants to fix warnings they can use the @problems mention)\\r\\n\\t\\t\\t],\\r\\n\\t\\t\\tthis.cwd,\\r\\n\\t\\t) // will be empty string if no errors\\r\\n\\t\\tconst newProblemsMessage =\\r\\n\\t\\t\\tnewProblems.length > 0 ? `\\\\n\\\\nNew problems detected after saving the file:\\\\n${newProblems}` : \\\"\\\"\\r\\n\\r\\n\\t\\t// If the edited content has different EOL characters, we don't want to show a diff with all the EOL differences.\\r\\n\\t\\tconst newContentEOL = this.newContent.includes(\\\"\\\\r\\\\n\\\") ? \\\"\\\\r\\\\n\\\" : \\\"\\\\n\\\"\\r\\n\\t\\tconst normalizedPreSaveContent = preSaveContent.replace(/\\\\r\\\\n|\\\\n/g, newContentEOL).trimEnd() + newContentEOL // trimEnd to fix issue where editor adds in extra new line automatically\\r\\n\\t\\tconst normalizedPostSaveContent = postSaveContent.replace(/\\\\r\\\\n|\\\\n/g, newContentEOL).trimEnd() + newContentEOL // this is the final content we return to the model to use as the new baseline for future edits\\r\\n\\t\\t// just in case the new content has a mix of varying EOL characters\\r\\n\\t\\tconst normalizedNewContent = this.newContent.replace(/\\\\r\\\\n|\\\\n/g, newContentEOL).trimEnd() + newContentEOL\\r\\n\\r\\n\\t\\tlet userEdits: string | undefined\\r\\n\\t\\tif (normalizedPreSaveContent !== normalizedNewContent) {\\r\\n\\t\\t\\t// user made changes before approving edit. let the model know about user made changes (not including post-save auto-formatting changes)\\r\\n\\t\\t\\tuserEdits = formatResponse.createPrettyPatch(this.relPath.toPosix(), normalizedNewContent, normalizedPreSaveContent)\\r\\n\\t\\t\\t// return { newProblemsMessage, userEdits, finalContent: normalizedPostSaveContent }\\r\\n\\t\\t} else {\\r\\n\\t\\t\\t// no changes to cline's edits\\r\\n\\t\\t\\t// return { newProblemsMessage, userEdits: undefined, finalContent: normalizedPostSaveContent }\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tlet autoFormattingEdits: string | undefined\\r\\n\\t\\tif (normalizedPreSaveContent !== normalizedPostSaveContent) {\\r\\n\\t\\t\\t// auto-formatting was done by the editor\\r\\n\\t\\t\\tautoFormattingEdits = formatResponse.createPrettyPatch(\\r\\n\\t\\t\\t\\tthis.relPath.toPosix(),\\r\\n\\t\\t\\t\\tnormalizedPreSaveContent,\\r\\n\\t\\t\\t\\tnormalizedPostSaveContent,\\r\\n\\t\\t\\t)\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tnewProblemsMessage,\\r\\n\\t\\t\\tuserEdits,\\r\\n\\t\\t\\tautoFormattingEdits,\\r\\n\\t\\t\\tfinalContent: normalizedPostSaveContent,\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tasync revertChanges(): Promise<void> {\\r\\n\\t\\tif (!this.relPath || !this.activeDiffEditor) {\\r\\n\\t\\t\\treturn\\r\\n\\t\\t}\\r\\n\\t\\tconst fileExists = this.editType === \\\"modify\\\"\\r\\n\\t\\tconst updatedDocument = this.activeDiffEditor.document\\r\\n\\t\\tconst absolutePath = path.resolve(this.cwd, this.relPath)\\r\\n\\t\\tif (!fileExists) {\\r\\n\\t\\t\\tif (updatedDocument.isDirty) {\\r\\n\\t\\t\\t\\tawait updatedDocument.save()\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tawait this.closeAllDiffViews()\\r\\n\\t\\t\\tawait fs.unlink(absolutePath)\\r\\n\\t\\t\\t// Remove only the directories we created, in reverse order\\r\\n\\t\\t\\tfor (let i = this.createdDirs.length - 1; i >= 0; i--) {\\r\\n\\t\\t\\t\\tawait fs.rmdir(this.createdDirs[i])\\r\\n\\t\\t\\t\\tconsole.log(`Directory ${this.createdDirs[i]} has been deleted.`)\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tconsole.log(`File ${absolutePath} has been deleted.`)\\r\\n\\t\\t} else {\\r\\n\\t\\t\\t// revert document\\r\\n\\t\\t\\tconst edit = new vscode.WorkspaceEdit()\\r\\n\\t\\t\\tconst fullRange = new vscode.Range(\\r\\n\\t\\t\\t\\tupdatedDocument.positionAt(0),\\r\\n\\t\\t\\t\\tupdatedDocument.positionAt(updatedDocument.getText().length),\\r\\n\\t\\t\\t)\\r\\n\\t\\t\\tedit.replace(updatedDocument.uri, fullRange, this.originalContent ?? \\\"\\\")\\r\\n\\t\\t\\t// Apply the edit and save, since contents shouldnt have changed this wont show in local history unless of course the user made changes and saved during the edit\\r\\n\\t\\t\\tawait vscode.workspace.applyEdit(edit)\\r\\n\\t\\t\\tawait updatedDocument.save()\\r\\n\\t\\t\\tconsole.log(`File ${absolutePath} has been reverted to its original content.`)\\r\\n\\t\\t\\tif (this.documentWasOpen) {\\r\\n\\t\\t\\t\\tawait vscode.window.showTextDocument(vscode.Uri.file(absolutePath), {\\r\\n\\t\\t\\t\\t\\tpreview: false,\\r\\n\\t\\t\\t\\t})\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tawait this.closeAllDiffViews()\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// edit is done\\r\\n\\t\\tawait this.reset()\\r\\n\\t}\\r\\n\\r\\n\\tprivate async closeAllDiffViews() {\\r\\n\\t\\tconst tabs = vscode.window.tabGroups.all\\r\\n\\t\\t\\t.flatMap((tg) => tg.tabs)\\r\\n\\t\\t\\t.filter((tab) => tab.input instanceof vscode.TabInputTextDiff && tab.input?.original?.scheme === DIFF_VIEW_URI_SCHEME)\\r\\n\\t\\tfor (const tab of tabs) {\\r\\n\\t\\t\\t// trying to close dirty views results in save popup\\r\\n\\t\\t\\tif (!tab.isDirty) {\\r\\n\\t\\t\\t\\tawait vscode.window.tabGroups.close(tab)\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tprivate async openDiffEditor(): Promise<vscode.TextEditor> {\\r\\n\\t\\tif (!this.relPath) {\\r\\n\\t\\t\\tthrow new Error(\\\"No file path set\\\")\\r\\n\\t\\t}\\r\\n\\t\\tconst uri = vscode.Uri.file(path.resolve(this.cwd, this.relPath))\\r\\n\\t\\t// If this diff editor is already open (ie if a previous write file was interrupted) then we should activate that instead of opening a new diff\\r\\n\\t\\tconst diffTab = vscode.window.tabGroups.all\\r\\n\\t\\t\\t.flatMap((group) => group.tabs)\\r\\n\\t\\t\\t.find(\\r\\n\\t\\t\\t\\t(tab) =>\\r\\n\\t\\t\\t\\t\\ttab.input instanceof vscode.TabInputTextDiff &&\\r\\n\\t\\t\\t\\t\\ttab.input?.original?.scheme === DIFF_VIEW_URI_SCHEME &&\\r\\n\\t\\t\\t\\t\\tarePathsEqual(tab.input.modified.fsPath, uri.fsPath),\\r\\n\\t\\t\\t)\\r\\n\\t\\tif (diffTab && diffTab.input instanceof vscode.TabInputTextDiff) {\\r\\n\\t\\t\\tconst editor = await vscode.window.showTextDocument(diffTab.input.modified)\\r\\n\\t\\t\\treturn editor\\r\\n\\t\\t}\\r\\n\\t\\t// Open new diff editor\\r\\n\\t\\treturn new Promise<vscode.TextEditor>((resolve, reject) => {\\r\\n\\t\\t\\tconst fileName = path.basename(uri.fsPath)\\r\\n\\t\\t\\tconst fileExists = this.editType === \\\"modify\\\"\\r\\n\\t\\t\\tconst disposable = vscode.window.onDidChangeActiveTextEditor((editor) => {\\r\\n\\t\\t\\t\\tif (editor && arePathsEqual(editor.document.uri.fsPath, uri.fsPath)) {\\r\\n\\t\\t\\t\\t\\tdisposable.dispose()\\r\\n\\t\\t\\t\\t\\tresolve(editor)\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t})\\r\\n\\t\\t\\tvscode.commands.executeCommand(\\r\\n\\t\\t\\t\\t\\\"vscode.diff\\\",\\r\\n\\t\\t\\t\\tvscode.Uri.parse(`${DIFF_VIEW_URI_SCHEME}:${fileName}`).with({\\r\\n\\t\\t\\t\\t\\tquery: Buffer.from(this.originalContent ?? \\\"\\\").toString(\\\"base64\\\"),\\r\\n\\t\\t\\t\\t}),\\r\\n\\t\\t\\t\\turi,\\r\\n\\t\\t\\t\\t`${fileName}: ${fileExists ? \\\"Original ↔ Cline's Changes\\\" : \\\"New File\\\"} (Editable)`,\\r\\n\\t\\t\\t)\\r\\n\\t\\t\\t// This may happen on very slow machines ie project idx\\r\\n\\t\\t\\tsetTimeout(() => {\\r\\n\\t\\t\\t\\tdisposable.dispose()\\r\\n\\t\\t\\t\\treject(new Error(\\\"Failed to open diff editor, please try again...\\\"))\\r\\n\\t\\t\\t}, 10_000)\\r\\n\\t\\t})\\r\\n\\t}\\r\\n\\r\\n\\tprivate scrollEditorToLine(line: number) {\\r\\n\\t\\tif (this.activeDiffEditor) {\\r\\n\\t\\t\\tconst scrollLine = line + 4\\r\\n\\t\\t\\tthis.activeDiffEditor.revealRange(\\r\\n\\t\\t\\t\\tnew vscode.Range(scrollLine, 0, scrollLine, 0),\\r\\n\\t\\t\\t\\tvscode.TextEditorRevealType.InCenter,\\r\\n\\t\\t\\t)\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tscrollToFirstDiff() {\\r\\n\\t\\tif (!this.activeDiffEditor) {\\r\\n\\t\\t\\treturn\\r\\n\\t\\t}\\r\\n\\t\\tconst currentContent = this.activeDiffEditor.document.getText()\\r\\n\\t\\tconst diffs = diff.diffLines(this.originalContent || \\\"\\\", currentContent)\\r\\n\\t\\tlet lineCount = 0\\r\\n\\t\\tfor (const part of diffs) {\\r\\n\\t\\t\\tif (part.added || part.removed) {\\r\\n\\t\\t\\t\\t// Found the first diff, scroll to it\\r\\n\\t\\t\\t\\tthis.activeDiffEditor.revealRange(\\r\\n\\t\\t\\t\\t\\tnew vscode.Range(lineCount, 0, lineCount, 0),\\r\\n\\t\\t\\t\\t\\tvscode.TextEditorRevealType.InCenter,\\r\\n\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\treturn\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tif (!part.removed) {\\r\\n\\t\\t\\t\\tlineCount += part.count || 0\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t// close editor if open?\\r\\n\\tasync reset() {\\r\\n\\t\\tthis.editType = undefined\\r\\n\\t\\tthis.isEditing = false\\r\\n\\t\\tthis.originalContent = undefined\\r\\n\\t\\tthis.createdDirs = []\\r\\n\\t\\tthis.documentWasOpen = false\\r\\n\\t\\tthis.activeDiffEditor = undefined\\r\\n\\t\\tthis.fadedOverlayController = undefined\\r\\n\\t\\tthis.activeLineController = undefined\\r\\n\\t\\tthis.streamedLines = []\\r\\n\\t\\tthis.preDiagnostics = []\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":15618,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.956Z\",\"createdTime\":\"2025-01-15T15:26:07.956Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":378,\"nonEmptyLines\":350,\"commentLines\":37,\"complexity\":76},\"dependencies\":[\"vscode\",\"path\",\"fs/promises\",\"../../utils/fs\",\"../../utils/path\",\"../../core/prompts/responses\",\"./DecorationController\",\"diff\",\"../diagnostics\"],\"quality\":{\"score\":-356,\"issues\":[],\"duplicateLines\":84,\"longLines\":18,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.363Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":378},\"analysis\":{\"metrics\":{\"lines\":378,\"nonEmptyLines\":350,\"commentLines\":37,\"complexity\":76},\"dependencies\":[\"vscode\",\"path\",\"fs/promises\",\"../../utils/fs\",\"../../utils/path\",\"../../core/prompts/responses\",\"./DecorationController\",\"diff\",\"../diagnostics\"],\"quality\":{\"score\":-356,\"issues\":[],\"duplicateLines\":84,\"longLines\":18,\"complexFunctions\":0}},\"lastModified\":1737046855364,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.364Z\",\"size\":17951},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\integrations\\\\editor\\\\detect-omission.ts\",\"content\":{\"content\":\"import * as vscode from \\\"vscode\\\"\\r\\n\\r\\n/**\\r\\n * Detects potential AI-generated code omissions in the given file content.\\r\\n * @param originalFileContent The original content of the file.\\r\\n * @param newFileContent The new content of the file to check.\\r\\n * @returns True if a potential omission is detected, false otherwise.\\r\\n */\\r\\nfunction detectCodeOmission(originalFileContent: string, newFileContent: string): boolean {\\r\\n\\tconst originalLines = originalFileContent.split(\\\"\\\\n\\\")\\r\\n\\tconst newLines = newFileContent.split(\\\"\\\\n\\\")\\r\\n\\tconst omissionKeywords = [\\\"remain\\\", \\\"remains\\\", \\\"unchanged\\\", \\\"rest\\\", \\\"previous\\\", \\\"existing\\\", \\\"...\\\"]\\r\\n\\r\\n\\tconst commentPatterns = [\\r\\n\\t\\t/^\\\\s*\\\\/\\\\//, // Single-line comment for most languages\\r\\n\\t\\t/^\\\\s*#/, // Single-line comment for Python, Ruby, etc.\\r\\n\\t\\t/^\\\\s*\\\\/\\\\*/, // Multi-line comment opening\\r\\n\\t\\t/^\\\\s*{\\\\s*\\\\/\\\\*/, // JSX comment opening\\r\\n\\t\\t/^\\\\s*<!--/, // HTML comment opening\\r\\n\\t]\\r\\n\\r\\n\\tfor (const line of newLines) {\\r\\n\\t\\tif (commentPatterns.some((pattern) => pattern.test(line))) {\\r\\n\\t\\t\\tconst words = line.toLowerCase().split(/\\\\s+/)\\r\\n\\t\\t\\tif (omissionKeywords.some((keyword) => words.includes(keyword))) {\\r\\n\\t\\t\\t\\tif (!originalLines.includes(line)) {\\r\\n\\t\\t\\t\\t\\treturn true\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\treturn false\\r\\n}\\r\\n\\r\\n/**\\r\\n * Shows a warning in VSCode if a potential code omission is detected.\\r\\n * @param originalFileContent The original content of the file.\\r\\n * @param newFileContent The new content of the file to check.\\r\\n */\\r\\nexport function showOmissionWarning(originalFileContent: string, newFileContent: string): void {\\r\\n\\tif (detectCodeOmission(originalFileContent, newFileContent)) {\\r\\n\\t\\tvscode.window\\r\\n\\t\\t\\t.showWarningMessage(\\r\\n\\t\\t\\t\\t\\\"Potential code truncation detected. This happens when the AI reaches its max output limit.\\\",\\r\\n\\t\\t\\t\\t\\\"Follow this guide to fix the issue\\\",\\r\\n\\t\\t\\t)\\r\\n\\t\\t\\t.then((selection) => {\\r\\n\\t\\t\\t\\tif (selection === \\\"Follow this guide to fix the issue\\\") {\\r\\n\\t\\t\\t\\t\\tvscode.env.openExternal(\\r\\n\\t\\t\\t\\t\\t\\tvscode.Uri.parse(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\\"https://github.com/cline/cline/wiki/Troubleshooting-%E2%80%90-Cline-Deleting-Code-with-%22Rest-of-Code-Here%22-Comments\\\",\\r\\n\\t\\t\\t\\t\\t\\t),\\r\\n\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t})\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":2099,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.846Z\",\"createdTime\":\"2024-12-29T05:57:23.846Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":59,\"nonEmptyLines\":53,\"commentLines\":2,\"complexity\":7},\"dependencies\":[\"vscode\"],\"quality\":{\"score\":38,\"issues\":[],\"duplicateLines\":12,\"longLines\":1,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.362Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":59},\"analysis\":{\"metrics\":{\"lines\":59,\"nonEmptyLines\":53,\"commentLines\":2,\"complexity\":7},\"dependencies\":[\"vscode\"],\"quality\":{\"score\":38,\"issues\":[],\"duplicateLines\":12,\"longLines\":1,\"complexFunctions\":0}},\"lastModified\":1737046855362,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.362Z\",\"size\":2826},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\integrations\\\\editor\\\\DecorationController.ts\",\"content\":{\"content\":\"import * as vscode from \\\"vscode\\\"\\r\\n\\r\\nconst fadedOverlayDecorationType = vscode.window.createTextEditorDecorationType({\\r\\n\\tbackgroundColor: \\\"rgba(255, 255, 0, 0.1)\\\",\\r\\n\\topacity: \\\"0.4\\\",\\r\\n\\tisWholeLine: true,\\r\\n})\\r\\n\\r\\nconst activeLineDecorationType = vscode.window.createTextEditorDecorationType({\\r\\n\\tbackgroundColor: \\\"rgba(255, 255, 0, 0.3)\\\",\\r\\n\\topacity: \\\"1\\\",\\r\\n\\tisWholeLine: true,\\r\\n\\tborder: \\\"1px solid rgba(255, 255, 0, 0.5)\\\",\\r\\n})\\r\\n\\r\\ntype DecorationType = \\\"fadedOverlay\\\" | \\\"activeLine\\\"\\r\\n\\r\\nexport class DecorationController {\\r\\n\\tprivate decorationType: DecorationType\\r\\n\\tprivate editor: vscode.TextEditor\\r\\n\\tprivate ranges: vscode.Range[] = []\\r\\n\\r\\n\\tconstructor(decorationType: DecorationType, editor: vscode.TextEditor) {\\r\\n\\t\\tthis.decorationType = decorationType\\r\\n\\t\\tthis.editor = editor\\r\\n\\t}\\r\\n\\r\\n\\tgetDecoration() {\\r\\n\\t\\tswitch (this.decorationType) {\\r\\n\\t\\t\\tcase \\\"fadedOverlay\\\":\\r\\n\\t\\t\\t\\treturn fadedOverlayDecorationType\\r\\n\\t\\t\\tcase \\\"activeLine\\\":\\r\\n\\t\\t\\t\\treturn activeLineDecorationType\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\taddLines(startIndex: number, numLines: number) {\\r\\n\\t\\t// Guard against invalid inputs\\r\\n\\t\\tif (startIndex < 0 || numLines <= 0) {\\r\\n\\t\\t\\treturn\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tconst lastRange = this.ranges[this.ranges.length - 1]\\r\\n\\t\\tif (lastRange && lastRange.end.line === startIndex - 1) {\\r\\n\\t\\t\\tthis.ranges[this.ranges.length - 1] = lastRange.with(undefined, lastRange.end.translate(numLines))\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tconst endLine = startIndex + numLines - 1\\r\\n\\t\\t\\tthis.ranges.push(new vscode.Range(startIndex, 0, endLine, Number.MAX_SAFE_INTEGER))\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.editor.setDecorations(this.getDecoration(), this.ranges)\\r\\n\\t}\\r\\n\\r\\n\\tclear() {\\r\\n\\t\\tthis.ranges = []\\r\\n\\t\\tthis.editor.setDecorations(this.getDecoration(), this.ranges)\\r\\n\\t}\\r\\n\\r\\n\\tupdateOverlayAfterLine(line: number, totalLines: number) {\\r\\n\\t\\t// Remove any existing ranges that start at or after the current line\\r\\n\\t\\tthis.ranges = this.ranges.filter((range) => range.end.line < line)\\r\\n\\r\\n\\t\\t// Add a new range for all lines after the current line\\r\\n\\t\\tif (line < totalLines - 1) {\\r\\n\\t\\t\\tthis.ranges.push(\\r\\n\\t\\t\\t\\tnew vscode.Range(new vscode.Position(line + 1, 0), new vscode.Position(totalLines - 1, Number.MAX_SAFE_INTEGER)),\\r\\n\\t\\t\\t)\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// Apply the updated decorations\\r\\n\\t\\tthis.editor.setDecorations(this.getDecoration(), this.ranges)\\r\\n\\t}\\r\\n\\r\\n\\tsetActiveLine(line: number) {\\r\\n\\t\\tthis.ranges = [new vscode.Range(line, 0, line, Number.MAX_SAFE_INTEGER)]\\r\\n\\t\\tthis.editor.setDecorations(this.getDecoration(), this.ranges)\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":2399,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.956Z\",\"createdTime\":\"2025-01-15T15:26:07.956Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":79,\"nonEmptyLines\":64,\"commentLines\":4,\"complexity\":9},\"dependencies\":[\"vscode\"],\"quality\":{\"score\":21,\"issues\":[],\"duplicateLines\":15,\"longLines\":2,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.360Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":79},\"analysis\":{\"metrics\":{\"lines\":79,\"nonEmptyLines\":64,\"commentLines\":4,\"complexity\":9},\"dependencies\":[\"vscode\"],\"quality\":{\"score\":21,\"issues\":[],\"duplicateLines\":15,\"longLines\":2,\"complexFunctions\":0}},\"lastModified\":1737046855361,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.361Z\",\"size\":3142},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\integrations\\\\diagnostics\\\\index.ts\",\"content\":{\"content\":\"import * as vscode from \\\"vscode\\\"\\r\\nimport * as path from \\\"path\\\"\\r\\nimport deepEqual from \\\"fast-deep-equal\\\"\\r\\n\\r\\nexport function getNewDiagnostics(\\r\\n\\toldDiagnostics: [vscode.Uri, vscode.Diagnostic[]][],\\r\\n\\tnewDiagnostics: [vscode.Uri, vscode.Diagnostic[]][],\\r\\n): [vscode.Uri, vscode.Diagnostic[]][] {\\r\\n\\tconst newProblems: [vscode.Uri, vscode.Diagnostic[]][] = []\\r\\n\\tconst oldMap = new Map(oldDiagnostics)\\r\\n\\r\\n\\tfor (const [uri, newDiags] of newDiagnostics) {\\r\\n\\t\\tconst oldDiags = oldMap.get(uri) || []\\r\\n\\t\\tconst newProblemsForUri = newDiags.filter((newDiag) => !oldDiags.some((oldDiag) => deepEqual(oldDiag, newDiag)))\\r\\n\\r\\n\\t\\tif (newProblemsForUri.length > 0) {\\r\\n\\t\\t\\tnewProblems.push([uri, newProblemsForUri])\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\treturn newProblems\\r\\n}\\r\\n\\r\\n// Usage:\\r\\n// const oldDiagnostics = // ... your old diagnostics array\\r\\n// const newDiagnostics = // ... your new diagnostics array\\r\\n// const newProblems = getNewDiagnostics(oldDiagnostics, newDiagnostics);\\r\\n\\r\\n// Example usage with mocks:\\r\\n//\\r\\n// // Mock old diagnostics\\r\\n// const oldDiagnostics: [vscode.Uri, vscode.Diagnostic[]][] = [\\r\\n// [vscode.Uri.file(\\\"/path/to/file1.ts\\\"), [\\r\\n// new vscode.Diagnostic(new vscode.Range(0, 0, 0, 10), \\\"Old error in file1\\\", vscode.DiagnosticSeverity.Error)\\r\\n// ]],\\r\\n// [vscode.Uri.file(\\\"/path/to/file2.ts\\\"), [\\r\\n// new vscode.Diagnostic(new vscode.Range(5, 5, 5, 15), \\\"Old warning in file2\\\", vscode.DiagnosticSeverity.Warning)\\r\\n// ]]\\r\\n// ];\\r\\n//\\r\\n// // Mock new diagnostics\\r\\n// const newDiagnostics: [vscode.Uri, vscode.Diagnostic[]][] = [\\r\\n// [vscode.Uri.file(\\\"/path/to/file1.ts\\\"), [\\r\\n// new vscode.Diagnostic(new vscode.Range(0, 0, 0, 10), \\\"Old error in file1\\\", vscode.DiagnosticSeverity.Error),\\r\\n// new vscode.Diagnostic(new vscode.Range(2, 2, 2, 12), \\\"New error in file1\\\", vscode.DiagnosticSeverity.Error)\\r\\n// ]],\\r\\n// [vscode.Uri.file(\\\"/path/to/file2.ts\\\"), [\\r\\n// new vscode.Diagnostic(new vscode.Range(5, 5, 5, 15), \\\"Old warning in file2\\\", vscode.DiagnosticSeverity.Warning)\\r\\n// ]],\\r\\n// [vscode.Uri.file(\\\"/path/to/file3.ts\\\"), [\\r\\n// new vscode.Diagnostic(new vscode.Range(1, 1, 1, 11), \\\"New error in file3\\\", vscode.DiagnosticSeverity.Error)\\r\\n// ]]\\r\\n// ];\\r\\n//\\r\\n// const newProblems = getNewProblems(oldDiagnostics, newDiagnostics);\\r\\n//\\r\\n// console.log(\\\"New problems:\\\");\\r\\n// for (const [uri, diagnostics] of newProblems) {\\r\\n// console.log(`File: ${uri.fsPath}`);\\r\\n// for (const diagnostic of diagnostics) {\\r\\n// console.log(`- ${diagnostic.message} (${diagnostic.range.start.line}:${diagnostic.range.start.character})`);\\r\\n// }\\r\\n// }\\r\\n//\\r\\n// // Expected output:\\r\\n// // New problems:\\r\\n// // File: /path/to/file1.ts\\r\\n// // - New error in file1 (2:2)\\r\\n// // File: /path/to/file3.ts\\r\\n// // - New error in file3 (1:1)\\r\\n\\r\\n// will return empty string if no problems with the given severity are found\\r\\nexport function diagnosticsToProblemsString(\\r\\n\\tdiagnostics: [vscode.Uri, vscode.Diagnostic[]][],\\r\\n\\tseverities: vscode.DiagnosticSeverity[],\\r\\n\\tcwd: string,\\r\\n): string {\\r\\n\\tlet result = \\\"\\\"\\r\\n\\tfor (const [uri, fileDiagnostics] of diagnostics) {\\r\\n\\t\\tconst problems = fileDiagnostics.filter((d) => severities.includes(d.severity))\\r\\n\\t\\tif (problems.length > 0) {\\r\\n\\t\\t\\tresult += `\\\\n\\\\n${path.relative(cwd, uri.fsPath).toPosix()}`\\r\\n\\t\\t\\tfor (const diagnostic of problems) {\\r\\n\\t\\t\\t\\tlet label: string\\r\\n\\t\\t\\t\\tswitch (diagnostic.severity) {\\r\\n\\t\\t\\t\\t\\tcase vscode.DiagnosticSeverity.Error:\\r\\n\\t\\t\\t\\t\\t\\tlabel = \\\"Error\\\"\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase vscode.DiagnosticSeverity.Warning:\\r\\n\\t\\t\\t\\t\\t\\tlabel = \\\"Warning\\\"\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase vscode.DiagnosticSeverity.Information:\\r\\n\\t\\t\\t\\t\\t\\tlabel = \\\"Information\\\"\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase vscode.DiagnosticSeverity.Hint:\\r\\n\\t\\t\\t\\t\\t\\tlabel = \\\"Hint\\\"\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\t\\t\\tlabel = \\\"Diagnostic\\\"\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tconst line = diagnostic.range.start.line + 1 // VSCode lines are 0-indexed\\r\\n\\t\\t\\t\\tconst source = diagnostic.source ? `${diagnostic.source} ` : \\\"\\\"\\r\\n\\t\\t\\t\\tresult += `\\\\n- [${source}${label}] Line ${line}: ${diagnostic.message}`\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\treturn result.trim()\\r\\n}\\r\\n\",\"metadata\":{\"size\":4038,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.845Z\",\"createdTime\":\"2024-12-29T05:57:23.843Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":109,\"nonEmptyLines\":101,\"commentLines\":47,\"complexity\":14},\"dependencies\":[\"vscode\",\"path\",\"fast-deep-equal\"],\"quality\":{\"score\":-21,\"issues\":[],\"duplicateLines\":21,\"longLines\":8,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.359Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":109},\"analysis\":{\"metrics\":{\"lines\":109,\"nonEmptyLines\":101,\"commentLines\":47,\"complexity\":14},\"dependencies\":[\"vscode\",\"path\",\"fast-deep-equal\"],\"quality\":{\"score\":-21,\"issues\":[],\"duplicateLines\":21,\"longLines\":8,\"complexFunctions\":0}},\"lastModified\":1737046855360,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.360Z\",\"size\":4938},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\integrations\\\\diagnostics\\\\DiagnosticsMonitor.ts\",\"content\":{\"content\":\"/*\\r\\nimport * as vscode from \\\"vscode\\\"\\r\\nimport deepEqual from \\\"fast-deep-equal\\\"\\r\\n\\r\\ntype FileDiagnostics = [vscode.Uri, vscode.Diagnostic[]][]\\r\\n\\r\\n\\r\\nAbout Diagnostics:\\r\\nThe Problems tab shows diagnostics that have been reported for your project. These diagnostics are categorized into:\\r\\nErrors: Critical issues that usually prevent your code from compiling or running correctly.\\r\\nWarnings: Potential problems in the code that may not prevent it from running but could cause issues (e.g., bad practices, unused variables).\\r\\nInformation: Non-critical suggestions or tips (e.g., formatting issues or notes from linters).\\r\\nThe Problems tab displays diagnostics from various sources:\\r\\n1. Language Servers:\\r\\n - TypeScript: Type errors, missing imports, syntax issues\\r\\n - Python: Syntax errors, invalid type hints, undefined variables\\r\\n - JavaScript/Node.js: Parsing and execution errors\\r\\n2. Linters:\\r\\n - ESLint: Code style, best practices, potential bugs\\r\\n - Pylint: Unused imports, naming conventions\\r\\n - TSLint: Style and correctness issues in TypeScript\\r\\n3. Build Tools:\\r\\n - Webpack: Module resolution failures, build errors\\r\\n - Gulp: Build errors during task execution\\r\\n4. Custom Validators:\\r\\n - Extensions can generate custom diagnostics for specific languages or tools\\r\\nEach problem typically indicates its source (e.g., language server, linter, build tool).\\r\\nDiagnostics update in real-time as you edit code, helping identify issues quickly. For example, if you introduce a syntax error in a TypeScript file, the Problems tab will immediately display the new error.\\r\\n\\r\\nNotes on diagnostics:\\r\\n- linter diagnostics are only captured for open editors\\r\\n- this works great for us since when cline edits/creates files its through vscode's textedit api's and we get those diagnostics for free\\r\\n- some tools might require you to save the file or manually refresh to clear the problem from the list.\\r\\n\\r\\nSystem Prompt\\r\\n- You will automatically receive workspace error diagnostics in environment_details. Be mindful that this may include issues beyond the scope of your task or the user's request. Only address errors relevant to your work, and avoid fixing pre-existing or unrelated issues unless the user specifically instructs you to do so.\\r\\n- If you are unable to resolve errors provided in environment_details after two attempts, consider using ask_followup_question to ask the user for additional information, such as the latest documentation related to a problematic framework, to help you make progress on the task. If the error remains unresolved after this step, proceed with your task while disregarding the error.\\r\\n\\r\\nclass DiagnosticsMonitor {\\r\\n\\tprivate diagnosticsChangeEmitter: vscode.EventEmitter<void> = new vscode.EventEmitter<void>()\\r\\n\\tprivate disposables: vscode.Disposable[] = []\\r\\n\\tprivate lastDiagnostics: FileDiagnostics = []\\r\\n\\r\\n\\tconstructor() {\\r\\n\\t\\tthis.disposables.push(\\r\\n\\t\\t\\tvscode.languages.onDidChangeDiagnostics(() => {\\r\\n\\t\\t\\t\\tthis.diagnosticsChangeEmitter.fire()\\r\\n\\t\\t\\t})\\r\\n\\t\\t)\\r\\n\\t}\\r\\n\\r\\n\\tpublic async getCurrentDiagnostics(shouldWaitForChanges: boolean): Promise<FileDiagnostics> {\\r\\n\\t\\tconst currentDiagnostics = this.getDiagnostics()\\r\\n\\t\\tif (!shouldWaitForChanges) {\\r\\n\\t\\t\\tthis.lastDiagnostics = currentDiagnostics\\r\\n\\t\\t\\treturn currentDiagnostics\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!deepEqual(this.lastDiagnostics, currentDiagnostics)) {\\r\\n\\t\\t\\tthis.lastDiagnostics = currentDiagnostics\\r\\n\\t\\t\\treturn currentDiagnostics\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tlet timeout = 300 // only way this happens is if theres no errors\\r\\n\\r\\n\\t\\t// if diagnostics contain existing errors (since the check above didn't trigger) then it's likely cline just did something that should have fixed the error, so we'll give a longer grace period for diagnostics to catch up\\r\\n\\t\\tconst hasErrors = currentDiagnostics.some(([_, diagnostics]) =>\\r\\n\\t\\t\\tdiagnostics.some((d) => d.severity === vscode.DiagnosticSeverity.Error)\\r\\n\\t\\t)\\r\\n\\t\\tif (hasErrors) {\\r\\n\\t\\t\\tconsole.log(\\\"Existing errors detected, extending timeout\\\", currentDiagnostics)\\r\\n\\t\\t\\ttimeout = 10_000\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn this.waitForUpdatedDiagnostics(timeout)\\r\\n\\t}\\r\\n\\r\\n\\tprivate async waitForUpdatedDiagnostics(timeout: number): Promise<FileDiagnostics> {\\r\\n\\t\\treturn new Promise((resolve, reject) => {\\r\\n\\t\\t\\tconst timer = setTimeout(() => {\\r\\n\\t\\t\\t\\tcleanup()\\r\\n\\t\\t\\t\\tconst finalDiagnostics = this.getDiagnostics()\\r\\n\\t\\t\\t\\tthis.lastDiagnostics = finalDiagnostics\\r\\n\\t\\t\\t\\tresolve(finalDiagnostics)\\r\\n\\t\\t\\t}, timeout)\\r\\n\\r\\n\\t\\t\\tconst disposable = this.diagnosticsChangeEmitter.event(() => {\\r\\n\\t\\t\\t\\tconst updatedDiagnostics = this.getDiagnostics() // I thought this would only trigger when diagnostics changed, but that's not the case.\\r\\n\\t\\t\\t\\tif (deepEqual(this.lastDiagnostics, updatedDiagnostics)) {\\r\\n\\t\\t\\t\\t\\t// diagnostics have not changed, ignoring...\\r\\n\\t\\t\\t\\t\\treturn\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tcleanup()\\r\\n\\t\\t\\t\\tthis.lastDiagnostics = updatedDiagnostics\\r\\n\\t\\t\\t\\tresolve(updatedDiagnostics)\\r\\n\\t\\t\\t})\\r\\n\\r\\n\\t\\t\\tconst cleanup = () => {\\r\\n\\t\\t\\t\\tclearTimeout(timer)\\r\\n\\t\\t\\t\\tdisposable.dispose()\\r\\n\\t\\t\\t}\\r\\n\\t\\t})\\r\\n\\t}\\r\\n\\r\\n\\tprivate getDiagnostics(): FileDiagnostics {\\r\\n\\t\\tconst allDiagnostics = vscode.languages.getDiagnostics()\\r\\n\\t\\treturn allDiagnostics\\r\\n\\t\\t\\t.filter(([_, diagnostics]) => diagnostics.some((d) => d.severity === vscode.DiagnosticSeverity.Error))\\r\\n\\t\\t\\t.map(([uri, diagnostics]) => [\\r\\n\\t\\t\\t\\turi,\\r\\n\\t\\t\\t\\tdiagnostics.filter((d) => d.severity === vscode.DiagnosticSeverity.Error),\\r\\n\\t\\t\\t])\\r\\n\\t}\\r\\n\\r\\n\\tpublic dispose() {\\r\\n\\t\\tthis.disposables.forEach((d) => d.dispose())\\r\\n\\t\\tthis.disposables = []\\r\\n\\t\\tthis.diagnosticsChangeEmitter.dispose()\\r\\n\\t}\\r\\n}\\r\\n\\r\\nexport default DiagnosticsMonitor\\r\\n*/\\r\\n\",\"metadata\":{\"size\":5486,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.843Z\",\"createdTime\":\"2024-12-29T05:57:23.843Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":124,\"nonEmptyLines\":105,\"commentLines\":3,\"complexity\":5},\"dependencies\":[\"vscode\",\"fast-deep-equal\"],\"quality\":{\"score\":0,\"issues\":[],\"duplicateLines\":16,\"longLines\":10,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.357Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":124},\"analysis\":{\"metrics\":{\"lines\":124,\"nonEmptyLines\":105,\"commentLines\":3,\"complexity\":5},\"dependencies\":[\"vscode\",\"fast-deep-equal\"],\"quality\":{\"score\":0,\"issues\":[],\"duplicateLines\":16,\"longLines\":10,\"complexFunctions\":0}},\"lastModified\":1737046855358,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.358Z\",\"size\":6401},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\integrations\\\\checkpoints\\\\CheckpointTracker.ts\",\"content\":{\"content\":\"import fs from \\\"fs/promises\\\"\\r\\nimport os from \\\"os\\\"\\r\\nimport * as path from \\\"path\\\"\\r\\nimport simpleGit, { SimpleGit } from \\\"simple-git\\\"\\r\\nimport * as vscode from \\\"vscode\\\"\\r\\nimport { ClineProvider } from \\\"../../core/webview/ClineProvider\\\"\\r\\nimport { fileExistsAtPath } from \\\"../../utils/fs\\\"\\r\\nimport { globby } from \\\"globby\\\"\\r\\n\\r\\nclass CheckpointTracker {\\r\\n\\tprivate providerRef: WeakRef<ClineProvider>\\r\\n\\tprivate taskId: string\\r\\n\\tprivate disposables: vscode.Disposable[] = []\\r\\n\\tprivate cwd: string\\r\\n\\tprivate lastRetrievedShadowGitConfigWorkTree?: string\\r\\n\\tlastCheckpointHash?: string\\r\\n\\r\\n\\tprivate constructor(provider: ClineProvider, taskId: string, cwd: string) {\\r\\n\\t\\tthis.providerRef = new WeakRef(provider)\\r\\n\\t\\tthis.taskId = taskId\\r\\n\\t\\tthis.cwd = cwd\\r\\n\\t}\\r\\n\\r\\n\\tpublic static async create(taskId: string, provider?: ClineProvider): Promise<CheckpointTracker> {\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tif (!provider) {\\r\\n\\t\\t\\t\\tthrow new Error(\\\"Provider is required to create a checkpoint tracker\\\")\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t// Check if git is installed by attempting to get version\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\tawait simpleGit().version()\\r\\n\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\tthrow new Error(\\\"Git must be installed to use checkpoints.\\\") // FIXME: must match what we check for in TaskHeader to show link\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tconst cwd = await CheckpointTracker.getWorkingDirectory()\\r\\n\\t\\t\\tconst newTracker = new CheckpointTracker(provider, taskId, cwd)\\r\\n\\t\\t\\tawait newTracker.initShadowGit()\\r\\n\\t\\t\\treturn newTracker\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\tconsole.error(\\\"Failed to create CheckpointTracker:\\\", error)\\r\\n\\t\\t\\tthrow error\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tprivate static async getWorkingDirectory(): Promise<string> {\\r\\n\\t\\tconst cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0)\\r\\n\\t\\tif (!cwd) {\\r\\n\\t\\t\\tthrow new Error(\\\"No workspace detected. Please open Cline in a workspace to use checkpoints.\\\")\\r\\n\\t\\t}\\r\\n\\t\\tconst homedir = os.homedir()\\r\\n\\t\\tconst desktopPath = path.join(homedir, \\\"Desktop\\\")\\r\\n\\t\\tconst documentsPath = path.join(homedir, \\\"Documents\\\")\\r\\n\\t\\tconst downloadsPath = path.join(homedir, \\\"Downloads\\\")\\r\\n\\r\\n\\t\\tswitch (cwd) {\\r\\n\\t\\t\\tcase homedir:\\r\\n\\t\\t\\t\\tthrow new Error(\\\"Cannot use checkpoints in home directory\\\")\\r\\n\\t\\t\\tcase desktopPath:\\r\\n\\t\\t\\t\\tthrow new Error(\\\"Cannot use checkpoints in Desktop directory\\\")\\r\\n\\t\\t\\tcase documentsPath:\\r\\n\\t\\t\\t\\tthrow new Error(\\\"Cannot use checkpoints in Documents directory\\\")\\r\\n\\t\\t\\tcase downloadsPath:\\r\\n\\t\\t\\t\\tthrow new Error(\\\"Cannot use checkpoints in Downloads directory\\\")\\r\\n\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\treturn cwd\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tprivate async getShadowGitPath(): Promise<string> {\\r\\n\\t\\tconst globalStoragePath = this.providerRef.deref()?.context.globalStorageUri.fsPath\\r\\n\\t\\tif (!globalStoragePath) {\\r\\n\\t\\t\\tthrow new Error(\\\"Global storage uri is invalid\\\")\\r\\n\\t\\t}\\r\\n\\t\\tconst checkpointsDir = path.join(globalStoragePath, \\\"tasks\\\", this.taskId, \\\"checkpoints\\\")\\r\\n\\t\\tawait fs.mkdir(checkpointsDir, { recursive: true })\\r\\n\\t\\tconst gitPath = path.join(checkpointsDir, \\\".git\\\")\\r\\n\\t\\treturn gitPath\\r\\n\\t}\\r\\n\\r\\n\\tpublic static async doesShadowGitExist(taskId: string, provider?: ClineProvider): Promise<boolean> {\\r\\n\\t\\tconst globalStoragePath = provider?.context.globalStorageUri.fsPath\\r\\n\\t\\tif (!globalStoragePath) {\\r\\n\\t\\t\\treturn false\\r\\n\\t\\t}\\r\\n\\t\\tconst gitPath = path.join(globalStoragePath, \\\"tasks\\\", taskId, \\\"checkpoints\\\", \\\".git\\\")\\r\\n\\t\\treturn await fileExistsAtPath(gitPath)\\r\\n\\t}\\r\\n\\r\\n\\tpublic async initShadowGit(): Promise<string> {\\r\\n\\t\\tconst gitPath = await this.getShadowGitPath()\\r\\n\\t\\tif (await fileExistsAtPath(gitPath)) {\\r\\n\\t\\t\\t// Make sure it's the same cwd as the configured worktree\\r\\n\\t\\t\\tconst worktree = await this.getShadowGitConfigWorkTree()\\r\\n\\t\\t\\tif (worktree !== this.cwd) {\\r\\n\\t\\t\\t\\tthrow new Error(\\\"Checkpoints can only be used in the original workspace: \\\" + worktree)\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\treturn gitPath\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tconst checkpointsDir = path.dirname(gitPath)\\r\\n\\t\\t\\tconst git = simpleGit(checkpointsDir)\\r\\n\\t\\t\\tawait git.init()\\r\\n\\r\\n\\t\\t\\tawait git.addConfig(\\\"core.worktree\\\", this.cwd) // sets the working tree to the current workspace\\r\\n\\r\\n\\t\\t\\t// Disable commit signing for shadow repo\\r\\n\\t\\t\\tawait git.addConfig(\\\"commit.gpgSign\\\", \\\"false\\\")\\r\\n\\r\\n\\t\\t\\t// Get LFS patterns from workspace if they exist\\r\\n\\t\\t\\tlet lfsPatterns: string[] = []\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\tconst attributesPath = path.join(this.cwd, \\\".gitattributes\\\")\\r\\n\\t\\t\\t\\tif (await fileExistsAtPath(attributesPath)) {\\r\\n\\t\\t\\t\\t\\tconst attributesContent = await fs.readFile(attributesPath, \\\"utf8\\\")\\r\\n\\t\\t\\t\\t\\tlfsPatterns = attributesContent\\r\\n\\t\\t\\t\\t\\t\\t.split(\\\"\\\\n\\\")\\r\\n\\t\\t\\t\\t\\t\\t.filter((line) => line.includes(\\\"filter=lfs\\\"))\\r\\n\\t\\t\\t\\t\\t\\t.map((line) => line.split(\\\" \\\")[0].trim())\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\tconsole.warn(\\\"Failed to read .gitattributes:\\\", error)\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t// Add basic excludes directly in git config, while respecting any .gitignore in the workspace\\r\\n\\t\\t\\t// .git/info/exclude is local to the shadow git repo, so it's not shared with the main repo - and won't conflict with user's .gitignore\\r\\n\\t\\t\\t// TODO: let user customize these\\r\\n\\t\\t\\tconst excludesPath = path.join(gitPath, \\\"info\\\", \\\"exclude\\\")\\r\\n\\t\\t\\tawait fs.mkdir(path.join(gitPath, \\\"info\\\"), { recursive: true })\\r\\n\\t\\t\\tawait fs.writeFile(\\r\\n\\t\\t\\t\\texcludesPath,\\r\\n\\t\\t\\t\\t[\\r\\n\\t\\t\\t\\t\\t\\\".git/\\\", // ignore the user's .git\\r\\n\\t\\t\\t\\t\\t`.git${GIT_DISABLED_SUFFIX}/`, // ignore the disabled nested git repos\\r\\n\\t\\t\\t\\t\\t\\\".DS_Store\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.log\\\",\\r\\n\\t\\t\\t\\t\\t\\\"node_modules/\\\",\\r\\n\\t\\t\\t\\t\\t\\\"__pycache__/\\\",\\r\\n\\t\\t\\t\\t\\t\\\"env/\\\",\\r\\n\\t\\t\\t\\t\\t\\\"venv/\\\",\\r\\n\\t\\t\\t\\t\\t\\\"target/dependency/\\\",\\r\\n\\t\\t\\t\\t\\t\\\"build/dependencies/\\\",\\r\\n\\t\\t\\t\\t\\t\\\"dist/\\\",\\r\\n\\t\\t\\t\\t\\t\\\"out/\\\",\\r\\n\\t\\t\\t\\t\\t\\\"bundle/\\\",\\r\\n\\t\\t\\t\\t\\t\\\"vendor/\\\",\\r\\n\\t\\t\\t\\t\\t\\\"tmp/\\\",\\r\\n\\t\\t\\t\\t\\t\\\"temp/\\\",\\r\\n\\t\\t\\t\\t\\t\\\"deps/\\\",\\r\\n\\t\\t\\t\\t\\t\\\"pkg/\\\",\\r\\n\\t\\t\\t\\t\\t\\\"Pods/\\\",\\r\\n\\t\\t\\t\\t\\t// Media files\\r\\n\\t\\t\\t\\t\\t\\\"*.jpg\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.jpeg\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.png\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.gif\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.bmp\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.ico\\\",\\r\\n\\t\\t\\t\\t\\t// \\\"*.svg\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.mp3\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.mp4\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.wav\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.avi\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.mov\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.wmv\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.webm\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.webp\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.m4a\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.flac\\\",\\r\\n\\t\\t\\t\\t\\t// Build and dependency directories\\r\\n\\t\\t\\t\\t\\t\\\"build/\\\",\\r\\n\\t\\t\\t\\t\\t\\\"bin/\\\",\\r\\n\\t\\t\\t\\t\\t\\\"obj/\\\",\\r\\n\\t\\t\\t\\t\\t\\\".gradle/\\\",\\r\\n\\t\\t\\t\\t\\t\\\".idea/\\\",\\r\\n\\t\\t\\t\\t\\t\\\".vscode/\\\",\\r\\n\\t\\t\\t\\t\\t\\\".vs/\\\",\\r\\n\\t\\t\\t\\t\\t\\\"coverage/\\\",\\r\\n\\t\\t\\t\\t\\t\\\".next/\\\",\\r\\n\\t\\t\\t\\t\\t\\\".nuxt/\\\",\\r\\n\\t\\t\\t\\t\\t// Cache and temporary files\\r\\n\\t\\t\\t\\t\\t\\\"*.cache\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.tmp\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.temp\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.swp\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.swo\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.pyc\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.pyo\\\",\\r\\n\\t\\t\\t\\t\\t\\\".pytest_cache/\\\",\\r\\n\\t\\t\\t\\t\\t\\\".eslintcache\\\",\\r\\n\\t\\t\\t\\t\\t// Environment and config files\\r\\n\\t\\t\\t\\t\\t\\\".env*\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.local\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.development\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.production\\\",\\r\\n\\t\\t\\t\\t\\t// Large data files\\r\\n\\t\\t\\t\\t\\t\\\"*.zip\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.tar\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.gz\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.rar\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.7z\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.iso\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.bin\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.exe\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.dll\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.so\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.dylib\\\",\\r\\n\\t\\t\\t\\t\\t// Database files\\r\\n\\t\\t\\t\\t\\t\\\"*.sqlite\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.db\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.sql\\\",\\r\\n\\t\\t\\t\\t\\t// Log files\\r\\n\\t\\t\\t\\t\\t\\\"*.logs\\\",\\r\\n\\t\\t\\t\\t\\t\\\"*.error\\\",\\r\\n\\t\\t\\t\\t\\t\\\"npm-debug.log*\\\",\\r\\n\\t\\t\\t\\t\\t\\\"yarn-debug.log*\\\",\\r\\n\\t\\t\\t\\t\\t\\\"yarn-error.log*\\\",\\r\\n\\t\\t\\t\\t\\t...lfsPatterns,\\r\\n\\t\\t\\t\\t].join(\\\"\\\\n\\\"),\\r\\n\\t\\t\\t)\\r\\n\\r\\n\\t\\t\\t// Set up git identity (git throws an error if user.name or user.email is not set)\\r\\n\\t\\t\\tawait git.addConfig(\\\"user.name\\\", \\\"Cline Checkpoint\\\")\\r\\n\\t\\t\\tawait git.addConfig(\\\"user.email\\\", \\\"noreply@example.com\\\")\\r\\n\\r\\n\\t\\t\\tawait this.addAllFiles(git)\\r\\n\\t\\t\\t// Initial commit (--allow-empty ensures it works even with no files)\\r\\n\\t\\t\\tawait git.commit(\\\"initial commit\\\", { \\\"--allow-empty\\\": null })\\r\\n\\r\\n\\t\\t\\treturn gitPath\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tpublic async getShadowGitConfigWorkTree(): Promise<string | undefined> {\\r\\n\\t\\tif (this.lastRetrievedShadowGitConfigWorkTree) {\\r\\n\\t\\t\\treturn this.lastRetrievedShadowGitConfigWorkTree\\r\\n\\t\\t}\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tconst gitPath = await this.getShadowGitPath()\\r\\n\\t\\t\\tconst git = simpleGit(path.dirname(gitPath))\\r\\n\\t\\t\\tconst worktree = await git.getConfig(\\\"core.worktree\\\")\\r\\n\\t\\t\\tthis.lastRetrievedShadowGitConfigWorkTree = worktree.value || undefined\\r\\n\\t\\t\\treturn this.lastRetrievedShadowGitConfigWorkTree\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\tconsole.error(\\\"Failed to get shadow git config worktree:\\\", error)\\r\\n\\t\\t\\treturn undefined\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tpublic async commit(): Promise<string | undefined> {\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tconst gitPath = await this.getShadowGitPath()\\r\\n\\t\\t\\tconst git = simpleGit(path.dirname(gitPath))\\r\\n\\t\\t\\tawait this.addAllFiles(git)\\r\\n\\t\\t\\tconst result = await git.commit(\\\"checkpoint\\\", {\\r\\n\\t\\t\\t\\t\\\"--allow-empty\\\": null,\\r\\n\\t\\t\\t})\\r\\n\\t\\t\\tconst commitHash = result.commit || \\\"\\\"\\r\\n\\t\\t\\tthis.lastCheckpointHash = commitHash\\r\\n\\t\\t\\treturn commitHash\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\tconsole.error(\\\"Failed to create checkpoint:\\\", error)\\r\\n\\t\\t\\treturn undefined\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tpublic async resetHead(commitHash: string): Promise<void> {\\r\\n\\t\\tconst gitPath = await this.getShadowGitPath()\\r\\n\\t\\tconst git = simpleGit(path.dirname(gitPath))\\r\\n\\r\\n\\t\\t// Clean working directory and force reset\\r\\n\\t\\t// This ensures that the operation will succeed regardless of:\\r\\n\\t\\t// - Untracked files in the workspace\\r\\n\\t\\t// - Staged changes\\r\\n\\t\\t// - Unstaged changes\\r\\n\\t\\t// - Partial commits\\r\\n\\t\\t// - Merge conflicts\\r\\n\\t\\tawait git.clean(\\\"f\\\", [\\\"-d\\\", \\\"-f\\\"]) // Remove untracked files and directories\\r\\n\\t\\tawait git.reset([\\\"--hard\\\", commitHash]) // Hard reset to target commit\\r\\n\\t}\\r\\n\\r\\n\\t/**\\r\\n\\t * Return an array describing changed files between one commit and either:\\r\\n\\t * - another commit, or\\r\\n\\t * - the current working directory (including uncommitted changes).\\r\\n\\t *\\r\\n\\t * If `rhsHash` is omitted, compares `lhsHash` to the working directory.\\r\\n\\t * If you want truly untracked files to appear, `git add` them first.\\r\\n\\t *\\r\\n\\t * @param lhsHash - The commit to compare from (older commit)\\r\\n\\t * @param rhsHash - The commit to compare to (newer commit).\\r\\n\\t * If omitted, we compare to the working directory.\\r\\n\\t * @returns Array of file changes with before/after content\\r\\n\\t */\\r\\n\\tpublic async getDiffSet(\\r\\n\\t\\tlhsHash?: string,\\r\\n\\t\\trhsHash?: string,\\r\\n\\t): Promise<\\r\\n\\t\\tArray<{\\r\\n\\t\\t\\trelativePath: string\\r\\n\\t\\t\\tabsolutePath: string\\r\\n\\t\\t\\tbefore: string\\r\\n\\t\\t\\tafter: string\\r\\n\\t\\t}>\\r\\n\\t> {\\r\\n\\t\\tconst gitPath = await this.getShadowGitPath()\\r\\n\\t\\tconst git = simpleGit(path.dirname(gitPath))\\r\\n\\r\\n\\t\\t// If lhsHash is missing, use the initial commit of the repo\\r\\n\\t\\tlet baseHash = lhsHash\\r\\n\\t\\tif (!baseHash) {\\r\\n\\t\\t\\tconst rootCommit = await git.raw([\\\"rev-list\\\", \\\"--max-parents=0\\\", \\\"HEAD\\\"])\\r\\n\\t\\t\\tbaseHash = rootCommit.trim()\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// Stage all changes so that untracked files appear in diff summary\\r\\n\\t\\tawait this.addAllFiles(git)\\r\\n\\r\\n\\t\\tconst diffSummary = rhsHash ? await git.diffSummary([`${baseHash}..${rhsHash}`]) : await git.diffSummary([baseHash])\\r\\n\\r\\n\\t\\t// For each changed file, gather before/after content\\r\\n\\t\\tconst result = []\\r\\n\\t\\tconst cwdPath = (await this.getShadowGitConfigWorkTree()) || this.cwd || \\\"\\\"\\r\\n\\r\\n\\t\\tfor (const file of diffSummary.files) {\\r\\n\\t\\t\\tconst filePath = file.file\\r\\n\\t\\t\\tconst absolutePath = path.join(cwdPath, filePath)\\r\\n\\r\\n\\t\\t\\tlet beforeContent = \\\"\\\"\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\tbeforeContent = await git.show([`${baseHash}:${filePath}`])\\r\\n\\t\\t\\t} catch (_) {\\r\\n\\t\\t\\t\\t// file didn't exist in older commit => remains empty\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tlet afterContent = \\\"\\\"\\r\\n\\t\\t\\tif (rhsHash) {\\r\\n\\t\\t\\t\\t// if user provided a newer commit, use git.show at that commit\\r\\n\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\tafterContent = await git.show([`${rhsHash}:${filePath}`])\\r\\n\\t\\t\\t\\t} catch (_) {\\r\\n\\t\\t\\t\\t\\t// file didn't exist in newer commit => remains empty\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t// otherwise, read from disk (includes uncommitted changes)\\r\\n\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\tafterContent = await fs.readFile(absolutePath, \\\"utf8\\\")\\r\\n\\t\\t\\t\\t} catch (_) {\\r\\n\\t\\t\\t\\t\\t// file might be deleted => remains empty\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tresult.push({\\r\\n\\t\\t\\t\\trelativePath: filePath,\\r\\n\\t\\t\\t\\tabsolutePath,\\r\\n\\t\\t\\t\\tbefore: beforeContent,\\r\\n\\t\\t\\t\\tafter: afterContent,\\r\\n\\t\\t\\t})\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn result\\r\\n\\t}\\r\\n\\r\\n\\tprivate async addAllFiles(git: SimpleGit) {\\r\\n\\t\\tawait this.renameNestedGitRepos(true)\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tawait git.add(\\\".\\\")\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\tconsole.error(\\\"Failed to add files to git:\\\", error)\\r\\n\\t\\t} finally {\\r\\n\\t\\t\\tawait this.renameNestedGitRepos(false)\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t// Since we use git to track checkpoints, we need to temporarily disable nested git repos to work around git's requirement of using submodules for nested repos.\\r\\n\\tprivate async renameNestedGitRepos(disable: boolean) {\\r\\n\\t\\t// Find all .git directories that are not at the root level\\r\\n\\t\\tconst gitPaths = await globby(\\\"**/.git\\\" + (disable ? \\\"\\\" : GIT_DISABLED_SUFFIX), {\\r\\n\\t\\t\\tcwd: this.cwd,\\r\\n\\t\\t\\tonlyDirectories: true,\\r\\n\\t\\t\\tignore: [\\\".git\\\"], // Ignore root level .git\\r\\n\\t\\t\\tdot: true,\\r\\n\\t\\t\\tmarkDirectories: false,\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\t// For each nested .git directory, rename it based on operation\\r\\n\\t\\tfor (const gitPath of gitPaths) {\\r\\n\\t\\t\\tconst fullPath = path.join(this.cwd, gitPath)\\r\\n\\t\\t\\tlet newPath: string\\r\\n\\t\\t\\tif (disable) {\\r\\n\\t\\t\\t\\tnewPath = fullPath + GIT_DISABLED_SUFFIX\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tnewPath = fullPath.endsWith(GIT_DISABLED_SUFFIX) ? fullPath.slice(0, -GIT_DISABLED_SUFFIX.length) : fullPath\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\tawait fs.rename(fullPath, newPath)\\r\\n\\t\\t\\t\\tconsole.log(`CheckpointTracker ${disable ? \\\"disabled\\\" : \\\"enabled\\\"} nested git repo ${gitPath}`)\\r\\n\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\tconsole.error(`CheckpointTracker failed to ${disable ? \\\"disable\\\" : \\\"enable\\\"} nested git repo ${gitPath}:`, error)\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tpublic dispose() {\\r\\n\\t\\tthis.disposables.forEach((d) => d.dispose())\\r\\n\\t\\tthis.disposables = []\\r\\n\\t}\\r\\n}\\r\\n\\r\\nconst GIT_DISABLED_SUFFIX = \\\"_disabled\\\"\\r\\n\\r\\nexport default CheckpointTracker\\r\\n\",\"metadata\":{\"size\":12846,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.956Z\",\"createdTime\":\"2025-01-15T15:26:07.956Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":415,\"nonEmptyLines\":375,\"commentLines\":36,\"complexity\":49},\"dependencies\":[\"fs/promises\",\"os\",\"path\",\"simple-git\",\"vscode\",\"../../core/webview/ClineProvider\",\"../../utils/fs\",\"globby\"],\"quality\":{\"score\":-279,\"issues\":[],\"duplicateLines\":73,\"longLines\":7,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.354Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":415},\"analysis\":{\"metrics\":{\"lines\":415,\"nonEmptyLines\":375,\"commentLines\":36,\"complexity\":49},\"dependencies\":[\"fs/promises\",\"os\",\"path\",\"simple-git\",\"vscode\",\"../../core/webview/ClineProvider\",\"../../utils/fs\",\"globby\"],\"quality\":{\"score\":-279,\"issues\":[],\"duplicateLines\":73,\"longLines\":7,\"complexFunctions\":0}},\"lastModified\":1737046855355,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.355Z\",\"size\":15687},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\extension.ts\",\"content\":{\"content\":\"// The module 'vscode' contains the VS Code extensibility API\\r\\n// Import the module and reference it with the alias vscode in your code below\\r\\nimport delay from \\\"delay\\\"\\r\\nimport * as vscode from \\\"vscode\\\"\\r\\nimport { ClineProvider } from \\\"./core/webview/ClineProvider\\\"\\r\\nimport { createClineAPI } from \\\"./exports\\\"\\r\\nimport \\\"./utils/path\\\" // necessary to have access to String.prototype.toPosix\\r\\nimport { DIFF_VIEW_URI_SCHEME } from \\\"./integrations/editor/DiffViewProvider\\\"\\r\\n\\r\\n/*\\r\\nBuilt using https://github.com/microsoft/vscode-webview-ui-toolkit\\r\\n\\r\\nInspired by\\r\\nhttps://github.com/microsoft/vscode-webview-ui-toolkit-samples/tree/main/default/weather-webview\\r\\nhttps://github.com/microsoft/vscode-webview-ui-toolkit-samples/tree/main/frameworks/hello-world-react-cra\\r\\n\\r\\n*/\\r\\n\\r\\nlet outputChannel: vscode.OutputChannel\\r\\n\\r\\n// This method is called when your extension is activated\\r\\n// Your extension is activated the very first time the command is executed\\r\\nexport function activate(context: vscode.ExtensionContext) {\\r\\n\\toutputChannel = vscode.window.createOutputChannel(\\\"Cline\\\")\\r\\n\\tcontext.subscriptions.push(outputChannel)\\r\\n\\r\\n\\toutputChannel.appendLine(\\\"Cline extension activated\\\")\\r\\n\\r\\n\\tconst sidebarProvider = new ClineProvider(context, outputChannel)\\r\\n\\r\\n\\tcontext.subscriptions.push(\\r\\n\\t\\tvscode.window.registerWebviewViewProvider(ClineProvider.sideBarId, sidebarProvider, {\\r\\n\\t\\t\\twebviewOptions: { retainContextWhenHidden: true },\\r\\n\\t\\t}),\\r\\n\\t)\\r\\n\\r\\n\\tcontext.subscriptions.push(\\r\\n\\t\\tvscode.commands.registerCommand(\\\"cline.plusButtonClicked\\\", async () => {\\r\\n\\t\\t\\toutputChannel.appendLine(\\\"Plus button Clicked\\\")\\r\\n\\t\\t\\tawait sidebarProvider.clearTask()\\r\\n\\t\\t\\tawait sidebarProvider.postStateToWebview()\\r\\n\\t\\t\\tawait sidebarProvider.postMessageToWebview({\\r\\n\\t\\t\\t\\ttype: \\\"action\\\",\\r\\n\\t\\t\\t\\taction: \\\"chatButtonClicked\\\",\\r\\n\\t\\t\\t})\\r\\n\\t\\t}),\\r\\n\\t)\\r\\n\\r\\n\\tcontext.subscriptions.push(\\r\\n\\t\\tvscode.commands.registerCommand(\\\"cline.mcpButtonClicked\\\", () => {\\r\\n\\t\\t\\tsidebarProvider.postMessageToWebview({\\r\\n\\t\\t\\t\\ttype: \\\"action\\\",\\r\\n\\t\\t\\t\\taction: \\\"mcpButtonClicked\\\",\\r\\n\\t\\t\\t})\\r\\n\\t\\t}),\\r\\n\\t)\\r\\n\\r\\n\\tconst openClineInNewTab = async () => {\\r\\n\\t\\toutputChannel.appendLine(\\\"Opening Cline in new tab\\\")\\r\\n\\t\\t// (this example uses webviewProvider activation event which is necessary to deserialize cached webview, but since we use retainContextWhenHidden, we don't need to use that event)\\r\\n\\t\\t// https://github.com/microsoft/vscode-extension-samples/blob/main/webview-sample/src/extension.ts\\r\\n\\t\\tconst tabProvider = new ClineProvider(context, outputChannel)\\r\\n\\t\\t//const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined\\r\\n\\t\\tconst lastCol = Math.max(...vscode.window.visibleTextEditors.map((editor) => editor.viewColumn || 0))\\r\\n\\r\\n\\t\\t// Check if there are any visible text editors, otherwise open a new group to the right\\r\\n\\t\\tconst hasVisibleEditors = vscode.window.visibleTextEditors.length > 0\\r\\n\\t\\tif (!hasVisibleEditors) {\\r\\n\\t\\t\\tawait vscode.commands.executeCommand(\\\"workbench.action.newGroupRight\\\")\\r\\n\\t\\t}\\r\\n\\t\\tconst targetCol = hasVisibleEditors ? Math.max(lastCol + 1, 1) : vscode.ViewColumn.Two\\r\\n\\r\\n\\t\\tconst panel = vscode.window.createWebviewPanel(ClineProvider.tabPanelId, \\\"Cline\\\", targetCol, {\\r\\n\\t\\t\\tenableScripts: true,\\r\\n\\t\\t\\tretainContextWhenHidden: true,\\r\\n\\t\\t\\tlocalResourceRoots: [context.extensionUri],\\r\\n\\t\\t})\\r\\n\\t\\t// TODO: use better svg icon with light and dark variants (see https://stackoverflow.com/questions/58365687/vscode-extension-iconpath)\\r\\n\\r\\n\\t\\tpanel.iconPath = {\\r\\n\\t\\t\\tlight: vscode.Uri.joinPath(context.extensionUri, \\\"assets\\\", \\\"icons\\\", \\\"robot_panel_light.png\\\"),\\r\\n\\t\\t\\tdark: vscode.Uri.joinPath(context.extensionUri, \\\"assets\\\", \\\"icons\\\", \\\"robot_panel_dark.png\\\"),\\r\\n\\t\\t}\\r\\n\\t\\ttabProvider.resolveWebviewView(panel)\\r\\n\\r\\n\\t\\t// Lock the editor group so clicking on files doesn't open them over the panel\\r\\n\\t\\tawait delay(100)\\r\\n\\t\\tawait vscode.commands.executeCommand(\\\"workbench.action.lockEditorGroup\\\")\\r\\n\\t}\\r\\n\\r\\n\\tcontext.subscriptions.push(vscode.commands.registerCommand(\\\"cline.popoutButtonClicked\\\", openClineInNewTab))\\r\\n\\tcontext.subscriptions.push(vscode.commands.registerCommand(\\\"cline.openInNewTab\\\", openClineInNewTab))\\r\\n\\r\\n\\tcontext.subscriptions.push(\\r\\n\\t\\tvscode.commands.registerCommand(\\\"cline.settingsButtonClicked\\\", () => {\\r\\n\\t\\t\\t//vscode.window.showInformationMessage(message)\\r\\n\\t\\t\\tsidebarProvider.postMessageToWebview({\\r\\n\\t\\t\\t\\ttype: \\\"action\\\",\\r\\n\\t\\t\\t\\taction: \\\"settingsButtonClicked\\\",\\r\\n\\t\\t\\t})\\r\\n\\t\\t}),\\r\\n\\t)\\r\\n\\r\\n\\tcontext.subscriptions.push(\\r\\n\\t\\tvscode.commands.registerCommand(\\\"cline.historyButtonClicked\\\", () => {\\r\\n\\t\\t\\tsidebarProvider.postMessageToWebview({\\r\\n\\t\\t\\t\\ttype: \\\"action\\\",\\r\\n\\t\\t\\t\\taction: \\\"historyButtonClicked\\\",\\r\\n\\t\\t\\t})\\r\\n\\t\\t}),\\r\\n\\t)\\r\\n\\r\\n\\t/*\\r\\n\\tWe use the text document content provider API to show the left side for diff view by creating a virtual document for the original content. This makes it readonly so users know to edit the right side if they want to keep their changes.\\r\\n\\r\\n\\t- This API allows you to create readonly documents in VSCode from arbitrary sources, and works by claiming an uri-scheme for which your provider then returns text contents. The scheme must be provided when registering a provider and cannot change afterwards.\\r\\n\\t- Note how the provider doesn't create uris for virtual documents - its role is to provide contents given such an uri. In return, content providers are wired into the open document logic so that providers are always considered.\\r\\n\\thttps://code.visualstudio.com/api/extension-guides/virtual-documents\\r\\n\\t*/\\r\\n\\tconst diffContentProvider = new (class implements vscode.TextDocumentContentProvider {\\r\\n\\t\\tprovideTextDocumentContent(uri: vscode.Uri): string {\\r\\n\\t\\t\\treturn Buffer.from(uri.query, \\\"base64\\\").toString(\\\"utf-8\\\")\\r\\n\\t\\t}\\r\\n\\t})()\\r\\n\\tcontext.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider(DIFF_VIEW_URI_SCHEME, diffContentProvider))\\r\\n\\r\\n\\t// URI Handler\\r\\n\\tconst handleUri = async (uri: vscode.Uri) => {\\r\\n\\t\\tconst path = uri.path\\r\\n\\t\\tconst query = new URLSearchParams(uri.query.replace(/\\\\+/g, \\\"%2B\\\"))\\r\\n\\t\\tconst visibleProvider = ClineProvider.getVisibleInstance()\\r\\n\\t\\tif (!visibleProvider) {\\r\\n\\t\\t\\treturn\\r\\n\\t\\t}\\r\\n\\t\\tswitch (path) {\\r\\n\\t\\t\\tcase \\\"/openrouter\\\": {\\r\\n\\t\\t\\t\\tconst code = query.get(\\\"code\\\")\\r\\n\\t\\t\\t\\tif (code) {\\r\\n\\t\\t\\t\\t\\tawait visibleProvider.handleOpenRouterCallback(code)\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\tcontext.subscriptions.push(vscode.window.registerUriHandler({ handleUri }))\\r\\n\\r\\n\\treturn createClineAPI(outputChannel, sidebarProvider)\\r\\n}\\r\\n\\r\\n// This method is called when your extension is deactivated\\r\\nexport function deactivate() {\\r\\n\\toutputChannel.appendLine(\\\"Cline extension deactivated\\\")\\r\\n}\\r\\n\",\"metadata\":{\"size\":6493,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.948Z\",\"createdTime\":\"2025-01-15T15:26:07.948Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":156,\"nonEmptyLines\":132,\"commentLines\":15,\"complexity\":8},\"dependencies\":[\"delay\",\"vscode\",\"./core/webview/ClineProvider\",\"./exports\",\"./integrations/editor/DiffViewProvider\"],\"quality\":{\"score\":-94,\"issues\":[],\"duplicateLines\":34,\"longLines\":12,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.351Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":156},\"analysis\":{\"metrics\":{\"lines\":156,\"nonEmptyLines\":132,\"commentLines\":15,\"complexity\":8},\"dependencies\":[\"delay\",\"vscode\",\"./core/webview/ClineProvider\",\"./exports\",\"./integrations/editor/DiffViewProvider\"],\"quality\":{\"score\":-94,\"issues\":[],\"duplicateLines\":34,\"longLines\":12,\"complexFunctions\":0}},\"lastModified\":1737046855352,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.352Z\",\"size\":7683},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\exports\\\\README.md\",\"content\":{\"content\":\"# Cline API\\r\\n\\r\\nThe Cline extension exposes an API that can be used by other extensions. To use this API in your extension:\\r\\n\\r\\n1. Copy `src/extension-api/cline.d.ts` to your extension's source directory.\\r\\n2. Include `cline.d.ts` in your extension's compilation.\\r\\n3. Get access to the API with the following code:\\r\\n\\r\\n ```ts\\r\\n const clineExtension = vscode.extensions.getExtension<ClineAPI>(\\\"saoudrizwan.claude-dev\\\")\\r\\n\\r\\n if (!clineExtension?.isActive) {\\r\\n \\tthrow new Error(\\\"Cline extension is not activated\\\")\\r\\n }\\r\\n\\r\\n const cline = clineExtension.exports\\r\\n\\r\\n if (cline) {\\r\\n \\t// Now you can use the API\\r\\n\\r\\n \\t// Set custom instructions\\r\\n \\tawait cline.setCustomInstructions(\\\"Talk like a pirate\\\")\\r\\n\\r\\n \\t// Get custom instructions\\r\\n \\tconst instructions = await cline.getCustomInstructions()\\r\\n \\tconsole.log(\\\"Current custom instructions:\\\", instructions)\\r\\n\\r\\n \\t// Start a new task with an initial message\\r\\n \\tawait cline.startNewTask(\\\"Hello, Cline! Let's make a new project...\\\")\\r\\n\\r\\n \\t// Start a new task with an initial message and images\\r\\n \\tawait cline.startNewTask(\\\"Use this design language\\\", [\\\"data:image/webp;base64,...\\\"])\\r\\n\\r\\n \\t// Send a message to the current task\\r\\n \\tawait cline.sendMessage(\\\"Can you fix the @problems?\\\")\\r\\n\\r\\n \\t// Simulate pressing the primary button in the chat interface (e.g. 'Save' or 'Proceed While Running')\\r\\n \\tawait cline.pressPrimaryButton()\\r\\n\\r\\n \\t// Simulate pressing the secondary button in the chat interface (e.g. 'Reject')\\r\\n \\tawait cline.pressSecondaryButton()\\r\\n } else {\\r\\n \\tconsole.error(\\\"Cline API is not available\\\")\\r\\n }\\r\\n ```\\r\\n\\r\\n **Note:** To ensure that the `saoudrizwan.claude-dev` extension is activated before your extension, add it to the `extensionDependencies` in your `package.json`:\\r\\n\\r\\n ```json\\r\\n \\\"extensionDependencies\\\": [\\r\\n \\\"saoudrizwan.claude-dev\\\"\\r\\n ]\\r\\n ```\\r\\n\\r\\nFor detailed information on the available methods and their usage, refer to the `cline.d.ts` file.\\r\\n\",\"metadata\":{\"size\":1997,\"mimeType\":\"text/markdown\",\"modifiedTime\":\"2024-12-29T05:57:23.843Z\",\"createdTime\":\"2024-12-29T05:57:23.843Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":56,\"nonEmptyLines\":39,\"commentLines\":0,\"complexity\":6},\"dependencies\":[],\"quality\":{\"score\":84,\"issues\":[],\"duplicateLines\":2,\"longLines\":3,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.350Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":56},\"analysis\":{\"metrics\":{\"lines\":56,\"nonEmptyLines\":39,\"commentLines\":0,\"complexity\":6},\"dependencies\":[],\"quality\":{\"score\":84,\"issues\":[],\"duplicateLines\":2,\"longLines\":3,\"complexFunctions\":0}},\"lastModified\":1737046855350,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.350Z\",\"size\":2604},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\exports\\\\index.ts\",\"content\":{\"content\":\"import * as vscode from \\\"vscode\\\"\\r\\nimport { ClineProvider } from \\\"../core/webview/ClineProvider\\\"\\r\\nimport { ClineAPI } from \\\"./cline\\\"\\r\\n\\r\\nexport function createClineAPI(outputChannel: vscode.OutputChannel, sidebarProvider: ClineProvider): ClineAPI {\\r\\n\\tconst api: ClineAPI = {\\r\\n\\t\\tsetCustomInstructions: async (value: string) => {\\r\\n\\t\\t\\tawait sidebarProvider.updateCustomInstructions(value)\\r\\n\\t\\t\\toutputChannel.appendLine(\\\"Custom instructions set\\\")\\r\\n\\t\\t},\\r\\n\\r\\n\\t\\tgetCustomInstructions: async () => {\\r\\n\\t\\t\\treturn (await sidebarProvider.getGlobalState(\\\"customInstructions\\\")) as string | undefined\\r\\n\\t\\t},\\r\\n\\r\\n\\t\\tstartNewTask: async (task?: string, images?: string[]) => {\\r\\n\\t\\t\\toutputChannel.appendLine(\\\"Starting new task\\\")\\r\\n\\t\\t\\tawait sidebarProvider.clearTask()\\r\\n\\t\\t\\tawait sidebarProvider.postStateToWebview()\\r\\n\\t\\t\\tawait sidebarProvider.postMessageToWebview({\\r\\n\\t\\t\\t\\ttype: \\\"action\\\",\\r\\n\\t\\t\\t\\taction: \\\"chatButtonClicked\\\",\\r\\n\\t\\t\\t})\\r\\n\\t\\t\\tawait sidebarProvider.postMessageToWebview({\\r\\n\\t\\t\\t\\ttype: \\\"invoke\\\",\\r\\n\\t\\t\\t\\tinvoke: \\\"sendMessage\\\",\\r\\n\\t\\t\\t\\ttext: task,\\r\\n\\t\\t\\t\\timages: images,\\r\\n\\t\\t\\t})\\r\\n\\t\\t\\toutputChannel.appendLine(\\r\\n\\t\\t\\t\\t`Task started with message: ${task ? `\\\"${task}\\\"` : \\\"undefined\\\"} and ${images?.length || 0} image(s)`,\\r\\n\\t\\t\\t)\\r\\n\\t\\t},\\r\\n\\r\\n\\t\\tsendMessage: async (message?: string, images?: string[]) => {\\r\\n\\t\\t\\toutputChannel.appendLine(\\r\\n\\t\\t\\t\\t`Sending message: ${message ? `\\\"${message}\\\"` : \\\"undefined\\\"} with ${images?.length || 0} image(s)`,\\r\\n\\t\\t\\t)\\r\\n\\t\\t\\tawait sidebarProvider.postMessageToWebview({\\r\\n\\t\\t\\t\\ttype: \\\"invoke\\\",\\r\\n\\t\\t\\t\\tinvoke: \\\"sendMessage\\\",\\r\\n\\t\\t\\t\\ttext: message,\\r\\n\\t\\t\\t\\timages: images,\\r\\n\\t\\t\\t})\\r\\n\\t\\t},\\r\\n\\r\\n\\t\\tpressPrimaryButton: async () => {\\r\\n\\t\\t\\toutputChannel.appendLine(\\\"Pressing primary button\\\")\\r\\n\\t\\t\\tawait sidebarProvider.postMessageToWebview({\\r\\n\\t\\t\\t\\ttype: \\\"invoke\\\",\\r\\n\\t\\t\\t\\tinvoke: \\\"primaryButtonClick\\\",\\r\\n\\t\\t\\t})\\r\\n\\t\\t},\\r\\n\\r\\n\\t\\tpressSecondaryButton: async () => {\\r\\n\\t\\t\\toutputChannel.appendLine(\\\"Pressing secondary button\\\")\\r\\n\\t\\t\\tawait sidebarProvider.postMessageToWebview({\\r\\n\\t\\t\\t\\ttype: \\\"invoke\\\",\\r\\n\\t\\t\\t\\tinvoke: \\\"secondaryButtonClick\\\",\\r\\n\\t\\t\\t})\\r\\n\\t\\t},\\r\\n\\t}\\r\\n\\r\\n\\treturn api\\r\\n}\\r\\n\",\"metadata\":{\"size\":2005,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.948Z\",\"createdTime\":\"2025-01-15T15:26:07.948Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":66,\"nonEmptyLines\":58,\"commentLines\":0,\"complexity\":11},\"dependencies\":[\"vscode\",\"../core/webview/ClineProvider\",\"./cline\"],\"quality\":{\"score\":-11,\"issues\":[],\"duplicateLines\":21,\"longLines\":3,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.348Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":66},\"analysis\":{\"metrics\":{\"lines\":66,\"nonEmptyLines\":58,\"commentLines\":0,\"complexity\":11},\"dependencies\":[\"vscode\",\"../core/webview/ClineProvider\",\"./cline\"],\"quality\":{\"score\":-11,\"issues\":[],\"duplicateLines\":21,\"longLines\":3,\"complexFunctions\":0}},\"lastModified\":1737046855349,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.349Z\",\"size\":2843},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\exports\\\\cline.d.ts\",\"content\":{\"content\":\"export interface ClineAPI {\\r\\n\\t/**\\r\\n\\t * Sets the custom instructions in the global storage.\\r\\n\\t * @param value The custom instructions to be saved.\\r\\n\\t */\\r\\n\\tsetCustomInstructions(value: string): Promise<void>\\r\\n\\r\\n\\t/**\\r\\n\\t * Retrieves the custom instructions from the global storage.\\r\\n\\t * @returns The saved custom instructions, or undefined if not set.\\r\\n\\t */\\r\\n\\tgetCustomInstructions(): Promise<string | undefined>\\r\\n\\r\\n\\t/**\\r\\n\\t * Starts a new task with an optional initial message and images.\\r\\n\\t * @param task Optional initial task message.\\r\\n\\t * @param images Optional array of image data URIs (e.g., \\\"data:image/webp;base64,...\\\").\\r\\n\\t */\\r\\n\\tstartNewTask(task?: string, images?: string[]): Promise<void>\\r\\n\\r\\n\\t/**\\r\\n\\t * Sends a message to the current task.\\r\\n\\t * @param message Optional message to send.\\r\\n\\t * @param images Optional array of image data URIs (e.g., \\\"data:image/webp;base64,...\\\").\\r\\n\\t */\\r\\n\\tsendMessage(message?: string, images?: string[]): Promise<void>\\r\\n\\r\\n\\t/**\\r\\n\\t * Simulates pressing the primary button in the chat interface.\\r\\n\\t */\\r\\n\\tpressPrimaryButton(): Promise<void>\\r\\n\\r\\n\\t/**\\r\\n\\t * Simulates pressing the secondary button in the chat interface.\\r\\n\\t */\\r\\n\\tpressSecondaryButton(): Promise<void>\\r\\n}\\r\\n\",\"metadata\":{\"size\":1197,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.843Z\",\"createdTime\":\"2024-12-29T05:57:23.843Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":38,\"nonEmptyLines\":32,\"commentLines\":6,\"complexity\":5},\"dependencies\":[],\"quality\":{\"score\":45,\"issues\":[],\"duplicateLines\":11,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.347Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":38},\"analysis\":{\"metrics\":{\"lines\":38,\"nonEmptyLines\":32,\"commentLines\":6,\"complexity\":5},\"dependencies\":[],\"quality\":{\"score\":45,\"issues\":[],\"duplicateLines\":11,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855348,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.348Z\",\"size\":1760},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\core\\\\webview\\\\getUri.ts\",\"content\":{\"content\":\"import { Uri, Webview } from \\\"vscode\\\"\\r\\n/**\\r\\n * A helper function which will get the webview URI of a given file or resource.\\r\\n *\\r\\n * @remarks This URI can be used within a webview's HTML as a link to the\\r\\n * given file/resource.\\r\\n *\\r\\n * @param webview A reference to the extension webview\\r\\n * @param extensionUri The URI of the directory containing the extension\\r\\n * @param pathList An array of strings representing the path to a file/resource\\r\\n * @returns A URI pointing to the file/resource\\r\\n */\\r\\nexport function getUri(webview: Webview, extensionUri: Uri, pathList: string[]) {\\r\\n\\treturn webview.asWebviewUri(Uri.joinPath(extensionUri, ...pathList))\\r\\n}\\r\\n\",\"metadata\":{\"size\":656,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.843Z\",\"createdTime\":\"2024-12-29T05:57:23.843Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":16,\"nonEmptyLines\":15,\"commentLines\":1,\"complexity\":1},\"dependencies\":[\"vscode\"],\"quality\":{\"score\":95,\"issues\":[],\"duplicateLines\":1,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.346Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":16},\"analysis\":{\"metrics\":{\"lines\":16,\"nonEmptyLines\":15,\"commentLines\":1,\"complexity\":1},\"dependencies\":[\"vscode\"],\"quality\":{\"score\":95,\"issues\":[],\"duplicateLines\":1,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855347,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.347Z\",\"size\":1150},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\core\\\\webview\\\\getNonce.ts\",\"content\":{\"content\":\"/**\\r\\n * A helper function that returns a unique alphanumeric identifier called a nonce.\\r\\n *\\r\\n * @remarks This function is primarily used to help enforce content security\\r\\n * policies for resources/scripts being executed in a webview context.\\r\\n *\\r\\n * @returns A nonce\\r\\n */\\r\\nexport function getNonce() {\\r\\n\\tlet text = \\\"\\\"\\r\\n\\tconst possible = \\\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\\\"\\r\\n\\tfor (let i = 0; i < 32; i++) {\\r\\n\\t\\ttext += possible.charAt(Math.floor(Math.random() * possible.length))\\r\\n\\t}\\r\\n\\treturn text\\r\\n}\\r\\n\",\"metadata\":{\"size\":529,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.843Z\",\"createdTime\":\"2024-12-29T05:57:23.843Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":17,\"nonEmptyLines\":16,\"commentLines\":1,\"complexity\":2},\"dependencies\":[],\"quality\":{\"score\":90,\"issues\":[],\"duplicateLines\":2,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.345Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":17},\"analysis\":{\"metrics\":{\"lines\":17,\"nonEmptyLines\":16,\"commentLines\":1,\"complexity\":2},\"dependencies\":[],\"quality\":{\"score\":90,\"issues\":[],\"duplicateLines\":2,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855345,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.345Z\",\"size\":1025},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\core\\\\webview\\\\ClineProvider.ts\",\"content\":{\"content\":\"import { Anthropic } from \\\"@anthropic-ai/sdk\\\"\\r\\nimport axios from \\\"axios\\\"\\r\\nimport fs from \\\"fs/promises\\\"\\r\\nimport os from \\\"os\\\"\\r\\nimport pWaitFor from \\\"p-wait-for\\\"\\r\\nimport * as path from \\\"path\\\"\\r\\nimport * as vscode from \\\"vscode\\\"\\r\\nimport { buildApiHandler } from \\\"../../api\\\"\\r\\nimport { downloadTask } from \\\"../../integrations/misc/export-markdown\\\"\\r\\nimport { openFile, openImage } from \\\"../../integrations/misc/open-file\\\"\\r\\nimport { selectImages } from \\\"../../integrations/misc/process-images\\\"\\r\\nimport { getTheme } from \\\"../../integrations/theme/getTheme\\\"\\r\\nimport WorkspaceTracker from \\\"../../integrations/workspace/WorkspaceTracker\\\"\\r\\nimport { McpHub } from \\\"../../services/mcp/McpHub\\\"\\r\\nimport { ApiProvider, ModelInfo } from \\\"../../shared/api\\\"\\r\\nimport { findLast } from \\\"../../shared/array\\\"\\r\\nimport { ExtensionMessage, ExtensionState } from \\\"../../shared/ExtensionMessage\\\"\\r\\nimport { HistoryItem } from \\\"../../shared/HistoryItem\\\"\\r\\nimport { ClineCheckpointRestore, WebviewMessage } from \\\"../../shared/WebviewMessage\\\"\\r\\nimport { fileExistsAtPath } from \\\"../../utils/fs\\\"\\r\\nimport { Cline } from \\\"../Cline\\\"\\r\\nimport { openMention } from \\\"../mentions\\\"\\r\\nimport { getNonce } from \\\"./getNonce\\\"\\r\\nimport { getUri } from \\\"./getUri\\\"\\r\\nimport { AutoApprovalSettings, DEFAULT_AUTO_APPROVAL_SETTINGS } from \\\"../../shared/AutoApprovalSettings\\\"\\r\\n\\r\\n/*\\r\\nhttps://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts\\r\\n\\r\\nhttps://github.com/KumarVariable/vscode-extension-sidebar-html/blob/master/src/customSidebarViewProvider.ts\\r\\n*/\\r\\n\\r\\ntype SecretKey =\\r\\n\\t| \\\"apiKey\\\"\\r\\n\\t| \\\"openRouterApiKey\\\"\\r\\n\\t| \\\"awsAccessKey\\\"\\r\\n\\t| \\\"awsSecretKey\\\"\\r\\n\\t| \\\"awsSessionToken\\\"\\r\\n\\t| \\\"openAiApiKey\\\"\\r\\n\\t| \\\"geminiApiKey\\\"\\r\\n\\t| \\\"openAiNativeApiKey\\\"\\r\\n\\t| \\\"deepSeekApiKey\\\"\\r\\ntype GlobalStateKey =\\r\\n\\t| \\\"apiProvider\\\"\\r\\n\\t| \\\"apiModelId\\\"\\r\\n\\t| \\\"awsRegion\\\"\\r\\n\\t| \\\"awsUseCrossRegionInference\\\"\\r\\n\\t| \\\"vertexProjectId\\\"\\r\\n\\t| \\\"vertexRegion\\\"\\r\\n\\t| \\\"lastShownAnnouncementId\\\"\\r\\n\\t| \\\"customInstructions\\\"\\r\\n\\t| \\\"taskHistory\\\"\\r\\n\\t| \\\"openAiBaseUrl\\\"\\r\\n\\t| \\\"openAiModelId\\\"\\r\\n\\t| \\\"ollamaModelId\\\"\\r\\n\\t| \\\"ollamaBaseUrl\\\"\\r\\n\\t| \\\"lmStudioModelId\\\"\\r\\n\\t| \\\"lmStudioBaseUrl\\\"\\r\\n\\t| \\\"anthropicBaseUrl\\\"\\r\\n\\t| \\\"azureApiVersion\\\"\\r\\n\\t| \\\"openRouterModelId\\\"\\r\\n\\t| \\\"openRouterModelInfo\\\"\\r\\n\\t| \\\"autoApprovalSettings\\\"\\r\\n\\r\\nexport const GlobalFileNames = {\\r\\n\\tapiConversationHistory: \\\"api_conversation_history.json\\\",\\r\\n\\tuiMessages: \\\"ui_messages.json\\\",\\r\\n\\topenRouterModels: \\\"openrouter_models.json\\\",\\r\\n\\tmcpSettings: \\\"cline_mcp_settings.json\\\",\\r\\n\\tclineRules: \\\".clinerules\\\",\\r\\n}\\r\\n\\r\\nexport class ClineProvider implements vscode.WebviewViewProvider {\\r\\n\\tpublic static readonly sideBarId = \\\"claude-dev.SidebarProvider\\\" // used in package.json as the view's id. This value cannot be changed due to how vscode caches views based on their id, and updating the id would break existing instances of the extension.\\r\\n\\tpublic static readonly tabPanelId = \\\"claude-dev.TabPanelProvider\\\"\\r\\n\\tprivate static activeInstances: Set<ClineProvider> = new Set()\\r\\n\\tprivate disposables: vscode.Disposable[] = []\\r\\n\\tprivate view?: vscode.WebviewView | vscode.WebviewPanel\\r\\n\\tprivate cline?: Cline\\r\\n\\tprivate workspaceTracker?: WorkspaceTracker\\r\\n\\tmcpHub?: McpHub\\r\\n\\tprivate latestAnnouncementId = \\\"jan-6-2025\\\" // update to some unique identifier when we add a new announcement\\r\\n\\r\\n\\tconstructor(\\r\\n\\t\\treadonly context: vscode.ExtensionContext,\\r\\n\\t\\tprivate readonly outputChannel: vscode.OutputChannel,\\r\\n\\t) {\\r\\n\\t\\tthis.outputChannel.appendLine(\\\"ClineProvider instantiated\\\")\\r\\n\\t\\tClineProvider.activeInstances.add(this)\\r\\n\\t\\tthis.workspaceTracker = new WorkspaceTracker(this)\\r\\n\\t\\tthis.mcpHub = new McpHub(this)\\r\\n\\t}\\r\\n\\r\\n\\t/*\\r\\n\\tVSCode extensions use the disposable pattern to clean up resources when the sidebar/editor tab is closed by the user or system. This applies to event listening, commands, interacting with the UI, etc.\\r\\n\\t- https://vscode-docs.readthedocs.io/en/stable/extensions/patterns-and-principles/\\r\\n\\t- https://github.com/microsoft/vscode-extension-samples/blob/main/webview-sample/src/extension.ts\\r\\n\\t*/\\r\\n\\tasync dispose() {\\r\\n\\t\\tthis.outputChannel.appendLine(\\\"Disposing ClineProvider...\\\")\\r\\n\\t\\tawait this.clearTask()\\r\\n\\t\\tthis.outputChannel.appendLine(\\\"Cleared task\\\")\\r\\n\\t\\tif (this.view && \\\"dispose\\\" in this.view) {\\r\\n\\t\\t\\tthis.view.dispose()\\r\\n\\t\\t\\tthis.outputChannel.appendLine(\\\"Disposed webview\\\")\\r\\n\\t\\t}\\r\\n\\t\\twhile (this.disposables.length) {\\r\\n\\t\\t\\tconst x = this.disposables.pop()\\r\\n\\t\\t\\tif (x) {\\r\\n\\t\\t\\t\\tx.dispose()\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\tthis.workspaceTracker?.dispose()\\r\\n\\t\\tthis.workspaceTracker = undefined\\r\\n\\t\\tthis.mcpHub?.dispose()\\r\\n\\t\\tthis.mcpHub = undefined\\r\\n\\t\\tthis.outputChannel.appendLine(\\\"Disposed all disposables\\\")\\r\\n\\t\\tClineProvider.activeInstances.delete(this)\\r\\n\\t}\\r\\n\\r\\n\\tpublic static getVisibleInstance(): ClineProvider | undefined {\\r\\n\\t\\treturn findLast(Array.from(this.activeInstances), (instance) => instance.view?.visible === true)\\r\\n\\t}\\r\\n\\r\\n\\tresolveWebviewView(\\r\\n\\t\\twebviewView: vscode.WebviewView | vscode.WebviewPanel,\\r\\n\\t\\t//context: vscode.WebviewViewResolveContext<unknown>, used to recreate a deallocated webview, but we don't need this since we use retainContextWhenHidden\\r\\n\\t\\t//token: vscode.CancellationToken\\r\\n\\t): void | Thenable<void> {\\r\\n\\t\\tthis.outputChannel.appendLine(\\\"Resolving webview view\\\")\\r\\n\\t\\tthis.view = webviewView\\r\\n\\r\\n\\t\\twebviewView.webview.options = {\\r\\n\\t\\t\\t// Allow scripts in the webview\\r\\n\\t\\t\\tenableScripts: true,\\r\\n\\t\\t\\tlocalResourceRoots: [this.context.extensionUri],\\r\\n\\t\\t}\\r\\n\\t\\twebviewView.webview.html = this.getHtmlContent(webviewView.webview)\\r\\n\\r\\n\\t\\t// Sets up an event listener to listen for messages passed from the webview view context\\r\\n\\t\\t// and executes code based on the message that is recieved\\r\\n\\t\\tthis.setWebviewMessageListener(webviewView.webview)\\r\\n\\r\\n\\t\\t// Logs show up in bottom panel > Debug Console\\r\\n\\t\\t//console.log(\\\"registering listener\\\")\\r\\n\\r\\n\\t\\t// Listen for when the panel becomes visible\\r\\n\\t\\t// https://github.com/microsoft/vscode-discussions/discussions/840\\r\\n\\t\\tif (\\\"onDidChangeViewState\\\" in webviewView) {\\r\\n\\t\\t\\t// WebviewView and WebviewPanel have all the same properties except for this visibility listener\\r\\n\\t\\t\\t// panel\\r\\n\\t\\t\\twebviewView.onDidChangeViewState(\\r\\n\\t\\t\\t\\t() => {\\r\\n\\t\\t\\t\\t\\tif (this.view?.visible) {\\r\\n\\t\\t\\t\\t\\t\\tthis.postMessageToWebview({\\r\\n\\t\\t\\t\\t\\t\\t\\ttype: \\\"action\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\taction: \\\"didBecomeVisible\\\",\\r\\n\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\tnull,\\r\\n\\t\\t\\t\\tthis.disposables,\\r\\n\\t\\t\\t)\\r\\n\\t\\t} else if (\\\"onDidChangeVisibility\\\" in webviewView) {\\r\\n\\t\\t\\t// sidebar\\r\\n\\t\\t\\twebviewView.onDidChangeVisibility(\\r\\n\\t\\t\\t\\t() => {\\r\\n\\t\\t\\t\\t\\tif (this.view?.visible) {\\r\\n\\t\\t\\t\\t\\t\\tthis.postMessageToWebview({\\r\\n\\t\\t\\t\\t\\t\\t\\ttype: \\\"action\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\taction: \\\"didBecomeVisible\\\",\\r\\n\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\tnull,\\r\\n\\t\\t\\t\\tthis.disposables,\\r\\n\\t\\t\\t)\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// Listen for when the view is disposed\\r\\n\\t\\t// This happens when the user closes the view or when the view is closed programmatically\\r\\n\\t\\twebviewView.onDidDispose(\\r\\n\\t\\t\\tasync () => {\\r\\n\\t\\t\\t\\tawait this.dispose()\\r\\n\\t\\t\\t},\\r\\n\\t\\t\\tnull,\\r\\n\\t\\t\\tthis.disposables,\\r\\n\\t\\t)\\r\\n\\r\\n\\t\\t// Listen for when color changes\\r\\n\\t\\tvscode.workspace.onDidChangeConfiguration(\\r\\n\\t\\t\\tasync (e) => {\\r\\n\\t\\t\\t\\tif (e && e.affectsConfiguration(\\\"workbench.colorTheme\\\")) {\\r\\n\\t\\t\\t\\t\\t// Sends latest theme name to webview\\r\\n\\t\\t\\t\\t\\tawait this.postMessageToWebview({\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"theme\\\",\\r\\n\\t\\t\\t\\t\\t\\ttext: JSON.stringify(await getTheme()),\\r\\n\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t},\\r\\n\\t\\t\\tnull,\\r\\n\\t\\t\\tthis.disposables,\\r\\n\\t\\t)\\r\\n\\r\\n\\t\\t// if the extension is starting a new session, clear previous task state\\r\\n\\t\\tthis.clearTask()\\r\\n\\r\\n\\t\\tthis.outputChannel.appendLine(\\\"Webview view resolved\\\")\\r\\n\\t}\\r\\n\\r\\n\\tasync initClineWithTask(task?: string, images?: string[]) {\\r\\n\\t\\tawait this.clearTask() // ensures that an exising task doesn't exist before starting a new one, although this shouldn't be possible since user must clear task before starting a new one\\r\\n\\t\\tconst { apiConfiguration, customInstructions, autoApprovalSettings } = await this.getState()\\r\\n\\t\\tthis.cline = new Cline(this, apiConfiguration, autoApprovalSettings, customInstructions, task, images)\\r\\n\\t}\\r\\n\\r\\n\\tasync initClineWithHistoryItem(historyItem: HistoryItem) {\\r\\n\\t\\tawait this.clearTask()\\r\\n\\t\\tconst { apiConfiguration, customInstructions, autoApprovalSettings } = await this.getState()\\r\\n\\t\\tthis.cline = new Cline(\\r\\n\\t\\t\\tthis,\\r\\n\\t\\t\\tapiConfiguration,\\r\\n\\t\\t\\tautoApprovalSettings,\\r\\n\\t\\t\\tcustomInstructions,\\r\\n\\t\\t\\tundefined,\\r\\n\\t\\t\\tundefined,\\r\\n\\t\\t\\thistoryItem,\\r\\n\\t\\t)\\r\\n\\t}\\r\\n\\r\\n\\t// Send any JSON serializable data to the react app\\r\\n\\tasync postMessageToWebview(message: ExtensionMessage) {\\r\\n\\t\\tawait this.view?.webview.postMessage(message)\\r\\n\\t}\\r\\n\\r\\n\\t/**\\r\\n\\t * Defines and returns the HTML that should be rendered within the webview panel.\\r\\n\\t *\\r\\n\\t * @remarks This is also the place where references to the React webview build files\\r\\n\\t * are created and inserted into the webview HTML.\\r\\n\\t *\\r\\n\\t * @param webview A reference to the extension webview\\r\\n\\t * @param extensionUri The URI of the directory containing the extension\\r\\n\\t * @returns A template string literal containing the HTML that should be\\r\\n\\t * rendered within the webview panel\\r\\n\\t */\\r\\n\\tprivate getHtmlContent(webview: vscode.Webview): string {\\r\\n\\t\\t// Get the local path to main script run in the webview,\\r\\n\\t\\t// then convert it to a uri we can use in the webview.\\r\\n\\r\\n\\t\\t// The CSS file from the React build output\\r\\n\\t\\tconst stylesUri = getUri(webview, this.context.extensionUri, [\\\"webview-ui\\\", \\\"build\\\", \\\"static\\\", \\\"css\\\", \\\"main.css\\\"])\\r\\n\\t\\t// The JS file from the React build output\\r\\n\\t\\tconst scriptUri = getUri(webview, this.context.extensionUri, [\\\"webview-ui\\\", \\\"build\\\", \\\"static\\\", \\\"js\\\", \\\"main.js\\\"])\\r\\n\\r\\n\\t\\t// The codicon font from the React build output\\r\\n\\t\\t// https://github.com/microsoft/vscode-extension-samples/blob/main/webview-codicons-sample/src/extension.ts\\r\\n\\t\\t// we installed this package in the extension so that we can access it how its intended from the extension (the font file is likely bundled in vscode), and we just import the css fileinto our react app we don't have access to it\\r\\n\\t\\t// don't forget to add font-src ${webview.cspSource};\\r\\n\\t\\tconst codiconsUri = getUri(webview, this.context.extensionUri, [\\r\\n\\t\\t\\t\\\"node_modules\\\",\\r\\n\\t\\t\\t\\\"@vscode\\\",\\r\\n\\t\\t\\t\\\"codicons\\\",\\r\\n\\t\\t\\t\\\"dist\\\",\\r\\n\\t\\t\\t\\\"codicon.css\\\",\\r\\n\\t\\t])\\r\\n\\r\\n\\t\\t// const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, \\\"assets\\\", \\\"main.js\\\"))\\r\\n\\r\\n\\t\\t// const styleResetUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, \\\"assets\\\", \\\"reset.css\\\"))\\r\\n\\t\\t// const styleVSCodeUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, \\\"assets\\\", \\\"vscode.css\\\"))\\r\\n\\r\\n\\t\\t// // Same for stylesheet\\r\\n\\t\\t// const stylesheetUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, \\\"assets\\\", \\\"main.css\\\"))\\r\\n\\r\\n\\t\\t// Use a nonce to only allow a specific script to be run.\\r\\n\\t\\t/*\\r\\n content security policy of your webview to only allow scripts that have a specific nonce\\r\\n create a content security policy meta tag so that only loading scripts with a nonce is allowed\\r\\n As your extension grows you will likely want to add custom styles, fonts, and/or images to your webview. If you do, you will need to update the content security policy meta tag to explicity allow for these resources. E.g.\\r\\n <meta http-equiv=\\\"Content-Security-Policy\\\" content=\\\"default-src 'none'; style-src ${webview.cspSource}; font-src ${webview.cspSource}; img-src ${webview.cspSource} https:; script-src 'nonce-${nonce}';\\\">\\r\\n\\t\\t- 'unsafe-inline' is required for styles due to vscode-webview-toolkit's dynamic style injection\\r\\n\\t\\t- since we pass base64 images to the webview, we need to specify img-src ${webview.cspSource} data:;\\r\\n\\r\\n in meta tag we add nonce attribute: A cryptographic nonce (only used once) to allow scripts. The server must generate a unique nonce value each time it transmits a policy. It is critical to provide a nonce that cannot be guessed as bypassing a resource's policy is otherwise trivial.\\r\\n */\\r\\n\\t\\tconst nonce = getNonce()\\r\\n\\r\\n\\t\\t// Tip: Install the es6-string-html VS Code extension to enable code highlighting below\\r\\n\\t\\treturn /*html*/ `\\r\\n <!DOCTYPE html>\\r\\n <html lang=\\\"en\\\">\\r\\n <head>\\r\\n <meta charset=\\\"utf-8\\\">\\r\\n <meta name=\\\"viewport\\\" content=\\\"width=device-width,initial-scale=1,shrink-to-fit=no\\\">\\r\\n <meta name=\\\"theme-color\\\" content=\\\"#000000\\\">\\r\\n <meta http-equiv=\\\"Content-Security-Policy\\\" content=\\\"default-src 'none'; font-src ${webview.cspSource}; style-src ${webview.cspSource} 'unsafe-inline'; img-src ${webview.cspSource} data:; script-src 'nonce-${nonce}';\\\">\\r\\n <link rel=\\\"stylesheet\\\" type=\\\"text/css\\\" href=\\\"${stylesUri}\\\">\\r\\n\\t\\t\\t<link href=\\\"${codiconsUri}\\\" rel=\\\"stylesheet\\\" />\\r\\n <title>Cline</title>\\r\\n </head>\\r\\n <body>\\r\\n <noscript>You need to enable JavaScript to run this app.</noscript>\\r\\n <div id=\\\"root\\\"></div>\\r\\n <script nonce=\\\"${nonce}\\\" src=\\\"${scriptUri}\\\"></script>\\r\\n </body>\\r\\n </html>\\r\\n `\\r\\n\\t}\\r\\n\\r\\n\\t/**\\r\\n\\t * Sets up an event listener to listen for messages passed from the webview context and\\r\\n\\t * executes code based on the message that is recieved.\\r\\n\\t *\\r\\n\\t * @param webview A reference to the extension webview\\r\\n\\t */\\r\\n\\tprivate setWebviewMessageListener(webview: vscode.Webview) {\\r\\n\\t\\twebview.onDidReceiveMessage(\\r\\n\\t\\t\\tasync (message: WebviewMessage) => {\\r\\n\\t\\t\\t\\tswitch (message.type) {\\r\\n\\t\\t\\t\\t\\tcase \\\"webviewDidLaunch\\\":\\r\\n\\t\\t\\t\\t\\t\\tthis.postStateToWebview()\\r\\n\\t\\t\\t\\t\\t\\tthis.workspaceTracker?.initializeFilePaths() // don't await\\r\\n\\t\\t\\t\\t\\t\\tgetTheme().then((theme) =>\\r\\n\\t\\t\\t\\t\\t\\t\\tthis.postMessageToWebview({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"theme\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttext: JSON.stringify(theme),\\r\\n\\t\\t\\t\\t\\t\\t\\t}),\\r\\n\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t// post last cached models in case the call to endpoint fails\\r\\n\\t\\t\\t\\t\\t\\tthis.readOpenRouterModels().then((openRouterModels) => {\\r\\n\\t\\t\\t\\t\\t\\t\\tif (openRouterModels) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tthis.postMessageToWebview({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"openRouterModels\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\topenRouterModels,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t\\t// gui relies on model info to be up-to-date to provide the most accurate pricing, so we need to fetch the latest details on launch.\\r\\n\\t\\t\\t\\t\\t\\t// we do this for all users since many users switch between api providers and if they were to switch back to openrouter it would be showing outdated model info if we hadn't retrieved the latest at this point\\r\\n\\t\\t\\t\\t\\t\\t// (see normalizeApiConfiguration > openrouter)\\r\\n\\t\\t\\t\\t\\t\\tthis.refreshOpenRouterModels().then(async (openRouterModels) => {\\r\\n\\t\\t\\t\\t\\t\\t\\tif (openRouterModels) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// update model info in state (this needs to be done here since we don't want to update state while settings is open, and we may refresh models there)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst { apiConfiguration } = await this.getState()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (apiConfiguration.openRouterModelId) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.updateGlobalState(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\\"openRouterModelInfo\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\topenRouterModels[apiConfiguration.openRouterModelId],\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.postStateToWebview()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase \\\"newTask\\\":\\r\\n\\t\\t\\t\\t\\t\\t// Code that should run in response to the hello message command\\r\\n\\t\\t\\t\\t\\t\\t//vscode.window.showInformationMessage(message.text!)\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t// Send a message to our webview.\\r\\n\\t\\t\\t\\t\\t\\t// You can send any JSON serializable data.\\r\\n\\t\\t\\t\\t\\t\\t// Could also do this in extension .ts\\r\\n\\t\\t\\t\\t\\t\\t//this.postMessageToWebview({ type: \\\"text\\\", text: `Extension: ${Date.now()}` })\\r\\n\\t\\t\\t\\t\\t\\t// initializing new instance of Cline will make sure that any agentically running promises in old instance don't affect our new task. this essentially creates a fresh slate for the new task\\r\\n\\t\\t\\t\\t\\t\\tawait this.initClineWithTask(message.text, message.images)\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase \\\"apiConfiguration\\\":\\r\\n\\t\\t\\t\\t\\t\\tif (message.apiConfiguration) {\\r\\n\\t\\t\\t\\t\\t\\t\\tconst {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tapiProvider,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tapiModelId,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tapiKey,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\topenRouterApiKey,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawsAccessKey,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawsSecretKey,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawsSessionToken,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawsRegion,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawsUseCrossRegionInference,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tvertexProjectId,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tvertexRegion,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\topenAiBaseUrl,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\topenAiApiKey,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\topenAiModelId,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tollamaModelId,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tollamaBaseUrl,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tlmStudioModelId,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tlmStudioBaseUrl,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tanthropicBaseUrl,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tgeminiApiKey,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\topenAiNativeApiKey,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tdeepSeekApiKey,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tazureApiVersion,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\topenRouterModelId,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\topenRouterModelInfo,\\r\\n\\t\\t\\t\\t\\t\\t\\t} = message.apiConfiguration\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.updateGlobalState(\\\"apiProvider\\\", apiProvider)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.updateGlobalState(\\\"apiModelId\\\", apiModelId)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.storeSecret(\\\"apiKey\\\", apiKey)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.storeSecret(\\\"openRouterApiKey\\\", openRouterApiKey)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.storeSecret(\\\"awsAccessKey\\\", awsAccessKey)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.storeSecret(\\\"awsSecretKey\\\", awsSecretKey)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.storeSecret(\\\"awsSessionToken\\\", awsSessionToken)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.updateGlobalState(\\\"awsRegion\\\", awsRegion)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.updateGlobalState(\\\"awsUseCrossRegionInference\\\", awsUseCrossRegionInference)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.updateGlobalState(\\\"vertexProjectId\\\", vertexProjectId)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.updateGlobalState(\\\"vertexRegion\\\", vertexRegion)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.updateGlobalState(\\\"openAiBaseUrl\\\", openAiBaseUrl)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.storeSecret(\\\"openAiApiKey\\\", openAiApiKey)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.updateGlobalState(\\\"openAiModelId\\\", openAiModelId)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.updateGlobalState(\\\"ollamaModelId\\\", ollamaModelId)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.updateGlobalState(\\\"ollamaBaseUrl\\\", ollamaBaseUrl)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.updateGlobalState(\\\"lmStudioModelId\\\", lmStudioModelId)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.updateGlobalState(\\\"lmStudioBaseUrl\\\", lmStudioBaseUrl)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.updateGlobalState(\\\"anthropicBaseUrl\\\", anthropicBaseUrl)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.storeSecret(\\\"geminiApiKey\\\", geminiApiKey)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.storeSecret(\\\"openAiNativeApiKey\\\", openAiNativeApiKey)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.storeSecret(\\\"deepSeekApiKey\\\", deepSeekApiKey)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.updateGlobalState(\\\"azureApiVersion\\\", azureApiVersion)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.updateGlobalState(\\\"openRouterModelId\\\", openRouterModelId)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.updateGlobalState(\\\"openRouterModelInfo\\\", openRouterModelInfo)\\r\\n\\t\\t\\t\\t\\t\\t\\tif (this.cline) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tthis.cline.api = buildApiHandler(message.apiConfiguration)\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\tawait this.postStateToWebview()\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase \\\"customInstructions\\\":\\r\\n\\t\\t\\t\\t\\t\\tawait this.updateCustomInstructions(message.text)\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase \\\"autoApprovalSettings\\\":\\r\\n\\t\\t\\t\\t\\t\\tif (message.autoApprovalSettings) {\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.updateGlobalState(\\\"autoApprovalSettings\\\", message.autoApprovalSettings)\\r\\n\\t\\t\\t\\t\\t\\t\\tif (this.cline) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tthis.cline.autoApprovalSettings = message.autoApprovalSettings\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.postStateToWebview()\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase \\\"askResponse\\\":\\r\\n\\t\\t\\t\\t\\t\\tthis.cline?.handleWebviewAskResponse(message.askResponse!, message.text, message.images)\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase \\\"clearTask\\\":\\r\\n\\t\\t\\t\\t\\t\\t// newTask will start a new task with a given task text, while clear task resets the current session and allows for a new task to be started\\r\\n\\t\\t\\t\\t\\t\\tawait this.clearTask()\\r\\n\\t\\t\\t\\t\\t\\tawait this.postStateToWebview()\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase \\\"didShowAnnouncement\\\":\\r\\n\\t\\t\\t\\t\\t\\tawait this.updateGlobalState(\\\"lastShownAnnouncementId\\\", this.latestAnnouncementId)\\r\\n\\t\\t\\t\\t\\t\\tawait this.postStateToWebview()\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase \\\"selectImages\\\":\\r\\n\\t\\t\\t\\t\\t\\tconst images = await selectImages()\\r\\n\\t\\t\\t\\t\\t\\tawait this.postMessageToWebview({\\r\\n\\t\\t\\t\\t\\t\\t\\ttype: \\\"selectedImages\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\timages,\\r\\n\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase \\\"exportCurrentTask\\\":\\r\\n\\t\\t\\t\\t\\t\\tconst currentTaskId = this.cline?.taskId\\r\\n\\t\\t\\t\\t\\t\\tif (currentTaskId) {\\r\\n\\t\\t\\t\\t\\t\\t\\tthis.exportTaskWithId(currentTaskId)\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase \\\"showTaskWithId\\\":\\r\\n\\t\\t\\t\\t\\t\\tthis.showTaskWithId(message.text!)\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase \\\"deleteTaskWithId\\\":\\r\\n\\t\\t\\t\\t\\t\\tthis.deleteTaskWithId(message.text!)\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase \\\"exportTaskWithId\\\":\\r\\n\\t\\t\\t\\t\\t\\tthis.exportTaskWithId(message.text!)\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase \\\"resetState\\\":\\r\\n\\t\\t\\t\\t\\t\\tawait this.resetState()\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase \\\"requestOllamaModels\\\":\\r\\n\\t\\t\\t\\t\\t\\tconst ollamaModels = await this.getOllamaModels(message.text)\\r\\n\\t\\t\\t\\t\\t\\tthis.postMessageToWebview({\\r\\n\\t\\t\\t\\t\\t\\t\\ttype: \\\"ollamaModels\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\tollamaModels,\\r\\n\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase \\\"requestLmStudioModels\\\":\\r\\n\\t\\t\\t\\t\\t\\tconst lmStudioModels = await this.getLmStudioModels(message.text)\\r\\n\\t\\t\\t\\t\\t\\tthis.postMessageToWebview({\\r\\n\\t\\t\\t\\t\\t\\t\\ttype: \\\"lmStudioModels\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\tlmStudioModels,\\r\\n\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase \\\"refreshOpenRouterModels\\\":\\r\\n\\t\\t\\t\\t\\t\\tawait this.refreshOpenRouterModels()\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase \\\"openImage\\\":\\r\\n\\t\\t\\t\\t\\t\\topenImage(message.text!)\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase \\\"openFile\\\":\\r\\n\\t\\t\\t\\t\\t\\topenFile(message.text!)\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase \\\"openMention\\\":\\r\\n\\t\\t\\t\\t\\t\\topenMention(message.text)\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase \\\"checkpointDiff\\\": {\\r\\n\\t\\t\\t\\t\\t\\tif (message.number) {\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.cline?.presentMultifileDiff(message.number, false)\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tcase \\\"checkpointRestore\\\": {\\r\\n\\t\\t\\t\\t\\t\\tawait this.cancelTask() // we cannot alter message history say if the task is active, as it could be in the middle of editing a file or running a command, which expect the ask to be responded to rather than being superceded by a new message eg add deleted_api_reqs\\r\\n\\t\\t\\t\\t\\t\\t// cancel task waits for any open editor to be reverted and starts a new cline instance\\r\\n\\t\\t\\t\\t\\t\\tif (message.number) {\\r\\n\\t\\t\\t\\t\\t\\t\\t// wait for messages to be loaded\\r\\n\\t\\t\\t\\t\\t\\t\\tawait pWaitFor(() => this.cline?.isInitialized === true, {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttimeout: 3_000,\\r\\n\\t\\t\\t\\t\\t\\t\\t}).catch(() => {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconsole.error(\\\"Failed to init new cline instance\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t\\t\\t// NOTE: cancelTask awaits abortTask, which awaits diffViewProvider.revertChanges, which reverts any edited files, allowing us to reset to a checkpoint rather than running into a state where the revertChanges function is called alongside or after the checkpoint reset\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.cline?.restoreCheckpoint(message.number, message.text! as ClineCheckpointRestore)\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tcase \\\"taskCompletionViewChanges\\\": {\\r\\n\\t\\t\\t\\t\\t\\tif (message.number) {\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.cline?.presentMultifileDiff(message.number, true)\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tcase \\\"cancelTask\\\":\\r\\n\\t\\t\\t\\t\\t\\tthis.cancelTask()\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase \\\"openMcpSettings\\\": {\\r\\n\\t\\t\\t\\t\\t\\tconst mcpSettingsFilePath = await this.mcpHub?.getMcpSettingsFilePath()\\r\\n\\t\\t\\t\\t\\t\\tif (mcpSettingsFilePath) {\\r\\n\\t\\t\\t\\t\\t\\t\\topenFile(mcpSettingsFilePath)\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tcase \\\"restartMcpServer\\\": {\\r\\n\\t\\t\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.mcpHub?.restartConnection(message.text!)\\r\\n\\t\\t\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\t\\t\\tconsole.error(`Failed to retry connection for ${message.text}:`, error)\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t// Add more switch case statements here as more webview message commands\\r\\n\\t\\t\\t\\t\\t// are created within the webview context (i.e. inside media/main.js)\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t},\\r\\n\\t\\t\\tnull,\\r\\n\\t\\t\\tthis.disposables,\\r\\n\\t\\t)\\r\\n\\t}\\r\\n\\r\\n\\tasync cancelTask() {\\r\\n\\t\\tif (this.cline) {\\r\\n\\t\\t\\tconst { historyItem } = await this.getTaskWithId(this.cline.taskId)\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\tawait this.cline.abortTask()\\r\\n\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\tconsole.error(\\\"Failed to abort task\\\", error)\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tawait pWaitFor(\\r\\n\\t\\t\\t\\t() => this.cline === undefined || this.cline.isStreaming === false || this.cline.didFinishAbortingStream,\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\ttimeout: 3_000,\\r\\n\\t\\t\\t\\t},\\r\\n\\t\\t\\t).catch(() => {\\r\\n\\t\\t\\t\\tconsole.error(\\\"Failed to abort task\\\")\\r\\n\\t\\t\\t})\\r\\n\\t\\t\\tif (this.cline) {\\r\\n\\t\\t\\t\\t// 'abandoned' will prevent this cline instance from affecting future cline instance gui. this may happen if its hanging on a streaming request\\r\\n\\t\\t\\t\\tthis.cline.abandoned = true\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tawait this.initClineWithHistoryItem(historyItem) // clears task again, so we need to abortTask manually above\\r\\n\\t\\t\\t// await this.postStateToWebview() // new Cline instance will post state when it's ready. having this here sent an empty messages array to webview leading to virtuoso having to reload the entire list\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tasync updateCustomInstructions(instructions?: string) {\\r\\n\\t\\t// User may be clearing the field\\r\\n\\t\\tawait this.updateGlobalState(\\\"customInstructions\\\", instructions || undefined)\\r\\n\\t\\tif (this.cline) {\\r\\n\\t\\t\\tthis.cline.customInstructions = instructions || undefined\\r\\n\\t\\t}\\r\\n\\t\\tawait this.postStateToWebview()\\r\\n\\t}\\r\\n\\r\\n\\t// MCP\\r\\n\\r\\n\\tasync ensureMcpServersDirectoryExists(): Promise<string> {\\r\\n\\t\\tconst mcpServersDir = path.join(os.homedir(), \\\"Documents\\\", \\\"Cline\\\", \\\"MCP\\\")\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tawait fs.mkdir(mcpServersDir, { recursive: true })\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\treturn \\\"~/Documents/Cline/MCP\\\" // in case creating a directory in documents fails for whatever reason (e.g. permissions) - this is fine since this path is only ever used in the system prompt\\r\\n\\t\\t}\\r\\n\\t\\treturn mcpServersDir\\r\\n\\t}\\r\\n\\r\\n\\tasync ensureSettingsDirectoryExists(): Promise<string> {\\r\\n\\t\\tconst settingsDir = path.join(this.context.globalStorageUri.fsPath, \\\"settings\\\")\\r\\n\\t\\tawait fs.mkdir(settingsDir, { recursive: true })\\r\\n\\t\\treturn settingsDir\\r\\n\\t}\\r\\n\\r\\n\\t// Ollama\\r\\n\\r\\n\\tasync getOllamaModels(baseUrl?: string) {\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tif (!baseUrl) {\\r\\n\\t\\t\\t\\tbaseUrl = \\\"http://localhost:11434\\\"\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tif (!URL.canParse(baseUrl)) {\\r\\n\\t\\t\\t\\treturn []\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tconst response = await axios.get(`${baseUrl}/api/tags`)\\r\\n\\t\\t\\tconst modelsArray = response.data?.models?.map((model: any) => model.name) || []\\r\\n\\t\\t\\tconst models = [...new Set<string>(modelsArray)]\\r\\n\\t\\t\\treturn models\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\treturn []\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t// LM Studio\\r\\n\\r\\n\\tasync getLmStudioModels(baseUrl?: string) {\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tif (!baseUrl) {\\r\\n\\t\\t\\t\\tbaseUrl = \\\"http://localhost:1234\\\"\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tif (!URL.canParse(baseUrl)) {\\r\\n\\t\\t\\t\\treturn []\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tconst response = await axios.get(`${baseUrl}/v1/models`)\\r\\n\\t\\t\\tconst modelsArray = response.data?.data?.map((model: any) => model.id) || []\\r\\n\\t\\t\\tconst models = [...new Set<string>(modelsArray)]\\r\\n\\t\\t\\treturn models\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\treturn []\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t// OpenRouter\\r\\n\\r\\n\\tasync handleOpenRouterCallback(code: string) {\\r\\n\\t\\tlet apiKey: string\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tconst response = await axios.post(\\\"https://openrouter.ai/api/v1/auth/keys\\\", { code })\\r\\n\\t\\t\\tif (response.data && response.data.key) {\\r\\n\\t\\t\\t\\tapiKey = response.data.key\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tthrow new Error(\\\"Invalid response from OpenRouter API\\\")\\r\\n\\t\\t\\t}\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\tconsole.error(\\\"Error exchanging code for API key:\\\", error)\\r\\n\\t\\t\\tthrow error\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tconst openrouter: ApiProvider = \\\"openrouter\\\"\\r\\n\\t\\tawait this.updateGlobalState(\\\"apiProvider\\\", openrouter)\\r\\n\\t\\tawait this.storeSecret(\\\"openRouterApiKey\\\", apiKey)\\r\\n\\t\\tawait this.postStateToWebview()\\r\\n\\t\\tif (this.cline) {\\r\\n\\t\\t\\tthis.cline.api = buildApiHandler({\\r\\n\\t\\t\\t\\tapiProvider: openrouter,\\r\\n\\t\\t\\t\\topenRouterApiKey: apiKey,\\r\\n\\t\\t\\t})\\r\\n\\t\\t}\\r\\n\\t\\t// await this.postMessageToWebview({ type: \\\"action\\\", action: \\\"settingsButtonClicked\\\" }) // bad ux if user is on welcome\\r\\n\\t}\\r\\n\\r\\n\\tprivate async ensureCacheDirectoryExists(): Promise<string> {\\r\\n\\t\\tconst cacheDir = path.join(this.context.globalStorageUri.fsPath, \\\"cache\\\")\\r\\n\\t\\tawait fs.mkdir(cacheDir, { recursive: true })\\r\\n\\t\\treturn cacheDir\\r\\n\\t}\\r\\n\\r\\n\\tasync readOpenRouterModels(): Promise<Record<string, ModelInfo> | undefined> {\\r\\n\\t\\tconst openRouterModelsFilePath = path.join(await this.ensureCacheDirectoryExists(), GlobalFileNames.openRouterModels)\\r\\n\\t\\tconst fileExists = await fileExistsAtPath(openRouterModelsFilePath)\\r\\n\\t\\tif (fileExists) {\\r\\n\\t\\t\\tconst fileContents = await fs.readFile(openRouterModelsFilePath, \\\"utf8\\\")\\r\\n\\t\\t\\treturn JSON.parse(fileContents)\\r\\n\\t\\t}\\r\\n\\t\\treturn undefined\\r\\n\\t}\\r\\n\\r\\n\\tasync refreshOpenRouterModels() {\\r\\n\\t\\tconst openRouterModelsFilePath = path.join(await this.ensureCacheDirectoryExists(), GlobalFileNames.openRouterModels)\\r\\n\\r\\n\\t\\tlet models: Record<string, ModelInfo> = {}\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tconst response = await axios.get(\\\"https://openrouter.ai/api/v1/models\\\")\\r\\n\\t\\t\\t/*\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\\"id\\\": \\\"anthropic/claude-3.5-sonnet\\\",\\r\\n\\t\\t\\t\\t\\\"name\\\": \\\"Anthropic: Claude 3.5 Sonnet\\\",\\r\\n\\t\\t\\t\\t\\\"created\\\": 1718841600,\\r\\n\\t\\t\\t\\t\\\"description\\\": \\\"Claude 3.5 Sonnet delivers better-than-Opus capabilities, faster-than-Sonnet speeds, at the same Sonnet prices. Sonnet is particularly good at:\\\\n\\\\n- Coding: Autonomously writes, edits, and runs code with reasoning and troubleshooting\\\\n- Data science: Augments human data science expertise; navigates unstructured data while using multiple tools for insights\\\\n- Visual processing: excelling at interpreting charts, graphs, and images, accurately transcribing text to derive insights beyond just the text alone\\\\n- Agentic tasks: exceptional tool use, making it great at agentic tasks (i.e. complex, multi-step problem solving tasks that require engaging with other systems)\\\\n\\\\n#multimodal\\\",\\r\\n\\t\\t\\t\\t\\\"context_length\\\": 200000,\\r\\n\\t\\t\\t\\t\\\"architecture\\\": {\\r\\n\\t\\t\\t\\t\\t\\\"modality\\\": \\\"text+image-\\\\u003Etext\\\",\\r\\n\\t\\t\\t\\t\\t\\\"tokenizer\\\": \\\"Claude\\\",\\r\\n\\t\\t\\t\\t\\t\\\"instruct_type\\\": null\\r\\n\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\\"pricing\\\": {\\r\\n\\t\\t\\t\\t\\t\\\"prompt\\\": \\\"0.000003\\\",\\r\\n\\t\\t\\t\\t\\t\\\"completion\\\": \\\"0.000015\\\",\\r\\n\\t\\t\\t\\t\\t\\\"image\\\": \\\"0.0048\\\",\\r\\n\\t\\t\\t\\t\\t\\\"request\\\": \\\"0\\\"\\r\\n\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\\"top_provider\\\": {\\r\\n\\t\\t\\t\\t\\t\\\"context_length\\\": 200000,\\r\\n\\t\\t\\t\\t\\t\\\"max_completion_tokens\\\": 8192,\\r\\n\\t\\t\\t\\t\\t\\\"is_moderated\\\": true\\r\\n\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\\"per_request_limits\\\": null\\r\\n\\t\\t\\t},\\r\\n\\t\\t\\t*/\\r\\n\\t\\t\\tif (response.data?.data) {\\r\\n\\t\\t\\t\\tconst rawModels = response.data.data\\r\\n\\t\\t\\t\\tconst parsePrice = (price: any) => {\\r\\n\\t\\t\\t\\t\\tif (price) {\\r\\n\\t\\t\\t\\t\\t\\treturn parseFloat(price) * 1_000_000\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\treturn undefined\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tfor (const rawModel of rawModels) {\\r\\n\\t\\t\\t\\t\\tconst modelInfo: ModelInfo = {\\r\\n\\t\\t\\t\\t\\t\\tmaxTokens: rawModel.top_provider?.max_completion_tokens,\\r\\n\\t\\t\\t\\t\\t\\tcontextWindow: rawModel.context_length,\\r\\n\\t\\t\\t\\t\\t\\tsupportsImages: rawModel.architecture?.modality?.includes(\\\"image\\\"),\\r\\n\\t\\t\\t\\t\\t\\tsupportsPromptCache: false,\\r\\n\\t\\t\\t\\t\\t\\tinputPrice: parsePrice(rawModel.pricing?.prompt),\\r\\n\\t\\t\\t\\t\\t\\toutputPrice: parsePrice(rawModel.pricing?.completion),\\r\\n\\t\\t\\t\\t\\t\\tdescription: rawModel.description,\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\tswitch (rawModel.id) {\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"anthropic/claude-3.5-sonnet\\\":\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"anthropic/claude-3.5-sonnet:beta\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\t// NOTE: this needs to be synced with api.ts/openrouter default model info\\r\\n\\t\\t\\t\\t\\t\\t\\tmodelInfo.supportsComputerUse = true\\r\\n\\t\\t\\t\\t\\t\\t\\tmodelInfo.supportsPromptCache = true\\r\\n\\t\\t\\t\\t\\t\\t\\tmodelInfo.cacheWritesPrice = 3.75\\r\\n\\t\\t\\t\\t\\t\\t\\tmodelInfo.cacheReadsPrice = 0.3\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"anthropic/claude-3.5-sonnet-20240620\\\":\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"anthropic/claude-3.5-sonnet-20240620:beta\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\tmodelInfo.supportsPromptCache = true\\r\\n\\t\\t\\t\\t\\t\\t\\tmodelInfo.cacheWritesPrice = 3.75\\r\\n\\t\\t\\t\\t\\t\\t\\tmodelInfo.cacheReadsPrice = 0.3\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"anthropic/claude-3-5-haiku\\\":\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"anthropic/claude-3-5-haiku:beta\\\":\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"anthropic/claude-3-5-haiku-20241022\\\":\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"anthropic/claude-3-5-haiku-20241022:beta\\\":\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"anthropic/claude-3.5-haiku\\\":\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"anthropic/claude-3.5-haiku:beta\\\":\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"anthropic/claude-3.5-haiku-20241022\\\":\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"anthropic/claude-3.5-haiku-20241022:beta\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\tmodelInfo.supportsPromptCache = true\\r\\n\\t\\t\\t\\t\\t\\t\\tmodelInfo.cacheWritesPrice = 1.25\\r\\n\\t\\t\\t\\t\\t\\t\\tmodelInfo.cacheReadsPrice = 0.1\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"anthropic/claude-3-opus\\\":\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"anthropic/claude-3-opus:beta\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\tmodelInfo.supportsPromptCache = true\\r\\n\\t\\t\\t\\t\\t\\t\\tmodelInfo.cacheWritesPrice = 18.75\\r\\n\\t\\t\\t\\t\\t\\t\\tmodelInfo.cacheReadsPrice = 1.5\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"anthropic/claude-3-haiku\\\":\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"anthropic/claude-3-haiku:beta\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\tmodelInfo.supportsPromptCache = true\\r\\n\\t\\t\\t\\t\\t\\t\\tmodelInfo.cacheWritesPrice = 0.3\\r\\n\\t\\t\\t\\t\\t\\t\\tmodelInfo.cacheReadsPrice = 0.03\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"deepseek/deepseek-chat\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\tmodelInfo.supportsPromptCache = true\\r\\n\\t\\t\\t\\t\\t\\t\\t// see api.ts/deepSeekModels for more info\\r\\n\\t\\t\\t\\t\\t\\t\\tmodelInfo.inputPrice = 0\\r\\n\\t\\t\\t\\t\\t\\t\\tmodelInfo.cacheWritesPrice = 0.14\\r\\n\\t\\t\\t\\t\\t\\t\\tmodelInfo.cacheReadsPrice = 0.014\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\tmodels[rawModel.id] = modelInfo\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tconsole.error(\\\"Invalid response from OpenRouter API\\\")\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tawait fs.writeFile(openRouterModelsFilePath, JSON.stringify(models))\\r\\n\\t\\t\\tconsole.log(\\\"OpenRouter models fetched and saved\\\", models)\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\tconsole.error(\\\"Error fetching OpenRouter models:\\\", error)\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tawait this.postMessageToWebview({\\r\\n\\t\\t\\ttype: \\\"openRouterModels\\\",\\r\\n\\t\\t\\topenRouterModels: models,\\r\\n\\t\\t})\\r\\n\\t\\treturn models\\r\\n\\t}\\r\\n\\r\\n\\t// Task history\\r\\n\\r\\n\\tasync getTaskWithId(id: string): Promise<{\\r\\n\\t\\thistoryItem: HistoryItem\\r\\n\\t\\ttaskDirPath: string\\r\\n\\t\\tapiConversationHistoryFilePath: string\\r\\n\\t\\tuiMessagesFilePath: string\\r\\n\\t\\tapiConversationHistory: Anthropic.MessageParam[]\\r\\n\\t}> {\\r\\n\\t\\tconst history = ((await this.getGlobalState(\\\"taskHistory\\\")) as HistoryItem[] | undefined) || []\\r\\n\\t\\tconst historyItem = history.find((item) => item.id === id)\\r\\n\\t\\tif (historyItem) {\\r\\n\\t\\t\\tconst taskDirPath = path.join(this.context.globalStorageUri.fsPath, \\\"tasks\\\", id)\\r\\n\\t\\t\\tconst apiConversationHistoryFilePath = path.join(taskDirPath, GlobalFileNames.apiConversationHistory)\\r\\n\\t\\t\\tconst uiMessagesFilePath = path.join(taskDirPath, GlobalFileNames.uiMessages)\\r\\n\\t\\t\\tconst fileExists = await fileExistsAtPath(apiConversationHistoryFilePath)\\r\\n\\t\\t\\tif (fileExists) {\\r\\n\\t\\t\\t\\tconst apiConversationHistory = JSON.parse(await fs.readFile(apiConversationHistoryFilePath, \\\"utf8\\\"))\\r\\n\\t\\t\\t\\treturn {\\r\\n\\t\\t\\t\\t\\thistoryItem,\\r\\n\\t\\t\\t\\t\\ttaskDirPath,\\r\\n\\t\\t\\t\\t\\tapiConversationHistoryFilePath,\\r\\n\\t\\t\\t\\t\\tuiMessagesFilePath,\\r\\n\\t\\t\\t\\t\\tapiConversationHistory,\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\t// if we tried to get a task that doesn't exist, remove it from state\\r\\n\\t\\t// FIXME: this seems to happen sometimes when the json file doesnt save to disk for some reason\\r\\n\\t\\tawait this.deleteTaskFromState(id)\\r\\n\\t\\tthrow new Error(\\\"Task not found\\\")\\r\\n\\t}\\r\\n\\r\\n\\tasync showTaskWithId(id: string) {\\r\\n\\t\\tif (id !== this.cline?.taskId) {\\r\\n\\t\\t\\t// non-current task\\r\\n\\t\\t\\tconst { historyItem } = await this.getTaskWithId(id)\\r\\n\\t\\t\\tawait this.initClineWithHistoryItem(historyItem) // clears existing task\\r\\n\\t\\t}\\r\\n\\t\\tawait this.postMessageToWebview({\\r\\n\\t\\t\\ttype: \\\"action\\\",\\r\\n\\t\\t\\taction: \\\"chatButtonClicked\\\",\\r\\n\\t\\t})\\r\\n\\t}\\r\\n\\r\\n\\tasync exportTaskWithId(id: string) {\\r\\n\\t\\tconst { historyItem, apiConversationHistory } = await this.getTaskWithId(id)\\r\\n\\t\\tawait downloadTask(historyItem.ts, apiConversationHistory)\\r\\n\\t}\\r\\n\\r\\n\\tasync deleteTaskWithId(id: string) {\\r\\n\\t\\tif (id === this.cline?.taskId) {\\r\\n\\t\\t\\tawait this.clearTask()\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tconst { taskDirPath, apiConversationHistoryFilePath, uiMessagesFilePath } = await this.getTaskWithId(id)\\r\\n\\r\\n\\t\\tawait this.deleteTaskFromState(id)\\r\\n\\r\\n\\t\\t// Delete the task files\\r\\n\\t\\tconst apiConversationHistoryFileExists = await fileExistsAtPath(apiConversationHistoryFilePath)\\r\\n\\t\\tif (apiConversationHistoryFileExists) {\\r\\n\\t\\t\\tawait fs.unlink(apiConversationHistoryFilePath)\\r\\n\\t\\t}\\r\\n\\t\\tconst uiMessagesFileExists = await fileExistsAtPath(uiMessagesFilePath)\\r\\n\\t\\tif (uiMessagesFileExists) {\\r\\n\\t\\t\\tawait fs.unlink(uiMessagesFilePath)\\r\\n\\t\\t}\\r\\n\\t\\tconst legacyMessagesFilePath = path.join(taskDirPath, \\\"claude_messages.json\\\")\\r\\n\\t\\tif (await fileExistsAtPath(legacyMessagesFilePath)) {\\r\\n\\t\\t\\tawait fs.unlink(legacyMessagesFilePath)\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// Delete the checkpoints directory if it exists\\r\\n\\t\\tconst checkpointsDir = path.join(taskDirPath, \\\"checkpoints\\\")\\r\\n\\t\\tif (await fileExistsAtPath(checkpointsDir)) {\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\tawait fs.rm(checkpointsDir, { recursive: true, force: true })\\r\\n\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\tconsole.error(`Failed to delete checkpoints directory for task ${id}:`, error)\\r\\n\\t\\t\\t\\t// Continue with deletion of task directory - don't throw since this is a cleanup operation\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tawait fs.rmdir(taskDirPath) // succeeds if the dir is empty\\r\\n\\t}\\r\\n\\r\\n\\tasync deleteTaskFromState(id: string) {\\r\\n\\t\\t// Remove the task from history\\r\\n\\t\\tconst taskHistory = ((await this.getGlobalState(\\\"taskHistory\\\")) as HistoryItem[] | undefined) || []\\r\\n\\t\\tconst updatedTaskHistory = taskHistory.filter((task) => task.id !== id)\\r\\n\\t\\tawait this.updateGlobalState(\\\"taskHistory\\\", updatedTaskHistory)\\r\\n\\r\\n\\t\\t// Notify the webview that the task has been deleted\\r\\n\\t\\tawait this.postStateToWebview()\\r\\n\\t}\\r\\n\\r\\n\\tasync postStateToWebview() {\\r\\n\\t\\tconst state = await this.getStateToPostToWebview()\\r\\n\\t\\tthis.postMessageToWebview({ type: \\\"state\\\", state })\\r\\n\\t}\\r\\n\\r\\n\\tasync getStateToPostToWebview(): Promise<ExtensionState> {\\r\\n\\t\\tconst { apiConfiguration, lastShownAnnouncementId, customInstructions, taskHistory, autoApprovalSettings } =\\r\\n\\t\\t\\tawait this.getState()\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tversion: this.context.extension?.packageJSON?.version ?? \\\"\\\",\\r\\n\\t\\t\\tapiConfiguration,\\r\\n\\t\\t\\tcustomInstructions,\\r\\n\\t\\t\\turiScheme: vscode.env.uriScheme,\\r\\n\\t\\t\\tcurrentTaskItem: this.cline?.taskId ? (taskHistory || []).find((item) => item.id === this.cline?.taskId) : undefined,\\r\\n\\t\\t\\tcheckpointTrackerErrorMessage: this.cline?.checkpointTrackerErrorMessage,\\r\\n\\t\\t\\tclineMessages: this.cline?.clineMessages || [],\\r\\n\\t\\t\\ttaskHistory: (taskHistory || []).filter((item) => item.ts && item.task).sort((a, b) => b.ts - a.ts),\\r\\n\\t\\t\\tshouldShowAnnouncement: lastShownAnnouncementId !== this.latestAnnouncementId,\\r\\n\\t\\t\\tautoApprovalSettings,\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tasync clearTask() {\\r\\n\\t\\tthis.cline?.abortTask()\\r\\n\\t\\tthis.cline = undefined // removes reference to it, so once promises end it will be garbage collected\\r\\n\\t}\\r\\n\\r\\n\\t// Caching mechanism to keep track of webview messages + API conversation history per provider instance\\r\\n\\r\\n\\t/*\\r\\n\\tNow that we use retainContextWhenHidden, we don't have to store a cache of cline messages in the user's state, but we could to reduce memory footprint in long conversations.\\r\\n\\r\\n\\t- We have to be careful of what state is shared between ClineProvider instances since there could be multiple instances of the extension running at once. For example when we cached cline messages using the same key, two instances of the extension could end up using the same key and overwriting each other's messages.\\r\\n\\t- Some state does need to be shared between the instances, i.e. the API key--however there doesn't seem to be a good way to notfy the other instances that the API key has changed.\\r\\n\\r\\n\\tWe need to use a unique identifier for each ClineProvider instance's message cache since we could be running several instances of the extension outside of just the sidebar i.e. in editor panels.\\r\\n\\r\\n\\t// conversation history to send in API requests\\r\\n\\r\\n\\t/*\\r\\n\\tIt seems that some API messages do not comply with vscode state requirements. Either the Anthropic library is manipulating these values somehow in the backend in a way thats creating cyclic references, or the API returns a function or a Symbol as part of the message content.\\r\\n\\tVSCode docs about state: \\\"The value must be JSON-stringifyable ... value — A value. MUST not contain cyclic references.\\\"\\r\\n\\tFor now we'll store the conversation history in memory, and if we need to store in state directly we'd need to do a manual conversion to ensure proper json stringification.\\r\\n\\t*/\\r\\n\\r\\n\\t// getApiConversationHistory(): Anthropic.MessageParam[] {\\r\\n\\t// \\t// const history = (await this.getGlobalState(\\r\\n\\t// \\t// \\tthis.getApiConversationHistoryStateKey()\\r\\n\\t// \\t// )) as Anthropic.MessageParam[]\\r\\n\\t// \\t// return history || []\\r\\n\\t// \\treturn this.apiConversationHistory\\r\\n\\t// }\\r\\n\\r\\n\\t// setApiConversationHistory(history: Anthropic.MessageParam[] | undefined) {\\r\\n\\t// \\t// await this.updateGlobalState(this.getApiConversationHistoryStateKey(), history)\\r\\n\\t// \\tthis.apiConversationHistory = history || []\\r\\n\\t// }\\r\\n\\r\\n\\t// addMessageToApiConversationHistory(message: Anthropic.MessageParam): Anthropic.MessageParam[] {\\r\\n\\t// \\t// const history = await this.getApiConversationHistory()\\r\\n\\t// \\t// history.push(message)\\r\\n\\t// \\t// await this.setApiConversationHistory(history)\\r\\n\\t// \\t// return history\\r\\n\\t// \\tthis.apiConversationHistory.push(message)\\r\\n\\t// \\treturn this.apiConversationHistory\\r\\n\\t// }\\r\\n\\r\\n\\t/*\\r\\n\\tStorage\\r\\n\\thttps://dev.to/kompotkot/how-to-use-secretstorage-in-your-vscode-extensions-2hco\\r\\n\\thttps://www.eliostruyf.com/devhack-code-extension-storage-options/\\r\\n\\t*/\\r\\n\\r\\n\\tasync getState() {\\r\\n\\t\\tconst [\\r\\n\\t\\t\\tstoredApiProvider,\\r\\n\\t\\t\\tapiModelId,\\r\\n\\t\\t\\tapiKey,\\r\\n\\t\\t\\topenRouterApiKey,\\r\\n\\t\\t\\tawsAccessKey,\\r\\n\\t\\t\\tawsSecretKey,\\r\\n\\t\\t\\tawsSessionToken,\\r\\n\\t\\t\\tawsRegion,\\r\\n\\t\\t\\tawsUseCrossRegionInference,\\r\\n\\t\\t\\tvertexProjectId,\\r\\n\\t\\t\\tvertexRegion,\\r\\n\\t\\t\\topenAiBaseUrl,\\r\\n\\t\\t\\topenAiApiKey,\\r\\n\\t\\t\\topenAiModelId,\\r\\n\\t\\t\\tollamaModelId,\\r\\n\\t\\t\\tollamaBaseUrl,\\r\\n\\t\\t\\tlmStudioModelId,\\r\\n\\t\\t\\tlmStudioBaseUrl,\\r\\n\\t\\t\\tanthropicBaseUrl,\\r\\n\\t\\t\\tgeminiApiKey,\\r\\n\\t\\t\\topenAiNativeApiKey,\\r\\n\\t\\t\\tdeepSeekApiKey,\\r\\n\\t\\t\\tazureApiVersion,\\r\\n\\t\\t\\topenRouterModelId,\\r\\n\\t\\t\\topenRouterModelInfo,\\r\\n\\t\\t\\tlastShownAnnouncementId,\\r\\n\\t\\t\\tcustomInstructions,\\r\\n\\t\\t\\ttaskHistory,\\r\\n\\t\\t\\tautoApprovalSettings,\\r\\n\\t\\t] = await Promise.all([\\r\\n\\t\\t\\tthis.getGlobalState(\\\"apiProvider\\\") as Promise<ApiProvider | undefined>,\\r\\n\\t\\t\\tthis.getGlobalState(\\\"apiModelId\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getSecret(\\\"apiKey\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getSecret(\\\"openRouterApiKey\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getSecret(\\\"awsAccessKey\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getSecret(\\\"awsSecretKey\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getSecret(\\\"awsSessionToken\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getGlobalState(\\\"awsRegion\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getGlobalState(\\\"awsUseCrossRegionInference\\\") as Promise<boolean | undefined>,\\r\\n\\t\\t\\tthis.getGlobalState(\\\"vertexProjectId\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getGlobalState(\\\"vertexRegion\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getGlobalState(\\\"openAiBaseUrl\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getSecret(\\\"openAiApiKey\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getGlobalState(\\\"openAiModelId\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getGlobalState(\\\"ollamaModelId\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getGlobalState(\\\"ollamaBaseUrl\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getGlobalState(\\\"lmStudioModelId\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getGlobalState(\\\"lmStudioBaseUrl\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getGlobalState(\\\"anthropicBaseUrl\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getSecret(\\\"geminiApiKey\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getSecret(\\\"openAiNativeApiKey\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getSecret(\\\"deepSeekApiKey\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getGlobalState(\\\"azureApiVersion\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getGlobalState(\\\"openRouterModelId\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getGlobalState(\\\"openRouterModelInfo\\\") as Promise<ModelInfo | undefined>,\\r\\n\\t\\t\\tthis.getGlobalState(\\\"lastShownAnnouncementId\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getGlobalState(\\\"customInstructions\\\") as Promise<string | undefined>,\\r\\n\\t\\t\\tthis.getGlobalState(\\\"taskHistory\\\") as Promise<HistoryItem[] | undefined>,\\r\\n\\t\\t\\tthis.getGlobalState(\\\"autoApprovalSettings\\\") as Promise<AutoApprovalSettings | undefined>,\\r\\n\\t\\t])\\r\\n\\r\\n\\t\\tlet apiProvider: ApiProvider\\r\\n\\t\\tif (storedApiProvider) {\\r\\n\\t\\t\\tapiProvider = storedApiProvider\\r\\n\\t\\t} else {\\r\\n\\t\\t\\t// Either new user or legacy user that doesn't have the apiProvider stored in state\\r\\n\\t\\t\\t// (If they're using OpenRouter or Bedrock, then apiProvider state will exist)\\r\\n\\t\\t\\tif (apiKey) {\\r\\n\\t\\t\\t\\tapiProvider = \\\"anthropic\\\"\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t// New users should default to openrouter\\r\\n\\t\\t\\t\\tapiProvider = \\\"openrouter\\\"\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tapiConfiguration: {\\r\\n\\t\\t\\t\\tapiProvider,\\r\\n\\t\\t\\t\\tapiModelId,\\r\\n\\t\\t\\t\\tapiKey,\\r\\n\\t\\t\\t\\topenRouterApiKey,\\r\\n\\t\\t\\t\\tawsAccessKey,\\r\\n\\t\\t\\t\\tawsSecretKey,\\r\\n\\t\\t\\t\\tawsSessionToken,\\r\\n\\t\\t\\t\\tawsRegion,\\r\\n\\t\\t\\t\\tawsUseCrossRegionInference,\\r\\n\\t\\t\\t\\tvertexProjectId,\\r\\n\\t\\t\\t\\tvertexRegion,\\r\\n\\t\\t\\t\\topenAiBaseUrl,\\r\\n\\t\\t\\t\\topenAiApiKey,\\r\\n\\t\\t\\t\\topenAiModelId,\\r\\n\\t\\t\\t\\tollamaModelId,\\r\\n\\t\\t\\t\\tollamaBaseUrl,\\r\\n\\t\\t\\t\\tlmStudioModelId,\\r\\n\\t\\t\\t\\tlmStudioBaseUrl,\\r\\n\\t\\t\\t\\tanthropicBaseUrl,\\r\\n\\t\\t\\t\\tgeminiApiKey,\\r\\n\\t\\t\\t\\topenAiNativeApiKey,\\r\\n\\t\\t\\t\\tdeepSeekApiKey,\\r\\n\\t\\t\\t\\tazureApiVersion,\\r\\n\\t\\t\\t\\topenRouterModelId,\\r\\n\\t\\t\\t\\topenRouterModelInfo,\\r\\n\\t\\t\\t},\\r\\n\\t\\t\\tlastShownAnnouncementId,\\r\\n\\t\\t\\tcustomInstructions,\\r\\n\\t\\t\\ttaskHistory,\\r\\n\\t\\t\\tautoApprovalSettings: autoApprovalSettings || DEFAULT_AUTO_APPROVAL_SETTINGS, // default value can be 0 or empty string\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tasync updateTaskHistory(item: HistoryItem): Promise<HistoryItem[]> {\\r\\n\\t\\tconst history = ((await this.getGlobalState(\\\"taskHistory\\\")) as HistoryItem[]) || []\\r\\n\\t\\tconst existingItemIndex = history.findIndex((h) => h.id === item.id)\\r\\n\\t\\tif (existingItemIndex !== -1) {\\r\\n\\t\\t\\thistory[existingItemIndex] = item\\r\\n\\t\\t} else {\\r\\n\\t\\t\\thistory.push(item)\\r\\n\\t\\t}\\r\\n\\t\\tawait this.updateGlobalState(\\\"taskHistory\\\", history)\\r\\n\\t\\treturn history\\r\\n\\t}\\r\\n\\r\\n\\t// global\\r\\n\\r\\n\\tasync updateGlobalState(key: GlobalStateKey, value: any) {\\r\\n\\t\\tawait this.context.globalState.update(key, value)\\r\\n\\t}\\r\\n\\r\\n\\tasync getGlobalState(key: GlobalStateKey) {\\r\\n\\t\\treturn await this.context.globalState.get(key)\\r\\n\\t}\\r\\n\\r\\n\\t// workspace\\r\\n\\r\\n\\tprivate async updateWorkspaceState(key: string, value: any) {\\r\\n\\t\\tawait this.context.workspaceState.update(key, value)\\r\\n\\t}\\r\\n\\r\\n\\tprivate async getWorkspaceState(key: string) {\\r\\n\\t\\treturn await this.context.workspaceState.get(key)\\r\\n\\t}\\r\\n\\r\\n\\t// private async clearState() {\\r\\n\\t// \\tthis.context.workspaceState.keys().forEach((key) => {\\r\\n\\t// \\t\\tthis.context.workspaceState.update(key, undefined)\\r\\n\\t// \\t})\\r\\n\\t// \\tthis.context.globalState.keys().forEach((key) => {\\r\\n\\t// \\t\\tthis.context.globalState.update(key, undefined)\\r\\n\\t// \\t})\\r\\n\\t// \\tthis.context.secrets.delete(\\\"apiKey\\\")\\r\\n\\t// }\\r\\n\\r\\n\\t// secrets\\r\\n\\r\\n\\tprivate async storeSecret(key: SecretKey, value?: string) {\\r\\n\\t\\tif (value) {\\r\\n\\t\\t\\tawait this.context.secrets.store(key, value)\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tawait this.context.secrets.delete(key)\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tprivate async getSecret(key: SecretKey) {\\r\\n\\t\\treturn await this.context.secrets.get(key)\\r\\n\\t}\\r\\n\\r\\n\\t// dev\\r\\n\\r\\n\\tasync resetState() {\\r\\n\\t\\tvscode.window.showInformationMessage(\\\"Resetting state...\\\")\\r\\n\\t\\tfor (const key of this.context.globalState.keys()) {\\r\\n\\t\\t\\tawait this.context.globalState.update(key, undefined)\\r\\n\\t\\t}\\r\\n\\t\\tconst secretKeys: SecretKey[] = [\\r\\n\\t\\t\\t\\\"apiKey\\\",\\r\\n\\t\\t\\t\\\"openRouterApiKey\\\",\\r\\n\\t\\t\\t\\\"awsAccessKey\\\",\\r\\n\\t\\t\\t\\\"awsSecretKey\\\",\\r\\n\\t\\t\\t\\\"awsSessionToken\\\",\\r\\n\\t\\t\\t\\\"openAiApiKey\\\",\\r\\n\\t\\t\\t\\\"geminiApiKey\\\",\\r\\n\\t\\t\\t\\\"openAiNativeApiKey\\\",\\r\\n\\t\\t\\t\\\"deepSeekApiKey\\\",\\r\\n\\t\\t]\\r\\n\\t\\tfor (const key of secretKeys) {\\r\\n\\t\\t\\tawait this.storeSecret(key, undefined)\\r\\n\\t\\t}\\r\\n\\t\\tif (this.cline) {\\r\\n\\t\\t\\tthis.cline.abortTask()\\r\\n\\t\\t\\tthis.cline = undefined\\r\\n\\t\\t}\\r\\n\\t\\tvscode.window.showInformationMessage(\\\"State reset\\\")\\r\\n\\t\\tawait this.postStateToWebview()\\r\\n\\t\\tawait this.postMessageToWebview({\\r\\n\\t\\t\\ttype: \\\"action\\\",\\r\\n\\t\\t\\taction: \\\"chatButtonClicked\\\",\\r\\n\\t\\t})\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":45515,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.948Z\",\"createdTime\":\"2025-01-15T15:26:07.948Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":1179,\"nonEmptyLines\":1087,\"commentLines\":116,\"complexity\":179},\"dependencies\":[\"@anthropic-ai/sdk\",\"axios\",\"fs/promises\",\"os\",\"p-wait-for\",\"path\",\"vscode\",\"../../api\",\"../../integrations/misc/export-markdown\",\"../../integrations/misc/open-file\",\"../../integrations/misc/process-images\",\"../../integrations/theme/getTheme\",\"../../integrations/workspace/WorkspaceTracker\",\"../../services/mcp/McpHub\",\"../../shared/api\",\"../../shared/array\",\"../../shared/ExtensionMessage\",\"../../shared/HistoryItem\",\"../../shared/WebviewMessage\",\"../../utils/fs\",\"../Cline\",\"../mentions\",\"./getNonce\",\"./getUri\",\"../../shared/AutoApprovalSettings\"],\"quality\":{\"score\":-1707,\"issues\":[],\"duplicateLines\":339,\"longLines\":56,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.343Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":1179},\"analysis\":{\"metrics\":{\"lines\":1179,\"nonEmptyLines\":1087,\"commentLines\":116,\"complexity\":179},\"dependencies\":[\"@anthropic-ai/sdk\",\"axios\",\"fs/promises\",\"os\",\"p-wait-for\",\"path\",\"vscode\",\"../../api\",\"../../integrations/misc/export-markdown\",\"../../integrations/misc/open-file\",\"../../integrations/misc/process-images\",\"../../integrations/theme/getTheme\",\"../../integrations/workspace/WorkspaceTracker\",\"../../services/mcp/McpHub\",\"../../shared/api\",\"../../shared/array\",\"../../shared/ExtensionMessage\",\"../../shared/HistoryItem\",\"../../shared/WebviewMessage\",\"../../utils/fs\",\"../Cline\",\"../mentions\",\"./getNonce\",\"./getUri\",\"../../shared/AutoApprovalSettings\"],\"quality\":{\"score\":-1707,\"issues\":[],\"duplicateLines\":339,\"longLines\":56,\"complexFunctions\":0}},\"lastModified\":1737046855344,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.344Z\",\"size\":53192},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\core\\\\sliding-window\\\\index.ts\",\"content\":{\"content\":\"import { Anthropic } from \\\"@anthropic-ai/sdk\\\"\\r\\n\\r\\n/*\\r\\nWe can't implement a dynamically updating sliding window as it would break prompt cache\\r\\nevery time. To maintain the benefits of caching, we need to keep conversation history\\r\\nstatic. This operation should be performed as infrequently as possible. If a user reaches\\r\\na 200k context, we can assume that the first half is likely irrelevant to their current task.\\r\\nTherefore, this function should only be called when absolutely necessary to fit within\\r\\ncontext limits, not as a continuous process.\\r\\n*/\\r\\n// export function truncateHalfConversation(\\r\\n// \\tmessages: Anthropic.Messages.MessageParam[],\\r\\n// ): Anthropic.Messages.MessageParam[] {\\r\\n// \\t// 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.\\r\\n\\r\\n// \\t// Always keep the first Task message (this includes the project's file structure in environment_details)\\r\\n// \\tconst truncatedMessages = [messages[0]]\\r\\n\\r\\n// \\t// Remove half of user-assistant pairs\\r\\n// \\tconst messagesToRemove = Math.floor(messages.length / 4) * 2 // has to be even number\\r\\n\\r\\n// \\tconst remainingMessages = messages.slice(messagesToRemove + 1) // has to start with assistant message since tool result cannot follow assistant message with no tool use\\r\\n// \\ttruncatedMessages.push(...remainingMessages)\\r\\n\\r\\n// \\treturn truncatedMessages\\r\\n// }\\r\\n\\r\\n/*\\r\\ngetNextTruncationRange: Calculates the next range of messages to be \\\"deleted\\\"\\r\\n- Takes the full messages array and optional current deleted range\\r\\n- Always preserves the first message (task message)\\r\\n- Removes 1/2 of remaining messages (rounded down to even number) after current deleted range\\r\\n- Returns [startIndex, endIndex] representing inclusive range to delete\\r\\n\\r\\ngetTruncatedMessages: Constructs the truncated array using the deleted range\\r\\n- Takes full messages array and optional deleted range\\r\\n- Returns new array with messages in deleted range removed\\r\\n- Preserves order and structure of remaining messages\\r\\n\\r\\nThe range is represented as [startIndex, endIndex] where both indices are inclusive\\r\\nThe functions maintain the original array integrity while allowing progressive truncation \\r\\nthrough the deletedRange parameter\\r\\n\\r\\nUsage example:\\r\\nconst messages = [user1, assistant1, user2, assistant2, user3, assistant3];\\r\\nlet deletedRange = getNextTruncationRange(messages); // [1,2] (assistant1,user2)\\r\\nlet truncated = getTruncatedMessages(messages, deletedRange); \\r\\n// [user1, assistant2, user3, assistant3]\\r\\n\\r\\ndeletedRange = getNextTruncationRange(messages, deletedRange); // [2,3] (assistant2,user3) \\r\\ntruncated = getTruncatedMessages(messages, deletedRange);\\r\\n// [user1, assistant3]\\r\\n*/\\r\\n\\r\\nexport function getNextTruncationRange(\\r\\n\\tmessages: Anthropic.Messages.MessageParam[],\\r\\n\\tcurrentDeletedRange: [number, number] | undefined = undefined,\\r\\n): [number, number] {\\r\\n\\t// Since we always keep the first message, currentDeletedRange[0] will always be 1 (for now until we have a smarter truncation algorithm)\\r\\n\\tconst rangeStartIndex = 1\\r\\n\\tconst startOfRest = currentDeletedRange ? currentDeletedRange[1] + 1 : 1\\r\\n\\r\\n\\t// Remove half of user-assistant pairs\\r\\n\\tconst messagesToRemove = Math.floor((messages.length - startOfRest) / 4) * 2 // Keep even number\\r\\n\\tlet rangeEndIndex = startOfRest + messagesToRemove - 1\\r\\n\\r\\n\\t// Make sure the last message being removed is a user message, so that the next message after the initial task message is an assistant message. This preservers the user-assistant-user-assistant structure.\\r\\n\\t// NOTE: anthropic format messages are always user-assitant-user-assistant, while openai format messages can have multiple user messages in a row (we use anthropic format throughout cline)\\r\\n\\tif (messages[rangeEndIndex].role !== \\\"user\\\") {\\r\\n\\t\\trangeEndIndex -= 1\\r\\n\\t}\\r\\n\\r\\n\\t// this is an inclusive range that will be removed from the conversation history\\r\\n\\treturn [rangeStartIndex, rangeEndIndex]\\r\\n}\\r\\n\\r\\nexport function getTruncatedMessages(\\r\\n\\tmessages: Anthropic.Messages.MessageParam[],\\r\\n\\tdeletedRange: [number, number] | undefined,\\r\\n): Anthropic.Messages.MessageParam[] {\\r\\n\\tif (!deletedRange) {\\r\\n\\t\\treturn messages\\r\\n\\t}\\r\\n\\r\\n\\tconst [start, end] = deletedRange\\r\\n\\t// the range is inclusive - both start and end indices and everything in between will be removed from the final result.\\r\\n\\t// NOTE: if you try to console log these, don't forget that logging a reference to an array may not provide the same result as logging a slice() snapshot of that array at that exact moment. The following DOES in fact include the latest assistant message.\\r\\n\\treturn [...messages.slice(0, start), ...messages.slice(end + 1)]\\r\\n}\\r\\n\",\"metadata\":{\"size\":4658,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.947Z\",\"createdTime\":\"2025-01-15T15:26:07.947Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":90,\"nonEmptyLines\":73,\"commentLines\":23,\"complexity\":4},\"dependencies\":[\"@anthropic-ai/sdk\"],\"quality\":{\"score\":54,\"issues\":[],\"duplicateLines\":6,\"longLines\":8,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.341Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":90},\"analysis\":{\"metrics\":{\"lines\":90,\"nonEmptyLines\":73,\"commentLines\":23,\"complexity\":4},\"dependencies\":[\"@anthropic-ai/sdk\"],\"quality\":{\"score\":54,\"issues\":[],\"duplicateLines\":6,\"longLines\":8,\"complexFunctions\":0}},\"lastModified\":1737046855341,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.341Z\",\"size\":5351},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\core\\\\prompts\\\\system.ts\",\"content\":{\"content\":\"import defaultShell from \\\"default-shell\\\"\\r\\nimport os from \\\"os\\\"\\r\\nimport osName from \\\"os-name\\\"\\r\\nimport { McpHub } from \\\"../../services/mcp/McpHub\\\"\\r\\n\\r\\nexport const SYSTEM_PROMPT = async (\\r\\n\\tcwd: string,\\r\\n\\tsupportsComputerUse: boolean,\\r\\n\\tmcpHub: McpHub,\\r\\n) => `You are Cline, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.\\r\\n\\r\\n====\\r\\n\\r\\nTOOL USE\\r\\n\\r\\nYou have access to a set of tools that are executed upon the user's approval. You can use one tool per message, and will receive the result of that tool use in the user's response. You use tools step-by-step to accomplish a given task, with each tool use informed by the result of the previous tool use.\\r\\n\\r\\n# Tool Use Formatting\\r\\n\\r\\nTool use is formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:\\r\\n\\r\\n<tool_name>\\r\\n<parameter1_name>value1</parameter1_name>\\r\\n<parameter2_name>value2</parameter2_name>\\r\\n...\\r\\n</tool_name>\\r\\n\\r\\nFor example:\\r\\n\\r\\n<read_file>\\r\\n<path>src/main.js</path>\\r\\n</read_file>\\r\\n\\r\\nAlways adhere to this format for the tool use to ensure proper parsing and execution.\\r\\n\\r\\n# Tools\\r\\n\\r\\n## execute_command\\r\\nDescription: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: ${cwd.toPosix()}\\r\\nParameters:\\r\\n- command: (required) The CLI command to execute. This should be valid for the current operating system. Ensure the command is properly formatted and does not contain any harmful instructions.\\r\\n- requires_approval: (required) A boolean indicating whether this command requires explicit user approval before execution in case the user has auto-approve mode enabled. Set to 'true' for potentially impactful operations like installing/uninstalling packages, deleting/overwriting files, system configuration changes, network operations, or any commands that could have unintended side effects. Set to 'false' for safe operations like reading files/directories, running development servers, building projects, and other non-destructive operations.\\r\\nUsage:\\r\\n<execute_command>\\r\\n<command>Your command here</command>\\r\\n<requires_approval>true or false</requires_approval>\\r\\n</execute_command>\\r\\n\\r\\n## read_file\\r\\nDescription: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string.\\r\\nParameters:\\r\\n- path: (required) The path of the file to read (relative to the current working directory ${cwd.toPosix()})\\r\\nUsage:\\r\\n<read_file>\\r\\n<path>File path here</path>\\r\\n</read_file>\\r\\n\\r\\n## write_to_file\\r\\nDescription: Request to write content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. This tool will automatically create any directories needed to write the file.\\r\\nParameters:\\r\\n- path: (required) The path of the file to write to (relative to the current working directory ${cwd.toPosix()})\\r\\n- content: (required) The content to write to the file. ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified.\\r\\nUsage:\\r\\n<write_to_file>\\r\\n<path>File path here</path>\\r\\n<content>\\r\\nYour file content here\\r\\n</content>\\r\\n</write_to_file>\\r\\n\\r\\n## replace_in_file\\r\\nDescription: Request to replace sections of content in an existing file using SEARCH/REPLACE blocks that define exact changes to specific parts of the file. This tool should be used when you need to make targeted changes to specific parts of a file.\\r\\nParameters:\\r\\n- path: (required) The path of the file to modify (relative to the current working directory ${cwd.toPosix()})\\r\\n- diff: (required) One or more SEARCH/REPLACE blocks following this exact format:\\r\\n \\\\`\\\\`\\\\`\\r\\n <<<<<<< SEARCH\\r\\n [exact content to find]\\r\\n =======\\r\\n [new content to replace with]\\r\\n >>>>>>> REPLACE\\r\\n \\\\`\\\\`\\\\`\\r\\n Critical rules:\\r\\n 1. SEARCH content must match the associated file section to find EXACTLY:\\r\\n * Match character-for-character including whitespace, indentation, line endings\\r\\n * Include all comments, docstrings, etc.\\r\\n 2. SEARCH/REPLACE blocks will ONLY replace the first match occurrence.\\r\\n * Including multiple unique SEARCH/REPLACE blocks if you need to make multiple changes.\\r\\n * Include *just* enough lines in each SEARCH section to uniquely match each set of lines that need to change.\\r\\n * When using multiple SEARCH/REPLACE blocks, list them in the order they appear in the file.\\r\\n 3. Keep SEARCH/REPLACE blocks concise:\\r\\n * Break large SEARCH/REPLACE blocks into a series of smaller blocks that each change a small portion of the file.\\r\\n * Include just the changing lines, and a few surrounding lines if needed for uniqueness.\\r\\n * Do not include long runs of unchanging lines in SEARCH/REPLACE blocks.\\r\\n * Each line must be complete. Never truncate lines mid-way through as this can cause matching failures.\\r\\n 4. Special operations:\\r\\n * To move code: Use two SEARCH/REPLACE blocks (one to delete from original + one to insert at new location)\\r\\n * To delete code: Use empty REPLACE section\\r\\nUsage:\\r\\n<replace_in_file>\\r\\n<path>File path here</path>\\r\\n<diff>\\r\\nSearch and replace blocks here\\r\\n</diff>\\r\\n</replace_in_file>\\r\\n\\r\\n## search_files\\r\\nDescription: Request to perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.\\r\\nParameters:\\r\\n- path: (required) The path of the directory to search in (relative to the current working directory ${cwd.toPosix()}). This directory will be recursively searched.\\r\\n- regex: (required) The regular expression pattern to search for. Uses Rust regex syntax.\\r\\n- file_pattern: (optional) Glob pattern to filter files (e.g., '*.ts' for TypeScript files). If not provided, it will search all files (*).\\r\\nUsage:\\r\\n<search_files>\\r\\n<path>Directory path here</path>\\r\\n<regex>Your regex pattern here</regex>\\r\\n<file_pattern>file pattern here (optional)</file_pattern>\\r\\n</search_files>\\r\\n\\r\\n## list_files\\r\\nDescription: Request to list files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents. Do not use this tool to confirm the existence of files you may have created, as the user will let you know if the files were created successfully or not.\\r\\nParameters:\\r\\n- path: (required) The path of the directory to list contents for (relative to the current working directory ${cwd.toPosix()})\\r\\n- recursive: (optional) Whether to list files recursively. Use true for recursive listing, false or omit for top-level only.\\r\\nUsage:\\r\\n<list_files>\\r\\n<path>Directory path here</path>\\r\\n<recursive>true or false (optional)</recursive>\\r\\n</list_files>\\r\\n\\r\\n## list_code_definition_names\\r\\nDescription: Request to list definition names (classes, functions, methods, etc.) used in source code files at the top level of the specified directory. This tool provides insights into the codebase structure and important constructs, encapsulating high-level concepts and relationships that are crucial for understanding the overall architecture.\\r\\nParameters:\\r\\n- path: (required) The path of the directory (relative to the current working directory ${cwd.toPosix()}) to list top level source code definitions for.\\r\\nUsage:\\r\\n<list_code_definition_names>\\r\\n<path>Directory path here</path>\\r\\n</list_code_definition_names>${\\r\\n\\tsupportsComputerUse\\r\\n\\t\\t? `\\r\\n\\r\\n## browser_action\\r\\nDescription: Request to interact with a Puppeteer-controlled browser. Every action, except \\\\`close\\\\`, will be responded to with a screenshot of the browser's current state, along with any new console logs. You may only perform one browser action per message, and wait for the user's response including a screenshot and logs to determine the next action.\\r\\n- The sequence of actions **must always start with** launching the browser at a URL, and **must always end with** closing the browser. If you need to visit a new URL that is not possible to navigate to from the current webpage, you must first close the browser, then launch again at the new URL.\\r\\n- While the browser is active, only the \\\\`browser_action\\\\` tool can be used. No other tools should be called during this time. You may proceed to use other tools only after closing the browser. For example if you run into an error and need to fix a file, you must close the browser, then use other tools to make the necessary changes, then re-launch the browser to verify the result.\\r\\n- The browser window has a resolution of **900x600** pixels. When performing any click actions, ensure the coordinates are within this resolution range.\\r\\n- Before clicking on any elements such as icons, links, or buttons, you must consult the provided screenshot of the page to determine the coordinates of the element. The click should be targeted at the **center of the element**, not on its edges.\\r\\nParameters:\\r\\n- action: (required) The action to perform. The available actions are:\\r\\n * launch: Launch a new Puppeteer-controlled browser instance at the specified URL. This **must always be the first action**.\\r\\n - Use with the \\\\`url\\\\` parameter to provide the URL.\\r\\n - Ensure the URL is valid and includes the appropriate protocol (e.g. http://localhost:3000/page, file:///path/to/file.html, etc.)\\r\\n * click: Click at a specific x,y coordinate.\\r\\n - Use with the \\\\`coordinate\\\\` parameter to specify the location.\\r\\n - Always click in the center of an element (icon, button, link, etc.) based on coordinates derived from a screenshot.\\r\\n * type: Type a string of text on the keyboard. You might use this after clicking on a text field to input text.\\r\\n - Use with the \\\\`text\\\\` parameter to provide the string to type.\\r\\n * scroll_down: Scroll down the page by one page height.\\r\\n * scroll_up: Scroll up the page by one page height.\\r\\n * close: Close the Puppeteer-controlled browser instance. This **must always be the final browser action**.\\r\\n - Example: \\\\`<action>close</action>\\\\`\\r\\n- url: (optional) Use this for providing the URL for the \\\\`launch\\\\` action.\\r\\n * Example: <url>https://example.com</url>\\r\\n- coordinate: (optional) The X and Y coordinates for the \\\\`click\\\\` action. Coordinates should be within the **900x600** resolution.\\r\\n * Example: <coordinate>450,300</coordinate>\\r\\n- text: (optional) Use this for providing the text for the \\\\`type\\\\` action.\\r\\n * Example: <text>Hello, world!</text>\\r\\nUsage:\\r\\n<browser_action>\\r\\n<action>Action to perform (e.g., launch, click, type, scroll_down, scroll_up, close)</action>\\r\\n<url>URL to launch the browser at (optional)</url>\\r\\n<coordinate>x,y coordinates (optional)</coordinate>\\r\\n<text>Text to type (optional)</text>\\r\\n</browser_action>`\\r\\n\\t\\t: \\\"\\\"\\r\\n}\\r\\n\\r\\n## use_mcp_tool\\r\\nDescription: Request to use a tool provided by a connected MCP server. Each MCP server can provide multiple tools with different capabilities. Tools have defined input schemas that specify required and optional parameters.\\r\\nParameters:\\r\\n- server_name: (required) The name of the MCP server providing the tool\\r\\n- tool_name: (required) The name of the tool to execute\\r\\n- arguments: (required) A JSON object containing the tool's input parameters, following the tool's input schema\\r\\nUsage:\\r\\n<use_mcp_tool>\\r\\n<server_name>server name here</server_name>\\r\\n<tool_name>tool name here</tool_name>\\r\\n<arguments>\\r\\n{\\r\\n \\\"param1\\\": \\\"value1\\\",\\r\\n \\\"param2\\\": \\\"value2\\\"\\r\\n}\\r\\n</arguments>\\r\\n</use_mcp_tool>\\r\\n\\r\\n## access_mcp_resource\\r\\nDescription: Request to access a resource provided by a connected MCP server. Resources represent data sources that can be used as context, such as files, API responses, or system information.\\r\\nParameters:\\r\\n- server_name: (required) The name of the MCP server providing the resource\\r\\n- uri: (required) The URI identifying the specific resource to access\\r\\nUsage:\\r\\n<access_mcp_resource>\\r\\n<server_name>server name here</server_name>\\r\\n<uri>resource URI here</uri>\\r\\n</access_mcp_resource>\\r\\n\\r\\n## ask_followup_question\\r\\nDescription: Ask the user a question to gather additional information needed to complete the task. This tool should be used when you encounter ambiguities, need clarification, or require more details to proceed effectively. It allows for interactive problem-solving by enabling direct communication with the user. Use this tool judiciously to maintain a balance between gathering necessary information and avoiding excessive back-and-forth.\\r\\nParameters:\\r\\n- question: (required) The question to ask the user. This should be a clear, specific question that addresses the information you need.\\r\\nUsage:\\r\\n<ask_followup_question>\\r\\n<question>Your question here</question>\\r\\n</ask_followup_question>\\r\\n\\r\\n## attempt_completion\\r\\nDescription: After each tool use, the user will respond with the result of that tool use, i.e. if it succeeded or failed, along with any reasons for failure. Once you've received the results of tool uses and can confirm that the task is complete, use this tool to present the result of your work to the user. Optionally you may provide a CLI command to showcase the result of your work. The user may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.\\r\\nIMPORTANT NOTE: This tool CANNOT be used until you've confirmed from the user that any previous tool uses were successful. Failure to do so will result in code corruption and system failure. Before using this tool, you must ask yourself in <thinking></thinking> tags if you've confirmed from the user that any previous tool uses were successful. If not, then DO NOT use this tool.\\r\\nParameters:\\r\\n- result: (required) The result of the task. Formulate this result in a way that is final and does not require further input from the user. Don't end your result with questions or offers for further assistance.\\r\\n- command: (optional) A CLI command to execute to show a live demo of the result to the user. For example, use \\\\`open index.html\\\\` to display a created html website, or \\\\`open localhost:3000\\\\` to display a locally running development server. But DO NOT use commands like \\\\`echo\\\\` or \\\\`cat\\\\` that merely print text. This command should be valid for the current operating system. Ensure the command is properly formatted and does not contain any harmful instructions.\\r\\nUsage:\\r\\n<attempt_completion>\\r\\n<result>\\r\\nYour final result description here\\r\\n</result>\\r\\n<command>Command to demonstrate result (optional)</command>\\r\\n</attempt_completion>\\r\\n\\r\\n# Tool Use Examples\\r\\n\\r\\n## Example 1: Requesting to execute a command\\r\\n\\r\\n<execute_command>\\r\\n<command>npm run dev</command>\\r\\n<requires_approval>false</requires_approval>\\r\\n</execute_command>\\r\\n\\r\\n## Example 2: Requesting to use an MCP tool\\r\\n\\r\\n<use_mcp_tool>\\r\\n<server_name>weather-server</server_name>\\r\\n<tool_name>get_forecast</tool_name>\\r\\n<arguments>\\r\\n{\\r\\n \\\"city\\\": \\\"San Francisco\\\",\\r\\n \\\"days\\\": 5\\r\\n}\\r\\n</arguments>\\r\\n</use_mcp_tool>\\r\\n\\r\\n## Example 3: Requesting to access an MCP resource\\r\\n\\r\\n<access_mcp_resource>\\r\\n<server_name>weather-server</server_name>\\r\\n<uri>weather://san-francisco/current</uri>\\r\\n</access_mcp_resource>\\r\\n\\r\\n## Example 4: Requesting to create a new file\\r\\n\\r\\n<write_to_file>\\r\\n<path>src/frontend-config.json</path>\\r\\n<content>\\r\\n{\\r\\n \\\"apiEndpoint\\\": \\\"https://api.example.com\\\",\\r\\n \\\"theme\\\": {\\r\\n \\\"primaryColor\\\": \\\"#007bff\\\",\\r\\n \\\"secondaryColor\\\": \\\"#6c757d\\\",\\r\\n \\\"fontFamily\\\": \\\"Arial, sans-serif\\\"\\r\\n },\\r\\n \\\"features\\\": {\\r\\n \\\"darkMode\\\": true,\\r\\n \\\"notifications\\\": true,\\r\\n \\\"analytics\\\": false\\r\\n },\\r\\n \\\"version\\\": \\\"1.0.0\\\"\\r\\n}\\r\\n</content>\\r\\n</write_to_file>\\r\\n\\r\\n## Example 6: Requesting to make targeted edits to a file\\r\\n\\r\\n<replace_in_file>\\r\\n<path>src/components/App.tsx</path>\\r\\n<diff>\\r\\n<<<<<<< SEARCH\\r\\nimport React from 'react';\\r\\n=======\\r\\nimport React, { useState } from 'react';\\r\\n>>>>>>> REPLACE\\r\\n\\r\\n<<<<<<< SEARCH\\r\\nfunction handleSubmit() {\\r\\n saveData();\\r\\n setLoading(false);\\r\\n}\\r\\n\\r\\n=======\\r\\n>>>>>>> REPLACE\\r\\n\\r\\n<<<<<<< SEARCH\\r\\nreturn (\\r\\n <div>\\r\\n=======\\r\\nfunction handleSubmit() {\\r\\n saveData();\\r\\n setLoading(false);\\r\\n}\\r\\n\\r\\nreturn (\\r\\n <div>\\r\\n>>>>>>> REPLACE\\r\\n</diff>\\r\\n</replace_in_file>\\r\\n\\r\\n# Tool Use Guidelines\\r\\n\\r\\n1. In <thinking> tags, assess what information you already have and what information you need to proceed with the task.\\r\\n2. Choose the most appropriate tool based on the task and the tool descriptions provided. Assess if you need additional information to proceed, and which of the available tools would be most effective for gathering this information. For example using the list_files tool is more effective than running a command like \\\\`ls\\\\` in the terminal. It's critical that you think about each available tool and use the one that best fits the current step in the task.\\r\\n3. If multiple actions are needed, use one tool at a time per message to accomplish the task iteratively, with each tool use being informed by the result of the previous tool use. Do not assume the outcome of any tool use. Each step must be informed by the previous step's result.\\r\\n4. Formulate your tool use using the XML format specified for each tool.\\r\\n5. After each tool use, the user will respond with the result of that tool use. This result will provide you with the necessary information to continue your task or make further decisions. This response may include:\\r\\n - Information about whether the tool succeeded or failed, along with any reasons for failure.\\r\\n - Linter errors that may have arisen due to the changes you made, which you'll need to address.\\r\\n - New terminal output in reaction to the changes, which you may need to consider or act upon.\\r\\n - Any other relevant feedback or information related to the tool use.\\r\\n6. ALWAYS wait for user confirmation after each tool use before proceeding. Never assume the success of a tool use without explicit confirmation of the result from the user.\\r\\n\\r\\nIt is crucial to proceed step-by-step, waiting for the user's message after each tool use before moving forward with the task. This approach allows you to:\\r\\n1. Confirm the success of each step before proceeding.\\r\\n2. Address any issues or errors that arise immediately.\\r\\n3. Adapt your approach based on new information or unexpected results.\\r\\n4. Ensure that each action builds correctly on the previous ones.\\r\\n\\r\\nBy waiting for and carefully considering the user's response after each tool use, you can react accordingly and make informed decisions about how to proceed with the task. This iterative process helps ensure the overall success and accuracy of your work.\\r\\n\\r\\n====\\r\\n\\r\\nMCP SERVERS\\r\\n\\r\\nThe Model Context Protocol (MCP) enables communication between the system and locally running MCP servers that provide additional tools and resources to extend your capabilities.\\r\\n\\r\\n# Connected MCP Servers\\r\\n\\r\\nWhen a server is connected, you can use the server's tools via the \\\\`use_mcp_tool\\\\` tool, and access the server's resources via the \\\\`access_mcp_resource\\\\` tool.\\r\\n\\r\\n${\\r\\n\\tmcpHub.getServers().length > 0\\r\\n\\t\\t? `${mcpHub\\r\\n\\t\\t\\t\\t.getServers()\\r\\n\\t\\t\\t\\t.filter((server) => server.status === \\\"connected\\\")\\r\\n\\t\\t\\t\\t.map((server) => {\\r\\n\\t\\t\\t\\t\\tconst tools = server.tools\\r\\n\\t\\t\\t\\t\\t\\t?.map((tool) => {\\r\\n\\t\\t\\t\\t\\t\\t\\tconst schemaStr = tool.inputSchema\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t? ` Input Schema:\\r\\n ${JSON.stringify(tool.inputSchema, null, 2).split(\\\"\\\\n\\\").join(\\\"\\\\n \\\")}`\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t: \\\"\\\"\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\treturn `- ${tool.name}: ${tool.description}\\\\n${schemaStr}`\\r\\n\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t\\t.join(\\\"\\\\n\\\\n\\\")\\r\\n\\r\\n\\t\\t\\t\\t\\tconst templates = server.resourceTemplates\\r\\n\\t\\t\\t\\t\\t\\t?.map((template) => `- ${template.uriTemplate} (${template.name}): ${template.description}`)\\r\\n\\t\\t\\t\\t\\t\\t.join(\\\"\\\\n\\\")\\r\\n\\r\\n\\t\\t\\t\\t\\tconst resources = server.resources\\r\\n\\t\\t\\t\\t\\t\\t?.map((resource) => `- ${resource.uri} (${resource.name}): ${resource.description}`)\\r\\n\\t\\t\\t\\t\\t\\t.join(\\\"\\\\n\\\")\\r\\n\\r\\n\\t\\t\\t\\t\\tconst config = JSON.parse(server.config)\\r\\n\\r\\n\\t\\t\\t\\t\\treturn (\\r\\n\\t\\t\\t\\t\\t\\t`## ${server.name} (\\\\`${config.command}${config.args && Array.isArray(config.args) ? ` ${config.args.join(\\\" \\\")}` : \\\"\\\"}\\\\`)` +\\r\\n\\t\\t\\t\\t\\t\\t(tools ? `\\\\n\\\\n### Available Tools\\\\n${tools}` : \\\"\\\") +\\r\\n\\t\\t\\t\\t\\t\\t(templates ? `\\\\n\\\\n### Resource Templates\\\\n${templates}` : \\\"\\\") +\\r\\n\\t\\t\\t\\t\\t\\t(resources ? `\\\\n\\\\n### Direct Resources\\\\n${resources}` : \\\"\\\")\\r\\n\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t.join(\\\"\\\\n\\\\n\\\")}`\\r\\n\\t\\t: \\\"(No MCP servers currently connected)\\\"\\r\\n}\\r\\n\\r\\n## Creating an MCP Server\\r\\n\\r\\nThe user may ask you something along the lines of \\\"add a tool\\\" that does some function, in other words to create an MCP server that provides tools and resources that may connect to external APIs for example. You have the ability to create an MCP server and add it to a configuration file that will then expose the tools and resources for you to use with \\\\`use_mcp_tool\\\\` and \\\\`access_mcp_resource\\\\`.\\r\\n\\r\\nWhen creating MCP servers, it's important to understand that they operate in a non-interactive environment. The server cannot initiate OAuth flows, open browser windows, or prompt for user input during runtime. All credentials and authentication tokens must be provided upfront through environment variables in the MCP settings configuration. For example, Spotify's API uses OAuth to get a refresh token for the user, but the MCP server cannot initiate this flow. While you can walk the user through obtaining an application client ID and secret, you may have to create a separate one-time setup script (like get-refresh-token.js) that captures and logs the final piece of the puzzle: the user's refresh token (i.e. you might run the script using execute_command which would open a browser for authentication, and then log the refresh token so that you can see it in the command output for you to use in the MCP settings configuration).\\r\\n\\r\\nUnless the user specifies otherwise, new MCP servers should be created in: ${await mcpHub.getMcpServersPath()}\\r\\n\\r\\n### Example MCP Server\\r\\n\\r\\nFor example, if the user wanted to give you the ability to retrieve weather information, you could create an MCP server that uses the OpenWeather API to get weather information, add it to the MCP settings configuration file, and then notice that you now have access to new tools and resources in the system prompt that you might use to show the user your new capabilities.\\r\\n\\r\\nThe following example demonstrates how to build an MCP server that provides weather data functionality. While this example shows how to implement resources, resource templates, and tools, in practice you should prefer using tools since they are more flexible and can handle dynamic parameters. The resource and resource template implementations are included here mainly for demonstration purposes of the different MCP capabilities, but a real weather server would likely just expose tools for fetching weather data. (The following steps are for macOS)\\r\\n\\r\\n1. Use the \\\\`create-typescript-server\\\\` tool to bootstrap a new project in the default MCP servers directory:\\r\\n\\r\\n\\\\`\\\\`\\\\`bash\\r\\ncd ${await mcpHub.getMcpServersPath()}\\r\\nnpx @modelcontextprotocol/create-server weather-server\\r\\ncd weather-server\\r\\n# Install dependencies\\r\\nnpm install axios\\r\\n\\\\`\\\\`\\\\`\\r\\n\\r\\nThis will create a new project with the following structure:\\r\\n\\r\\n\\\\`\\\\`\\\\`\\r\\nweather-server/\\r\\n ├── package.json\\r\\n {\\r\\n ...\\r\\n \\\"type\\\": \\\"module\\\", // added by default, uses ES module syntax (import/export) rather than CommonJS (require/module.exports) (Important to know if you create additional scripts in this server repository like a get-refresh-token.js script)\\r\\n \\\"scripts\\\": {\\r\\n \\\"build\\\": \\\"tsc && node -e \\\\\\\"require('fs').chmodSync('build/index.js', '755')\\\\\\\"\\\",\\r\\n ...\\r\\n }\\r\\n ...\\r\\n }\\r\\n ├── tsconfig.json\\r\\n └── src/\\r\\n └── weather-server/\\r\\n └── index.ts # Main server implementation\\r\\n\\\\`\\\\`\\\\`\\r\\n\\r\\n2. Replace \\\\`src/index.ts\\\\` with the following:\\r\\n\\r\\n\\\\`\\\\`\\\\`typescript\\r\\n#!/usr/bin/env node\\r\\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\\r\\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\\r\\nimport {\\r\\n CallToolRequestSchema,\\r\\n ErrorCode,\\r\\n ListResourcesRequestSchema,\\r\\n ListResourceTemplatesRequestSchema,\\r\\n ListToolsRequestSchema,\\r\\n McpError,\\r\\n ReadResourceRequestSchema,\\r\\n} from '@modelcontextprotocol/sdk/types.js';\\r\\nimport axios from 'axios';\\r\\n\\r\\nconst API_KEY = process.env.OPENWEATHER_API_KEY; // provided by MCP config\\r\\nif (!API_KEY) {\\r\\n throw new Error('OPENWEATHER_API_KEY environment variable is required');\\r\\n}\\r\\n\\r\\ninterface OpenWeatherResponse {\\r\\n main: {\\r\\n temp: number;\\r\\n humidity: number;\\r\\n };\\r\\n weather: [{ description: string }];\\r\\n wind: { speed: number };\\r\\n dt_txt?: string;\\r\\n}\\r\\n\\r\\nconst isValidForecastArgs = (\\r\\n args: any\\r\\n): args is { city: string; days?: number } =>\\r\\n typeof args === 'object' &&\\r\\n args !== null &&\\r\\n typeof args.city === 'string' &&\\r\\n (args.days === undefined || typeof args.days === 'number');\\r\\n\\r\\nclass WeatherServer {\\r\\n private server: Server;\\r\\n private axiosInstance;\\r\\n\\r\\n constructor() {\\r\\n this.server = new Server(\\r\\n {\\r\\n name: 'example-weather-server',\\r\\n version: '0.1.0',\\r\\n },\\r\\n {\\r\\n capabilities: {\\r\\n resources: {},\\r\\n tools: {},\\r\\n },\\r\\n }\\r\\n );\\r\\n\\r\\n this.axiosInstance = axios.create({\\r\\n baseURL: 'http://api.openweathermap.org/data/2.5',\\r\\n params: {\\r\\n appid: API_KEY,\\r\\n units: 'metric',\\r\\n },\\r\\n });\\r\\n\\r\\n this.setupResourceHandlers();\\r\\n this.setupToolHandlers();\\r\\n \\r\\n // Error handling\\r\\n this.server.onerror = (error) => console.error('[MCP Error]', error);\\r\\n process.on('SIGINT', async () => {\\r\\n await this.server.close();\\r\\n process.exit(0);\\r\\n });\\r\\n }\\r\\n\\r\\n // MCP Resources represent any kind of UTF-8 encoded data that an MCP server wants to make available to clients, such as database records, API responses, log files, and more. Servers define direct resources with a static URI or dynamic resources with a URI template that follows the format \\\\`[protocol]://[host]/[path]\\\\`.\\r\\n private setupResourceHandlers() {\\r\\n // For static resources, servers can expose a list of resources:\\r\\n this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({\\r\\n resources: [\\r\\n // This is a poor example since you could use the resource template to get the same information but this demonstrates how to define a static resource\\r\\n {\\r\\n uri: \\\\`weather://San Francisco/current\\\\`, // Unique identifier for San Francisco weather resource\\r\\n name: \\\\`Current weather in San Francisco\\\\`, // Human-readable name\\r\\n mimeType: 'application/json', // Optional MIME type\\r\\n // Optional description\\r\\n description:\\r\\n 'Real-time weather data for San Francisco including temperature, conditions, humidity, and wind speed',\\r\\n },\\r\\n ],\\r\\n }));\\r\\n\\r\\n // For dynamic resources, servers can expose resource templates:\\r\\n this.server.setRequestHandler(\\r\\n ListResourceTemplatesRequestSchema,\\r\\n async () => ({\\r\\n resourceTemplates: [\\r\\n {\\r\\n uriTemplate: 'weather://{city}/current', // URI template (RFC 6570)\\r\\n name: 'Current weather for a given city', // Human-readable name\\r\\n mimeType: 'application/json', // Optional MIME type\\r\\n description: 'Real-time weather data for a specified city', // Optional description\\r\\n },\\r\\n ],\\r\\n })\\r\\n );\\r\\n\\r\\n // ReadResourceRequestSchema is used for both static resources and dynamic resource templates\\r\\n this.server.setRequestHandler(\\r\\n ReadResourceRequestSchema,\\r\\n async (request) => {\\r\\n const match = request.params.uri.match(\\r\\n /^weather:\\\\/\\\\/([^/]+)\\\\/current$/\\r\\n );\\r\\n if (!match) {\\r\\n throw new McpError(\\r\\n ErrorCode.InvalidRequest,\\r\\n \\\\`Invalid URI format: \\\\${request.params.uri}\\\\`\\r\\n );\\r\\n }\\r\\n const city = decodeURIComponent(match[1]);\\r\\n\\r\\n try {\\r\\n const response = await this.axiosInstance.get(\\r\\n 'weather', // current weather\\r\\n {\\r\\n params: { q: city },\\r\\n }\\r\\n );\\r\\n\\r\\n return {\\r\\n contents: [\\r\\n {\\r\\n uri: request.params.uri,\\r\\n mimeType: 'application/json',\\r\\n text: JSON.stringify(\\r\\n {\\r\\n temperature: response.data.main.temp,\\r\\n conditions: response.data.weather[0].description,\\r\\n humidity: response.data.main.humidity,\\r\\n wind_speed: response.data.wind.speed,\\r\\n timestamp: new Date().toISOString(),\\r\\n },\\r\\n null,\\r\\n 2\\r\\n ),\\r\\n },\\r\\n ],\\r\\n };\\r\\n } catch (error) {\\r\\n if (axios.isAxiosError(error)) {\\r\\n throw new McpError(\\r\\n ErrorCode.InternalError,\\r\\n \\\\`Weather API error: \\\\${\\r\\n error.response?.data.message ?? error.message\\r\\n }\\\\`\\r\\n );\\r\\n }\\r\\n throw error;\\r\\n }\\r\\n }\\r\\n );\\r\\n }\\r\\n\\r\\n /* MCP Tools enable servers to expose executable functionality to the system. Through these tools, you can interact with external systems, perform computations, and take actions in the real world.\\r\\n * - Like resources, tools are identified by unique names and can include descriptions to guide their usage. However, unlike resources, tools represent dynamic operations that can modify state or interact with external systems.\\r\\n * - While resources and tools are similar, you should prefer to create tools over resources when possible as they provide more flexibility.\\r\\n */\\r\\n private setupToolHandlers() {\\r\\n this.server.setRequestHandler(ListToolsRequestSchema, async () => ({\\r\\n tools: [\\r\\n {\\r\\n name: 'get_forecast', // Unique identifier\\r\\n description: 'Get weather forecast for a city', // Human-readable description\\r\\n inputSchema: {\\r\\n // JSON Schema for parameters\\r\\n type: 'object',\\r\\n properties: {\\r\\n city: {\\r\\n type: 'string',\\r\\n description: 'City name',\\r\\n },\\r\\n days: {\\r\\n type: 'number',\\r\\n description: 'Number of days (1-5)',\\r\\n minimum: 1,\\r\\n maximum: 5,\\r\\n },\\r\\n },\\r\\n required: ['city'], // Array of required property names\\r\\n },\\r\\n },\\r\\n ],\\r\\n }));\\r\\n\\r\\n this.server.setRequestHandler(CallToolRequestSchema, async (request) => {\\r\\n if (request.params.name !== 'get_forecast') {\\r\\n throw new McpError(\\r\\n ErrorCode.MethodNotFound,\\r\\n \\\\`Unknown tool: \\\\${request.params.name}\\\\`\\r\\n );\\r\\n }\\r\\n\\r\\n if (!isValidForecastArgs(request.params.arguments)) {\\r\\n throw new McpError(\\r\\n ErrorCode.InvalidParams,\\r\\n 'Invalid forecast arguments'\\r\\n );\\r\\n }\\r\\n\\r\\n const city = request.params.arguments.city;\\r\\n const days = Math.min(request.params.arguments.days || 3, 5);\\r\\n\\r\\n try {\\r\\n const response = await this.axiosInstance.get<{\\r\\n list: OpenWeatherResponse[];\\r\\n }>('forecast', {\\r\\n params: {\\r\\n q: city,\\r\\n cnt: days * 8,\\r\\n },\\r\\n });\\r\\n\\r\\n return {\\r\\n content: [\\r\\n {\\r\\n type: 'text',\\r\\n text: JSON.stringify(response.data.list, null, 2),\\r\\n },\\r\\n ],\\r\\n };\\r\\n } catch (error) {\\r\\n if (axios.isAxiosError(error)) {\\r\\n return {\\r\\n content: [\\r\\n {\\r\\n type: 'text',\\r\\n text: \\\\`Weather API error: \\\\${\\r\\n error.response?.data.message ?? error.message\\r\\n }\\\\`,\\r\\n },\\r\\n ],\\r\\n isError: true,\\r\\n };\\r\\n }\\r\\n throw error;\\r\\n }\\r\\n });\\r\\n }\\r\\n\\r\\n async run() {\\r\\n const transport = new StdioServerTransport();\\r\\n await this.server.connect(transport);\\r\\n console.error('Weather MCP server running on stdio');\\r\\n }\\r\\n}\\r\\n\\r\\nconst server = new WeatherServer();\\r\\nserver.run().catch(console.error);\\r\\n\\\\`\\\\`\\\\`\\r\\n\\r\\n(Remember: This is just an example–you may use different dependencies, break the implementation up into multiple files, etc.)\\r\\n\\r\\n3. Build and compile the executable JavaScript file\\r\\n\\r\\n\\\\`\\\\`\\\\`bash\\r\\nnpm run build\\r\\n\\\\`\\\\`\\\\`\\r\\n\\r\\n4. Whenever you need an environment variable such as an API key to configure the MCP server, walk the user through the process of getting the key. For example, they may need to create an account and go to a developer dashboard to generate the key. Provide step-by-step instructions and URLs to make it easy for the user to retrieve the necessary information. Then use the ask_followup_question tool to ask the user for the key, in this case the OpenWeather API key.\\r\\n\\r\\n5. Install the MCP Server by adding the MCP server configuration to the settings file located at '${await mcpHub.getMcpSettingsFilePath()}'. The settings file may have other MCP servers already configured, so you would read it first and then add your new server to the existing \\\\`mcpServers\\\\` object.\\r\\n\\r\\n\\\\`\\\\`\\\\`json\\r\\n{\\r\\n \\\"mcpServers\\\": {\\r\\n ...,\\r\\n \\\"weather\\\": {\\r\\n \\\"command\\\": \\\"node\\\",\\r\\n \\\"args\\\": [\\\"/path/to/weather-server/build/index.js\\\"],\\r\\n \\\"env\\\": {\\r\\n \\\"OPENWEATHER_API_KEY\\\": \\\"user-provided-api-key\\\"\\r\\n }\\r\\n },\\r\\n }\\r\\n}\\r\\n\\\\`\\\\`\\\\`\\r\\n\\r\\n(Note: the user may also ask you to install the MCP server to the Claude desktop app, in which case you would read then modify \\\\`~/Library/Application\\\\ Support/Claude/claude_desktop_config.json\\\\` on macOS for example. It follows the same format of a top level \\\\`mcpServers\\\\` object.)\\r\\n\\r\\n6. After you have edited the MCP settings configuration file, the system will automatically run all the servers and expose the available tools and resources in the 'Connected MCP Servers' section.\\r\\n\\r\\n7. Now that you have access to these new tools and resources, you may suggest ways the user can command you to invoke them - for example, with this new weather tool now available, you can invite the user to ask \\\"what's the weather in San Francisco?\\\"\\r\\n\\r\\n## Editing MCP Servers\\r\\n\\r\\nThe user may ask to add tools or resources that may make sense to add to an existing MCP server (listed under 'Connected MCP Servers' above: ${\\r\\n\\tmcpHub\\r\\n\\t\\t.getServers()\\r\\n\\t\\t.map((server) => server.name)\\r\\n\\t\\t.join(\\\", \\\") || \\\"(None running currently)\\\"\\r\\n}, e.g. if it would use the same API. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath. You might then use list_files and read_file to explore the files in the repository, and use replace_in_file to make changes to the files.\\r\\n\\r\\nHowever some MCP servers may be running from installed packages rather than a local repository, in which case it may make more sense to create a new MCP server.\\r\\n\\r\\n# MCP Servers Are Not Always Necessary\\r\\n\\r\\nThe user may not always request the use or creation of MCP servers. Instead, they might provide tasks that can be completed with existing tools. While using the MCP SDK to extend your capabilities can be useful, it's important to understand that this is just one specialized type of task you can accomplish. You should only implement MCP servers when the user explicitly requests it (e.g., \\\"add a tool that...\\\").\\r\\n\\r\\nRemember: The MCP documentation and example provided above are to help you understand and work with existing MCP servers or create new ones when requested by the user. You already have access to tools and capabilities that can be used to accomplish a wide range of tasks.\\r\\n\\r\\n====\\r\\n\\r\\nEDITING FILES\\r\\n\\r\\nYou have access to two tools for working with files: **write_to_file** and **replace_in_file**. Understanding their roles and selecting the right one for the job will help ensure efficient and accurate modifications.\\r\\n\\r\\n# write_to_file\\r\\n\\r\\n## Purpose\\r\\n\\r\\n- Create a new file, or overwrite the entire contents of an existing file.\\r\\n\\r\\n## When to Use\\r\\n\\r\\n- Initial file creation, such as when scaffolding a new project. \\r\\n- Overwriting large boilerplate files where you want to replace the entire content at once.\\r\\n- When the complexity or number of changes would make replace_in_file unwieldy or error-prone.\\r\\n- When you need to completely restructure a file's content or change its fundamental organization.\\r\\n\\r\\n## Important Considerations\\r\\n\\r\\n- Using write_to_file requires providing the file’s complete final content. \\r\\n- If you only need to make small changes to an existing file, consider using replace_in_file instead to avoid unnecessarily rewriting the entire file.\\r\\n- While write_to_file should not be your default choice, don't hesitate to use it when the situation truly calls for it.\\r\\n\\r\\n# replace_in_file\\r\\n\\r\\n## Purpose\\r\\n\\r\\n- Make targeted edits to specific parts of an existing file without overwriting the entire file.\\r\\n\\r\\n## When to Use\\r\\n\\r\\n- Small, localized changes like updating a few lines, function implementations, changing variable names, modifying a section of text, etc.\\r\\n- Targeted improvements where only specific portions of the file’s content needs to be altered.\\r\\n- Especially useful for long files where much of the file will remain unchanged.\\r\\n\\r\\n## Advantages\\r\\n\\r\\n- More efficient for minor edits, since you don’t need to supply the entire file content. \\r\\n- Reduces the chance of errors that can occur when overwriting large files.\\r\\n\\r\\n# Choosing the Appropriate Tool\\r\\n\\r\\n- **Default to replace_in_file** for most changes. It's the safer, more precise option that minimizes potential issues.\\r\\n- **Use write_to_file** when:\\r\\n - Creating new files\\r\\n - The changes are so extensive that using replace_in_file would be more complex or risky\\r\\n - You need to completely reorganize or restructure a file\\r\\n - The file is relatively small and the changes affect most of its content\\r\\n - You're generating boilerplate or template files\\r\\n\\r\\n# Auto-formatting Considerations\\r\\n\\r\\n- After using either write_to_file or replace_in_file, the user's editor may automatically format the file\\r\\n- This auto-formatting may modify the file contents, for example:\\r\\n - Breaking single lines into multiple lines\\r\\n - Adjusting indentation to match project style (e.g. 2 spaces vs 4 spaces vs tabs)\\r\\n - Converting single quotes to double quotes (or vice versa based on project preferences)\\r\\n - Organizing imports (e.g. sorting, grouping by type)\\r\\n - Adding/removing trailing commas in objects and arrays\\r\\n - Enforcing consistent brace style (e.g. same-line vs new-line)\\r\\n - Standardizing semicolon usage (adding or removing based on style)\\r\\n- The write_to_file and replace_in_file tool responses will include the final state of the file after any auto-formatting\\r\\n- Use this final state as your reference point for any subsequent edits. This is ESPECIALLY important when crafting SEARCH blocks for replace_in_file which require the content to match what's in the file exactly.\\r\\n\\r\\n# Workflow Tips\\r\\n\\r\\n1. Before editing, assess the scope of your changes and decide which tool to use.\\r\\n2. For targeted edits, apply replace_in_file with carefully crafted SEARCH/REPLACE blocks. If you need multiple changes, you can stack multiple SEARCH/REPLACE blocks within a single replace_in_file call.\\r\\n3. For major overhauls or initial file creation, rely on write_to_file.\\r\\n4. Once the file has been edited with either write_to_file or replace_in_file, the system will provide you with the final state of the modified file. Use this updated content as the reference point for any subsequent SEARCH/REPLACE operations, since it reflects any auto-formatting or user-applied changes.\\r\\n\\r\\nBy thoughtfully selecting between write_to_file and replace_in_file, you can make your file editing process smoother, safer, and more efficient.\\r\\n\\r\\n====\\r\\n \\r\\nCAPABILITIES\\r\\n\\r\\n- You have access to tools that let you execute CLI commands on the user's computer, list files, view source code definitions, regex search${\\r\\n\\tsupportsComputerUse ? \\\", use the browser\\\" : \\\"\\\"\\r\\n}, read and edit files, and ask follow-up questions. These tools help you effectively accomplish a wide range of tasks, such as writing code, making edits or improvements to existing files, understanding the current state of a project, performing system operations, and much more.\\r\\n- When the user initially gives you a task, a recursive list of all filepaths in the current working directory ('${cwd.toPosix()}') will be included in environment_details. This provides an overview of the project's file structure, offering key insights into the project from directory/file names (how developers conceptualize and organize their code) and file extensions (the language used). This can also guide decision-making on which files to explore further. If you need to further explore directories such as outside the current working directory, you can use the list_files tool. If you pass 'true' for the recursive parameter, it will list files recursively. Otherwise, it will list files at the top level, which is better suited for generic directories where you don't necessarily need the nested structure, like the Desktop.\\r\\n- You can use search_files to perform regex searches across files in a specified directory, outputting context-rich results that include surrounding lines. This is particularly useful for understanding code patterns, finding specific implementations, or identifying areas that need refactoring.\\r\\n- You can use the list_code_definition_names tool to get an overview of source code definitions for all files at the top level of a specified directory. This can be particularly useful when you need to understand the broader context and relationships between certain parts of the code. You may need to call this tool multiple times to understand various parts of the codebase related to the task.\\r\\n\\t- For example, when asked to make edits or improvements you might analyze the file structure in the initial environment_details to get an overview of the project, then use list_code_definition_names to get further insight using source code definitions for files located in relevant directories, then read_file to examine the contents of relevant files, analyze the code and suggest improvements or make necessary edits, then use the replace_in_file tool to implement changes. If you refactored code that could affect other parts of the codebase, you could use search_files to ensure you update other files as needed.\\r\\n- You can use the execute_command tool to run commands on the user's computer whenever you feel it can help accomplish the user's task. When you need to execute a CLI command, you must provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, since they are more flexible and easier to run. Interactive and long-running commands are allowed, since the commands are run in the user's VSCode terminal. The user may keep commands running in the background and you will be kept updated on their status along the way. Each command you execute is run in a new terminal instance.${\\r\\n\\tsupportsComputerUse\\r\\n\\t\\t? \\\"\\\\n- You can use the browser_action tool to interact with websites (including html files and locally running development servers) through a Puppeteer-controlled browser when you feel it is necessary in accomplishing the user's task. This tool is particularly useful for web development tasks as it allows you to launch a browser, navigate to pages, interact with elements through clicks and keyboard input, and capture the results through screenshots and console logs. This tool may be useful at key stages of web development tasks-such as after implementing new features, making substantial changes, when troubleshooting issues, or to verify the result of your work. You can analyze the provided screenshots to ensure correct rendering or identify errors, and review console logs for runtime issues.\\\\n\\t- For example, if asked to add a component to a react website, you might create the necessary files, use execute_command to run the site locally, then use browser_action to launch the browser, navigate to the local server, and verify the component renders & functions correctly before closing the browser.\\\"\\r\\n\\t\\t: \\\"\\\"\\r\\n}\\r\\n- You have access to MCP servers that may provide additional tools and resources. Each server may provide different capabilities that you can use to accomplish tasks more effectively.\\r\\n\\r\\n====\\r\\n\\r\\nRULES\\r\\n\\r\\n- Your current working directory is: ${cwd.toPosix()}\\r\\n- You cannot \\\\`cd\\\\` into a different directory to complete a task. You are stuck operating from '${cwd.toPosix()}', so be sure to pass in the correct 'path' parameter when using tools that require a path.\\r\\n- Do not use the ~ character or $HOME to refer to the home directory.\\r\\n- Before using the execute_command tool, you must first think about the SYSTEM INFORMATION context provided to understand the user's environment and tailor your commands to ensure they are compatible with their system. You must also consider if the command you need to run should be executed in a specific directory outside of the current working directory '${cwd.toPosix()}', and if so prepend with \\\\`cd\\\\`'ing into that directory && then executing the command (as one command since you are stuck operating from '${cwd.toPosix()}'). For example, if you needed to run \\\\`npm install\\\\` in a project outside of '${cwd.toPosix()}', you would need to prepend with a \\\\`cd\\\\` i.e. pseudocode for this would be \\\\`cd (path to project) && (command, in this case npm install)\\\\`.\\r\\n- When using the search_files tool, craft your regex patterns carefully to balance specificity and flexibility. Based on the user's task you may use it to find code patterns, TODO comments, function definitions, or any text-based information across the project. The results include context, so analyze the surrounding code to better understand the matches. Leverage the search_files tool in combination with other tools for more comprehensive analysis. For example, use it to find specific code patterns, then use read_file to examine the full context of interesting matches before using replace_in_file to make informed changes.\\r\\n- When creating a new project (such as an app, website, or any software project), organize all new files within a dedicated project directory unless the user specifies otherwise. Use appropriate file paths when creating files, as the write_to_file tool will automatically create any necessary directories. Structure the project logically, adhering to best practices for the specific type of project being created. Unless otherwise specified, new projects should be easily run without additional setup, for example most projects can be built in HTML, CSS, and JavaScript - which you can open in a browser.\\r\\n- Be sure to consider the type of project (e.g. Python, JavaScript, web application) when determining the appropriate structure and files to include. Also consider what files may be most relevant to accomplishing the task, for example looking at a project's manifest file would help you understand the project's dependencies, which you could incorporate into any code you write.\\r\\n- When making changes to code, always consider the context in which the code is being used. Ensure that your changes are compatible with the existing codebase and that they follow the project's coding standards and best practices.\\r\\n- When you want to modify a file, use the replace_in_file or write_to_file tool directly with the desired changes. You do not need to display the changes before using the tool.\\r\\n- Do not ask for more information than necessary. Use the tools provided to accomplish the user's request efficiently and effectively. When you've completed your task, you must use the attempt_completion tool to present the result to the user. The user may provide feedback, which you can use to make improvements and try again.\\r\\n- You are only allowed to ask the user questions using the ask_followup_question tool. Use this tool only when you need additional details to complete a task, and be sure to use a clear and concise question that will help you move forward with the task. However if you can use the available tools to avoid having to ask the user questions, you should do so. For example, if the user mentions a file that may be in an outside directory like the Desktop, you should use the list_files tool to list the files in the Desktop and check if the file they are talking about is there, rather than asking the user to provide the file path themselves.\\r\\n- When executing commands, if you don't see the expected output, assume the terminal executed the command successfully and proceed with the task. The user's terminal may be unable to stream the output back properly. If you absolutely need to see the actual terminal output, use the ask_followup_question tool to request the user to copy and paste it back to you.\\r\\n- The user may provide a file's contents directly in their message, in which case you shouldn't use the read_file tool to get the file contents again since you already have it.\\r\\n- Your goal is to try to accomplish the user's task, NOT engage in a back and forth conversation.${\\r\\n\\tsupportsComputerUse\\r\\n\\t\\t? '\\\\n- The user may ask generic non-development tasks, such as \\\"what\\\\'s the latest news\\\" or \\\"look up the weather in San Diego\\\", in which case you might use the browser_action tool to complete the task if it makes sense to do so, rather than trying to create a website or using curl to answer the question. However, if an available MCP server tool or resource can be used instead, you should prefer to use it over browser_action.'\\r\\n\\t\\t: \\\"\\\"\\r\\n}\\r\\n- NEVER end attempt_completion result with a question or request to engage in further conversation! Formulate the end of your result in a way that is final and does not require further input from the user.\\r\\n- You are STRICTLY FORBIDDEN from starting your messages with \\\"Great\\\", \\\"Certainly\\\", \\\"Okay\\\", \\\"Sure\\\". You should NOT be conversational in your responses, but rather direct and to the point. For example you should NOT say \\\"Great, I've updated the CSS\\\" but instead something like \\\"I've updated the CSS\\\". It is important you be clear and technical in your messages.\\r\\n- When presented with images, utilize your vision capabilities to thoroughly examine them and extract meaningful information. Incorporate these insights into your thought process as you accomplish the user's task.\\r\\n- At the end of each user message, you will automatically receive environment_details. This information is not written by the user themselves, but is auto-generated to provide potentially relevant context about the project structure and environment. While this information can be valuable for understanding the project context, do not treat it as a direct part of the user's request or response. Use it to inform your actions and decisions, but don't assume the user is explicitly asking about or referring to this information unless they clearly do so in their message. When using environment_details, explain your actions clearly to ensure the user understands, as they may not be aware of these details.\\r\\n- Before executing commands, check the \\\"Actively Running Terminals\\\" section in environment_details. If present, consider how these active processes might impact your task. For example, if a local development server is already running, you wouldn't need to start it again. If no active terminals are listed, proceed with command execution as normal.\\r\\n- MCP operations should be used one at a time, similar to other tool usage. Wait for confirmation of success before proceeding with additional operations.\\r\\n- When using the replace_in_file tool, you must include complete lines in your SEARCH blocks, not partial lines. The system requires exact line matches and cannot match partial lines. For example, if you want to match a line containing \\\"const x = 5;\\\", your SEARCH block must include the entire line, not just \\\"x = 5\\\" or other fragments.\\r\\n- When using the replace_in_file tool, if you use multiple SEARCH/REPLACE blocks, list them in the order they appear in the file. For example if you need to make changes to both line 10 and line 50, first include the SEARCH/REPLACE block for line 10, followed by the SEARCH/REPLACE block for line 50.\\r\\n- It is critical you wait for the user's response after each tool use, in order to confirm the success of the tool use. For example, if asked to make a todo app, you would create a file, wait for the user's response it was created successfully, then create another file if needed, wait for the user's response it was created successfully, etc.${\\r\\n\\tsupportsComputerUse\\r\\n\\t\\t? \\\" Then if you want to test your work, you might use browser_action to launch the site, wait for the user's response confirming the site was launched along with a screenshot, then perhaps e.g., click a button to test functionality if needed, wait for the user's response confirming the button was clicked along with a screenshot of the new state, before finally closing the browser.\\\"\\r\\n\\t\\t: \\\"\\\"\\r\\n}\\r\\n\\r\\n====\\r\\n\\r\\nSYSTEM INFORMATION\\r\\n\\r\\nOperating System: ${osName()}\\r\\nDefault Shell: ${defaultShell}\\r\\nHome Directory: ${os.homedir().toPosix()}\\r\\nCurrent Working Directory: ${cwd.toPosix()}\\r\\n\\r\\n====\\r\\n\\r\\nOBJECTIVE\\r\\n\\r\\nYou accomplish a given task iteratively, breaking it down into clear steps and working through them methodically.\\r\\n\\r\\n1. Analyze the user's task and set clear, achievable goals to accomplish it. Prioritize these goals in a logical order.\\r\\n2. Work through these goals sequentially, utilizing available tools one at a time as necessary. Each goal should correspond to a distinct step in your problem-solving process. You will be informed on the work completed and what's remaining as you go.\\r\\n3. Remember, you have extensive capabilities with access to a wide range of tools that can be used in powerful and clever ways as necessary to accomplish each goal. Before calling a tool, do some analysis within <thinking></thinking> tags. First, analyze the file structure provided in environment_details to gain context and insights for proceeding effectively. Then, think about which of the provided tools is the most relevant tool to accomplish the user's task. Next, go through each of the required parameters of the relevant tool and determine if the user has directly provided or given enough information to infer a value. When deciding if the parameter can be inferred, carefully consider all the context to see if it supports a specific value. If all of the required parameters are present or can be reasonably inferred, close the thinking tag and proceed with the tool use. BUT, if one of the values for a required parameter is missing, DO NOT invoke the tool (not even with fillers for the missing params) and instead, ask the user to provide the missing parameters using the ask_followup_question tool. DO NOT ask for more information on optional parameters if it is not provided.\\r\\n4. Once you've completed the user's task, you must use the attempt_completion tool to present the result of the task to the user. You may also provide a CLI command to showcase the result of your task; this can be particularly useful for web development tasks, where you can run e.g. \\\\`open index.html\\\\` to show the website you've built.\\r\\n5. The user may provide feedback, which you can use to make improvements and try again. But DO NOT continue in pointless back and forth conversations, i.e. don't end your responses with questions or offers for further assistance.`\\r\\n\\r\\nexport function addUserInstructions(settingsCustomInstructions?: string, clineRulesFileInstructions?: string) {\\r\\n\\tlet customInstructions = \\\"\\\"\\r\\n\\tif (settingsCustomInstructions) {\\r\\n\\t\\tcustomInstructions += settingsCustomInstructions + \\\"\\\\n\\\\n\\\"\\r\\n\\t}\\r\\n\\tif (clineRulesFileInstructions) {\\r\\n\\t\\tcustomInstructions += clineRulesFileInstructions\\r\\n\\t}\\r\\n\\r\\n\\treturn `\\r\\n====\\r\\n\\r\\nUSER'S CUSTOM INSTRUCTIONS\\r\\n\\r\\nThe following additional instructions are provided by the user, and should be followed to the best of your ability without interfering with the TOOL USE guidelines.\\r\\n\\r\\n${customInstructions.trim()}`\\r\\n}\\r\\n\",\"metadata\":{\"size\":56940,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.946Z\",\"createdTime\":\"2025-01-15T15:26:07.946Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":910,\"nonEmptyLines\":765,\"commentLines\":9,\"complexity\":57},\"dependencies\":[\"default-shell\",\"os\",\"os-name\",\"../../services/mcp/McpHub\",\"react\",\"@modelcontextprotocol/sdk/server/index.js\",\"@modelcontextprotocol/sdk/server/stdio.js\",\"axios\",\"fs\"],\"quality\":{\"score\":-1130,\"issues\":[],\"duplicateLines\":194,\"longLines\":130,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.337Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":910},\"analysis\":{\"metrics\":{\"lines\":910,\"nonEmptyLines\":765,\"commentLines\":9,\"complexity\":57},\"dependencies\":[\"default-shell\",\"os\",\"os-name\",\"../../services/mcp/McpHub\",\"react\",\"@modelcontextprotocol/sdk/server/index.js\",\"@modelcontextprotocol/sdk/server/stdio.js\",\"axios\",\"fs\"],\"quality\":{\"score\":-1130,\"issues\":[],\"duplicateLines\":194,\"longLines\":130,\"complexFunctions\":0}},\"lastModified\":1737046855339,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.339Z\",\"size\":59891},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\core\\\\prompts\\\\responses.ts\",\"content\":{\"content\":\"import { Anthropic } from \\\"@anthropic-ai/sdk\\\"\\r\\nimport * as path from \\\"path\\\"\\r\\nimport * as diff from \\\"diff\\\"\\r\\n\\r\\nexport const formatResponse = {\\r\\n\\ttoolDenied: () => `The user denied this operation.`,\\r\\n\\r\\n\\ttoolDeniedWithFeedback: (feedback?: string) =>\\r\\n\\t\\t`The user denied this operation and provided the following feedback:\\\\n<feedback>\\\\n${feedback}\\\\n</feedback>`,\\r\\n\\r\\n\\ttoolError: (error?: string) => `The tool execution failed with the following error:\\\\n<error>\\\\n${error}\\\\n</error>`,\\r\\n\\r\\n\\tnoToolsUsed: () =>\\r\\n\\t\\t`[ERROR] You did not use a tool in your previous response! Please retry with a tool use.\\r\\n\\r\\n${toolUseInstructionsReminder}\\r\\n\\r\\n# Next Steps\\r\\n\\r\\nIf you have completed the user's task, use the attempt_completion tool. \\r\\nIf you require additional information from the user, use the ask_followup_question tool. \\r\\nOtherwise, if you have not completed the task and do not need additional information, then proceed with the next step of the task. \\r\\n(This is an automated message, so do not respond to it conversationally.)`,\\r\\n\\r\\n\\ttooManyMistakes: (feedback?: string) =>\\r\\n\\t\\t`You seem to be having trouble proceeding. The user has provided the following feedback to help guide you:\\\\n<feedback>\\\\n${feedback}\\\\n</feedback>`,\\r\\n\\r\\n\\tmissingToolParameterError: (paramName: string) =>\\r\\n\\t\\t`Missing value for required parameter '${paramName}'. Please retry with complete response.\\\\n\\\\n${toolUseInstructionsReminder}`,\\r\\n\\r\\n\\tinvalidMcpToolArgumentError: (serverName: string, toolName: string) =>\\r\\n\\t\\t`Invalid JSON argument used with ${serverName} for ${toolName}. Please retry with a properly formatted JSON argument.`,\\r\\n\\r\\n\\ttoolResult: (text: string, images?: string[]): string | Array<Anthropic.TextBlockParam | Anthropic.ImageBlockParam> => {\\r\\n\\t\\tif (images && images.length > 0) {\\r\\n\\t\\t\\tconst textBlock: Anthropic.TextBlockParam = { type: \\\"text\\\", text }\\r\\n\\t\\t\\tconst imageBlocks: Anthropic.ImageBlockParam[] = formatImagesIntoBlocks(images)\\r\\n\\t\\t\\t// Placing images after text leads to better results\\r\\n\\t\\t\\treturn [textBlock, ...imageBlocks]\\r\\n\\t\\t} else {\\r\\n\\t\\t\\treturn text\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\timageBlocks: (images?: string[]): Anthropic.ImageBlockParam[] => {\\r\\n\\t\\treturn formatImagesIntoBlocks(images)\\r\\n\\t},\\r\\n\\r\\n\\tformatFilesList: (absolutePath: string, files: string[], didHitLimit: boolean): string => {\\r\\n\\t\\tconst sorted = files\\r\\n\\t\\t\\t.map((file) => {\\r\\n\\t\\t\\t\\t// convert absolute path to relative path\\r\\n\\t\\t\\t\\tconst relativePath = path.relative(absolutePath, file).toPosix()\\r\\n\\t\\t\\t\\treturn file.endsWith(\\\"/\\\") ? relativePath + \\\"/\\\" : relativePath\\r\\n\\t\\t\\t})\\r\\n\\t\\t\\t// Sort so files are listed under their respective directories to make it clear what files are children of what directories. Since we build file list top down, even if file list is truncated it will show directories that cline can then explore further.\\r\\n\\t\\t\\t.sort((a, b) => {\\r\\n\\t\\t\\t\\tconst aParts = a.split(\\\"/\\\") // only works if we use toPosix first\\r\\n\\t\\t\\t\\tconst bParts = b.split(\\\"/\\\")\\r\\n\\t\\t\\t\\tfor (let i = 0; i < Math.min(aParts.length, bParts.length); i++) {\\r\\n\\t\\t\\t\\t\\tif (aParts[i] !== bParts[i]) {\\r\\n\\t\\t\\t\\t\\t\\t// If one is a directory and the other isn't at this level, sort the directory first\\r\\n\\t\\t\\t\\t\\t\\tif (i + 1 === aParts.length && i + 1 < bParts.length) {\\r\\n\\t\\t\\t\\t\\t\\t\\treturn -1\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\tif (i + 1 === bParts.length && i + 1 < aParts.length) {\\r\\n\\t\\t\\t\\t\\t\\t\\treturn 1\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t// Otherwise, sort alphabetically\\r\\n\\t\\t\\t\\t\\t\\treturn aParts[i].localeCompare(bParts[i], undefined, {\\r\\n\\t\\t\\t\\t\\t\\t\\tnumeric: true,\\r\\n\\t\\t\\t\\t\\t\\t\\tsensitivity: \\\"base\\\",\\r\\n\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t// If all parts are the same up to the length of the shorter path,\\r\\n\\t\\t\\t\\t// the shorter one comes first\\r\\n\\t\\t\\t\\treturn aParts.length - bParts.length\\r\\n\\t\\t\\t})\\r\\n\\t\\tif (didHitLimit) {\\r\\n\\t\\t\\treturn `${sorted.join(\\r\\n\\t\\t\\t\\t\\\"\\\\n\\\",\\r\\n\\t\\t\\t)}\\\\n\\\\n(File list truncated. Use list_files on specific subdirectories if you need to explore further.)`\\r\\n\\t\\t} else if (sorted.length === 0 || (sorted.length === 1 && sorted[0] === \\\"\\\")) {\\r\\n\\t\\t\\treturn \\\"No files found.\\\"\\r\\n\\t\\t} else {\\r\\n\\t\\t\\treturn sorted.join(\\\"\\\\n\\\")\\r\\n\\t\\t}\\r\\n\\t},\\r\\n\\r\\n\\tcreatePrettyPatch: (filename = \\\"file\\\", oldStr?: string, newStr?: string) => {\\r\\n\\t\\t// strings cannot be undefined or diff throws exception\\r\\n\\t\\tconst patch = diff.createPatch(filename.toPosix(), oldStr || \\\"\\\", newStr || \\\"\\\")\\r\\n\\t\\tconst lines = patch.split(\\\"\\\\n\\\")\\r\\n\\t\\tconst prettyPatchLines = lines.slice(4)\\r\\n\\t\\treturn prettyPatchLines.join(\\\"\\\\n\\\")\\r\\n\\t},\\r\\n}\\r\\n\\r\\n// to avoid circular dependency\\r\\nconst formatImagesIntoBlocks = (images?: string[]): Anthropic.ImageBlockParam[] => {\\r\\n\\treturn images\\r\\n\\t\\t? images.map((dataUrl) => {\\r\\n\\t\\t\\t\\t// data:image/png;base64,base64string\\r\\n\\t\\t\\t\\tconst [rest, base64] = dataUrl.split(\\\",\\\")\\r\\n\\t\\t\\t\\tconst mimeType = rest.split(\\\":\\\")[1].split(\\\";\\\")[0]\\r\\n\\t\\t\\t\\treturn {\\r\\n\\t\\t\\t\\t\\ttype: \\\"image\\\",\\r\\n\\t\\t\\t\\t\\tsource: {\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"base64\\\",\\r\\n\\t\\t\\t\\t\\t\\tmedia_type: mimeType,\\r\\n\\t\\t\\t\\t\\t\\tdata: base64,\\r\\n\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t} as Anthropic.ImageBlockParam\\r\\n\\t\\t\\t})\\r\\n\\t\\t: []\\r\\n}\\r\\n\\r\\nconst toolUseInstructionsReminder = `# Reminder: Instructions for Tool Use\\r\\n\\r\\nTool uses are formatted using XML-style tags. The tool name is enclosed in opening and closing tags, and each parameter is similarly enclosed within its own set of tags. Here's the structure:\\r\\n\\r\\n<tool_name>\\r\\n<parameter1_name>value1</parameter1_name>\\r\\n<parameter2_name>value2</parameter2_name>\\r\\n...\\r\\n</tool_name>\\r\\n\\r\\nFor example:\\r\\n\\r\\n<attempt_completion>\\r\\n<result>\\r\\nI have completed the task...\\r\\n</result>\\r\\n</attempt_completion>\\r\\n\\r\\nAlways adhere to this format for all tool uses to ensure proper parsing and execution.`\\r\\n\",\"metadata\":{\"size\":5373,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.944Z\",\"createdTime\":\"2025-01-15T15:26:07.944Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":138,\"nonEmptyLines\":116,\"commentLines\":10,\"complexity\":29},\"dependencies\":[\"@anthropic-ai/sdk\",\"path\",\"diff\"],\"quality\":{\"score\":5,\"issues\":[],\"duplicateLines\":15,\"longLines\":10,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.335Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":138},\"analysis\":{\"metrics\":{\"lines\":138,\"nonEmptyLines\":116,\"commentLines\":10,\"complexity\":29},\"dependencies\":[\"@anthropic-ai/sdk\",\"path\",\"diff\"],\"quality\":{\"score\":5,\"issues\":[],\"duplicateLines\":15,\"longLines\":10,\"complexFunctions\":0}},\"lastModified\":1737046855335,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.335Z\",\"size\":6489},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\core\\\\mentions\\\\index.ts\",\"content\":{\"content\":\"import * as vscode from \\\"vscode\\\"\\r\\nimport * as path from \\\"path\\\"\\r\\nimport { openFile } from \\\"../../integrations/misc/open-file\\\"\\r\\nimport { UrlContentFetcher } from \\\"../../services/browser/UrlContentFetcher\\\"\\r\\nimport { mentionRegexGlobal } from \\\"../../shared/context-mentions\\\"\\r\\nimport fs from \\\"fs/promises\\\"\\r\\nimport { extractTextFromFile } from \\\"../../integrations/misc/extract-text\\\"\\r\\nimport { isBinaryFile } from \\\"isbinaryfile\\\"\\r\\nimport { diagnosticsToProblemsString } from \\\"../../integrations/diagnostics\\\"\\r\\n\\r\\nexport function openMention(mention?: string): void {\\r\\n\\tif (!mention) {\\r\\n\\t\\treturn\\r\\n\\t}\\r\\n\\r\\n\\tif (mention.startsWith(\\\"/\\\")) {\\r\\n\\t\\tconst relPath = mention.slice(1)\\r\\n\\t\\tconst cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0)\\r\\n\\t\\tif (!cwd) {\\r\\n\\t\\t\\treturn\\r\\n\\t\\t}\\r\\n\\t\\tconst absPath = path.resolve(cwd, relPath)\\r\\n\\t\\tif (mention.endsWith(\\\"/\\\")) {\\r\\n\\t\\t\\tvscode.commands.executeCommand(\\\"revealInExplorer\\\", vscode.Uri.file(absPath))\\r\\n\\t\\t\\t// vscode.commands.executeCommand(\\\"vscode.openFolder\\\", , { forceNewWindow: false }) opens in new window\\r\\n\\t\\t} else {\\r\\n\\t\\t\\topenFile(absPath)\\r\\n\\t\\t}\\r\\n\\t} else if (mention === \\\"problems\\\") {\\r\\n\\t\\tvscode.commands.executeCommand(\\\"workbench.actions.view.problems\\\")\\r\\n\\t} else if (mention.startsWith(\\\"http\\\")) {\\r\\n\\t\\tvscode.env.openExternal(vscode.Uri.parse(mention))\\r\\n\\t}\\r\\n}\\r\\n\\r\\nexport async function parseMentions(text: string, cwd: string, urlContentFetcher: UrlContentFetcher): Promise<string> {\\r\\n\\tconst mentions: Set<string> = new Set()\\r\\n\\tlet parsedText = text.replace(mentionRegexGlobal, (match, mention) => {\\r\\n\\t\\tmentions.add(mention)\\r\\n\\t\\tif (mention.startsWith(\\\"http\\\")) {\\r\\n\\t\\t\\treturn `'${mention}' (see below for site content)`\\r\\n\\t\\t} else if (mention.startsWith(\\\"/\\\")) {\\r\\n\\t\\t\\tconst mentionPath = mention.slice(1) // Remove the leading '/'\\r\\n\\t\\t\\treturn mentionPath.endsWith(\\\"/\\\")\\r\\n\\t\\t\\t\\t? `'${mentionPath}' (see below for folder content)`\\r\\n\\t\\t\\t\\t: `'${mentionPath}' (see below for file content)`\\r\\n\\t\\t} else if (mention === \\\"problems\\\") {\\r\\n\\t\\t\\treturn `Workspace Problems (see below for diagnostics)`\\r\\n\\t\\t}\\r\\n\\t\\treturn match\\r\\n\\t})\\r\\n\\r\\n\\tconst urlMention = Array.from(mentions).find((mention) => mention.startsWith(\\\"http\\\"))\\r\\n\\tlet launchBrowserError: Error | undefined\\r\\n\\tif (urlMention) {\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tawait urlContentFetcher.launchBrowser()\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\tlaunchBrowserError = error\\r\\n\\t\\t\\tvscode.window.showErrorMessage(`Error fetching content for ${urlMention}: ${error.message}`)\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tfor (const mention of mentions) {\\r\\n\\t\\tif (mention.startsWith(\\\"http\\\")) {\\r\\n\\t\\t\\tlet result: string\\r\\n\\t\\t\\tif (launchBrowserError) {\\r\\n\\t\\t\\t\\tresult = `Error fetching content: ${launchBrowserError.message}`\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\tconst markdown = await urlContentFetcher.urlToMarkdown(mention)\\r\\n\\t\\t\\t\\t\\tresult = markdown\\r\\n\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\tvscode.window.showErrorMessage(`Error fetching content for ${mention}: ${error.message}`)\\r\\n\\t\\t\\t\\t\\tresult = `Error fetching content: ${error.message}`\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tparsedText += `\\\\n\\\\n<url_content url=\\\"${mention}\\\">\\\\n${result}\\\\n</url_content>`\\r\\n\\t\\t} else if (mention.startsWith(\\\"/\\\")) {\\r\\n\\t\\t\\tconst mentionPath = mention.slice(1)\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\tconst content = await getFileOrFolderContent(mentionPath, cwd)\\r\\n\\t\\t\\t\\tif (mention.endsWith(\\\"/\\\")) {\\r\\n\\t\\t\\t\\t\\tparsedText += `\\\\n\\\\n<folder_content path=\\\"${mentionPath}\\\">\\\\n${content}\\\\n</folder_content>`\\r\\n\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\tparsedText += `\\\\n\\\\n<file_content path=\\\"${mentionPath}\\\">\\\\n${content}\\\\n</file_content>`\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\tif (mention.endsWith(\\\"/\\\")) {\\r\\n\\t\\t\\t\\t\\tparsedText += `\\\\n\\\\n<folder_content path=\\\"${mentionPath}\\\">\\\\nError fetching content: ${error.message}\\\\n</folder_content>`\\r\\n\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\tparsedText += `\\\\n\\\\n<file_content path=\\\"${mentionPath}\\\">\\\\nError fetching content: ${error.message}\\\\n</file_content>`\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t} else if (mention === \\\"problems\\\") {\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\tconst problems = getWorkspaceProblems(cwd)\\r\\n\\t\\t\\t\\tparsedText += `\\\\n\\\\n<workspace_diagnostics>\\\\n${problems}\\\\n</workspace_diagnostics>`\\r\\n\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\tparsedText += `\\\\n\\\\n<workspace_diagnostics>\\\\nError fetching diagnostics: ${error.message}\\\\n</workspace_diagnostics>`\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tif (urlMention) {\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tawait urlContentFetcher.closeBrowser()\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\tconsole.error(`Error closing browser: ${error.message}`)\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\treturn parsedText\\r\\n}\\r\\n\\r\\nasync function getFileOrFolderContent(mentionPath: string, cwd: string): Promise<string> {\\r\\n\\tconst absPath = path.resolve(cwd, mentionPath)\\r\\n\\r\\n\\ttry {\\r\\n\\t\\tconst stats = await fs.stat(absPath)\\r\\n\\r\\n\\t\\tif (stats.isFile()) {\\r\\n\\t\\t\\tconst isBinary = await isBinaryFile(absPath).catch(() => false)\\r\\n\\t\\t\\tif (isBinary) {\\r\\n\\t\\t\\t\\treturn \\\"(Binary file, unable to display content)\\\"\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tconst content = await extractTextFromFile(absPath)\\r\\n\\t\\t\\treturn content\\r\\n\\t\\t} else if (stats.isDirectory()) {\\r\\n\\t\\t\\tconst entries = await fs.readdir(absPath, { withFileTypes: true })\\r\\n\\t\\t\\tlet folderContent = \\\"\\\"\\r\\n\\t\\t\\tconst fileContentPromises: Promise<string | undefined>[] = []\\r\\n\\t\\t\\tentries.forEach((entry, index) => {\\r\\n\\t\\t\\t\\tconst isLast = index === entries.length - 1\\r\\n\\t\\t\\t\\tconst linePrefix = isLast ? \\\"└── \\\" : \\\"├── \\\"\\r\\n\\t\\t\\t\\tif (entry.isFile()) {\\r\\n\\t\\t\\t\\t\\tfolderContent += `${linePrefix}${entry.name}\\\\n`\\r\\n\\t\\t\\t\\t\\tconst filePath = path.join(mentionPath, entry.name)\\r\\n\\t\\t\\t\\t\\tconst absoluteFilePath = path.resolve(absPath, entry.name)\\r\\n\\t\\t\\t\\t\\t// const relativeFilePath = path.relative(cwd, absoluteFilePath);\\r\\n\\t\\t\\t\\t\\tfileContentPromises.push(\\r\\n\\t\\t\\t\\t\\t\\t(async () => {\\r\\n\\t\\t\\t\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst isBinary = await isBinaryFile(absoluteFilePath).catch(() => false)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (isBinary) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\treturn undefined\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst content = await extractTextFromFile(absoluteFilePath)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\treturn `<file_content path=\\\"${filePath.toPosix()}\\\">\\\\n${content}\\\\n</file_content>`\\r\\n\\t\\t\\t\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\treturn undefined\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t})(),\\r\\n\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t} else if (entry.isDirectory()) {\\r\\n\\t\\t\\t\\t\\tfolderContent += `${linePrefix}${entry.name}/\\\\n`\\r\\n\\t\\t\\t\\t\\t// not recursively getting folder contents\\r\\n\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\tfolderContent += `${linePrefix}${entry.name}\\\\n`\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t})\\r\\n\\t\\t\\tconst fileContents = (await Promise.all(fileContentPromises)).filter((content) => content)\\r\\n\\t\\t\\treturn `${folderContent}\\\\n${fileContents.join(\\\"\\\\n\\\\n\\\")}`.trim()\\r\\n\\t\\t} else {\\r\\n\\t\\t\\treturn `(Failed to read contents of ${mentionPath})`\\r\\n\\t\\t}\\r\\n\\t} catch (error) {\\r\\n\\t\\tthrow new Error(`Failed to access path \\\"${mentionPath}\\\": ${error.message}`)\\r\\n\\t}\\r\\n}\\r\\n\\r\\nfunction getWorkspaceProblems(cwd: string): string {\\r\\n\\tconst diagnostics = vscode.languages.getDiagnostics()\\r\\n\\tconst result = diagnosticsToProblemsString(\\r\\n\\t\\tdiagnostics,\\r\\n\\t\\t[vscode.DiagnosticSeverity.Error, vscode.DiagnosticSeverity.Warning],\\r\\n\\t\\tcwd,\\r\\n\\t)\\r\\n\\tif (!result) {\\r\\n\\t\\treturn \\\"No errors or warnings detected.\\\"\\r\\n\\t}\\r\\n\\treturn result\\r\\n}\\r\\n\",\"metadata\":{\"size\":6697,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.842Z\",\"createdTime\":\"2024-12-29T05:57:23.842Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":184,\"nonEmptyLines\":172,\"commentLines\":3,\"complexity\":61},\"dependencies\":[\"vscode\",\"path\",\"../../integrations/misc/open-file\",\"../../services/browser/UrlContentFetcher\",\"../../shared/context-mentions\",\"fs/promises\",\"../../integrations/misc/extract-text\",\"isbinaryfile\",\"../../integrations/diagnostics\"],\"quality\":{\"score\":-195,\"issues\":[],\"duplicateLines\":57,\"longLines\":5,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.333Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":184},\"analysis\":{\"metrics\":{\"lines\":184,\"nonEmptyLines\":172,\"commentLines\":3,\"complexity\":61},\"dependencies\":[\"vscode\",\"path\",\"../../integrations/misc/open-file\",\"../../services/browser/UrlContentFetcher\",\"../../shared/context-mentions\",\"fs/promises\",\"../../integrations/misc/extract-text\",\"isbinaryfile\",\"../../integrations/diagnostics\"],\"quality\":{\"score\":-195,\"issues\":[],\"duplicateLines\":57,\"longLines\":5,\"complexFunctions\":0}},\"lastModified\":1737046855333,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.333Z\",\"size\":8348},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\core\\\\Cline.ts\",\"content\":{\"content\":\"import { Anthropic } from \\\"@anthropic-ai/sdk\\\"\\r\\nimport cloneDeep from \\\"clone-deep\\\"\\r\\nimport delay from \\\"delay\\\"\\r\\nimport fs from \\\"fs/promises\\\"\\r\\nimport os from \\\"os\\\"\\r\\nimport pWaitFor from \\\"p-wait-for\\\"\\r\\nimport * as path from \\\"path\\\"\\r\\nimport { serializeError } from \\\"serialize-error\\\"\\r\\nimport * as vscode from \\\"vscode\\\"\\r\\nimport { ApiHandler, buildApiHandler } from \\\"../api\\\"\\r\\nimport { ApiStream } from \\\"../api/transform/stream\\\"\\r\\nimport { DIFF_VIEW_URI_SCHEME, DiffViewProvider } from \\\"../integrations/editor/DiffViewProvider\\\"\\r\\nimport { findToolName, formatContentBlockToMarkdown } from \\\"../integrations/misc/export-markdown\\\"\\r\\nimport { extractTextFromFile } from \\\"../integrations/misc/extract-text\\\"\\r\\nimport { TerminalManager } from \\\"../integrations/terminal/TerminalManager\\\"\\r\\nimport { BrowserSession } from \\\"../services/browser/BrowserSession\\\"\\r\\nimport { UrlContentFetcher } from \\\"../services/browser/UrlContentFetcher\\\"\\r\\nimport { listFiles } from \\\"../services/glob/list-files\\\"\\r\\nimport { regexSearchFiles } from \\\"../services/ripgrep\\\"\\r\\nimport { parseSourceCodeForDefinitionsTopLevel } from \\\"../services/tree-sitter\\\"\\r\\nimport { ApiConfiguration } from \\\"../shared/api\\\"\\r\\nimport { findLast, findLastIndex } from \\\"../shared/array\\\"\\r\\nimport { AutoApprovalSettings } from \\\"../shared/AutoApprovalSettings\\\"\\r\\nimport { combineApiRequests } from \\\"../shared/combineApiRequests\\\"\\r\\nimport { combineCommandSequences, COMMAND_REQ_APP_STRING } from \\\"../shared/combineCommandSequences\\\"\\r\\nimport {\\r\\n\\tBrowserAction,\\r\\n\\tBrowserActionResult,\\r\\n\\tbrowserActions,\\r\\n\\tClineApiReqCancelReason,\\r\\n\\tClineApiReqInfo,\\r\\n\\tClineAsk,\\r\\n\\tClineAskUseMcpServer,\\r\\n\\tClineMessage,\\r\\n\\tClineSay,\\r\\n\\tClineSayBrowserAction,\\r\\n\\tClineSayTool,\\r\\n\\tCOMPLETION_RESULT_CHANGES_FLAG,\\r\\n} from \\\"../shared/ExtensionMessage\\\"\\r\\nimport { getApiMetrics } from \\\"../shared/getApiMetrics\\\"\\r\\nimport { HistoryItem } from \\\"../shared/HistoryItem\\\"\\r\\nimport { ClineAskResponse, ClineCheckpointRestore } from \\\"../shared/WebviewMessage\\\"\\r\\nimport { calculateApiCost } from \\\"../utils/cost\\\"\\r\\nimport { fileExistsAtPath } from \\\"../utils/fs\\\"\\r\\nimport { arePathsEqual, getReadablePath } from \\\"../utils/path\\\"\\r\\nimport { AssistantMessageContent, parseAssistantMessage, ToolParamName, ToolUseName } from \\\"./assistant-message\\\"\\r\\nimport { constructNewFileContent } from \\\"./assistant-message/diff\\\"\\r\\nimport { parseMentions } from \\\"./mentions\\\"\\r\\nimport { formatResponse } from \\\"./prompts/responses\\\"\\r\\nimport { addUserInstructions, SYSTEM_PROMPT } from \\\"./prompts/system\\\"\\r\\nimport { getNextTruncationRange, getTruncatedMessages } from \\\"./sliding-window\\\"\\r\\nimport { ClineProvider, GlobalFileNames } from \\\"./webview/ClineProvider\\\"\\r\\nimport { showSystemNotification } from \\\"../integrations/notifications\\\"\\r\\nimport { removeInvalidChars } from \\\"../utils/string\\\"\\r\\nimport { fixModelHtmlEscaping } from \\\"../utils/string\\\"\\r\\nimport { OpenAiHandler } from \\\"../api/providers/openai\\\"\\r\\nimport CheckpointTracker from \\\"../integrations/checkpoints/CheckpointTracker\\\"\\r\\nimport getFolderSize from \\\"get-folder-size\\\"\\r\\n\\r\\nconst 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\\r\\n\\r\\ntype ToolResponse = string | Array<Anthropic.TextBlockParam | Anthropic.ImageBlockParam>\\r\\ntype UserContent = Array<\\r\\n\\tAnthropic.TextBlockParam | Anthropic.ImageBlockParam | Anthropic.ToolUseBlockParam | Anthropic.ToolResultBlockParam\\r\\n>\\r\\n\\r\\nexport class Cline {\\r\\n\\treadonly taskId: string\\r\\n\\tapi: ApiHandler\\r\\n\\tprivate terminalManager: TerminalManager\\r\\n\\tprivate urlContentFetcher: UrlContentFetcher\\r\\n\\tprivate browserSession: BrowserSession\\r\\n\\tprivate didEditFile: boolean = false\\r\\n\\tcustomInstructions?: string\\r\\n\\tautoApprovalSettings: AutoApprovalSettings\\r\\n\\tapiConversationHistory: Anthropic.MessageParam[] = []\\r\\n\\tclineMessages: ClineMessage[] = []\\r\\n\\tprivate askResponse?: ClineAskResponse\\r\\n\\tprivate askResponseText?: string\\r\\n\\tprivate askResponseImages?: string[]\\r\\n\\tprivate lastMessageTs?: number\\r\\n\\tprivate consecutiveAutoApprovedRequestsCount: number = 0\\r\\n\\tprivate consecutiveMistakeCount: number = 0\\r\\n\\tprivate providerRef: WeakRef<ClineProvider>\\r\\n\\tprivate abort: boolean = false\\r\\n\\tdidFinishAbortingStream = false\\r\\n\\tabandoned = false\\r\\n\\tprivate diffViewProvider: DiffViewProvider\\r\\n\\tprivate checkpointTracker?: CheckpointTracker\\r\\n\\tcheckpointTrackerErrorMessage?: string\\r\\n\\tconversationHistoryDeletedRange?: [number, number]\\r\\n\\tisInitialized = false\\r\\n\\r\\n\\t// streaming\\r\\n\\tisStreaming = false\\r\\n\\tprivate currentStreamingContentIndex = 0\\r\\n\\tprivate assistantMessageContent: AssistantMessageContent[] = []\\r\\n\\tprivate presentAssistantMessageLocked = false\\r\\n\\tprivate presentAssistantMessageHasPendingUpdates = false\\r\\n\\tprivate userMessageContent: (Anthropic.TextBlockParam | Anthropic.ImageBlockParam)[] = []\\r\\n\\tprivate userMessageContentReady = false\\r\\n\\tprivate didRejectTool = false\\r\\n\\tprivate didAlreadyUseTool = false\\r\\n\\tprivate didCompleteReadingStream = false\\r\\n\\r\\n\\tconstructor(\\r\\n\\t\\tprovider: ClineProvider,\\r\\n\\t\\tapiConfiguration: ApiConfiguration,\\r\\n\\t\\tautoApprovalSettings: AutoApprovalSettings,\\r\\n\\t\\tcustomInstructions?: string,\\r\\n\\t\\ttask?: string,\\r\\n\\t\\timages?: string[],\\r\\n\\t\\thistoryItem?: HistoryItem,\\r\\n\\t) {\\r\\n\\t\\tthis.providerRef = new WeakRef(provider)\\r\\n\\t\\tthis.api = buildApiHandler(apiConfiguration)\\r\\n\\t\\tthis.terminalManager = new TerminalManager()\\r\\n\\t\\tthis.urlContentFetcher = new UrlContentFetcher(provider.context)\\r\\n\\t\\tthis.browserSession = new BrowserSession(provider.context)\\r\\n\\t\\tthis.diffViewProvider = new DiffViewProvider(cwd)\\r\\n\\t\\tthis.customInstructions = customInstructions\\r\\n\\t\\tthis.autoApprovalSettings = autoApprovalSettings\\r\\n\\t\\tif (historyItem) {\\r\\n\\t\\t\\tthis.taskId = historyItem.id\\r\\n\\t\\t\\tthis.conversationHistoryDeletedRange = historyItem.conversationHistoryDeletedRange\\r\\n\\t\\t\\tthis.resumeTaskFromHistory()\\r\\n\\t\\t} else if (task || images) {\\r\\n\\t\\t\\tthis.taskId = Date.now().toString()\\r\\n\\t\\t\\tthis.startTask(task, images)\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tthrow new Error(\\\"Either historyItem or task/images must be provided\\\")\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t// Storing task to disk for history\\r\\n\\r\\n\\tprivate async ensureTaskDirectoryExists(): Promise<string> {\\r\\n\\t\\tconst globalStoragePath = this.providerRef.deref()?.context.globalStorageUri.fsPath\\r\\n\\t\\tif (!globalStoragePath) {\\r\\n\\t\\t\\tthrow new Error(\\\"Global storage uri is invalid\\\")\\r\\n\\t\\t}\\r\\n\\t\\tconst taskDir = path.join(globalStoragePath, \\\"tasks\\\", this.taskId)\\r\\n\\t\\tawait fs.mkdir(taskDir, { recursive: true })\\r\\n\\t\\treturn taskDir\\r\\n\\t}\\r\\n\\r\\n\\tprivate async getSavedApiConversationHistory(): Promise<Anthropic.MessageParam[]> {\\r\\n\\t\\tconst filePath = path.join(await this.ensureTaskDirectoryExists(), GlobalFileNames.apiConversationHistory)\\r\\n\\t\\tconst fileExists = await fileExistsAtPath(filePath)\\r\\n\\t\\tif (fileExists) {\\r\\n\\t\\t\\treturn JSON.parse(await fs.readFile(filePath, \\\"utf8\\\"))\\r\\n\\t\\t}\\r\\n\\t\\treturn []\\r\\n\\t}\\r\\n\\r\\n\\tprivate async addToApiConversationHistory(message: Anthropic.MessageParam) {\\r\\n\\t\\tthis.apiConversationHistory.push(message)\\r\\n\\t\\tawait this.saveApiConversationHistory()\\r\\n\\t}\\r\\n\\r\\n\\tprivate async overwriteApiConversationHistory(newHistory: Anthropic.MessageParam[]) {\\r\\n\\t\\tthis.apiConversationHistory = newHistory\\r\\n\\t\\tawait this.saveApiConversationHistory()\\r\\n\\t}\\r\\n\\r\\n\\tprivate async saveApiConversationHistory() {\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tconst filePath = path.join(await this.ensureTaskDirectoryExists(), GlobalFileNames.apiConversationHistory)\\r\\n\\t\\t\\tawait fs.writeFile(filePath, JSON.stringify(this.apiConversationHistory))\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\t// in the off chance this fails, we don't want to stop the task\\r\\n\\t\\t\\tconsole.error(\\\"Failed to save API conversation history:\\\", error)\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tprivate async getSavedClineMessages(): Promise<ClineMessage[]> {\\r\\n\\t\\tconst filePath = path.join(await this.ensureTaskDirectoryExists(), GlobalFileNames.uiMessages)\\r\\n\\t\\tif (await fileExistsAtPath(filePath)) {\\r\\n\\t\\t\\treturn JSON.parse(await fs.readFile(filePath, \\\"utf8\\\"))\\r\\n\\t\\t} else {\\r\\n\\t\\t\\t// check old location\\r\\n\\t\\t\\tconst oldPath = path.join(await this.ensureTaskDirectoryExists(), \\\"claude_messages.json\\\")\\r\\n\\t\\t\\tif (await fileExistsAtPath(oldPath)) {\\r\\n\\t\\t\\t\\tconst data = JSON.parse(await fs.readFile(oldPath, \\\"utf8\\\"))\\r\\n\\t\\t\\t\\tawait fs.unlink(oldPath) // remove old file\\r\\n\\t\\t\\t\\treturn data\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\treturn []\\r\\n\\t}\\r\\n\\r\\n\\tprivate async addToClineMessages(message: ClineMessage) {\\r\\n\\t\\t// these values allow us to reconstruct the conversation history at the time this cline message was created\\r\\n\\t\\t// it's important that apiConversationHistory is initialized before we add cline messages\\r\\n\\t\\tmessage.conversationHistoryIndex = this.apiConversationHistory.length - 1 // NOTE: this is the index of the last added message which is the user message, and once the clinemessages have been presented we update the apiconversationhistory with the completed assistant message. This means when reseting to a message, we need to +1 this index to get the correct assistant message that this tool use corresponds to\\r\\n\\t\\tmessage.conversationHistoryDeletedRange = this.conversationHistoryDeletedRange\\r\\n\\t\\tthis.clineMessages.push(message)\\r\\n\\t\\tawait this.saveClineMessages()\\r\\n\\t}\\r\\n\\r\\n\\tprivate async overwriteClineMessages(newMessages: ClineMessage[]) {\\r\\n\\t\\tthis.clineMessages = newMessages\\r\\n\\t\\tawait this.saveClineMessages()\\r\\n\\t}\\r\\n\\r\\n\\tprivate async saveClineMessages() {\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tconst taskDir = await this.ensureTaskDirectoryExists()\\r\\n\\t\\t\\tconst filePath = path.join(taskDir, GlobalFileNames.uiMessages)\\r\\n\\t\\t\\tawait fs.writeFile(filePath, JSON.stringify(this.clineMessages))\\r\\n\\t\\t\\t// combined as they are in ChatView\\r\\n\\t\\t\\tconst apiMetrics = getApiMetrics(combineApiRequests(combineCommandSequences(this.clineMessages.slice(1))))\\r\\n\\t\\t\\tconst taskMessage = this.clineMessages[0] // first message is always the task say\\r\\n\\t\\t\\tconst lastRelevantMessage =\\r\\n\\t\\t\\t\\tthis.clineMessages[\\r\\n\\t\\t\\t\\t\\tfindLastIndex(this.clineMessages, (m) => !(m.ask === \\\"resume_task\\\" || m.ask === \\\"resume_completed_task\\\"))\\r\\n\\t\\t\\t\\t]\\r\\n\\t\\t\\tlet taskDirSize = 0\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t// getFolderSize.loose silently ignores errors\\r\\n\\t\\t\\t\\t// returns # of bytes, size/1000/1000 = MB\\r\\n\\t\\t\\t\\ttaskDirSize = await getFolderSize.loose(taskDir)\\r\\n\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\tconsole.error(\\\"Failed to get task directory size:\\\", taskDir, error)\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tawait this.providerRef.deref()?.updateTaskHistory({\\r\\n\\t\\t\\t\\tid: this.taskId,\\r\\n\\t\\t\\t\\tts: lastRelevantMessage.ts,\\r\\n\\t\\t\\t\\ttask: taskMessage.text ?? \\\"\\\",\\r\\n\\t\\t\\t\\ttokensIn: apiMetrics.totalTokensIn,\\r\\n\\t\\t\\t\\ttokensOut: apiMetrics.totalTokensOut,\\r\\n\\t\\t\\t\\tcacheWrites: apiMetrics.totalCacheWrites,\\r\\n\\t\\t\\t\\tcacheReads: apiMetrics.totalCacheReads,\\r\\n\\t\\t\\t\\ttotalCost: apiMetrics.totalCost,\\r\\n\\t\\t\\t\\tsize: taskDirSize,\\r\\n\\t\\t\\t\\tshadowGitConfigWorkTree: await this.checkpointTracker?.getShadowGitConfigWorkTree(),\\r\\n\\t\\t\\t\\tconversationHistoryDeletedRange: this.conversationHistoryDeletedRange,\\r\\n\\t\\t\\t})\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\tconsole.error(\\\"Failed to save cline messages:\\\", error)\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tasync restoreCheckpoint(messageTs: number, restoreType: ClineCheckpointRestore) {\\r\\n\\t\\tconst messageIndex = this.clineMessages.findIndex((m) => m.ts === messageTs)\\r\\n\\t\\tconst message = this.clineMessages[messageIndex]\\r\\n\\t\\tif (!message) {\\r\\n\\t\\t\\tconsole.error(\\\"Message not found\\\", this.clineMessages)\\r\\n\\t\\t\\treturn\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tlet didWorkspaceRestoreFail = false\\r\\n\\r\\n\\t\\tswitch (restoreType) {\\r\\n\\t\\t\\tcase \\\"task\\\":\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\tcase \\\"taskAndWorkspace\\\":\\r\\n\\t\\t\\tcase \\\"workspace\\\":\\r\\n\\t\\t\\t\\tif (!this.checkpointTracker) {\\r\\n\\t\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\t\\tthis.checkpointTracker = await CheckpointTracker.create(this.taskId, this.providerRef.deref())\\r\\n\\t\\t\\t\\t\\t\\tthis.checkpointTrackerErrorMessage = undefined\\r\\n\\t\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\t\\tconst errorMessage = error instanceof Error ? error.message : \\\"Unknown error\\\"\\r\\n\\t\\t\\t\\t\\t\\tconsole.error(\\\"Failed to initialize checkpoint tracker:\\\", errorMessage)\\r\\n\\t\\t\\t\\t\\t\\tthis.checkpointTrackerErrorMessage = errorMessage\\r\\n\\t\\t\\t\\t\\t\\tawait this.providerRef.deref()?.postStateToWebview()\\r\\n\\t\\t\\t\\t\\t\\tvscode.window.showErrorMessage(errorMessage)\\r\\n\\t\\t\\t\\t\\t\\tdidWorkspaceRestoreFail = true\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tif (message.lastCheckpointHash && this.checkpointTracker) {\\r\\n\\t\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\t\\tawait this.checkpointTracker.resetHead(message.lastCheckpointHash)\\r\\n\\t\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\t\\tconst errorMessage = error instanceof Error ? error.message : \\\"Unknown error\\\"\\r\\n\\t\\t\\t\\t\\t\\tvscode.window.showErrorMessage(\\\"Failed to restore checkpoint: \\\" + errorMessage)\\r\\n\\t\\t\\t\\t\\t\\tdidWorkspaceRestoreFail = true\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!didWorkspaceRestoreFail) {\\r\\n\\t\\t\\tswitch (restoreType) {\\r\\n\\t\\t\\t\\tcase \\\"task\\\":\\r\\n\\t\\t\\t\\tcase \\\"taskAndWorkspace\\\":\\r\\n\\t\\t\\t\\t\\tthis.conversationHistoryDeletedRange = message.conversationHistoryDeletedRange\\r\\n\\t\\t\\t\\t\\tconst newConversationHistory = this.apiConversationHistory.slice(\\r\\n\\t\\t\\t\\t\\t\\t0,\\r\\n\\t\\t\\t\\t\\t\\t(message.conversationHistoryIndex || 0) + 2,\\r\\n\\t\\t\\t\\t\\t) // +1 since this index corresponds to the last user message, and another +1 since slice end index is exclusive\\r\\n\\t\\t\\t\\t\\tawait this.overwriteApiConversationHistory(newConversationHistory)\\r\\n\\r\\n\\t\\t\\t\\t\\t// aggregate deleted api reqs info so we don't lose costs/tokens\\r\\n\\t\\t\\t\\t\\tconst deletedMessages = this.clineMessages.slice(messageIndex + 1)\\r\\n\\t\\t\\t\\t\\tconst deletedApiReqsMetrics = getApiMetrics(combineApiRequests(combineCommandSequences(deletedMessages)))\\r\\n\\r\\n\\t\\t\\t\\t\\tconst newClineMessages = this.clineMessages.slice(0, messageIndex + 1)\\r\\n\\t\\t\\t\\t\\tawait this.overwriteClineMessages(newClineMessages) // calls saveClineMessages which saves historyItem\\r\\n\\r\\n\\t\\t\\t\\t\\tawait this.say(\\r\\n\\t\\t\\t\\t\\t\\t\\\"deleted_api_reqs\\\",\\r\\n\\t\\t\\t\\t\\t\\tJSON.stringify({\\r\\n\\t\\t\\t\\t\\t\\t\\ttokensIn: deletedApiReqsMetrics.totalTokensIn,\\r\\n\\t\\t\\t\\t\\t\\t\\ttokensOut: deletedApiReqsMetrics.totalTokensOut,\\r\\n\\t\\t\\t\\t\\t\\t\\tcacheWrites: deletedApiReqsMetrics.totalCacheWrites,\\r\\n\\t\\t\\t\\t\\t\\t\\tcacheReads: deletedApiReqsMetrics.totalCacheReads,\\r\\n\\t\\t\\t\\t\\t\\t\\tcost: deletedApiReqsMetrics.totalCost,\\r\\n\\t\\t\\t\\t\\t\\t} satisfies ClineApiReqInfo),\\r\\n\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\tcase \\\"workspace\\\":\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tswitch (restoreType) {\\r\\n\\t\\t\\t\\tcase \\\"task\\\":\\r\\n\\t\\t\\t\\t\\tvscode.window.showInformationMessage(\\\"Task messages have been restored to the checkpoint\\\")\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\tcase \\\"workspace\\\":\\r\\n\\t\\t\\t\\t\\tvscode.window.showInformationMessage(\\\"Workspace files have been restored to the checkpoint\\\")\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\tcase \\\"taskAndWorkspace\\\":\\r\\n\\t\\t\\t\\t\\tvscode.window.showInformationMessage(\\\"Task and workspace have been restored to the checkpoint\\\")\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tawait this.providerRef.deref()?.postMessageToWebview({ type: \\\"relinquishControl\\\" })\\r\\n\\r\\n\\t\\t\\tthis.providerRef.deref()?.cancelTask() // the task is already cancelled by the provider beforehand, but we need to re-init to get the updated messages\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tawait this.providerRef.deref()?.postMessageToWebview({ type: \\\"relinquishControl\\\" })\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tasync presentMultifileDiff(messageTs: number, seeNewChangesSinceLastTaskCompletion: boolean) {\\r\\n\\t\\tconst relinquishButton = () => {\\r\\n\\t\\t\\tthis.providerRef.deref()?.postMessageToWebview({ type: \\\"relinquishControl\\\" })\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tconsole.log(\\\"presentMultifileDiff\\\", messageTs)\\r\\n\\t\\tconst messageIndex = this.clineMessages.findIndex((m) => m.ts === messageTs)\\r\\n\\t\\tconst message = this.clineMessages[messageIndex]\\r\\n\\t\\tif (!message) {\\r\\n\\t\\t\\tconsole.error(\\\"Message not found\\\")\\r\\n\\t\\t\\trelinquishButton()\\r\\n\\t\\t\\treturn\\r\\n\\t\\t}\\r\\n\\t\\tconst hash = message.lastCheckpointHash\\r\\n\\t\\tif (!hash) {\\r\\n\\t\\t\\tconsole.error(\\\"No checkpoint hash found\\\")\\r\\n\\t\\t\\trelinquishButton()\\r\\n\\t\\t\\treturn\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// TODO: handle if this is called from outside original workspace, in which case we need to show user error message we cant show diff outside of workspace?\\r\\n\\t\\tif (!this.checkpointTracker) {\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\tthis.checkpointTracker = await CheckpointTracker.create(this.taskId, this.providerRef.deref())\\r\\n\\t\\t\\t\\tthis.checkpointTrackerErrorMessage = undefined\\r\\n\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\tconst errorMessage = error instanceof Error ? error.message : \\\"Unknown error\\\"\\r\\n\\t\\t\\t\\tconsole.error(\\\"Failed to initialize checkpoint tracker:\\\", errorMessage)\\r\\n\\t\\t\\t\\tthis.checkpointTrackerErrorMessage = errorMessage\\r\\n\\t\\t\\t\\tawait this.providerRef.deref()?.postStateToWebview()\\r\\n\\t\\t\\t\\tvscode.window.showErrorMessage(errorMessage)\\r\\n\\t\\t\\t\\trelinquishButton()\\r\\n\\t\\t\\t\\treturn\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tlet changedFiles:\\r\\n\\t\\t\\t| {\\r\\n\\t\\t\\t\\t\\trelativePath: string\\r\\n\\t\\t\\t\\t\\tabsolutePath: string\\r\\n\\t\\t\\t\\t\\tbefore: string\\r\\n\\t\\t\\t\\t\\tafter: string\\r\\n\\t\\t\\t }[]\\r\\n\\t\\t\\t| undefined\\r\\n\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tif (seeNewChangesSinceLastTaskCompletion) {\\r\\n\\t\\t\\t\\t// Get last task completed\\r\\n\\t\\t\\t\\tconst lastTaskCompletedMessage = findLast(\\r\\n\\t\\t\\t\\t\\tthis.clineMessages.slice(0, messageIndex),\\r\\n\\t\\t\\t\\t\\t(m) => m.say === \\\"completion_result\\\",\\r\\n\\t\\t\\t\\t) // ask is only used to relinquish control, its the last say we care about\\r\\n\\t\\t\\t\\t// if undefined, then we get diff from beginning of git\\r\\n\\t\\t\\t\\t// if (!lastTaskCompletedMessage) {\\r\\n\\t\\t\\t\\t// \\tconsole.error(\\\"No previous task completion message found\\\")\\r\\n\\t\\t\\t\\t// \\treturn\\r\\n\\t\\t\\t\\t// }\\r\\n\\r\\n\\t\\t\\t\\t// Get changed files between current state and commit\\r\\n\\t\\t\\t\\tchangedFiles = await this.checkpointTracker?.getDiffSet(\\r\\n\\t\\t\\t\\t\\tlastTaskCompletedMessage?.lastCheckpointHash, // if undefined, then we get diff from beginning of git history, AKA when the task was started\\r\\n\\t\\t\\t\\t\\thash,\\r\\n\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\tif (!changedFiles?.length) {\\r\\n\\t\\t\\t\\t\\tvscode.window.showInformationMessage(\\\"No changes found\\\")\\r\\n\\t\\t\\t\\t\\trelinquishButton()\\r\\n\\t\\t\\t\\t\\treturn\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t// Get changed files between current state and commit\\r\\n\\t\\t\\t\\tchangedFiles = await this.checkpointTracker?.getDiffSet(hash)\\r\\n\\t\\t\\t\\tif (!changedFiles?.length) {\\r\\n\\t\\t\\t\\t\\tvscode.window.showInformationMessage(\\\"No changes found\\\")\\r\\n\\t\\t\\t\\t\\trelinquishButton()\\r\\n\\t\\t\\t\\t\\treturn\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\tconst errorMessage = error instanceof Error ? error.message : \\\"Unknown error\\\"\\r\\n\\t\\t\\tvscode.window.showErrorMessage(\\\"Failed to retrieve diff set: \\\" + errorMessage)\\r\\n\\t\\t\\trelinquishButton()\\r\\n\\t\\t\\treturn\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// Check if multi-diff editor is enabled in VS Code settings\\r\\n\\t\\t// const config = vscode.workspace.getConfiguration()\\r\\n\\t\\t// const isMultiDiffEnabled = config.get(\\\"multiDiffEditor.experimental.enabled\\\")\\r\\n\\r\\n\\t\\t// if (!isMultiDiffEnabled) {\\r\\n\\t\\t// \\tvscode.window.showErrorMessage(\\r\\n\\t\\t// \\t\\t\\\"Please enable 'multiDiffEditor.experimental.enabled' in your VS Code settings to use this feature.\\\",\\r\\n\\t\\t// \\t)\\r\\n\\t\\t// \\trelinquishButton()\\r\\n\\t\\t// \\treturn\\r\\n\\t\\t// }\\r\\n\\t\\t// Open multi-diff editor\\r\\n\\t\\tawait vscode.commands.executeCommand(\\r\\n\\t\\t\\t\\\"vscode.changes\\\",\\r\\n\\t\\t\\tseeNewChangesSinceLastTaskCompletion ? \\\"New changes\\\" : \\\"Changes since snapshot\\\",\\r\\n\\t\\t\\tchangedFiles.map((file) => [\\r\\n\\t\\t\\t\\tvscode.Uri.file(file.absolutePath),\\r\\n\\t\\t\\t\\tvscode.Uri.parse(`${DIFF_VIEW_URI_SCHEME}:${file.relativePath}`).with({\\r\\n\\t\\t\\t\\t\\tquery: Buffer.from(file.before ?? \\\"\\\").toString(\\\"base64\\\"),\\r\\n\\t\\t\\t\\t}),\\r\\n\\t\\t\\t\\tvscode.Uri.parse(`${DIFF_VIEW_URI_SCHEME}:${file.relativePath}`).with({\\r\\n\\t\\t\\t\\t\\tquery: Buffer.from(file.after ?? \\\"\\\").toString(\\\"base64\\\"),\\r\\n\\t\\t\\t\\t}),\\r\\n\\t\\t\\t]),\\r\\n\\t\\t)\\r\\n\\t\\trelinquishButton()\\r\\n\\t}\\r\\n\\r\\n\\tasync doesLatestTaskCompletionHaveNewChanges() {\\r\\n\\t\\tconst messageIndex = findLastIndex(this.clineMessages, (m) => m.say === \\\"completion_result\\\")\\r\\n\\t\\tconst message = this.clineMessages[messageIndex]\\r\\n\\t\\tif (!message) {\\r\\n\\t\\t\\tconsole.error(\\\"Completion message not found\\\")\\r\\n\\t\\t\\treturn false\\r\\n\\t\\t}\\r\\n\\t\\tconst hash = message.lastCheckpointHash\\r\\n\\t\\tif (!hash) {\\r\\n\\t\\t\\tconsole.error(\\\"No checkpoint hash found\\\")\\r\\n\\t\\t\\treturn false\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!this.checkpointTracker) {\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\tthis.checkpointTracker = await CheckpointTracker.create(this.taskId, this.providerRef.deref())\\r\\n\\t\\t\\t\\tthis.checkpointTrackerErrorMessage = undefined\\r\\n\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\tconst errorMessage = error instanceof Error ? error.message : \\\"Unknown error\\\"\\r\\n\\t\\t\\t\\tconsole.error(\\\"Failed to initialize checkpoint tracker:\\\", errorMessage)\\r\\n\\t\\t\\t\\treturn false\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// Get last task completed\\r\\n\\t\\tconst lastTaskCompletedMessage = findLast(this.clineMessages.slice(0, messageIndex), (m) => m.say === \\\"completion_result\\\")\\r\\n\\r\\n\\t\\ttry {\\r\\n\\t\\t\\t// Get changed files between current state and commit\\r\\n\\t\\t\\tconst changedFiles = await this.checkpointTracker?.getDiffSet(\\r\\n\\t\\t\\t\\tlastTaskCompletedMessage?.lastCheckpointHash, // if undefined, then we get diff from beginning of git history, AKA when the task was started\\r\\n\\t\\t\\t\\thash,\\r\\n\\t\\t\\t)\\r\\n\\t\\t\\tconst changedFilesCount = changedFiles?.length || 0\\r\\n\\t\\t\\tif (changedFilesCount > 0) {\\r\\n\\t\\t\\t\\treturn true\\r\\n\\t\\t\\t}\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\tconsole.error(\\\"Failed to get diff set:\\\", error)\\r\\n\\t\\t\\treturn false\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn false\\r\\n\\t}\\r\\n\\r\\n\\t// Communicate with webview\\r\\n\\r\\n\\t// partial has three valid states true (partial message), false (completion of partial message), undefined (individual complete message)\\r\\n\\tasync ask(\\r\\n\\t\\ttype: ClineAsk,\\r\\n\\t\\ttext?: string,\\r\\n\\t\\tpartial?: boolean,\\r\\n\\t): Promise<{\\r\\n\\t\\tresponse: ClineAskResponse\\r\\n\\t\\ttext?: string\\r\\n\\t\\timages?: string[]\\r\\n\\t}> {\\r\\n\\t\\t// If this Cline instance was aborted by the provider, then the only thing keeping us alive is a promise still running in the background, in which case we don't want to send its result to the webview as it is attached to a new instance of Cline now. So we can safely ignore the result of any active promises, and this class will be deallocated. (Although we set Cline = undefined in provider, that simply removes the reference to this instance, but the instance is still alive until this promise resolves or rejects.)\\r\\n\\t\\tif (this.abort) {\\r\\n\\t\\t\\tthrow new Error(\\\"Cline instance aborted\\\")\\r\\n\\t\\t}\\r\\n\\t\\tlet askTs: number\\r\\n\\t\\tif (partial !== undefined) {\\r\\n\\t\\t\\tconst lastMessage = this.clineMessages.at(-1)\\r\\n\\t\\t\\tconst isUpdatingPreviousPartial =\\r\\n\\t\\t\\t\\tlastMessage && lastMessage.partial && lastMessage.type === \\\"ask\\\" && lastMessage.ask === type\\r\\n\\t\\t\\tif (partial) {\\r\\n\\t\\t\\t\\tif (isUpdatingPreviousPartial) {\\r\\n\\t\\t\\t\\t\\t// existing partial message, so update it\\r\\n\\t\\t\\t\\t\\tlastMessage.text = text\\r\\n\\t\\t\\t\\t\\tlastMessage.partial = partial\\r\\n\\t\\t\\t\\t\\t// 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\\r\\n\\t\\t\\t\\t\\t// await this.saveClineMessages()\\r\\n\\t\\t\\t\\t\\t// await this.providerRef.deref()?.postStateToWebview()\\r\\n\\t\\t\\t\\t\\tawait this.providerRef.deref()?.postMessageToWebview({\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"partialMessage\\\",\\r\\n\\t\\t\\t\\t\\t\\tpartialMessage: lastMessage,\\r\\n\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\tthrow new Error(\\\"Current ask promise was ignored 1\\\")\\r\\n\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t// this is a new partial message, so add it with partial state\\r\\n\\t\\t\\t\\t\\t// this.askResponse = undefined\\r\\n\\t\\t\\t\\t\\t// this.askResponseText = undefined\\r\\n\\t\\t\\t\\t\\t// this.askResponseImages = undefined\\r\\n\\t\\t\\t\\t\\taskTs = Date.now()\\r\\n\\t\\t\\t\\t\\tthis.lastMessageTs = askTs\\r\\n\\t\\t\\t\\t\\tawait this.addToClineMessages({\\r\\n\\t\\t\\t\\t\\t\\tts: askTs,\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"ask\\\",\\r\\n\\t\\t\\t\\t\\t\\task: type,\\r\\n\\t\\t\\t\\t\\t\\ttext,\\r\\n\\t\\t\\t\\t\\t\\tpartial,\\r\\n\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\tawait this.providerRef.deref()?.postStateToWebview()\\r\\n\\t\\t\\t\\t\\tthrow new Error(\\\"Current ask promise was ignored 2\\\")\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t// partial=false means its a complete version of a previously partial message\\r\\n\\t\\t\\t\\tif (isUpdatingPreviousPartial) {\\r\\n\\t\\t\\t\\t\\t// this is the complete version of a previously partial message, so replace the partial with the complete version\\r\\n\\t\\t\\t\\t\\tthis.askResponse = undefined\\r\\n\\t\\t\\t\\t\\tthis.askResponseText = undefined\\r\\n\\t\\t\\t\\t\\tthis.askResponseImages = undefined\\r\\n\\r\\n\\t\\t\\t\\t\\t/*\\r\\n\\t\\t\\t\\t\\tBug for the history books:\\r\\n\\t\\t\\t\\t\\tIn the webview we use the ts as the chatrow key for the virtuoso list. Since we would update this ts right at the end of streaming, it would cause the view to flicker. The key prop has to be stable otherwise react has trouble reconciling items between renders, causing unmounting and remounting of components (flickering).\\r\\n\\t\\t\\t\\t\\tThe lesson here is if you see flickering when rendering lists, it's likely because the key prop is not stable.\\r\\n\\t\\t\\t\\t\\tSo in this case we must make sure that the message ts is never altered after first setting it.\\r\\n\\t\\t\\t\\t\\t*/\\r\\n\\t\\t\\t\\t\\taskTs = lastMessage.ts\\r\\n\\t\\t\\t\\t\\tthis.lastMessageTs = askTs\\r\\n\\t\\t\\t\\t\\t// lastMessage.ts = askTs\\r\\n\\t\\t\\t\\t\\tlastMessage.text = text\\r\\n\\t\\t\\t\\t\\tlastMessage.partial = false\\r\\n\\t\\t\\t\\t\\tawait this.saveClineMessages()\\r\\n\\t\\t\\t\\t\\t// await this.providerRef.deref()?.postStateToWebview()\\r\\n\\t\\t\\t\\t\\tawait this.providerRef.deref()?.postMessageToWebview({\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"partialMessage\\\",\\r\\n\\t\\t\\t\\t\\t\\tpartialMessage: lastMessage,\\r\\n\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t// this is a new partial=false message, so add it like normal\\r\\n\\t\\t\\t\\t\\tthis.askResponse = undefined\\r\\n\\t\\t\\t\\t\\tthis.askResponseText = undefined\\r\\n\\t\\t\\t\\t\\tthis.askResponseImages = undefined\\r\\n\\t\\t\\t\\t\\taskTs = Date.now()\\r\\n\\t\\t\\t\\t\\tthis.lastMessageTs = askTs\\r\\n\\t\\t\\t\\t\\tawait this.addToClineMessages({\\r\\n\\t\\t\\t\\t\\t\\tts: askTs,\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"ask\\\",\\r\\n\\t\\t\\t\\t\\t\\task: type,\\r\\n\\t\\t\\t\\t\\t\\ttext,\\r\\n\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\tawait this.providerRef.deref()?.postStateToWebview()\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t} else {\\r\\n\\t\\t\\t// this is a new non-partial message, so add it like normal\\r\\n\\t\\t\\t// const lastMessage = this.clineMessages.at(-1)\\r\\n\\t\\t\\tthis.askResponse = undefined\\r\\n\\t\\t\\tthis.askResponseText = undefined\\r\\n\\t\\t\\tthis.askResponseImages = undefined\\r\\n\\t\\t\\taskTs = Date.now()\\r\\n\\t\\t\\tthis.lastMessageTs = askTs\\r\\n\\t\\t\\tawait this.addToClineMessages({\\r\\n\\t\\t\\t\\tts: askTs,\\r\\n\\t\\t\\t\\ttype: \\\"ask\\\",\\r\\n\\t\\t\\t\\task: type,\\r\\n\\t\\t\\t\\ttext,\\r\\n\\t\\t\\t})\\r\\n\\t\\t\\tawait this.providerRef.deref()?.postStateToWebview()\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tawait pWaitFor(() => this.askResponse !== undefined || this.lastMessageTs !== askTs, { interval: 100 })\\r\\n\\t\\tif (this.lastMessageTs !== askTs) {\\r\\n\\t\\t\\tthrow new Error(\\\"Current ask promise was ignored\\\") // could happen if we send multiple asks in a row i.e. with command_output. It's important that when we know an ask could fail, it is handled gracefully\\r\\n\\t\\t}\\r\\n\\t\\tconst result = {\\r\\n\\t\\t\\tresponse: this.askResponse!,\\r\\n\\t\\t\\ttext: this.askResponseText,\\r\\n\\t\\t\\timages: this.askResponseImages,\\r\\n\\t\\t}\\r\\n\\t\\tthis.askResponse = undefined\\r\\n\\t\\tthis.askResponseText = undefined\\r\\n\\t\\tthis.askResponseImages = undefined\\r\\n\\t\\treturn result\\r\\n\\t}\\r\\n\\r\\n\\tasync handleWebviewAskResponse(askResponse: ClineAskResponse, text?: string, images?: string[]) {\\r\\n\\t\\tthis.askResponse = askResponse\\r\\n\\t\\tthis.askResponseText = text\\r\\n\\t\\tthis.askResponseImages = images\\r\\n\\t}\\r\\n\\r\\n\\tasync say(type: ClineSay, text?: string, images?: string[], partial?: boolean): Promise<undefined> {\\r\\n\\t\\tif (this.abort) {\\r\\n\\t\\t\\tthrow new Error(\\\"Cline instance aborted\\\")\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (partial !== undefined) {\\r\\n\\t\\t\\tconst lastMessage = this.clineMessages.at(-1)\\r\\n\\t\\t\\tconst isUpdatingPreviousPartial =\\r\\n\\t\\t\\t\\tlastMessage && lastMessage.partial && lastMessage.type === \\\"say\\\" && lastMessage.say === type\\r\\n\\t\\t\\tif (partial) {\\r\\n\\t\\t\\t\\tif (isUpdatingPreviousPartial) {\\r\\n\\t\\t\\t\\t\\t// existing partial message, so update it\\r\\n\\t\\t\\t\\t\\tlastMessage.text = text\\r\\n\\t\\t\\t\\t\\tlastMessage.images = images\\r\\n\\t\\t\\t\\t\\tlastMessage.partial = partial\\r\\n\\t\\t\\t\\t\\tawait this.providerRef.deref()?.postMessageToWebview({\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"partialMessage\\\",\\r\\n\\t\\t\\t\\t\\t\\tpartialMessage: lastMessage,\\r\\n\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t// this is a new partial message, so add it with partial state\\r\\n\\t\\t\\t\\t\\tconst sayTs = Date.now()\\r\\n\\t\\t\\t\\t\\tthis.lastMessageTs = sayTs\\r\\n\\t\\t\\t\\t\\tawait this.addToClineMessages({\\r\\n\\t\\t\\t\\t\\t\\tts: sayTs,\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"say\\\",\\r\\n\\t\\t\\t\\t\\t\\tsay: type,\\r\\n\\t\\t\\t\\t\\t\\ttext,\\r\\n\\t\\t\\t\\t\\t\\timages,\\r\\n\\t\\t\\t\\t\\t\\tpartial,\\r\\n\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\tawait this.providerRef.deref()?.postStateToWebview()\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t// partial=false means its a complete version of a previously partial message\\r\\n\\t\\t\\t\\tif (isUpdatingPreviousPartial) {\\r\\n\\t\\t\\t\\t\\t// this is the complete version of a previously partial message, so replace the partial with the complete version\\r\\n\\t\\t\\t\\t\\tthis.lastMessageTs = lastMessage.ts\\r\\n\\t\\t\\t\\t\\t// lastMessage.ts = sayTs\\r\\n\\t\\t\\t\\t\\tlastMessage.text = text\\r\\n\\t\\t\\t\\t\\tlastMessage.images = images\\r\\n\\t\\t\\t\\t\\tlastMessage.partial = false\\r\\n\\r\\n\\t\\t\\t\\t\\t// instead of streaming partialMessage events, we do a save and post like normal to persist to disk\\r\\n\\t\\t\\t\\t\\tawait this.saveClineMessages()\\r\\n\\t\\t\\t\\t\\t// await this.providerRef.deref()?.postStateToWebview()\\r\\n\\t\\t\\t\\t\\tawait this.providerRef.deref()?.postMessageToWebview({\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"partialMessage\\\",\\r\\n\\t\\t\\t\\t\\t\\tpartialMessage: lastMessage,\\r\\n\\t\\t\\t\\t\\t}) // more performant than an entire postStateToWebview\\r\\n\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t// this is a new partial=false message, so add it like normal\\r\\n\\t\\t\\t\\t\\tconst sayTs = Date.now()\\r\\n\\t\\t\\t\\t\\tthis.lastMessageTs = sayTs\\r\\n\\t\\t\\t\\t\\tawait this.addToClineMessages({\\r\\n\\t\\t\\t\\t\\t\\tts: sayTs,\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"say\\\",\\r\\n\\t\\t\\t\\t\\t\\tsay: type,\\r\\n\\t\\t\\t\\t\\t\\ttext,\\r\\n\\t\\t\\t\\t\\t\\timages,\\r\\n\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\tawait this.providerRef.deref()?.postStateToWebview()\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t} else {\\r\\n\\t\\t\\t// this is a new non-partial message, so add it like normal\\r\\n\\t\\t\\tconst sayTs = Date.now()\\r\\n\\t\\t\\tthis.lastMessageTs = sayTs\\r\\n\\t\\t\\tawait this.addToClineMessages({\\r\\n\\t\\t\\t\\tts: sayTs,\\r\\n\\t\\t\\t\\ttype: \\\"say\\\",\\r\\n\\t\\t\\t\\tsay: type,\\r\\n\\t\\t\\t\\ttext,\\r\\n\\t\\t\\t\\timages,\\r\\n\\t\\t\\t})\\r\\n\\t\\t\\tawait this.providerRef.deref()?.postStateToWebview()\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tasync sayAndCreateMissingParamError(toolName: ToolUseName, paramName: string, relPath?: string) {\\r\\n\\t\\tawait this.say(\\r\\n\\t\\t\\t\\\"error\\\",\\r\\n\\t\\t\\t`Cline tried to use ${toolName}${\\r\\n\\t\\t\\t\\trelPath ? ` for '${relPath.toPosix()}'` : \\\"\\\"\\r\\n\\t\\t\\t} without value for required parameter '${paramName}'. Retrying...`,\\r\\n\\t\\t)\\r\\n\\t\\treturn formatResponse.toolError(formatResponse.missingToolParameterError(paramName))\\r\\n\\t}\\r\\n\\r\\n\\tasync removeLastPartialMessageIfExistsWithType(type: \\\"ask\\\" | \\\"say\\\", askOrSay: ClineAsk | ClineSay) {\\r\\n\\t\\tconst lastMessage = this.clineMessages.at(-1)\\r\\n\\t\\tif (lastMessage?.partial && lastMessage.type === type && (lastMessage.ask === askOrSay || lastMessage.say === askOrSay)) {\\r\\n\\t\\t\\tthis.clineMessages.pop()\\r\\n\\t\\t\\tawait this.saveClineMessages()\\r\\n\\t\\t\\tawait this.providerRef.deref()?.postStateToWebview()\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t// Task lifecycle\\r\\n\\r\\n\\tprivate async startTask(task?: string, images?: string[]): Promise<void> {\\r\\n\\t\\t// conversationHistory (for API) and clineMessages (for webview) need to be in sync\\r\\n\\t\\t// if the extension process were killed, then on restart the clineMessages might not be empty, so we need to set it to [] when we create a new Cline client (otherwise webview would show stale messages from previous session)\\r\\n\\t\\tthis.clineMessages = []\\r\\n\\t\\tthis.apiConversationHistory = []\\r\\n\\t\\tawait this.providerRef.deref()?.postStateToWebview()\\r\\n\\r\\n\\t\\tawait this.say(\\\"text\\\", task, images)\\r\\n\\r\\n\\t\\tthis.isInitialized = true\\r\\n\\r\\n\\t\\tlet imageBlocks: Anthropic.ImageBlockParam[] = formatResponse.imageBlocks(images)\\r\\n\\t\\tawait this.initiateTaskLoop(\\r\\n\\t\\t\\t[\\r\\n\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\ttext: `<task>\\\\n${task}\\\\n</task>`,\\r\\n\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t...imageBlocks,\\r\\n\\t\\t\\t],\\r\\n\\t\\t\\ttrue,\\r\\n\\t\\t)\\r\\n\\t}\\r\\n\\r\\n\\tprivate async resumeTaskFromHistory() {\\r\\n\\t\\t// TODO: right now we let users init checkpoints for old tasks, assuming they're continuing them from the same workspace (which we never tied to tasks, so no way for us to know if it's opened in the right workspace)\\r\\n\\t\\t// const doesShadowGitExist = await CheckpointTracker.doesShadowGitExist(this.taskId, this.providerRef.deref())\\r\\n\\t\\t// if (!doesShadowGitExist) {\\r\\n\\t\\t// \\tthis.checkpointTrackerErrorMessage = \\\"Checkpoints are only available for new tasks\\\"\\r\\n\\t\\t// }\\r\\n\\r\\n\\t\\tconst modifiedClineMessages = await this.getSavedClineMessages()\\r\\n\\r\\n\\t\\t// Remove any resume messages that may have been added before\\r\\n\\t\\tconst lastRelevantMessageIndex = findLastIndex(\\r\\n\\t\\t\\tmodifiedClineMessages,\\r\\n\\t\\t\\t(m) => !(m.ask === \\\"resume_task\\\" || m.ask === \\\"resume_completed_task\\\"),\\r\\n\\t\\t)\\r\\n\\t\\tif (lastRelevantMessageIndex !== -1) {\\r\\n\\t\\t\\tmodifiedClineMessages.splice(lastRelevantMessageIndex + 1)\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// since we don't use api_req_finished anymore, we need to check if the last api_req_started has a cost value, if it doesn't and no cancellation reason to present, then we remove it since it indicates an api request without any partial content streamed\\r\\n\\t\\tconst lastApiReqStartedIndex = findLastIndex(\\r\\n\\t\\t\\tmodifiedClineMessages,\\r\\n\\t\\t\\t(m) => m.type === \\\"say\\\" && m.say === \\\"api_req_started\\\",\\r\\n\\t\\t)\\r\\n\\t\\tif (lastApiReqStartedIndex !== -1) {\\r\\n\\t\\t\\tconst lastApiReqStarted = modifiedClineMessages[lastApiReqStartedIndex]\\r\\n\\t\\t\\tconst { cost, cancelReason }: ClineApiReqInfo = JSON.parse(lastApiReqStarted.text || \\\"{}\\\")\\r\\n\\t\\t\\tif (cost === undefined && cancelReason === undefined) {\\r\\n\\t\\t\\t\\tmodifiedClineMessages.splice(lastApiReqStartedIndex, 1)\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tawait this.overwriteClineMessages(modifiedClineMessages)\\r\\n\\t\\tthis.clineMessages = await this.getSavedClineMessages()\\r\\n\\r\\n\\t\\t// Now present the cline messages to the user and ask if they want to resume (NOTE: we ran into a bug before where the apiconversationhistory wouldnt be initialized when opening a old task, and it was because we were waiting for resume)\\r\\n\\t\\t// This is important in case the user deletes messages without resuming the task first\\r\\n\\t\\tthis.apiConversationHistory = await this.getSavedApiConversationHistory()\\r\\n\\r\\n\\t\\tconst lastClineMessage = this.clineMessages\\r\\n\\t\\t\\t.slice()\\r\\n\\t\\t\\t.reverse()\\r\\n\\t\\t\\t.find((m) => !(m.ask === \\\"resume_task\\\" || m.ask === \\\"resume_completed_task\\\")) // could be multiple resume tasks\\r\\n\\t\\t// const lastClineMessage = this.clineMessages[lastClineMessageIndex]\\r\\n\\t\\t// could be a completion result with a command\\r\\n\\t\\t// const secondLastClineMessage = this.clineMessages\\r\\n\\t\\t// \\t.slice()\\r\\n\\t\\t// \\t.reverse()\\r\\n\\t\\t// \\t.find(\\r\\n\\t\\t// \\t\\t(m, index) =>\\r\\n\\t\\t// \\t\\t\\tindex !== lastClineMessageIndex && !(m.ask === \\\"resume_task\\\" || m.ask === \\\"resume_completed_task\\\")\\r\\n\\t\\t// \\t)\\r\\n\\t\\t// (lastClineMessage?.ask === \\\"command\\\" && secondLastClineMessage?.ask === \\\"completion_result\\\")\\r\\n\\r\\n\\t\\tlet askType: ClineAsk\\r\\n\\t\\tif (lastClineMessage?.ask === \\\"completion_result\\\") {\\r\\n\\t\\t\\taskType = \\\"resume_completed_task\\\"\\r\\n\\t\\t} else {\\r\\n\\t\\t\\taskType = \\\"resume_task\\\"\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tthis.isInitialized = true\\r\\n\\r\\n\\t\\tconst { response, text, images } = await this.ask(askType) // calls poststatetowebview\\r\\n\\t\\tlet responseText: string | undefined\\r\\n\\t\\tlet responseImages: string[] | undefined\\r\\n\\t\\tif (response === \\\"messageResponse\\\") {\\r\\n\\t\\t\\tawait this.say(\\\"user_feedback\\\", text, images)\\r\\n\\t\\t\\tresponseText = text\\r\\n\\t\\t\\tresponseImages = images\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// 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\\r\\n\\r\\n\\t\\tlet existingApiConversationHistory: Anthropic.Messages.MessageParam[] = await this.getSavedApiConversationHistory()\\r\\n\\r\\n\\t\\t// 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\\r\\n\\t\\tconst conversationWithoutToolBlocks = existingApiConversationHistory.map((message) => {\\r\\n\\t\\t\\tif (Array.isArray(message.content)) {\\r\\n\\t\\t\\t\\tconst newContent = message.content.map((block) => {\\r\\n\\t\\t\\t\\t\\tif (block.type === \\\"tool_use\\\") {\\r\\n\\t\\t\\t\\t\\t\\t// it's important we convert to the new tool schema format so the model doesn't get confused about how to invoke tools\\r\\n\\t\\t\\t\\t\\t\\tconst inputAsXml = Object.entries(block.input as Record<string, string>)\\r\\n\\t\\t\\t\\t\\t\\t\\t.map(([key, value]) => `<${key}>\\\\n${value}\\\\n</${key}>`)\\r\\n\\t\\t\\t\\t\\t\\t\\t.join(\\\"\\\\n\\\")\\r\\n\\t\\t\\t\\t\\t\\treturn {\\r\\n\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\ttext: `<${block.name}>\\\\n${inputAsXml}\\\\n</${block.name}>`,\\r\\n\\t\\t\\t\\t\\t\\t} as Anthropic.Messages.TextBlockParam\\r\\n\\t\\t\\t\\t\\t} else if (block.type === \\\"tool_result\\\") {\\r\\n\\t\\t\\t\\t\\t\\t// Convert block.content to text block array, removing images\\r\\n\\t\\t\\t\\t\\t\\tconst contentAsTextBlocks = Array.isArray(block.content)\\r\\n\\t\\t\\t\\t\\t\\t\\t? block.content.filter((item) => item.type === \\\"text\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t: [{ type: \\\"text\\\", text: block.content }]\\r\\n\\t\\t\\t\\t\\t\\tconst textContent = contentAsTextBlocks.map((item) => item.text).join(\\\"\\\\n\\\\n\\\")\\r\\n\\t\\t\\t\\t\\t\\tconst toolName = findToolName(block.tool_use_id, existingApiConversationHistory)\\r\\n\\t\\t\\t\\t\\t\\treturn {\\r\\n\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\ttext: `[${toolName} Result]\\\\n\\\\n${textContent}`,\\r\\n\\t\\t\\t\\t\\t\\t} as Anthropic.Messages.TextBlockParam\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\treturn block\\r\\n\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\treturn { ...message, content: newContent }\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\treturn message\\r\\n\\t\\t})\\r\\n\\t\\texistingApiConversationHistory = conversationWithoutToolBlocks\\r\\n\\r\\n\\t\\t// FIXME: remove tool use blocks altogether\\r\\n\\r\\n\\t\\t// if the last message is an assistant message, we need to check if there's tool use since every tool use has to have a tool response\\r\\n\\t\\t// if there's no tool use and only a text block, then we can just add a user message\\r\\n\\t\\t// (note this isn't relevant anymore since we use custom tool prompts instead of tool use blocks, but this is here for legacy purposes in case users resume old tasks)\\r\\n\\r\\n\\t\\t// if the last message is a user message, we can need to get the assistant message before it to see if it made tool calls, and if so, fill in the remaining tool responses with 'interrupted'\\r\\n\\r\\n\\t\\tlet modifiedOldUserContent: UserContent // either the last message if its user message, or the user message before the last (assistant) message\\r\\n\\t\\tlet modifiedApiConversationHistory: Anthropic.Messages.MessageParam[] // need to remove the last user message to replace with new modified user message\\r\\n\\t\\tif (existingApiConversationHistory.length > 0) {\\r\\n\\t\\t\\tconst lastMessage = existingApiConversationHistory[existingApiConversationHistory.length - 1]\\r\\n\\r\\n\\t\\t\\tif (lastMessage.role === \\\"assistant\\\") {\\r\\n\\t\\t\\t\\tconst content = Array.isArray(lastMessage.content)\\r\\n\\t\\t\\t\\t\\t? lastMessage.content\\r\\n\\t\\t\\t\\t\\t: [{ type: \\\"text\\\", text: lastMessage.content }]\\r\\n\\t\\t\\t\\tconst hasToolUse = content.some((block) => block.type === \\\"tool_use\\\")\\r\\n\\r\\n\\t\\t\\t\\tif (hasToolUse) {\\r\\n\\t\\t\\t\\t\\tconst toolUseBlocks = content.filter(\\r\\n\\t\\t\\t\\t\\t\\t(block) => block.type === \\\"tool_use\\\",\\r\\n\\t\\t\\t\\t\\t) as Anthropic.Messages.ToolUseBlock[]\\r\\n\\t\\t\\t\\t\\tconst toolResponses: Anthropic.ToolResultBlockParam[] = toolUseBlocks.map((block) => ({\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"tool_result\\\",\\r\\n\\t\\t\\t\\t\\t\\ttool_use_id: block.id,\\r\\n\\t\\t\\t\\t\\t\\tcontent: \\\"Task was interrupted before this tool call could be completed.\\\",\\r\\n\\t\\t\\t\\t\\t}))\\r\\n\\t\\t\\t\\t\\tmodifiedApiConversationHistory = [...existingApiConversationHistory] // no changes\\r\\n\\t\\t\\t\\t\\tmodifiedOldUserContent = [...toolResponses]\\r\\n\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\tmodifiedApiConversationHistory = [...existingApiConversationHistory]\\r\\n\\t\\t\\t\\t\\tmodifiedOldUserContent = []\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} else if (lastMessage.role === \\\"user\\\") {\\r\\n\\t\\t\\t\\tconst previousAssistantMessage: Anthropic.Messages.MessageParam | undefined =\\r\\n\\t\\t\\t\\t\\texistingApiConversationHistory[existingApiConversationHistory.length - 2]\\r\\n\\r\\n\\t\\t\\t\\tconst existingUserContent: UserContent = Array.isArray(lastMessage.content)\\r\\n\\t\\t\\t\\t\\t? lastMessage.content\\r\\n\\t\\t\\t\\t\\t: [{ type: \\\"text\\\", text: lastMessage.content }]\\r\\n\\t\\t\\t\\tif (previousAssistantMessage && previousAssistantMessage.role === \\\"assistant\\\") {\\r\\n\\t\\t\\t\\t\\tconst assistantContent = Array.isArray(previousAssistantMessage.content)\\r\\n\\t\\t\\t\\t\\t\\t? previousAssistantMessage.content\\r\\n\\t\\t\\t\\t\\t\\t: [\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttext: previousAssistantMessage.content,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\t\\t\\t]\\r\\n\\r\\n\\t\\t\\t\\t\\tconst toolUseBlocks = assistantContent.filter(\\r\\n\\t\\t\\t\\t\\t\\t(block) => block.type === \\\"tool_use\\\",\\r\\n\\t\\t\\t\\t\\t) as Anthropic.Messages.ToolUseBlock[]\\r\\n\\r\\n\\t\\t\\t\\t\\tif (toolUseBlocks.length > 0) {\\r\\n\\t\\t\\t\\t\\t\\tconst existingToolResults = existingUserContent.filter(\\r\\n\\t\\t\\t\\t\\t\\t\\t(block) => block.type === \\\"tool_result\\\",\\r\\n\\t\\t\\t\\t\\t\\t) as Anthropic.ToolResultBlockParam[]\\r\\n\\r\\n\\t\\t\\t\\t\\t\\tconst missingToolResponses: Anthropic.ToolResultBlockParam[] = toolUseBlocks\\r\\n\\t\\t\\t\\t\\t\\t\\t.filter((toolUse) => !existingToolResults.some((result) => result.tool_use_id === toolUse.id))\\r\\n\\t\\t\\t\\t\\t\\t\\t.map((toolUse) => ({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"tool_result\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttool_use_id: toolUse.id,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tcontent: \\\"Task was interrupted before this tool call could be completed.\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t}))\\r\\n\\r\\n\\t\\t\\t\\t\\t\\tmodifiedApiConversationHistory = existingApiConversationHistory.slice(0, -1) // removes the last user message\\r\\n\\t\\t\\t\\t\\t\\tmodifiedOldUserContent = [...existingUserContent, ...missingToolResponses]\\r\\n\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\tmodifiedApiConversationHistory = existingApiConversationHistory.slice(0, -1)\\r\\n\\t\\t\\t\\t\\t\\tmodifiedOldUserContent = [...existingUserContent]\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\tmodifiedApiConversationHistory = existingApiConversationHistory.slice(0, -1)\\r\\n\\t\\t\\t\\t\\tmodifiedOldUserContent = [...existingUserContent]\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tthrow new Error(\\\"Unexpected: Last message is not a user or assistant message\\\")\\r\\n\\t\\t\\t}\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tthrow new Error(\\\"Unexpected: No existing API conversation history\\\")\\r\\n\\t\\t\\t// console.error(\\\"Unexpected: No existing API conversation history\\\")\\r\\n\\t\\t\\t// modifiedApiConversationHistory = []\\r\\n\\t\\t\\t// modifiedOldUserContent = []\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tlet newUserContent: UserContent = [...modifiedOldUserContent]\\r\\n\\r\\n\\t\\tconst agoText = (() => {\\r\\n\\t\\t\\tconst timestamp = lastClineMessage?.ts ?? Date.now()\\r\\n\\t\\t\\tconst now = Date.now()\\r\\n\\t\\t\\tconst diff = now - timestamp\\r\\n\\t\\t\\tconst minutes = Math.floor(diff / 60000)\\r\\n\\t\\t\\tconst hours = Math.floor(minutes / 60)\\r\\n\\t\\t\\tconst days = Math.floor(hours / 24)\\r\\n\\r\\n\\t\\t\\tif (days > 0) {\\r\\n\\t\\t\\t\\treturn `${days} day${days > 1 ? \\\"s\\\" : \\\"\\\"} ago`\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tif (hours > 0) {\\r\\n\\t\\t\\t\\treturn `${hours} hour${hours > 1 ? \\\"s\\\" : \\\"\\\"} ago`\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tif (minutes > 0) {\\r\\n\\t\\t\\t\\treturn `${minutes} minute${minutes > 1 ? \\\"s\\\" : \\\"\\\"} ago`\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\treturn \\\"just now\\\"\\r\\n\\t\\t})()\\r\\n\\r\\n\\t\\tconst wasRecent = lastClineMessage?.ts && Date.now() - lastClineMessage.ts < 30_000\\r\\n\\r\\n\\t\\tnewUserContent.push({\\r\\n\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\ttext:\\r\\n\\t\\t\\t\\t`[TASK RESUMPTION] This task was interrupted ${agoText}. It may or may not be complete, so please reassess the task context. Be aware that the project state may have changed since then. The current working directory is now '${cwd.toPosix()}'. If the task has not been completed, retry the last step before interruption and proceed with completing the task.\\\\n\\\\nNote: If you previously attempted a tool use that the user did not provide a result for, you should assume the tool use was not successful and assess whether you should retry. If the last tool was a browser_action, the browser has been closed and you must launch a new browser if needed.${\\r\\n\\t\\t\\t\\t\\twasRecent\\r\\n\\t\\t\\t\\t\\t\\t? \\\"\\\\n\\\\nIMPORTANT: If the last tool use was a replace_in_file or write_to_file that was interrupted, the file was reverted back to its original state before the interrupted edit, and you do NOT need to re-read the file as you already have its up-to-date contents.\\\"\\r\\n\\t\\t\\t\\t\\t\\t: \\\"\\\"\\r\\n\\t\\t\\t\\t}` +\\r\\n\\t\\t\\t\\t(responseText\\r\\n\\t\\t\\t\\t\\t? `\\\\n\\\\nNew instructions for task continuation:\\\\n<user_message>\\\\n${responseText}\\\\n</user_message>`\\r\\n\\t\\t\\t\\t\\t: \\\"\\\"),\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tif (responseImages && responseImages.length > 0) {\\r\\n\\t\\t\\tnewUserContent.push(...formatResponse.imageBlocks(responseImages))\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tawait this.overwriteApiConversationHistory(modifiedApiConversationHistory)\\r\\n\\t\\tawait this.initiateTaskLoop(newUserContent, false)\\r\\n\\t}\\r\\n\\r\\n\\tprivate async initiateTaskLoop(userContent: UserContent, isNewTask: boolean): Promise<void> {\\r\\n\\t\\tlet nextUserContent = userContent\\r\\n\\t\\tlet includeFileDetails = true\\r\\n\\t\\twhile (!this.abort) {\\r\\n\\t\\t\\tconst didEndLoop = await this.recursivelyMakeClineRequests(nextUserContent, includeFileDetails, isNewTask)\\r\\n\\t\\t\\tincludeFileDetails = false // we only need file details the first time\\r\\n\\r\\n\\t\\t\\t// The way this agentic loop works is that cline will be given a task that he then calls tools to complete. unless there's an attempt_completion call, we keep responding back to him with his tool's responses until he either attempt_completion or does not use anymore tools. If he does not use anymore tools, we ask him to consider if he's completed the task and then call attempt_completion, otherwise proceed with completing the task.\\r\\n\\t\\t\\t// There is a MAX_REQUESTS_PER_TASK limit to prevent infinite requests, but Cline is prompted to finish the task as efficiently as he can.\\r\\n\\r\\n\\t\\t\\t//const totalCost = this.calculateApiCost(totalInputTokens, totalOutputTokens)\\r\\n\\t\\t\\tif (didEndLoop) {\\r\\n\\t\\t\\t\\t// For now a task never 'completes'. This will only happen if the user hits max requests and denies resetting the count.\\r\\n\\t\\t\\t\\t//this.say(\\\"task_completed\\\", `Task completed. Total API usage cost: ${totalCost}`)\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t// this.say(\\r\\n\\t\\t\\t\\t// \\t\\\"tool\\\",\\r\\n\\t\\t\\t\\t// \\t\\\"Cline responded with only text blocks but has not called attempt_completion yet. Forcing him to continue with task...\\\"\\r\\n\\t\\t\\t\\t// )\\r\\n\\t\\t\\t\\tnextUserContent = [\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\ttext: formatResponse.noToolsUsed(),\\r\\n\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t]\\r\\n\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tasync abortTask() {\\r\\n\\t\\tthis.abort = true // will stop any autonomously running promises\\r\\n\\t\\tthis.terminalManager.disposeAll()\\r\\n\\t\\tthis.urlContentFetcher.closeBrowser()\\r\\n\\t\\tthis.browserSession.closeBrowser()\\r\\n\\t\\tawait this.diffViewProvider.revertChanges() // need to await for when we want to make sure directories/files are reverted before re-starting the task from a checkpoint\\r\\n\\t}\\r\\n\\r\\n\\t// Checkpoints\\r\\n\\r\\n\\tasync saveCheckpoint() {\\r\\n\\t\\tconst commitHash = await this.checkpointTracker?.commit() // silently fails for now\\r\\n\\t\\tif (commitHash) {\\r\\n\\t\\t\\t// Start from the end and work backwards until we find a tool use or another message with a hash\\r\\n\\t\\t\\tfor (let i = this.clineMessages.length - 1; i >= 0; i--) {\\r\\n\\t\\t\\t\\tconst message = this.clineMessages[i]\\r\\n\\t\\t\\t\\tif (message.lastCheckpointHash) {\\r\\n\\t\\t\\t\\t\\t// Found a message with a hash, so we can stop\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t// Update this message with a hash\\r\\n\\t\\t\\t\\tmessage.lastCheckpointHash = commitHash\\r\\n\\r\\n\\t\\t\\t\\t// We only care about adding the hash to the last tool use (we don't want to add this hash to every prior message ie for tasks pre-checkpoint)\\r\\n\\t\\t\\t\\tconst isToolUse =\\r\\n\\t\\t\\t\\t\\tmessage.say === \\\"tool\\\" ||\\r\\n\\t\\t\\t\\t\\tmessage.ask === \\\"tool\\\" ||\\r\\n\\t\\t\\t\\t\\tmessage.say === \\\"command\\\" ||\\r\\n\\t\\t\\t\\t\\tmessage.ask === \\\"command\\\" ||\\r\\n\\t\\t\\t\\t\\tmessage.say === \\\"completion_result\\\" ||\\r\\n\\t\\t\\t\\t\\tmessage.ask === \\\"completion_result\\\" ||\\r\\n\\t\\t\\t\\t\\tmessage.ask === \\\"followup\\\" ||\\r\\n\\t\\t\\t\\t\\tmessage.say === \\\"use_mcp_server\\\" ||\\r\\n\\t\\t\\t\\t\\tmessage.ask === \\\"use_mcp_server\\\" ||\\r\\n\\t\\t\\t\\t\\tmessage.say === \\\"browser_action\\\" ||\\r\\n\\t\\t\\t\\t\\tmessage.say === \\\"browser_action_launch\\\" ||\\r\\n\\t\\t\\t\\t\\tmessage.ask === \\\"browser_action_launch\\\"\\r\\n\\r\\n\\t\\t\\t\\tif (isToolUse) {\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t// Save the updated messages\\r\\n\\t\\t\\tawait this.saveClineMessages()\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t// Tools\\r\\n\\r\\n\\tasync executeCommandTool(command: string): Promise<[boolean, ToolResponse]> {\\r\\n\\t\\tconst terminalInfo = await this.terminalManager.getOrCreateTerminal(cwd)\\r\\n\\t\\tterminalInfo.terminal.show() // weird visual bug when creating new terminals (even manually) where there's an empty space at the top.\\r\\n\\t\\tconst process = this.terminalManager.runCommand(terminalInfo, command)\\r\\n\\r\\n\\t\\tlet userFeedback: { text?: string; images?: string[] } | undefined\\r\\n\\t\\tlet didContinue = false\\r\\n\\t\\tconst sendCommandOutput = async (line: string): Promise<void> => {\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\tconst { response, text, images } = await this.ask(\\\"command_output\\\", line)\\r\\n\\t\\t\\t\\tif (response === \\\"yesButtonClicked\\\") {\\r\\n\\t\\t\\t\\t\\t// proceed while running\\r\\n\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\tuserFeedback = { text, images }\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tdidContinue = true\\r\\n\\t\\t\\t\\tprocess.continue() // continue past the await\\r\\n\\t\\t\\t} catch {\\r\\n\\t\\t\\t\\t// This can only happen if this ask promise was ignored, so ignore this error\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tlet result = \\\"\\\"\\r\\n\\t\\tprocess.on(\\\"line\\\", (line) => {\\r\\n\\t\\t\\tresult += line + \\\"\\\\n\\\"\\r\\n\\t\\t\\tif (!didContinue) {\\r\\n\\t\\t\\t\\tsendCommandOutput(line)\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tthis.say(\\\"command_output\\\", line)\\r\\n\\t\\t\\t}\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tlet completed = false\\r\\n\\t\\tprocess.once(\\\"completed\\\", () => {\\r\\n\\t\\t\\tcompleted = true\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tprocess.once(\\\"no_shell_integration\\\", async () => {\\r\\n\\t\\t\\tawait this.say(\\\"shell_integration_warning\\\")\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tawait process\\r\\n\\r\\n\\t\\t// Wait for a short delay to ensure all messages are sent to the webview\\r\\n\\t\\t// This delay allows time for non-awaited promises to be created and\\r\\n\\t\\t// for their associated messages to be sent to the webview, maintaining\\r\\n\\t\\t// the correct order of messages (although the webview is smart about\\r\\n\\t\\t// grouping command_output messages despite any gaps anyways)\\r\\n\\t\\tawait delay(50)\\r\\n\\r\\n\\t\\tresult = result.trim()\\r\\n\\r\\n\\t\\tif (userFeedback) {\\r\\n\\t\\t\\tawait this.say(\\\"user_feedback\\\", userFeedback.text, userFeedback.images)\\r\\n\\t\\t\\treturn [\\r\\n\\t\\t\\t\\ttrue,\\r\\n\\t\\t\\t\\tformatResponse.toolResult(\\r\\n\\t\\t\\t\\t\\t`Command is still running in the user's terminal.${\\r\\n\\t\\t\\t\\t\\t\\tresult.length > 0 ? `\\\\nHere's the output so far:\\\\n${result}` : \\\"\\\"\\r\\n\\t\\t\\t\\t\\t}\\\\n\\\\nThe user provided the following feedback:\\\\n<feedback>\\\\n${userFeedback.text}\\\\n</feedback>`,\\r\\n\\t\\t\\t\\t\\tuserFeedback.images,\\r\\n\\t\\t\\t\\t),\\r\\n\\t\\t\\t]\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (completed) {\\r\\n\\t\\t\\treturn [false, `Command executed.${result.length > 0 ? `\\\\nOutput:\\\\n${result}` : \\\"\\\"}`]\\r\\n\\t\\t} else {\\r\\n\\t\\t\\treturn [\\r\\n\\t\\t\\t\\tfalse,\\r\\n\\t\\t\\t\\t`Command is still running in the user's terminal.${\\r\\n\\t\\t\\t\\t\\tresult.length > 0 ? `\\\\nHere's the output so far:\\\\n${result}` : \\\"\\\"\\r\\n\\t\\t\\t\\t}\\\\n\\\\nYou will be updated on the terminal status and new output in the future.`,\\r\\n\\t\\t\\t]\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tshouldAutoApproveTool(toolName: ToolUseName): boolean {\\r\\n\\t\\tif (this.autoApprovalSettings.enabled) {\\r\\n\\t\\t\\tswitch (toolName) {\\r\\n\\t\\t\\t\\tcase \\\"read_file\\\":\\r\\n\\t\\t\\t\\tcase \\\"list_files\\\":\\r\\n\\t\\t\\t\\tcase \\\"list_code_definition_names\\\":\\r\\n\\t\\t\\t\\tcase \\\"search_files\\\":\\r\\n\\t\\t\\t\\t\\treturn this.autoApprovalSettings.actions.readFiles\\r\\n\\t\\t\\t\\tcase \\\"write_to_file\\\":\\r\\n\\t\\t\\t\\tcase \\\"replace_in_file\\\":\\r\\n\\t\\t\\t\\t\\treturn this.autoApprovalSettings.actions.editFiles\\r\\n\\t\\t\\t\\tcase \\\"execute_command\\\":\\r\\n\\t\\t\\t\\t\\treturn this.autoApprovalSettings.actions.executeCommands\\r\\n\\t\\t\\t\\tcase \\\"browser_action\\\":\\r\\n\\t\\t\\t\\t\\treturn this.autoApprovalSettings.actions.useBrowser\\r\\n\\t\\t\\t\\tcase \\\"access_mcp_resource\\\":\\r\\n\\t\\t\\t\\tcase \\\"use_mcp_tool\\\":\\r\\n\\t\\t\\t\\t\\treturn this.autoApprovalSettings.actions.useMcp\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\treturn false\\r\\n\\t}\\r\\n\\r\\n\\tasync *attemptApiRequest(previousApiReqIndex: number): ApiStream {\\r\\n\\t\\t// Wait for MCP servers to be connected before generating system prompt\\r\\n\\t\\tawait pWaitFor(() => this.providerRef.deref()?.mcpHub?.isConnecting !== true, { timeout: 10_000 }).catch(() => {\\r\\n\\t\\t\\tconsole.error(\\\"MCP servers failed to connect in time\\\")\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tconst mcpHub = this.providerRef.deref()?.mcpHub\\r\\n\\t\\tif (!mcpHub) {\\r\\n\\t\\t\\tthrow new Error(\\\"MCP hub not available\\\")\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tlet systemPrompt = await SYSTEM_PROMPT(cwd, this.api.getModel().info.supportsComputerUse ?? false, mcpHub)\\r\\n\\t\\tlet settingsCustomInstructions = this.customInstructions?.trim()\\r\\n\\t\\tconst clineRulesFilePath = path.resolve(cwd, GlobalFileNames.clineRules)\\r\\n\\t\\tlet clineRulesFileInstructions: string | undefined\\r\\n\\t\\tif (await fileExistsAtPath(clineRulesFilePath)) {\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\tconst ruleFileContent = (await fs.readFile(clineRulesFilePath, \\\"utf8\\\")).trim()\\r\\n\\t\\t\\t\\tif (ruleFileContent) {\\r\\n\\t\\t\\t\\t\\tclineRulesFileInstructions = `# .clinerules\\\\n\\\\nThe following is provided by a root-level .clinerules file where the user has specified instructions for this working directory (${cwd.toPosix()})\\\\n\\\\n${ruleFileContent}`\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} catch {\\r\\n\\t\\t\\t\\tconsole.error(`Failed to read .clinerules file at ${clineRulesFilePath}`)\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (settingsCustomInstructions || clineRulesFileInstructions) {\\r\\n\\t\\t\\t// altering the system prompt mid-task will break the prompt cache, but in the grand scheme this will not change often so it's better to not pollute user messages with it the way we have to with <potentially relevant details>\\r\\n\\t\\t\\tsystemPrompt += addUserInstructions(settingsCustomInstructions, clineRulesFileInstructions)\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// If the previous API request's total token usage is close to the context window, truncate the conversation history to free up space for the new request\\r\\n\\t\\tif (previousApiReqIndex >= 0) {\\r\\n\\t\\t\\tconst previousRequest = this.clineMessages[previousApiReqIndex]\\r\\n\\t\\t\\tif (previousRequest && previousRequest.text) {\\r\\n\\t\\t\\t\\tconst { tokensIn, tokensOut, cacheWrites, cacheReads }: ClineApiReqInfo = JSON.parse(previousRequest.text)\\r\\n\\t\\t\\t\\tconst totalTokens = (tokensIn || 0) + (tokensOut || 0) + (cacheWrites || 0) + (cacheReads || 0)\\r\\n\\t\\t\\t\\tlet contextWindow = this.api.getModel().info.contextWindow || 128_000\\r\\n\\t\\t\\t\\t// 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\\r\\n\\t\\t\\t\\tif (this.api instanceof OpenAiHandler && this.api.getModel().id.toLowerCase().includes(\\\"deepseek\\\")) {\\r\\n\\t\\t\\t\\t\\tcontextWindow = 64_000\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tlet maxAllowedSize: number\\r\\n\\t\\t\\t\\tswitch (contextWindow) {\\r\\n\\t\\t\\t\\t\\tcase 64_000: // deepseek models\\r\\n\\t\\t\\t\\t\\t\\tmaxAllowedSize = contextWindow - 27_000\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase 128_000: // most models\\r\\n\\t\\t\\t\\t\\t\\tmaxAllowedSize = contextWindow - 30_000\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tcase 200_000: // claude models\\r\\n\\t\\t\\t\\t\\t\\tmaxAllowedSize = contextWindow - 40_000\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\t\\t\\tmaxAllowedSize = Math.max(contextWindow - 40_000, contextWindow * 0.8) // for deepseek, 80% of 64k meant only ~10k buffer which was too small and resulted in users getting context window errors.\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t// This is the most reliable way to know when we're close to hitting the context window.\\r\\n\\t\\t\\t\\tif (totalTokens >= maxAllowedSize) {\\r\\n\\t\\t\\t\\t\\t// NOTE: it's okay that we overwriteConversationHistory in resume task since we're only ever removing the last user message and not anything in the middle which would affect this range\\r\\n\\t\\t\\t\\t\\tthis.conversationHistoryDeletedRange = getNextTruncationRange(\\r\\n\\t\\t\\t\\t\\t\\tthis.apiConversationHistory,\\r\\n\\t\\t\\t\\t\\t\\tthis.conversationHistoryDeletedRange,\\r\\n\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\tawait this.saveClineMessages() // saves task history item which we use to keep track of conversation history deleted range\\r\\n\\t\\t\\t\\t\\t// await this.overwriteApiConversationHistory(truncatedMessages)\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// conversationHistoryDeletedRange is updated only when we're close to hitting the context window, so we don't continuously break the prompt cache\\r\\n\\t\\tconst truncatedConversationHistory = getTruncatedMessages(\\r\\n\\t\\t\\tthis.apiConversationHistory,\\r\\n\\t\\t\\tthis.conversationHistoryDeletedRange,\\r\\n\\t\\t)\\r\\n\\r\\n\\t\\tconst stream = this.api.createMessage(systemPrompt, truncatedConversationHistory)\\r\\n\\t\\tconst iterator = stream[Symbol.asyncIterator]()\\r\\n\\r\\n\\t\\ttry {\\r\\n\\t\\t\\t// awaiting first chunk to see if it will throw an error\\r\\n\\t\\t\\tconst firstChunk = await iterator.next()\\r\\n\\t\\t\\tyield firstChunk.value\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\t// 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.\\r\\n\\t\\t\\tconst { response } = await this.ask(\\\"api_req_failed\\\", error.message ?? JSON.stringify(serializeError(error), null, 2))\\r\\n\\t\\t\\tif (response !== \\\"yesButtonClicked\\\") {\\r\\n\\t\\t\\t\\t// this will never happen since if noButtonClicked, we will clear current task, aborting this instance\\r\\n\\t\\t\\t\\tthrow new Error(\\\"API request failed\\\")\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tawait this.say(\\\"api_req_retried\\\")\\r\\n\\t\\t\\t// delegate generator output from the recursive call\\r\\n\\t\\t\\tyield* this.attemptApiRequest(previousApiReqIndex)\\r\\n\\t\\t\\treturn\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// no error, so we can continue to yield all remaining chunks\\r\\n\\t\\t// (needs to be placed outside of try/catch since it we want caller to handle errors not with api_req_failed as that is reserved for first chunk failures only)\\r\\n\\t\\t// this delegates to another generator or iterable object. In this case, it's saying \\\"yield all remaining values from this iterator\\\". This effectively passes along all subsequent chunks from the original stream.\\r\\n\\t\\tyield* iterator\\r\\n\\t}\\r\\n\\r\\n\\tasync presentAssistantMessage() {\\r\\n\\t\\tif (this.abort) {\\r\\n\\t\\t\\tthrow new Error(\\\"Cline instance aborted\\\")\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (this.presentAssistantMessageLocked) {\\r\\n\\t\\t\\tthis.presentAssistantMessageHasPendingUpdates = true\\r\\n\\t\\t\\treturn\\r\\n\\t\\t}\\r\\n\\t\\tthis.presentAssistantMessageLocked = true\\r\\n\\t\\tthis.presentAssistantMessageHasPendingUpdates = false\\r\\n\\r\\n\\t\\tif (this.currentStreamingContentIndex >= this.assistantMessageContent.length) {\\r\\n\\t\\t\\t// this may happen if the last content block was completed before streaming could finish. if streaming is finished, and we're out of bounds then this means we already presented/executed the last content block and are ready to continue to next request\\r\\n\\t\\t\\tif (this.didCompleteReadingStream) {\\r\\n\\t\\t\\t\\tthis.userMessageContentReady = true\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t// console.log(\\\"no more content blocks to stream! this shouldn't happen?\\\")\\r\\n\\t\\t\\tthis.presentAssistantMessageLocked = false\\r\\n\\t\\t\\treturn\\r\\n\\t\\t\\t//throw new Error(\\\"No more content blocks to stream! This shouldn't happen...\\\") // remove and just return after testing\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tconst block = cloneDeep(this.assistantMessageContent[this.currentStreamingContentIndex]) // need to create copy bc while stream is updating the array, it could be updating the reference block properties too\\r\\n\\t\\tswitch (block.type) {\\r\\n\\t\\t\\tcase \\\"text\\\": {\\r\\n\\t\\t\\t\\tif (this.didRejectTool || this.didAlreadyUseTool) {\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tlet content = block.content\\r\\n\\t\\t\\t\\tif (content) {\\r\\n\\t\\t\\t\\t\\t// (have to do this for partial and complete since sending content in thinking tags to markdown renderer will automatically be removed)\\r\\n\\t\\t\\t\\t\\t// Remove end substrings of <thinking or </thinking (below xml parsing is only for opening tags)\\r\\n\\t\\t\\t\\t\\t// (this is done with the xml parsing below now, but keeping here for reference)\\r\\n\\t\\t\\t\\t\\t// content = content.replace(/<\\\\/?t(?:h(?:i(?:n(?:k(?:i(?:n(?:g)?)?)?)?)?)?)?$/, \\\"\\\")\\r\\n\\t\\t\\t\\t\\t// Remove all instances of <thinking> (with optional line break after) and </thinking> (with optional line break before)\\r\\n\\t\\t\\t\\t\\t// - Needs to be separate since we dont want to remove the line break before the first tag\\r\\n\\t\\t\\t\\t\\t// - Needs to happen before the xml parsing below\\r\\n\\t\\t\\t\\t\\tcontent = content.replace(/<thinking>\\\\s?/g, \\\"\\\")\\r\\n\\t\\t\\t\\t\\tcontent = content.replace(/\\\\s?<\\\\/thinking>/g, \\\"\\\")\\r\\n\\r\\n\\t\\t\\t\\t\\t// Remove partial XML tag at the very end of the content (for tool use and thinking tags)\\r\\n\\t\\t\\t\\t\\t// (prevents scrollview from jumping when tags are automatically removed)\\r\\n\\t\\t\\t\\t\\tconst lastOpenBracketIndex = content.lastIndexOf(\\\"<\\\")\\r\\n\\t\\t\\t\\t\\tif (lastOpenBracketIndex !== -1) {\\r\\n\\t\\t\\t\\t\\t\\tconst possibleTag = content.slice(lastOpenBracketIndex)\\r\\n\\t\\t\\t\\t\\t\\t// Check if there's a '>' after the last '<' (i.e., if the tag is complete) (complete thinking and tool tags will have been removed by now)\\r\\n\\t\\t\\t\\t\\t\\tconst hasCloseBracket = possibleTag.includes(\\\">\\\")\\r\\n\\t\\t\\t\\t\\t\\tif (!hasCloseBracket) {\\r\\n\\t\\t\\t\\t\\t\\t\\t// Extract the potential tag name\\r\\n\\t\\t\\t\\t\\t\\t\\tlet tagContent: string\\r\\n\\t\\t\\t\\t\\t\\t\\tif (possibleTag.startsWith(\\\"</\\\")) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttagContent = possibleTag.slice(2).trim()\\r\\n\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttagContent = possibleTag.slice(1).trim()\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t// Check if tagContent is likely an incomplete tag name (letters and underscores only)\\r\\n\\t\\t\\t\\t\\t\\t\\tconst isLikelyTagName = /^[a-zA-Z_]+$/.test(tagContent)\\r\\n\\t\\t\\t\\t\\t\\t\\t// Preemptively remove < or </ to keep from these artifacts showing up in chat (also handles closing thinking tags)\\r\\n\\t\\t\\t\\t\\t\\t\\tconst isOpeningOrClosing = possibleTag === \\\"<\\\" || possibleTag === \\\"</\\\"\\r\\n\\t\\t\\t\\t\\t\\t\\t// If the tag is incomplete and at the end, remove it from the content\\r\\n\\t\\t\\t\\t\\t\\t\\tif (isOpeningOrClosing || isLikelyTagName) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tcontent = content.slice(0, lastOpenBracketIndex).trim()\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tif (!block.partial) {\\r\\n\\t\\t\\t\\t\\t// Some models add code block artifacts (around the tool calls) which show up at the end of text content\\r\\n\\t\\t\\t\\t\\t// matches ``` with atleast one char after the last backtick, at the end of the string\\r\\n\\t\\t\\t\\t\\tconst match = content?.trimEnd().match(/```[a-zA-Z0-9_-]+$/)\\r\\n\\t\\t\\t\\t\\tif (match) {\\r\\n\\t\\t\\t\\t\\t\\tconst matchLength = match[0].length\\r\\n\\t\\t\\t\\t\\t\\tcontent = content.trimEnd().slice(0, -matchLength)\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tawait this.say(\\\"text\\\", content, undefined, block.partial)\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tcase \\\"tool_use\\\":\\r\\n\\t\\t\\t\\tconst toolDescription = () => {\\r\\n\\t\\t\\t\\t\\tswitch (block.name) {\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"execute_command\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\treturn `[${block.name} for '${block.params.command}']`\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"read_file\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\treturn `[${block.name} for '${block.params.path}']`\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"write_to_file\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\treturn `[${block.name} for '${block.params.path}']`\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"replace_in_file\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\treturn `[${block.name} for '${block.params.path}']`\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"search_files\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\treturn `[${block.name} for '${block.params.regex}'${\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tblock.params.file_pattern ? ` in '${block.params.file_pattern}'` : \\\"\\\"\\r\\n\\t\\t\\t\\t\\t\\t\\t}]`\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"list_files\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\treturn `[${block.name} for '${block.params.path}']`\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"list_code_definition_names\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\treturn `[${block.name} for '${block.params.path}']`\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"browser_action\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\treturn `[${block.name} for '${block.params.action}']`\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"use_mcp_tool\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\treturn `[${block.name} for '${block.params.server_name}']`\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"access_mcp_resource\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\treturn `[${block.name} for '${block.params.server_name}']`\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"ask_followup_question\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\treturn `[${block.name} for '${block.params.question}']`\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"attempt_completion\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\treturn `[${block.name}]`\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tif (this.didRejectTool) {\\r\\n\\t\\t\\t\\t\\t// ignore any tool content after user has rejected tool once\\r\\n\\t\\t\\t\\t\\tif (!block.partial) {\\r\\n\\t\\t\\t\\t\\t\\tthis.userMessageContent.push({\\r\\n\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\ttext: `Skipping tool ${toolDescription()} due to user rejecting a previous tool.`,\\r\\n\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t// partial tool after user rejected a previous tool\\r\\n\\t\\t\\t\\t\\t\\tthis.userMessageContent.push({\\r\\n\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\ttext: `Tool ${toolDescription()} was interrupted and not executed due to user rejecting a previous tool.`,\\r\\n\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tif (this.didAlreadyUseTool) {\\r\\n\\t\\t\\t\\t\\t// ignore any content after a tool has already been used\\r\\n\\t\\t\\t\\t\\tthis.userMessageContent.push({\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\ttext: `Tool [${block.name}] was not executed because a tool has already been used in this message. Only one tool may be used per message. You must assess the first tool's result before proceeding to use the next tool.`,\\r\\n\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tconst pushToolResult = (content: ToolResponse) => {\\r\\n\\t\\t\\t\\t\\tthis.userMessageContent.push({\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\ttext: `${toolDescription()} Result:`,\\r\\n\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\tif (typeof content === \\\"string\\\") {\\r\\n\\t\\t\\t\\t\\t\\tthis.userMessageContent.push({\\r\\n\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\ttext: content || \\\"(tool did not return anything)\\\",\\r\\n\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\tthis.userMessageContent.push(...content)\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t// once a tool result has been collected, ignore all other tool uses since we should only ever present one tool result per message\\r\\n\\t\\t\\t\\t\\tthis.didAlreadyUseTool = true\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tconst askApproval = async (type: ClineAsk, partialMessage?: string) => {\\r\\n\\t\\t\\t\\t\\tconst { response, text, images } = await this.ask(type, partialMessage, false)\\r\\n\\t\\t\\t\\t\\tif (response !== \\\"yesButtonClicked\\\") {\\r\\n\\t\\t\\t\\t\\t\\tif (response === \\\"messageResponse\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"user_feedback\\\", text, images)\\r\\n\\t\\t\\t\\t\\t\\t\\tpushToolResult(formatResponse.toolResult(formatResponse.toolDeniedWithFeedback(text), images))\\r\\n\\t\\t\\t\\t\\t\\t\\t// this.userMessageContent.push({\\r\\n\\t\\t\\t\\t\\t\\t\\t// \\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t// \\ttext: `${toolDescription()}`,\\r\\n\\t\\t\\t\\t\\t\\t\\t// })\\r\\n\\t\\t\\t\\t\\t\\t\\t// this.toolResults.push({\\r\\n\\t\\t\\t\\t\\t\\t\\t// \\ttype: \\\"tool_result\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t// \\ttool_use_id: toolUseId,\\r\\n\\t\\t\\t\\t\\t\\t\\t// \\tcontent: this.formatToolResponseWithImages(\\r\\n\\t\\t\\t\\t\\t\\t\\t// \\t\\tawait this.formatToolDeniedFeedback(text),\\r\\n\\t\\t\\t\\t\\t\\t\\t// \\t\\timages\\r\\n\\t\\t\\t\\t\\t\\t\\t// \\t),\\r\\n\\t\\t\\t\\t\\t\\t\\t// })\\r\\n\\t\\t\\t\\t\\t\\t\\tthis.didRejectTool = true\\r\\n\\t\\t\\t\\t\\t\\t\\treturn false\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\tpushToolResult(formatResponse.toolDenied())\\r\\n\\t\\t\\t\\t\\t\\t// this.toolResults.push({\\r\\n\\t\\t\\t\\t\\t\\t// \\ttype: \\\"tool_result\\\",\\r\\n\\t\\t\\t\\t\\t\\t// \\ttool_use_id: toolUseId,\\r\\n\\t\\t\\t\\t\\t\\t// \\tcontent: await this.formatToolDenied(),\\r\\n\\t\\t\\t\\t\\t\\t// })\\r\\n\\t\\t\\t\\t\\t\\tthis.didRejectTool = true\\r\\n\\t\\t\\t\\t\\t\\treturn false\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\treturn true\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tconst showNotificationForApprovalIfAutoApprovalEnabled = (message: string) => {\\r\\n\\t\\t\\t\\t\\tif (this.autoApprovalSettings.enabled && this.autoApprovalSettings.enableNotifications) {\\r\\n\\t\\t\\t\\t\\t\\tshowSystemNotification({\\r\\n\\t\\t\\t\\t\\t\\t\\tsubtitle: \\\"Approval Required\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\tmessage,\\r\\n\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tconst handleError = async (action: string, error: Error) => {\\r\\n\\t\\t\\t\\t\\tif (this.abandoned) {\\r\\n\\t\\t\\t\\t\\t\\tconsole.log(\\\"Ignoring error since task was abandoned (i.e. from task cancellation after resetting)\\\")\\r\\n\\t\\t\\t\\t\\t\\treturn\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tconst errorString = `Error ${action}: ${JSON.stringify(serializeError(error))}`\\r\\n\\t\\t\\t\\t\\tawait this.say(\\r\\n\\t\\t\\t\\t\\t\\t\\\"error\\\",\\r\\n\\t\\t\\t\\t\\t\\t`Error ${action}:\\\\n${error.message ?? JSON.stringify(serializeError(error), null, 2)}`,\\r\\n\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t// this.toolResults.push({\\r\\n\\t\\t\\t\\t\\t// \\ttype: \\\"tool_result\\\",\\r\\n\\t\\t\\t\\t\\t// \\ttool_use_id: toolUseId,\\r\\n\\t\\t\\t\\t\\t// \\tcontent: await this.formatToolError(errorString),\\r\\n\\t\\t\\t\\t\\t// })\\r\\n\\t\\t\\t\\t\\tpushToolResult(formatResponse.toolError(errorString))\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t// If block is partial, remove partial closing tag so its not presented to user\\r\\n\\t\\t\\t\\tconst removeClosingTag = (tag: ToolParamName, text?: string) => {\\r\\n\\t\\t\\t\\t\\tif (!block.partial) {\\r\\n\\t\\t\\t\\t\\t\\treturn text || \\\"\\\"\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tif (!text) {\\r\\n\\t\\t\\t\\t\\t\\treturn \\\"\\\"\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t// This regex dynamically constructs a pattern to match the closing tag:\\r\\n\\t\\t\\t\\t\\t// - Optionally matches whitespace before the tag\\r\\n\\t\\t\\t\\t\\t// - Matches '<' or '</' optionally followed by any subset of characters from the tag name\\r\\n\\t\\t\\t\\t\\tconst tagRegex = new RegExp(\\r\\n\\t\\t\\t\\t\\t\\t`\\\\\\\\s?<\\\\/?${tag\\r\\n\\t\\t\\t\\t\\t\\t\\t.split(\\\"\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t.map((char) => `(?:${char})?`)\\r\\n\\t\\t\\t\\t\\t\\t\\t.join(\\\"\\\")}$`,\\r\\n\\t\\t\\t\\t\\t\\t\\\"g\\\",\\r\\n\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\treturn text.replace(tagRegex, \\\"\\\")\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tif (block.name !== \\\"browser_action\\\") {\\r\\n\\t\\t\\t\\t\\tawait this.browserSession.closeBrowser()\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tswitch (block.name) {\\r\\n\\t\\t\\t\\t\\tcase \\\"write_to_file\\\":\\r\\n\\t\\t\\t\\t\\tcase \\\"replace_in_file\\\": {\\r\\n\\t\\t\\t\\t\\t\\tconst relPath: string | undefined = block.params.path\\r\\n\\t\\t\\t\\t\\t\\tlet content: string | undefined = block.params.content // for write_to_file\\r\\n\\t\\t\\t\\t\\t\\tlet diff: string | undefined = block.params.diff // for replace_in_file\\r\\n\\t\\t\\t\\t\\t\\tif (!relPath || (!content && !diff)) {\\r\\n\\t\\t\\t\\t\\t\\t\\t// checking for content/diff ensures relPath is complete\\r\\n\\t\\t\\t\\t\\t\\t\\t// wait so we can determine if it's a new file or editing an existing file\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t// Check if file exists using cached map or fs.access\\r\\n\\t\\t\\t\\t\\t\\tlet fileExists: boolean\\r\\n\\t\\t\\t\\t\\t\\tif (this.diffViewProvider.editType !== undefined) {\\r\\n\\t\\t\\t\\t\\t\\t\\tfileExists = this.diffViewProvider.editType === \\\"modify\\\"\\r\\n\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\tconst absolutePath = path.resolve(cwd, relPath)\\r\\n\\t\\t\\t\\t\\t\\t\\tfileExists = await fileExistsAtPath(absolutePath)\\r\\n\\t\\t\\t\\t\\t\\t\\tthis.diffViewProvider.editType = fileExists ? \\\"modify\\\" : \\\"create\\\"\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\t\\t\\t// Construct newContent from diff\\r\\n\\t\\t\\t\\t\\t\\t\\tlet newContent: string\\r\\n\\t\\t\\t\\t\\t\\t\\tif (diff) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (!this.api.getModel().id.includes(\\\"claude\\\")) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// deepseek models tend to use unescaped html entities in diffs\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tdiff = fixModelHtmlEscaping(diff)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tdiff = removeInvalidChars(diff)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tnewContent = await constructNewFileContent(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tdiff,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.diffViewProvider.originalContent || \\\"\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t!block.partial,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"diff_error\\\", relPath)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tformatResponse.toolError(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`${(error as Error)?.message}\\\\n\\\\n` +\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`This is likely because the SEARCH block content doesn't match exactly with what's in the file, or if you used multiple SEARCH/REPLACE blocks they may not have been in the order they appear in the file.\\\\n\\\\n` +\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`The file was reverted to its original state:\\\\n\\\\n` +\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`<file_content path=\\\"${relPath.toPosix()}\\\">\\\\n${this.diffViewProvider.originalContent}\\\\n</file_content>\\\\n\\\\n` +\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`Try again with a more precise SEARCH block.\\\\n(If you keep running into this error, you may use the write_to_file tool as a workaround.)`,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.diffViewProvider.revertChanges()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.diffViewProvider.reset()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t} else if (content) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tnewContent = content\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// pre-processing newContent for cases where weaker models might add artifacts like markdown codeblock markers (deepseek/llama) or extra escape characters (gemini)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (newContent.startsWith(\\\"```\\\")) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// this handles cases where it includes language specifiers like ```python ```js\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tnewContent = newContent.split(\\\"\\\\n\\\").slice(1).join(\\\"\\\\n\\\").trim()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (newContent.endsWith(\\\"```\\\")) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tnewContent = newContent.split(\\\"\\\\n\\\").slice(0, -1).join(\\\"\\\\n\\\").trim()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (!this.api.getModel().id.includes(\\\"claude\\\")) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// it seems not just llama models are doing this, but also gemini and potentially others\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tnewContent = fixModelHtmlEscaping(newContent)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tnewContent = removeInvalidChars(newContent)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// can't happen, since we already checked for content/diff above. but need to do this for type error\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\tnewContent = newContent.trimEnd() // remove any trailing newlines, since it's automatically inserted by the editor\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\tconst sharedMessageProps: ClineSayTool = {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttool: fileExists ? \\\"editedExistingFile\\\" : \\\"newFileCreated\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tpath: getReadablePath(cwd, removeClosingTag(\\\"path\\\", relPath)),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tcontent: diff || content,\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\tif (block.partial) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// update gui message\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst partialMessage = JSON.stringify(sharedMessageProps)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (this.shouldAutoApproveTool(block.name)) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"ask\\\", \\\"tool\\\") // in case the user changes auto-approval settings mid stream\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"tool\\\", partialMessage, undefined, block.partial)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"say\\\", \\\"tool\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.ask(\\\"tool\\\", partialMessage, block.partial).catch(() => {})\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// update editor\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (!this.diffViewProvider.isEditing) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// open the editor and prepare to stream content in\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.diffViewProvider.open(relPath)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// editor is open, stream content in\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait this.diffViewProvider.update(newContent, false)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (!relPath) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(await this.sayAndCreateMissingParamError(block.name, \\\"path\\\"))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.diffViewProvider.reset()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (block.name === \\\"replace_in_file\\\" && !diff) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\"replace_in_file\\\", \\\"diff\\\"))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.diffViewProvider.reset()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (block.name === \\\"write_to_file\\\" && !content) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\"write_to_file\\\", \\\"content\\\"))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.diffViewProvider.reset()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount = 0\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// if isEditingFile false, that means we have the full contents of the file already.\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// it's important to note how this function works, you can't make the assumption that the block.partial conditional will always be called since it may immediately get complete, non-partial data. So this part of the logic will always be called.\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// in other words, you must always repeat the block.partial logic here\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (!this.diffViewProvider.isEditing) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// show gui message before showing edit animation\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tconst partialMessage = JSON.stringify(sharedMessageProps)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.ask(\\\"tool\\\", partialMessage, true).catch(() => {}) // sending true for partial even though it's not a partial, this shows the edit row before the content is streamed into the editor\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.diffViewProvider.open(relPath)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait this.diffViewProvider.update(newContent, true)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait delay(300) // wait for diff view to update\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tthis.diffViewProvider.scrollToFirstDiff()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// showOmissionWarning(this.diffViewProvider.originalContent || \\\"\\\", newContent)\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst completeMessage = JSON.stringify({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t...sharedMessageProps,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tcontent: diff || content,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// ? formatResponse.createPrettyPatch(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// \\t\\trelPath,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// \\t\\tthis.diffViewProvider.originalContent,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// \\t\\tnewContent,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// \\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// : undefined,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} satisfies ClineSayTool)\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (this.shouldAutoApproveTool(block.name)) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"ask\\\", \\\"tool\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"tool\\\", completeMessage, undefined, false)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveAutoApprovedRequestsCount++\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// we need an artificial delay to let the diagnostics catch up to the changes\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait delay(3_500)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// If auto-approval is enabled but this tool wasn't auto-approved, send notification\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tshowNotificationForApprovalIfAutoApprovalEnabled(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`Cline wants to ${fileExists ? \\\"edit\\\" : \\\"create\\\"} ${path.basename(relPath)}`,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"say\\\", \\\"tool\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// const didApprove = await askApproval(\\\"tool\\\", completeMessage)\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// Need a more customized tool response for file edits to highlight the fact that the file was not updated (particularly important for deepseek)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tlet didApprove = true\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tconst { response, text, images } = await this.ask(\\\"tool\\\", completeMessage, false)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tif (response !== \\\"yesButtonClicked\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t// TODO: add similar context for other tool denial responses, to emphasize ie that a command was not run\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tconst fileDeniedNote = fileExists\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t? \\\"The file was not updated, and maintains its original contents.\\\"\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t: \\\"The file was not created.\\\"\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tif (response === \\\"messageResponse\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"user_feedback\\\", text, images)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tformatResponse.toolResult(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`The user denied this operation. ${fileDeniedNote}\\\\nThe user provided the following feedback:\\\\n<feedback>\\\\n${text}\\\\n</feedback>`,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\timages,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.didRejectTool = true\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tdidApprove = false\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(`The user denied this operation. ${fileDeniedNote}`)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.didRejectTool = true\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tdidApprove = false\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tif (!didApprove) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.diffViewProvider.revertChanges()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst { newProblemsMessage, userEdits, autoFormattingEdits, finalContent } =\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.diffViewProvider.saveChanges()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tthis.didEditFile = true // used to determine if we should wait for busy terminal to update before sending api request\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (userEdits) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\\"user_feedback_diff\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tJSON.stringify({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\ttool: fileExists ? \\\"editedExistingFile\\\" : \\\"newFileCreated\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tpath: getReadablePath(cwd, relPath),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tdiff: userEdits,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t} satisfies ClineSayTool),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`The user made the following updates to your content:\\\\n\\\\n${userEdits}\\\\n\\\\n` +\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t(autoFormattingEdits\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t? `The user's editor also applied the following auto-formatting to your content:\\\\n\\\\n${autoFormattingEdits}\\\\n\\\\n(Note: Pay close attention to changes such as single quotes being converted to double quotes, semicolons being removed or added, long lines being broken into multiple lines, adjusting indentation style, adding/removing trailing commas, etc. This will help you ensure future SEARCH/REPLACE operations to this file are accurate.)\\\\n\\\\n`\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t: \\\"\\\") +\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`The updated content, which includes both your original modifications and the additional edits, has been successfully saved to ${relPath.toPosix()}. Here is the full, updated content of the file that was saved:\\\\n\\\\n` +\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`<final_file_content path=\\\"${relPath.toPosix()}\\\">\\\\n${finalContent}\\\\n</final_file_content>\\\\n\\\\n` +\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`Please note:\\\\n` +\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`1. You do not need to re-write the file with these changes, as they have already been applied.\\\\n` +\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`2. Proceed with the task using this updated file content as the new baseline.\\\\n` +\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`3. If the user's edits have addressed part of the task or changed the requirements, adjust your approach accordingly.` +\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`4. IMPORTANT: For any future changes to this file, use the final_file_content shown above as your reference. This content reflects the current state of the file, including both user edits and any auto-formatting (e.g., if you used single quotes but the formatter converted them to double quotes). Always base your SEARCH/REPLACE operations on this final version to ensure accuracy.\\\\n` +\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`${newProblemsMessage}`,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`The content was successfully saved to ${relPath.toPosix()}.\\\\n\\\\n` +\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t(autoFormattingEdits\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t? `Along with your edits, the user's editor applied the following auto-formatting to your content:\\\\n\\\\n${autoFormattingEdits}\\\\n\\\\n(Note: Pay close attention to changes such as single quotes being converted to double quotes, semicolons being removed or added, long lines being broken into multiple lines, adjusting indentation style, adding/removing trailing commas, etc. This will help you ensure future SEARCH/REPLACE operations to this file are accurate.)\\\\n\\\\n`\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t: \\\"\\\") +\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`Here is the full, updated content of the file that was saved:\\\\n\\\\n` +\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`<final_file_content path=\\\"${relPath.toPosix()}\\\">\\\\n${finalContent}\\\\n</final_file_content>\\\\n\\\\n` +\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`IMPORTANT: For any future changes to this file, use the final_file_content shown above as your reference. This content reflects the current state of the file, including any auto-formatting (e.g., if you used single quotes but the formatter converted them to double quotes). Always base your SEARCH/REPLACE operations on this final version to ensure accuracy.\\\\n\\\\n` +\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`${newProblemsMessage}`,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait this.diffViewProvider.reset()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\t\\t\\tawait handleError(\\\"writing file\\\", error)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.diffViewProvider.revertChanges()\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.diffViewProvider.reset()\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tcase \\\"read_file\\\": {\\r\\n\\t\\t\\t\\t\\t\\tconst relPath: string | undefined = block.params.path\\r\\n\\t\\t\\t\\t\\t\\tconst sharedMessageProps: ClineSayTool = {\\r\\n\\t\\t\\t\\t\\t\\t\\ttool: \\\"readFile\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\tpath: getReadablePath(cwd, removeClosingTag(\\\"path\\\", relPath)),\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\t\\t\\tif (block.partial) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst partialMessage = JSON.stringify({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t...sharedMessageProps,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tcontent: undefined,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} satisfies ClineSayTool)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (this.shouldAutoApproveTool(block.name)) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"ask\\\", \\\"tool\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"tool\\\", partialMessage, undefined, block.partial)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"say\\\", \\\"tool\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.ask(\\\"tool\\\", partialMessage, block.partial).catch(() => {})\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (!relPath) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\"read_file\\\", \\\"path\\\"))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount = 0\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst absolutePath = path.resolve(cwd, relPath)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst completeMessage = JSON.stringify({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t...sharedMessageProps,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tcontent: absolutePath,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} satisfies ClineSayTool)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (this.shouldAutoApproveTool(block.name)) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"ask\\\", \\\"tool\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"tool\\\", completeMessage, undefined, false) // need to be sending partialValue bool, since undefined has its own purpose in that the message is treated neither as a partial or completion of a partial, but as a single complete message\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveAutoApprovedRequestsCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tshowNotificationForApprovalIfAutoApprovalEnabled(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`Cline wants to read ${path.basename(absolutePath)}`,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"say\\\", \\\"tool\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tconst didApprove = await askApproval(\\\"tool\\\", completeMessage)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tif (!didApprove) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// now execute the tool like normal\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst content = await extractTextFromFile(absolutePath)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(content)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\t\\t\\tawait handleError(\\\"reading file\\\", error)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tcase \\\"list_files\\\": {\\r\\n\\t\\t\\t\\t\\t\\tconst relDirPath: string | undefined = block.params.path\\r\\n\\t\\t\\t\\t\\t\\tconst recursiveRaw: string | undefined = block.params.recursive\\r\\n\\t\\t\\t\\t\\t\\tconst recursive = recursiveRaw?.toLowerCase() === \\\"true\\\"\\r\\n\\t\\t\\t\\t\\t\\tconst sharedMessageProps: ClineSayTool = {\\r\\n\\t\\t\\t\\t\\t\\t\\ttool: !recursive ? \\\"listFilesTopLevel\\\" : \\\"listFilesRecursive\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\tpath: getReadablePath(cwd, removeClosingTag(\\\"path\\\", relDirPath)),\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\t\\t\\tif (block.partial) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst partialMessage = JSON.stringify({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t...sharedMessageProps,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tcontent: \\\"\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} satisfies ClineSayTool)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (this.shouldAutoApproveTool(block.name)) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"ask\\\", \\\"tool\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"tool\\\", partialMessage, undefined, block.partial)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"say\\\", \\\"tool\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.ask(\\\"tool\\\", partialMessage, block.partial).catch(() => {})\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (!relDirPath) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\"list_files\\\", \\\"path\\\"))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount = 0\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst absolutePath = path.resolve(cwd, relDirPath)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst [files, didHitLimit] = await listFiles(absolutePath, recursive, 200)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst result = formatResponse.formatFilesList(absolutePath, files, didHitLimit)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst completeMessage = JSON.stringify({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t...sharedMessageProps,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tcontent: result,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} satisfies ClineSayTool)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (this.shouldAutoApproveTool(block.name)) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"ask\\\", \\\"tool\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"tool\\\", completeMessage, undefined, false)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveAutoApprovedRequestsCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tshowNotificationForApprovalIfAutoApprovalEnabled(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`Cline wants to view directory ${path.basename(absolutePath)}/`,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"say\\\", \\\"tool\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tconst didApprove = await askApproval(\\\"tool\\\", completeMessage)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tif (!didApprove) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(result)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\t\\t\\tawait handleError(\\\"listing files\\\", error)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tcase \\\"list_code_definition_names\\\": {\\r\\n\\t\\t\\t\\t\\t\\tconst relDirPath: string | undefined = block.params.path\\r\\n\\t\\t\\t\\t\\t\\tconst sharedMessageProps: ClineSayTool = {\\r\\n\\t\\t\\t\\t\\t\\t\\ttool: \\\"listCodeDefinitionNames\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\tpath: getReadablePath(cwd, removeClosingTag(\\\"path\\\", relDirPath)),\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\t\\t\\tif (block.partial) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst partialMessage = JSON.stringify({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t...sharedMessageProps,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tcontent: \\\"\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} satisfies ClineSayTool)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (this.shouldAutoApproveTool(block.name)) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"ask\\\", \\\"tool\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"tool\\\", partialMessage, undefined, block.partial)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"say\\\", \\\"tool\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.ask(\\\"tool\\\", partialMessage, block.partial).catch(() => {})\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (!relDirPath) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\"list_code_definition_names\\\", \\\"path\\\"))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount = 0\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst absolutePath = path.resolve(cwd, relDirPath)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst result = await parseSourceCodeForDefinitionsTopLevel(absolutePath)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst completeMessage = JSON.stringify({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t...sharedMessageProps,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tcontent: result,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} satisfies ClineSayTool)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (this.shouldAutoApproveTool(block.name)) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"ask\\\", \\\"tool\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"tool\\\", completeMessage, undefined, false)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveAutoApprovedRequestsCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tshowNotificationForApprovalIfAutoApprovalEnabled(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`Cline wants to view source code definitions in ${path.basename(absolutePath)}/`,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"say\\\", \\\"tool\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tconst didApprove = await askApproval(\\\"tool\\\", completeMessage)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tif (!didApprove) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(result)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\t\\t\\tawait handleError(\\\"parsing source code definitions\\\", error)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tcase \\\"search_files\\\": {\\r\\n\\t\\t\\t\\t\\t\\tconst relDirPath: string | undefined = block.params.path\\r\\n\\t\\t\\t\\t\\t\\tconst regex: string | undefined = block.params.regex\\r\\n\\t\\t\\t\\t\\t\\tconst filePattern: string | undefined = block.params.file_pattern\\r\\n\\t\\t\\t\\t\\t\\tconst sharedMessageProps: ClineSayTool = {\\r\\n\\t\\t\\t\\t\\t\\t\\ttool: \\\"searchFiles\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\tpath: getReadablePath(cwd, removeClosingTag(\\\"path\\\", relDirPath)),\\r\\n\\t\\t\\t\\t\\t\\t\\tregex: removeClosingTag(\\\"regex\\\", regex),\\r\\n\\t\\t\\t\\t\\t\\t\\tfilePattern: removeClosingTag(\\\"file_pattern\\\", filePattern),\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\t\\t\\tif (block.partial) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst partialMessage = JSON.stringify({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t...sharedMessageProps,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tcontent: \\\"\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} satisfies ClineSayTool)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (this.shouldAutoApproveTool(block.name)) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"ask\\\", \\\"tool\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"tool\\\", partialMessage, undefined, block.partial)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"say\\\", \\\"tool\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.ask(\\\"tool\\\", partialMessage, block.partial).catch(() => {})\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (!relDirPath) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\"search_files\\\", \\\"path\\\"))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (!regex) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\"search_files\\\", \\\"regex\\\"))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount = 0\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst absolutePath = path.resolve(cwd, relDirPath)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst results = await regexSearchFiles(cwd, absolutePath, regex, filePattern)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst completeMessage = JSON.stringify({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t...sharedMessageProps,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tcontent: results,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} satisfies ClineSayTool)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (this.shouldAutoApproveTool(block.name)) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"ask\\\", \\\"tool\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"tool\\\", completeMessage, undefined, false)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveAutoApprovedRequestsCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tshowNotificationForApprovalIfAutoApprovalEnabled(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`Cline wants to search files in ${path.basename(absolutePath)}/`,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"say\\\", \\\"tool\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tconst didApprove = await askApproval(\\\"tool\\\", completeMessage)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tif (!didApprove) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(results)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\t\\t\\tawait handleError(\\\"searching files\\\", error)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tcase \\\"browser_action\\\": {\\r\\n\\t\\t\\t\\t\\t\\tconst action: BrowserAction | undefined = block.params.action as BrowserAction\\r\\n\\t\\t\\t\\t\\t\\tconst url: string | undefined = block.params.url\\r\\n\\t\\t\\t\\t\\t\\tconst coordinate: string | undefined = block.params.coordinate\\r\\n\\t\\t\\t\\t\\t\\tconst text: string | undefined = block.params.text\\r\\n\\t\\t\\t\\t\\t\\tif (!action || !browserActions.includes(action)) {\\r\\n\\t\\t\\t\\t\\t\\t\\t// checking for action to ensure it is complete and valid\\r\\n\\t\\t\\t\\t\\t\\t\\tif (!block.partial) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// if the block is complete and we don't have a valid action this is a mistake\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\"browser_action\\\", \\\"action\\\"))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait this.browserSession.closeBrowser()\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\t\\t\\tif (block.partial) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (action === \\\"launch\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tif (this.shouldAutoApproveTool(block.name)) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"ask\\\", \\\"browser_action_launch\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\\"browser_action_launch\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tremoveClosingTag(\\\"url\\\", url),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tundefined,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tblock.partial,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"say\\\", \\\"browser_action_launch\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.ask(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\\"browser_action_launch\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tremoveClosingTag(\\\"url\\\", url),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tblock.partial,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t).catch(() => {})\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\\"browser_action\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tJSON.stringify({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\taction: action as BrowserAction,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tcoordinate: removeClosingTag(\\\"coordinate\\\", coordinate),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\ttext: removeClosingTag(\\\"text\\\", text),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t} satisfies ClineSayBrowserAction),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tundefined,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tblock.partial,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tlet browserActionResult: BrowserActionResult\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (action === \\\"launch\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tif (!url) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\"browser_action\\\", \\\"url\\\"))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.browserSession.closeBrowser()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount = 0\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tif (this.shouldAutoApproveTool(block.name)) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"ask\\\", \\\"browser_action_launch\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"browser_action_launch\\\", url, undefined, false)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveAutoApprovedRequestsCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tshowNotificationForApprovalIfAutoApprovalEnabled(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`Cline wants to use a browser and launch ${url}`,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"say\\\", \\\"browser_action_launch\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tconst didApprove = await askApproval(\\\"browser_action_launch\\\", url)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tif (!didApprove) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// NOTE: it's okay that we call this message since the partial inspect_site is finished streaming. The only scenario we have to avoid is sending messages WHILE a partial message exists at the end of the messages array. For example the api_req_finished message would interfere with the partial message, so we needed to remove that.\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// await this.say(\\\"inspect_site_result\\\", \\\"\\\") // no result, starts the loading spinner waiting for result\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"browser_action_result\\\", \\\"\\\") // starts loading spinner\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.browserSession.launchBrowser()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tbrowserActionResult = await this.browserSession.navigateToUrl(url)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tif (action === \\\"click\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tif (!coordinate) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.sayAndCreateMissingParamError(\\\"browser_action\\\", \\\"coordinate\\\"),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.browserSession.closeBrowser()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak // can't be within an inner switch\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tif (action === \\\"type\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tif (!text) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\"browser_action\\\", \\\"text\\\"))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.browserSession.closeBrowser()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount = 0\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\\"browser_action\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tJSON.stringify({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\taction: action as BrowserAction,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tcoordinate,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\ttext,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t} satisfies ClineSayBrowserAction),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tundefined,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tfalse,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tswitch (action) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tcase \\\"click\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbrowserActionResult = await this.browserSession.click(coordinate!)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tcase \\\"type\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbrowserActionResult = await this.browserSession.type(text!)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tcase \\\"scroll_down\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbrowserActionResult = await this.browserSession.scrollDown()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tcase \\\"scroll_up\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbrowserActionResult = await this.browserSession.scrollUp()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tcase \\\"close\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbrowserActionResult = await this.browserSession.closeBrowser()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tswitch (action) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tcase \\\"launch\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tcase \\\"click\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tcase \\\"type\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tcase \\\"scroll_down\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tcase \\\"scroll_up\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"browser_action_result\\\", JSON.stringify(browserActionResult))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tformatResponse.toolResult(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`The browser action has been executed. The console logs and screenshot have been captured for your analysis.\\\\n\\\\nConsole logs:\\\\n${\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbrowserActionResult.logs || \\\"(No new logs)\\\"\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\\\n\\\\n(REMEMBER: if you need to proceed to using non-\\\\`browser_action\\\\` tools or launch a new browser, you MUST first close this browser. For example, if after analyzing the logs and screenshot you need to edit a file, you must first close the browser before you can use the write_to_file tool.)`,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbrowserActionResult.screenshot ? [browserActionResult.screenshot] : [],\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tcase \\\"close\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tformatResponse.toolResult(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`The browser has been closed. You may now proceed to using other tools.`,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.browserSession.closeBrowser() // if any error occurs, the browser session is terminated\\r\\n\\t\\t\\t\\t\\t\\t\\tawait handleError(\\\"executing browser action\\\", error)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tcase \\\"execute_command\\\": {\\r\\n\\t\\t\\t\\t\\t\\tconst command: string | undefined = block.params.command\\r\\n\\t\\t\\t\\t\\t\\tconst requiresApprovalRaw: string | undefined = block.params.requires_approval\\r\\n\\t\\t\\t\\t\\t\\tconst requiresApproval = requiresApprovalRaw?.toLowerCase() === \\\"true\\\"\\r\\n\\r\\n\\t\\t\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\t\\t\\tif (block.partial) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (this.shouldAutoApproveTool(block.name)) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// since depending on an upcoming parameter, requiresApproval this may become an ask - we cant partially stream a say prematurely. So in this particular case we have to wait for the requiresApproval parameter to be completed before presenting it.\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// await this.say(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// \\t\\\"command\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// \\tremoveClosingTag(\\\"command\\\", command),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// \\tundefined,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// \\tblock.partial,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// ).catch(() => {})\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// don't need to remove last partial since we couldn't have streamed a say\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.ask(\\\"command\\\", removeClosingTag(\\\"command\\\", command), block.partial).catch(() => {})\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (!command) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\"execute_command\\\", \\\"command\\\"))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (!requiresApprovalRaw) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.sayAndCreateMissingParamError(\\\"execute_command\\\", \\\"requires_approval\\\"),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount = 0\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tlet didAutoApprove = false\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (!requiresApproval && this.shouldAutoApproveTool(block.name)) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"ask\\\", \\\"command\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"command\\\", command, undefined, false)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveAutoApprovedRequestsCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tdidAutoApprove = true\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tshowNotificationForApprovalIfAutoApprovalEnabled(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`Cline wants to execute a command: ${command}`,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// this.removeLastPartialMessageIfExistsWithType(\\\"say\\\", \\\"command\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tconst didApprove = await askApproval(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\\"command\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tcommand +\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`${this.shouldAutoApproveTool(block.name) && requiresApproval ? COMMAND_REQ_APP_STRING : \\\"\\\"}`, // ugly hack until we refactor combineCommandSequences\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tif (!didApprove) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tlet timeoutId: NodeJS.Timeout | undefined\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (didAutoApprove && this.autoApprovalSettings.enableNotifications) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// if the command was auto-approved, and it's long running we need to notify the user after some time has passed without proceeding\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttimeoutId = setTimeout(() => {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tshowSystemNotification({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tsubtitle: \\\"Command is still running\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tmessage:\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\\"An auto-approved command has been running for 30s, and may need your attention.\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}, 30_000)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst [userRejected, result] = await this.executeCommandTool(command)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (timeoutId) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tclearTimeout(timeoutId)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (userRejected) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.didRejectTool = true\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(result)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\t\\t\\tawait handleError(\\\"executing command\\\", error)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tcase \\\"use_mcp_tool\\\": {\\r\\n\\t\\t\\t\\t\\t\\tconst server_name: string | undefined = block.params.server_name\\r\\n\\t\\t\\t\\t\\t\\tconst tool_name: string | undefined = block.params.tool_name\\r\\n\\t\\t\\t\\t\\t\\tconst mcp_arguments: string | undefined = block.params.arguments\\r\\n\\t\\t\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\t\\t\\tif (block.partial) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst partialMessage = JSON.stringify({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"use_mcp_tool\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tserverName: removeClosingTag(\\\"server_name\\\", server_name),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttoolName: removeClosingTag(\\\"tool_name\\\", tool_name),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\targuments: removeClosingTag(\\\"arguments\\\", mcp_arguments),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} satisfies ClineAskUseMcpServer)\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (this.shouldAutoApproveTool(block.name)) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"ask\\\", \\\"use_mcp_server\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"use_mcp_server\\\", partialMessage, undefined, block.partial)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"say\\\", \\\"use_mcp_server\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.ask(\\\"use_mcp_server\\\", partialMessage, block.partial).catch(() => {})\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (!server_name) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\"use_mcp_tool\\\", \\\"server_name\\\"))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (!tool_name) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\"use_mcp_tool\\\", \\\"tool_name\\\"))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// arguments are optional, but if they are provided they must be valid JSON\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// if (!mcp_arguments) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// \\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// \\tpushToolResult(await this.sayAndCreateMissingParamError(\\\"use_mcp_tool\\\", \\\"arguments\\\"))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// \\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// }\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tlet parsedArguments: Record<string, unknown> | undefined\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (mcp_arguments) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tparsedArguments = JSON.parse(mcp_arguments)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\\"error\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`Cline tried to use ${tool_name} with an invalid JSON argument. Retrying...`,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tformatResponse.toolError(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tformatResponse.invalidMcpToolArgumentError(server_name, tool_name),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount = 0\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst completeMessage = JSON.stringify({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"use_mcp_tool\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tserverName: server_name,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttoolName: tool_name,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\targuments: mcp_arguments,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} satisfies ClineAskUseMcpServer)\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (this.shouldAutoApproveTool(block.name)) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"ask\\\", \\\"use_mcp_server\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"use_mcp_server\\\", completeMessage, undefined, false)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveAutoApprovedRequestsCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tshowNotificationForApprovalIfAutoApprovalEnabled(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`Cline wants to use ${tool_name} on ${server_name}`,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"say\\\", \\\"use_mcp_server\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tconst didApprove = await askApproval(\\\"use_mcp_server\\\", completeMessage)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tif (!didApprove) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// now execute the tool\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"mcp_server_request_started\\\") // same as browser_action_result\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst toolResult = await this.providerRef\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t.deref()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t?.mcpHub?.callTool(server_name, tool_name, parsedArguments)\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// TODO: add progress indicator and ability to parse images and non-text responses\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst toolResultPretty =\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t(toolResult?.isError ? \\\"Error:\\\\n\\\" : \\\"\\\") +\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\ttoolResult?.content\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t.map((item) => {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tif (item.type === \\\"text\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\treturn item.text\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tif (item.type === \\\"resource\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tconst { blob, ...rest } = item.resource\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\treturn JSON.stringify(rest, null, 2)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\treturn \\\"\\\"\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t.filter(Boolean)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t.join(\\\"\\\\n\\\\n\\\") || \\\"(No response)\\\"\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"mcp_server_response\\\", toolResultPretty)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(formatResponse.toolResult(toolResultPretty))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\t\\t\\tawait handleError(\\\"executing MCP tool\\\", error)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tcase \\\"access_mcp_resource\\\": {\\r\\n\\t\\t\\t\\t\\t\\tconst server_name: string | undefined = block.params.server_name\\r\\n\\t\\t\\t\\t\\t\\tconst uri: string | undefined = block.params.uri\\r\\n\\t\\t\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\t\\t\\tif (block.partial) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst partialMessage = JSON.stringify({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"access_mcp_resource\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tserverName: removeClosingTag(\\\"server_name\\\", server_name),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\turi: removeClosingTag(\\\"uri\\\", uri),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} satisfies ClineAskUseMcpServer)\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (this.shouldAutoApproveTool(block.name)) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"ask\\\", \\\"use_mcp_server\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"use_mcp_server\\\", partialMessage, undefined, block.partial)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"say\\\", \\\"use_mcp_server\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.ask(\\\"use_mcp_server\\\", partialMessage, block.partial).catch(() => {})\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (!server_name) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\"access_mcp_resource\\\", \\\"server_name\\\"))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (!uri) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\"access_mcp_resource\\\", \\\"uri\\\"))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount = 0\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst completeMessage = JSON.stringify({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"access_mcp_resource\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tserverName: server_name,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\turi,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} satisfies ClineAskUseMcpServer)\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (this.shouldAutoApproveTool(block.name)) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"ask\\\", \\\"use_mcp_server\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"use_mcp_server\\\", completeMessage, undefined, false)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveAutoApprovedRequestsCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tshowNotificationForApprovalIfAutoApprovalEnabled(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t`Cline wants to access ${uri} on ${server_name}`,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.removeLastPartialMessageIfExistsWithType(\\\"say\\\", \\\"use_mcp_server\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tconst didApprove = await askApproval(\\\"use_mcp_server\\\", completeMessage)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tif (!didApprove) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// now execute the tool\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"mcp_server_request_started\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst resourceResult = await this.providerRef.deref()?.mcpHub?.readResource(server_name, uri)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst resourceResultPretty =\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tresourceResult?.contents\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t.map((item) => {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tif (item.text) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\treturn item.text\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\treturn \\\"\\\"\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t.filter(Boolean)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t.join(\\\"\\\\n\\\\n\\\") || \\\"(Empty response)\\\"\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"mcp_server_response\\\", resourceResultPretty)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(formatResponse.toolResult(resourceResultPretty))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\t\\t\\tawait handleError(\\\"accessing MCP resource\\\", error)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tcase \\\"ask_followup_question\\\": {\\r\\n\\t\\t\\t\\t\\t\\tconst question: string | undefined = block.params.question\\r\\n\\t\\t\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\t\\t\\tif (block.partial) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait this.ask(\\\"followup\\\", removeClosingTag(\\\"question\\\", question), block.partial).catch(() => {})\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (!question) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\"ask_followup_question\\\", \\\"question\\\"))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount = 0\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (this.autoApprovalSettings.enabled && this.autoApprovalSettings.enableNotifications) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tshowSystemNotification({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tsubtitle: \\\"Cline has a question...\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tmessage: question.replace(/\\\\n/g, \\\" \\\"),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst { text, images } = await this.ask(\\\"followup\\\", question, false)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"user_feedback\\\", text ?? \\\"\\\", images)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(formatResponse.toolResult(`<answer>\\\\n${text}\\\\n</answer>`, images))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\t\\t\\tawait handleError(\\\"asking question\\\", error)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tcase \\\"attempt_completion\\\": {\\r\\n\\t\\t\\t\\t\\t\\t/*\\r\\n\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount = 0\\r\\n\\t\\t\\t\\t\\t\\tlet resultToSend = result\\r\\n\\t\\t\\t\\t\\t\\tif (command) {\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"completion_result\\\", resultToSend)\\r\\n\\t\\t\\t\\t\\t\\t\\t// TODO: currently we don't handle if this command fails, it could be useful to let cline know and retry\\r\\n\\t\\t\\t\\t\\t\\t\\tconst [didUserReject, commandResult] = await this.executeCommand(command, true)\\r\\n\\t\\t\\t\\t\\t\\t\\t// if we received non-empty string, the command was rejected or failed\\r\\n\\t\\t\\t\\t\\t\\t\\tif (commandResult) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\treturn [didUserReject, commandResult]\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\tresultToSend = \\\"\\\"\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\tconst { response, text, images } = await this.ask(\\\"completion_result\\\", resultToSend) // this prompts webview to show 'new task' button, and enable text input (which would be the 'text' here)\\r\\n\\t\\t\\t\\t\\t\\tif (response === \\\"yesButtonClicked\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\treturn [false, \\\"\\\"] // signals to recursive loop to stop (for now this never happens since yesButtonClicked will trigger a new task)\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\tawait this.say(\\\"user_feedback\\\", text ?? \\\"\\\", images)\\r\\n\\t\\t\\t\\t\\t\\treturn [\\r\\n\\t\\t\\t\\t\\t\\t*/\\r\\n\\t\\t\\t\\t\\t\\tconst result: string | undefined = block.params.result\\r\\n\\t\\t\\t\\t\\t\\tconst command: string | undefined = block.params.command\\r\\n\\r\\n\\t\\t\\t\\t\\t\\tconst addNewChangesFlagToLastCompletionResultMessage = async () => {\\r\\n\\t\\t\\t\\t\\t\\t\\t// Add newchanges flag if there are new changes to the workspace\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\tconst hasNewChanges = await this.doesLatestTaskCompletionHaveNewChanges()\\r\\n\\t\\t\\t\\t\\t\\t\\tconst lastCompletionResultMessage = findLast(this.clineMessages, (m) => m.say === \\\"completion_result\\\")\\r\\n\\t\\t\\t\\t\\t\\t\\tif (\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tlastCompletionResultMessage &&\\r\\n\\t\\t\\t\\t\\t\\t\\t\\thasNewChanges &&\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t!lastCompletionResultMessage.text?.endsWith(COMPLETION_RESULT_CHANGES_FLAG)\\r\\n\\t\\t\\t\\t\\t\\t\\t) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tlastCompletionResultMessage.text += COMPLETION_RESULT_CHANGES_FLAG\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.saveClineMessages()\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\t\\t\\tconst lastMessage = this.clineMessages.at(-1)\\r\\n\\t\\t\\t\\t\\t\\t\\tif (block.partial) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (command) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// the attempt_completion text is done, now we're getting command\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// remove the previous partial attempt_completion ask, replace with say, post state to webview, then stream command\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// const secondLastMessage = this.clineMessages.at(-2)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// NOTE: we do not want to auto approve a command run as part of the attempt_completion tool\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tif (lastMessage && lastMessage.ask === \\\"command\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t// update command\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.ask(\\\"command\\\", removeClosingTag(\\\"command\\\", command), block.partial).catch(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t() => {},\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t// last message is completion_result\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t// we have command string, which means we have the result as well, so finish it (doesnt have to exist yet)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"completion_result\\\", removeClosingTag(\\\"result\\\", result), undefined, false)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait addNewChangesFlagToLastCompletionResultMessage()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.ask(\\\"command\\\", removeClosingTag(\\\"command\\\", command), block.partial).catch(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t() => {},\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// no command, still outputting partial result\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\\"completion_result\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tremoveClosingTag(\\\"result\\\", result),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tundefined,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tblock.partial,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (!result) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(await this.sayAndCreateMissingParamError(\\\"attempt_completion\\\", \\\"result\\\"))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tthis.consecutiveMistakeCount = 0\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (this.autoApprovalSettings.enabled && this.autoApprovalSettings.enableNotifications) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tshowSystemNotification({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tsubtitle: \\\"Task Completed\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tmessage: result.replace(/\\\\n/g, \\\" \\\"),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tlet commandResult: ToolResponse | undefined\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (command) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tif (lastMessage && lastMessage.ask !== \\\"command\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t// havent sent a command message yet so first send completion_result then command\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"completion_result\\\", result, undefined, false)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait addNewChangesFlagToLastCompletionResultMessage()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t// we already sent a command message, meaning the complete completion message has also been sent\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// complete command message\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tconst didApprove = await askApproval(\\\"command\\\", command)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tif (!didApprove) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tconst [userRejected, execCommandResult] = await this.executeCommandTool(command!)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tif (userRejected) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tthis.didRejectTool = true\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(execCommandResult)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t// user didn't reject, but the command may have output\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tcommandResult = execCommandResult\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"completion_result\\\", result, undefined, false)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tawait addNewChangesFlagToLastCompletionResultMessage()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// we already sent completion_result says, an empty string asks relinquishes control over button and field\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst { response, text, images } = await this.ask(\\\"completion_result\\\", \\\"\\\", false)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (response === \\\"yesButtonClicked\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tpushToolResult(\\\"\\\") // signals to recursive loop to stop (for now this never happens since yesButtonClicked will trigger a new task)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tawait this.say(\\\"user_feedback\\\", text ?? \\\"\\\", images)\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tconst toolResults: (Anthropic.TextBlockParam | Anthropic.ImageBlockParam)[] = []\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tif (commandResult) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tif (typeof commandResult === \\\"string\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\ttoolResults.push({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\ttext: commandResult,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t} else if (Array.isArray(commandResult)) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\ttoolResults.push(...commandResult)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttoolResults.push({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttext: `The user has provided feedback on the results. Consider their input to continue the task, and then attempt completion again.\\\\n<feedback>\\\\n${text}\\\\n</feedback>`,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttoolResults.push(...formatResponse.imageBlocks(images))\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tthis.userMessageContent.push({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttext: `${toolDescription()} Result:`,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tthis.userMessageContent.push(...toolResults)\\r\\n\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t// await this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\t\\t\\tawait handleError(\\\"attempting completion\\\", error)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait this.saveCheckpoint()\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t/*\\r\\n\\t\\tSeeing out of bounds is fine, it means that the next too call is being built up and ready to add to assistantMessageContent to present. \\r\\n\\t\\tWhen you see the UI inactive during this, it means that a tool is breaking without presenting any UI. For example the write_to_file tool was breaking when relpath was undefined, and for invalid relpath it never presented UI.\\r\\n\\t\\t*/\\r\\n\\t\\tthis.presentAssistantMessageLocked = false // this needs to be placed here, if not then calling this.presentAssistantMessage below would fail (sometimes) since it's locked\\r\\n\\t\\t// NOTE: when tool is rejected, iterator stream is interrupted and it waits for userMessageContentReady to be true. Future calls to present will skip execution since didRejectTool and iterate until contentIndex is set to message length and it sets userMessageContentReady to true itself (instead of preemptively doing it in iterator)\\r\\n\\t\\tif (!block.partial || this.didRejectTool || this.didAlreadyUseTool) {\\r\\n\\t\\t\\t// block is finished streaming and executing\\r\\n\\t\\t\\tif (this.currentStreamingContentIndex === this.assistantMessageContent.length - 1) {\\r\\n\\t\\t\\t\\t// its okay that we increment if !didCompleteReadingStream, it'll just return bc out of bounds and as streaming continues it will call presentAssitantMessage if a new block is ready. if streaming is finished then we set userMessageContentReady to true when out of bounds. This gracefully allows the stream to continue on and all potential content blocks be presented.\\r\\n\\t\\t\\t\\t// last block is complete and it is finished executing\\r\\n\\t\\t\\t\\tthis.userMessageContentReady = true // will allow pwaitfor to continue\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t// call next block if it exists (if not then read stream will call it when its ready)\\r\\n\\t\\t\\tthis.currentStreamingContentIndex++ // need to increment regardless, so when read stream calls this function again it will be streaming the next block\\r\\n\\r\\n\\t\\t\\tif (this.currentStreamingContentIndex < this.assistantMessageContent.length) {\\r\\n\\t\\t\\t\\t// there are already more content blocks to stream, so we'll call this function ourselves\\r\\n\\t\\t\\t\\t// await this.presentAssistantContent()\\r\\n\\r\\n\\t\\t\\t\\tthis.presentAssistantMessage()\\r\\n\\t\\t\\t\\treturn\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\t// block is partial, but the read stream may have finished\\r\\n\\t\\tif (this.presentAssistantMessageHasPendingUpdates) {\\r\\n\\t\\t\\tthis.presentAssistantMessage()\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tasync recursivelyMakeClineRequests(\\r\\n\\t\\tuserContent: UserContent,\\r\\n\\t\\tincludeFileDetails: boolean = false,\\r\\n\\t\\tisNewTask: boolean = false,\\r\\n\\t): Promise<boolean> {\\r\\n\\t\\tif (this.abort) {\\r\\n\\t\\t\\tthrow new Error(\\\"Cline instance aborted\\\")\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (this.consecutiveMistakeCount >= 3) {\\r\\n\\t\\t\\tif (this.autoApprovalSettings.enabled && this.autoApprovalSettings.enableNotifications) {\\r\\n\\t\\t\\t\\tshowSystemNotification({\\r\\n\\t\\t\\t\\t\\tsubtitle: \\\"Error\\\",\\r\\n\\t\\t\\t\\t\\tmessage: \\\"Cline is having trouble. Would you like to continue the task?\\\",\\r\\n\\t\\t\\t\\t})\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tconst { response, text, images } = await this.ask(\\r\\n\\t\\t\\t\\t\\\"mistake_limit_reached\\\",\\r\\n\\t\\t\\t\\tthis.api.getModel().id.includes(\\\"claude\\\")\\r\\n\\t\\t\\t\\t\\t? `This may indicate a failure in his thought process or inability to use a tool properly, which can be mitigated with some user guidance (e.g. \\\"Try breaking down the task into smaller steps\\\").`\\r\\n\\t\\t\\t\\t\\t: \\\"Cline uses complex prompts and iterative task execution that may be challenging for less capable models. For best results, it's recommended to use Claude 3.5 Sonnet for its advanced agentic coding capabilities.\\\",\\r\\n\\t\\t\\t)\\r\\n\\t\\t\\tif (response === \\\"messageResponse\\\") {\\r\\n\\t\\t\\t\\tuserContent.push(\\r\\n\\t\\t\\t\\t\\t...[\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\ttext: formatResponse.tooManyMistakes(text),\\r\\n\\t\\t\\t\\t\\t\\t} as Anthropic.Messages.TextBlockParam,\\r\\n\\t\\t\\t\\t\\t\\t...formatResponse.imageBlocks(images),\\r\\n\\t\\t\\t\\t\\t],\\r\\n\\t\\t\\t\\t)\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tthis.consecutiveMistakeCount = 0\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (\\r\\n\\t\\t\\tthis.autoApprovalSettings.enabled &&\\r\\n\\t\\t\\tthis.consecutiveAutoApprovedRequestsCount >= this.autoApprovalSettings.maxRequests\\r\\n\\t\\t) {\\r\\n\\t\\t\\tif (this.autoApprovalSettings.enableNotifications) {\\r\\n\\t\\t\\t\\tshowSystemNotification({\\r\\n\\t\\t\\t\\t\\tsubtitle: \\\"Max Requests Reached\\\",\\r\\n\\t\\t\\t\\t\\tmessage: `Cline has auto-approved ${this.autoApprovalSettings.maxRequests.toString()} API requests.`,\\r\\n\\t\\t\\t\\t})\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tawait this.ask(\\r\\n\\t\\t\\t\\t\\\"auto_approval_max_req_reached\\\",\\r\\n\\t\\t\\t\\t`Cline has auto-approved ${this.autoApprovalSettings.maxRequests.toString()} API requests. Would you like to reset the count and proceed with the task?`,\\r\\n\\t\\t\\t)\\r\\n\\t\\t\\t// if we get past the promise it means the user approved and did not start a new task\\r\\n\\t\\t\\tthis.consecutiveAutoApprovedRequestsCount = 0\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// get previous api req's index to check token usage and determine if we need to truncate conversation history\\r\\n\\t\\tconst previousApiReqIndex = findLastIndex(this.clineMessages, (m) => m.say === \\\"api_req_started\\\")\\r\\n\\r\\n\\t\\t// getting verbose details is an expensive operation, it uses globby to top-down build file structure of project which for large projects can take a few seconds\\r\\n\\t\\t// for the best UX we show a placeholder api_req_started message with a loading spinner as this happens\\r\\n\\t\\tawait this.say(\\r\\n\\t\\t\\t\\\"api_req_started\\\",\\r\\n\\t\\t\\tJSON.stringify({\\r\\n\\t\\t\\t\\trequest: userContent.map((block) => formatContentBlockToMarkdown(block)).join(\\\"\\\\n\\\\n\\\") + \\\"\\\\n\\\\nLoading...\\\",\\r\\n\\t\\t\\t}),\\r\\n\\t\\t)\\r\\n\\r\\n\\t\\t// use this opportunity to initialize the checkpoint tracker (can be expensive to initialize in the constructor)\\r\\n\\t\\t// FIXME: right now we're letting users init checkpoints for old tasks, but this could be a problem if opening a task in the wrong workspace\\r\\n\\t\\t// isNewTask &&\\r\\n\\t\\tif (!this.checkpointTracker) {\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\tthis.checkpointTracker = await CheckpointTracker.create(this.taskId, this.providerRef.deref())\\r\\n\\t\\t\\t\\tthis.checkpointTrackerErrorMessage = undefined\\r\\n\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\tconst errorMessage = error instanceof Error ? error.message : \\\"Unknown error\\\"\\r\\n\\t\\t\\t\\tconsole.error(\\\"Failed to initialize checkpoint tracker:\\\", errorMessage)\\r\\n\\t\\t\\t\\tthis.checkpointTrackerErrorMessage = errorMessage // will be displayed right away since we saveClineMessages next which posts state to webview\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tconst [parsedUserContent, environmentDetails] = await this.loadContext(userContent, includeFileDetails)\\r\\n\\t\\tuserContent = parsedUserContent\\r\\n\\t\\t// add environment details as its own text block, separate from tool results\\r\\n\\t\\tuserContent.push({ type: \\\"text\\\", text: environmentDetails })\\r\\n\\r\\n\\t\\tawait this.addToApiConversationHistory({\\r\\n\\t\\t\\trole: \\\"user\\\",\\r\\n\\t\\t\\tcontent: userContent,\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\t// since we sent off a placeholder api_req_started message to update the webview while waiting to actually start the API request (to load potential details for example), we need to update the text of that message\\r\\n\\t\\tconst lastApiReqIndex = findLastIndex(this.clineMessages, (m) => m.say === \\\"api_req_started\\\")\\r\\n\\t\\tthis.clineMessages[lastApiReqIndex].text = JSON.stringify({\\r\\n\\t\\t\\trequest: userContent.map((block) => formatContentBlockToMarkdown(block)).join(\\\"\\\\n\\\\n\\\"),\\r\\n\\t\\t} satisfies ClineApiReqInfo)\\r\\n\\t\\tawait this.saveClineMessages()\\r\\n\\t\\tawait this.providerRef.deref()?.postStateToWebview()\\r\\n\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tlet cacheWriteTokens = 0\\r\\n\\t\\t\\tlet cacheReadTokens = 0\\r\\n\\t\\t\\tlet inputTokens = 0\\r\\n\\t\\t\\tlet outputTokens = 0\\r\\n\\t\\t\\tlet totalCost: number | undefined\\r\\n\\r\\n\\t\\t\\t// update api_req_started. we can't use api_req_finished anymore since it's a unique case where it could come after a streaming message (ie in the middle of being updated or executed)\\r\\n\\t\\t\\t// fortunately api_req_finished was always parsed out for the gui anyways, so it remains solely for legacy purposes to keep track of prices in tasks from history\\r\\n\\t\\t\\t// (it's worth removing a few months from now)\\r\\n\\t\\t\\tconst updateApiReqMsg = (cancelReason?: ClineApiReqCancelReason, streamingFailedMessage?: string) => {\\r\\n\\t\\t\\t\\tthis.clineMessages[lastApiReqIndex].text = JSON.stringify({\\r\\n\\t\\t\\t\\t\\t...JSON.parse(this.clineMessages[lastApiReqIndex].text || \\\"{}\\\"),\\r\\n\\t\\t\\t\\t\\ttokensIn: inputTokens,\\r\\n\\t\\t\\t\\t\\ttokensOut: outputTokens,\\r\\n\\t\\t\\t\\t\\tcacheWrites: cacheWriteTokens,\\r\\n\\t\\t\\t\\t\\tcacheReads: cacheReadTokens,\\r\\n\\t\\t\\t\\t\\tcost:\\r\\n\\t\\t\\t\\t\\t\\ttotalCost ??\\r\\n\\t\\t\\t\\t\\t\\tcalculateApiCost(this.api.getModel().info, inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens),\\r\\n\\t\\t\\t\\t\\tcancelReason,\\r\\n\\t\\t\\t\\t\\tstreamingFailedMessage,\\r\\n\\t\\t\\t\\t} satisfies ClineApiReqInfo)\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tconst abortStream = async (cancelReason: ClineApiReqCancelReason, streamingFailedMessage?: string) => {\\r\\n\\t\\t\\t\\tif (this.diffViewProvider.isEditing) {\\r\\n\\t\\t\\t\\t\\tawait this.diffViewProvider.revertChanges() // closes diff view\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t// if last message is a partial we need to update and save it\\r\\n\\t\\t\\t\\tconst lastMessage = this.clineMessages.at(-1)\\r\\n\\t\\t\\t\\tif (lastMessage && lastMessage.partial) {\\r\\n\\t\\t\\t\\t\\t// lastMessage.ts = Date.now() DO NOT update ts since it is used as a key for virtuoso list\\r\\n\\t\\t\\t\\t\\tlastMessage.partial = false\\r\\n\\t\\t\\t\\t\\t// instead of streaming partialMessage events, we do a save and post like normal to persist to disk\\r\\n\\t\\t\\t\\t\\tconsole.log(\\\"updating partial message\\\", lastMessage)\\r\\n\\t\\t\\t\\t\\t// await this.saveClineMessages()\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t// Let assistant know their response was interrupted for when task is resumed\\r\\n\\t\\t\\t\\tawait this.addToApiConversationHistory({\\r\\n\\t\\t\\t\\t\\trole: \\\"assistant\\\",\\r\\n\\t\\t\\t\\t\\tcontent: [\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\ttext:\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tassistantMessage +\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t`\\\\n\\\\n[${\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tcancelReason === \\\"streaming_failed\\\"\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t? \\\"Response interrupted by API Error\\\"\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t: \\\"Response interrupted by user\\\"\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}]`,\\r\\n\\t\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\t],\\r\\n\\t\\t\\t\\t})\\r\\n\\r\\n\\t\\t\\t\\t// update api_req_started to have cancelled and cost, so that we can display the cost of the partial stream\\r\\n\\t\\t\\t\\tupdateApiReqMsg(cancelReason, streamingFailedMessage)\\r\\n\\t\\t\\t\\tawait this.saveClineMessages()\\r\\n\\r\\n\\t\\t\\t\\t// signals to provider that it can retrieve the saved messages from disk, as abortTask can not be awaited on in nature\\r\\n\\t\\t\\t\\tthis.didFinishAbortingStream = true\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t// reset streaming state\\r\\n\\t\\t\\tthis.currentStreamingContentIndex = 0\\r\\n\\t\\t\\tthis.assistantMessageContent = []\\r\\n\\t\\t\\tthis.didCompleteReadingStream = false\\r\\n\\t\\t\\tthis.userMessageContent = []\\r\\n\\t\\t\\tthis.userMessageContentReady = false\\r\\n\\t\\t\\tthis.didRejectTool = false\\r\\n\\t\\t\\tthis.didAlreadyUseTool = false\\r\\n\\t\\t\\tthis.presentAssistantMessageLocked = false\\r\\n\\t\\t\\tthis.presentAssistantMessageHasPendingUpdates = false\\r\\n\\t\\t\\tawait this.diffViewProvider.reset()\\r\\n\\r\\n\\t\\t\\tconst stream = this.attemptApiRequest(previousApiReqIndex) // yields only if the first chunk is successful, otherwise will allow the user to retry the request (most likely due to rate limit error, which gets thrown on the first chunk)\\r\\n\\t\\t\\tlet assistantMessage = \\\"\\\"\\r\\n\\t\\t\\tthis.isStreaming = true\\r\\n\\t\\t\\ttry {\\r\\n\\t\\t\\t\\tfor await (const chunk of stream) {\\r\\n\\t\\t\\t\\t\\tswitch (chunk.type) {\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"usage\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\tinputTokens += chunk.inputTokens\\r\\n\\t\\t\\t\\t\\t\\t\\toutputTokens += chunk.outputTokens\\r\\n\\t\\t\\t\\t\\t\\t\\tcacheWriteTokens += chunk.cacheWriteTokens ?? 0\\r\\n\\t\\t\\t\\t\\t\\t\\tcacheReadTokens += chunk.cacheReadTokens ?? 0\\r\\n\\t\\t\\t\\t\\t\\t\\ttotalCost = chunk.totalCost\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"text\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\tassistantMessage += chunk.text\\r\\n\\t\\t\\t\\t\\t\\t\\t// parse raw assistant message into content blocks\\r\\n\\t\\t\\t\\t\\t\\t\\tconst prevLength = this.assistantMessageContent.length\\r\\n\\t\\t\\t\\t\\t\\t\\tthis.assistantMessageContent = parseAssistantMessage(assistantMessage)\\r\\n\\t\\t\\t\\t\\t\\t\\tif (this.assistantMessageContent.length > prevLength) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tthis.userMessageContentReady = false // new content we need to present, reset to false in case previous content set this to true\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t// present content to user\\r\\n\\t\\t\\t\\t\\t\\t\\tthis.presentAssistantMessage()\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\tif (this.abort) {\\r\\n\\t\\t\\t\\t\\t\\tconsole.log(\\\"aborting stream...\\\")\\r\\n\\t\\t\\t\\t\\t\\tif (!this.abandoned) {\\r\\n\\t\\t\\t\\t\\t\\t\\t// only need to gracefully abort if this instance isn't abandoned (sometimes openrouter stream hangs, in which case this would affect future instances of cline)\\r\\n\\t\\t\\t\\t\\t\\t\\tawait abortStream(\\\"user_cancelled\\\")\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\tbreak // aborts the stream\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\tif (this.didRejectTool) {\\r\\n\\t\\t\\t\\t\\t\\t// userContent has a tool rejection, so interrupt the assistant's response to present the user's feedback\\r\\n\\t\\t\\t\\t\\t\\tassistantMessage += \\\"\\\\n\\\\n[Response interrupted by user feedback]\\\"\\r\\n\\t\\t\\t\\t\\t\\t// this.userMessageContentReady = true // instead of setting this premptively, we allow the present iterator to finish and set userMessageContentReady when its ready\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t// PREV: we need to let the request finish for openrouter to get generation details\\r\\n\\t\\t\\t\\t\\t// UPDATE: it's better UX to interrupt the request at the cost of the api cost not being retrieved\\r\\n\\t\\t\\t\\t\\tif (this.didAlreadyUseTool) {\\r\\n\\t\\t\\t\\t\\t\\tassistantMessage +=\\r\\n\\t\\t\\t\\t\\t\\t\\t\\\"\\\\n\\\\n[Response interrupted by a tool use result. Only one tool may be used at a time and should be placed at the end of the message.]\\\"\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t// 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)\\r\\n\\t\\t\\t\\tif (!this.abandoned) {\\r\\n\\t\\t\\t\\t\\tthis.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\\r\\n\\t\\t\\t\\t\\tawait abortStream(\\\"streaming_failed\\\", error.message ?? JSON.stringify(serializeError(error), null, 2))\\r\\n\\t\\t\\t\\t\\tconst history = await this.providerRef.deref()?.getTaskWithId(this.taskId)\\r\\n\\t\\t\\t\\t\\tif (history) {\\r\\n\\t\\t\\t\\t\\t\\tawait this.providerRef.deref()?.initClineWithHistoryItem(history.historyItem)\\r\\n\\t\\t\\t\\t\\t\\t// await this.providerRef.deref()?.postStateToWebview()\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} finally {\\r\\n\\t\\t\\t\\tthis.isStreaming = false\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t// need to call here in case the stream was aborted\\r\\n\\t\\t\\tif (this.abort) {\\r\\n\\t\\t\\t\\tthrow new Error(\\\"Cline instance aborted\\\")\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tthis.didCompleteReadingStream = true\\r\\n\\r\\n\\t\\t\\t// set any blocks to be complete to allow presentAssistantMessage to finish and set userMessageContentReady to true\\r\\n\\t\\t\\t// (could be a text block that had no subsequent tool uses, or a text block at the very end, or an invalid tool use, etc. whatever the case, presentAssistantMessage relies on these blocks either to be completed or the user to reject a block in order to proceed and eventually set userMessageContentReady to true)\\r\\n\\t\\t\\tconst partialBlocks = this.assistantMessageContent.filter((block) => block.partial)\\r\\n\\t\\t\\tpartialBlocks.forEach((block) => {\\r\\n\\t\\t\\t\\tblock.partial = false\\r\\n\\t\\t\\t})\\r\\n\\t\\t\\t// this.assistantMessageContent.forEach((e) => (e.partial = false)) // cant just do this bc a tool could be in the middle of executing ()\\r\\n\\t\\t\\tif (partialBlocks.length > 0) {\\r\\n\\t\\t\\t\\tthis.presentAssistantMessage() // if there is content to update then it will complete and update this.userMessageContentReady to true, which we pwaitfor before making the next request. all this is really doing is presenting the last partial message that we just set to complete\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tupdateApiReqMsg()\\r\\n\\t\\t\\tawait this.saveClineMessages()\\r\\n\\t\\t\\tawait this.providerRef.deref()?.postStateToWebview()\\r\\n\\r\\n\\t\\t\\t// now add to apiconversationhistory\\r\\n\\t\\t\\t// need to save assistant responses to file before proceeding to tool use since user can exit at any moment and we wouldn't be able to save the assistant's response\\r\\n\\t\\t\\tlet didEndLoop = false\\r\\n\\t\\t\\tif (assistantMessage.length > 0) {\\r\\n\\t\\t\\t\\tawait this.addToApiConversationHistory({\\r\\n\\t\\t\\t\\t\\trole: \\\"assistant\\\",\\r\\n\\t\\t\\t\\t\\tcontent: [{ type: \\\"text\\\", text: assistantMessage }],\\r\\n\\t\\t\\t\\t})\\r\\n\\r\\n\\t\\t\\t\\t// NOTE: this comment is here for future reference - this was a workaround for userMessageContent not getting set to true. It was due to it not recursively calling for partial blocks when didRejectTool, so it would get stuck waiting for a partial block to complete before it could continue.\\r\\n\\t\\t\\t\\t// in case the content blocks finished\\r\\n\\t\\t\\t\\t// it may be the api stream finished after the last parsed content block was executed, so we are able to detect out of bounds and set userMessageContentReady to true (note you should not call presentAssistantMessage since if the last block is completed it will be presented again)\\r\\n\\t\\t\\t\\t// const completeBlocks = this.assistantMessageContent.filter((block) => !block.partial) // if there are any partial blocks after the stream ended we can consider them invalid\\r\\n\\t\\t\\t\\t// if (this.currentStreamingContentIndex >= completeBlocks.length) {\\r\\n\\t\\t\\t\\t// \\tthis.userMessageContentReady = true\\r\\n\\t\\t\\t\\t// }\\r\\n\\r\\n\\t\\t\\t\\tawait pWaitFor(() => this.userMessageContentReady)\\r\\n\\r\\n\\t\\t\\t\\t// if the model did not tool use, then we need to tell it to either use a tool or attempt_completion\\r\\n\\t\\t\\t\\tconst didToolUse = this.assistantMessageContent.some((block) => block.type === \\\"tool_use\\\")\\r\\n\\t\\t\\t\\tif (!didToolUse) {\\r\\n\\t\\t\\t\\t\\tthis.userMessageContent.push({\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\ttext: formatResponse.noToolsUsed(),\\r\\n\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\tthis.consecutiveMistakeCount++\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tconst recDidEndLoop = await this.recursivelyMakeClineRequests(this.userMessageContent)\\r\\n\\t\\t\\t\\tdidEndLoop = recDidEndLoop\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t// if there's no assistant_responses, that means we got no text or tool_use content blocks from API which we should assume is an error\\r\\n\\t\\t\\t\\tawait this.say(\\r\\n\\t\\t\\t\\t\\t\\\"error\\\",\\r\\n\\t\\t\\t\\t\\t\\\"Unexpected API Response: The language model did not provide any assistant messages. This may indicate an issue with the API or the model's output.\\\",\\r\\n\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\tawait this.addToApiConversationHistory({\\r\\n\\t\\t\\t\\t\\trole: \\\"assistant\\\",\\r\\n\\t\\t\\t\\t\\tcontent: [\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\ttext: \\\"Failure: I did not provide a response.\\\",\\r\\n\\t\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\t],\\r\\n\\t\\t\\t\\t})\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\treturn didEndLoop // will always be false for now\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\t// this should never happen since the only thing that can throw an error is the attemptApiRequest, which is wrapped in a try catch that sends an ask where if noButtonClicked, will clear current task and destroy this instance. However to avoid unhandled promise rejection, we will end this loop which will end execution of this instance (see startTask)\\r\\n\\t\\t\\treturn true // needs to be true so parent loop knows to end task\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tasync loadContext(userContent: UserContent, includeFileDetails: boolean = false) {\\r\\n\\t\\treturn await Promise.all([\\r\\n\\t\\t\\t// This is a temporary solution to dynamically load context mentions from tool results. It checks for the presence of tags that indicate that the tool was rejected and feedback was provided (see formatToolDeniedFeedback, attemptCompletion, executeCommand, and consecutiveMistakeCount >= 3) or \\\"<answer>\\\" (see askFollowupQuestion), we place all user generated content in these tags so they can effectively be used as markers for when we should parse mentions). However if we allow multiple tools responses in the future, we will need to parse mentions specifically within the user content tags.\\r\\n\\t\\t\\t// (Note: this caused the @/ import alias bug where file contents were being parsed as well, since v2 converted tool results to text blocks)\\r\\n\\t\\t\\tPromise.all(\\r\\n\\t\\t\\t\\tuserContent.map(async (block) => {\\r\\n\\t\\t\\t\\t\\tif (block.type === \\\"text\\\") {\\r\\n\\t\\t\\t\\t\\t\\t// We need to ensure any user generated content is wrapped in one of these tags so that we know to parse mentions\\r\\n\\t\\t\\t\\t\\t\\t// FIXME: Only parse text in between these tags instead of the entire text block which may contain other tool results. This is part of a larger issue where we shouldn't be using regex to parse mentions in the first place (ie for cases where file paths have spaces)\\r\\n\\t\\t\\t\\t\\t\\tif (\\r\\n\\t\\t\\t\\t\\t\\t\\tblock.text.includes(\\\"<feedback>\\\") ||\\r\\n\\t\\t\\t\\t\\t\\t\\tblock.text.includes(\\\"<answer>\\\") ||\\r\\n\\t\\t\\t\\t\\t\\t\\tblock.text.includes(\\\"<task>\\\")\\r\\n\\t\\t\\t\\t\\t\\t) {\\r\\n\\t\\t\\t\\t\\t\\t\\treturn {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t...block,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttext: await parseMentions(block.text, cwd, this.urlContentFetcher),\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\treturn block\\r\\n\\t\\t\\t\\t}),\\r\\n\\t\\t\\t),\\r\\n\\t\\t\\tthis.getEnvironmentDetails(includeFileDetails),\\r\\n\\t\\t])\\r\\n\\t}\\r\\n\\r\\n\\tasync getEnvironmentDetails(includeFileDetails: boolean = false) {\\r\\n\\t\\tlet details = \\\"\\\"\\r\\n\\r\\n\\t\\t// It could be useful for cline to know if the user went from one or no file to another between messages, so we always include this context\\r\\n\\t\\tdetails += \\\"\\\\n\\\\n# VSCode Visible Files\\\"\\r\\n\\t\\tconst visibleFiles = vscode.window.visibleTextEditors\\r\\n\\t\\t\\t?.map((editor) => editor.document?.uri?.fsPath)\\r\\n\\t\\t\\t.filter(Boolean)\\r\\n\\t\\t\\t.map((absolutePath) => path.relative(cwd, absolutePath).toPosix())\\r\\n\\t\\t\\t.join(\\\"\\\\n\\\")\\r\\n\\t\\tif (visibleFiles) {\\r\\n\\t\\t\\tdetails += `\\\\n${visibleFiles}`\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tdetails += \\\"\\\\n(No visible files)\\\"\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tdetails += \\\"\\\\n\\\\n# VSCode Open Tabs\\\"\\r\\n\\t\\tconst openTabs = vscode.window.tabGroups.all\\r\\n\\t\\t\\t.flatMap((group) => group.tabs)\\r\\n\\t\\t\\t.map((tab) => (tab.input as vscode.TabInputText)?.uri?.fsPath)\\r\\n\\t\\t\\t.filter(Boolean)\\r\\n\\t\\t\\t.map((absolutePath) => path.relative(cwd, absolutePath).toPosix())\\r\\n\\t\\t\\t.join(\\\"\\\\n\\\")\\r\\n\\t\\tif (openTabs) {\\r\\n\\t\\t\\tdetails += `\\\\n${openTabs}`\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tdetails += \\\"\\\\n(No open tabs)\\\"\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tconst busyTerminals = this.terminalManager.getTerminals(true)\\r\\n\\t\\tconst inactiveTerminals = this.terminalManager.getTerminals(false)\\r\\n\\t\\t// const allTerminals = [...busyTerminals, ...inactiveTerminals]\\r\\n\\r\\n\\t\\tif (busyTerminals.length > 0 && this.didEditFile) {\\r\\n\\t\\t\\t// || this.didEditFile\\r\\n\\t\\t\\tawait delay(300) // delay after saving file to let terminals catch up\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// let terminalWasBusy = false\\r\\n\\t\\tif (busyTerminals.length > 0) {\\r\\n\\t\\t\\t// wait for terminals to cool down\\r\\n\\t\\t\\t// terminalWasBusy = allTerminals.some((t) => this.terminalManager.isProcessHot(t.id))\\r\\n\\t\\t\\tawait pWaitFor(() => busyTerminals.every((t) => !this.terminalManager.isProcessHot(t.id)), {\\r\\n\\t\\t\\t\\tinterval: 100,\\r\\n\\t\\t\\t\\ttimeout: 15_000,\\r\\n\\t\\t\\t}).catch(() => {})\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// we want to get diagnostics AFTER terminal cools down for a few reasons: terminal could be scaffolding a project, dev servers (compilers like webpack) will first re-compile and then send diagnostics, etc\\r\\n\\t\\t/*\\r\\n\\t\\tlet diagnosticsDetails = \\\"\\\"\\r\\n\\t\\tconst diagnostics = await this.diagnosticsMonitor.getCurrentDiagnostics(this.didEditFile || terminalWasBusy) // if cline ran a command (ie npm install) or edited the workspace then wait a bit for updated diagnostics\\r\\n\\t\\tfor (const [uri, fileDiagnostics] of diagnostics) {\\r\\n\\t\\t\\tconst problems = fileDiagnostics.filter((d) => d.severity === vscode.DiagnosticSeverity.Error)\\r\\n\\t\\t\\tif (problems.length > 0) {\\r\\n\\t\\t\\t\\tdiagnosticsDetails += `\\\\n## ${path.relative(cwd, uri.fsPath)}`\\r\\n\\t\\t\\t\\tfor (const diagnostic of problems) {\\r\\n\\t\\t\\t\\t\\t// let severity = diagnostic.severity === vscode.DiagnosticSeverity.Error ? \\\"Error\\\" : \\\"Warning\\\"\\r\\n\\t\\t\\t\\t\\tconst line = diagnostic.range.start.line + 1 // VSCode lines are 0-indexed\\r\\n\\t\\t\\t\\t\\tconst source = diagnostic.source ? `[${diagnostic.source}] ` : \\\"\\\"\\r\\n\\t\\t\\t\\t\\tdiagnosticsDetails += `\\\\n- ${source}Line ${line}: ${diagnostic.message}`\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\t*/\\r\\n\\t\\tthis.didEditFile = false // reset, this lets us know when to wait for saved files to update terminals\\r\\n\\r\\n\\t\\t// waiting for updated diagnostics lets terminal output be the most up-to-date possible\\r\\n\\t\\tlet terminalDetails = \\\"\\\"\\r\\n\\t\\tif (busyTerminals.length > 0) {\\r\\n\\t\\t\\t// terminals are cool, let's retrieve their output\\r\\n\\t\\t\\tterminalDetails += \\\"\\\\n\\\\n# Actively Running Terminals\\\"\\r\\n\\t\\t\\tfor (const busyTerminal of busyTerminals) {\\r\\n\\t\\t\\t\\tterminalDetails += `\\\\n## Original command: \\\\`${busyTerminal.lastCommand}\\\\``\\r\\n\\t\\t\\t\\tconst newOutput = this.terminalManager.getUnretrievedOutput(busyTerminal.id)\\r\\n\\t\\t\\t\\tif (newOutput) {\\r\\n\\t\\t\\t\\t\\tterminalDetails += `\\\\n### New Output\\\\n${newOutput}`\\r\\n\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t// details += `\\\\n(Still running, no new output)` // don't want to show this right after running the command\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t\\t// only show inactive terminals if there's output to show\\r\\n\\t\\tif (inactiveTerminals.length > 0) {\\r\\n\\t\\t\\tconst inactiveTerminalOutputs = new Map<number, string>()\\r\\n\\t\\t\\tfor (const inactiveTerminal of inactiveTerminals) {\\r\\n\\t\\t\\t\\tconst newOutput = this.terminalManager.getUnretrievedOutput(inactiveTerminal.id)\\r\\n\\t\\t\\t\\tif (newOutput) {\\r\\n\\t\\t\\t\\t\\tinactiveTerminalOutputs.set(inactiveTerminal.id, newOutput)\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tif (inactiveTerminalOutputs.size > 0) {\\r\\n\\t\\t\\t\\tterminalDetails += \\\"\\\\n\\\\n# Inactive Terminals\\\"\\r\\n\\t\\t\\t\\tfor (const [terminalId, newOutput] of inactiveTerminalOutputs) {\\r\\n\\t\\t\\t\\t\\tconst inactiveTerminal = inactiveTerminals.find((t) => t.id === terminalId)\\r\\n\\t\\t\\t\\t\\tif (inactiveTerminal) {\\r\\n\\t\\t\\t\\t\\t\\tterminalDetails += `\\\\n## ${inactiveTerminal.lastCommand}`\\r\\n\\t\\t\\t\\t\\t\\tterminalDetails += `\\\\n### New Output\\\\n${newOutput}`\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// details += \\\"\\\\n\\\\n# VSCode Workspace Errors\\\"\\r\\n\\t\\t// if (diagnosticsDetails) {\\r\\n\\t\\t// \\tdetails += diagnosticsDetails\\r\\n\\t\\t// } else {\\r\\n\\t\\t// \\tdetails += \\\"\\\\n(No errors detected)\\\"\\r\\n\\t\\t// }\\r\\n\\r\\n\\t\\tif (terminalDetails) {\\r\\n\\t\\t\\tdetails += terminalDetails\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// Add current time information with timezone\\r\\n\\t\\tconst now = new Date()\\r\\n\\t\\tconst formatter = new Intl.DateTimeFormat(undefined, {\\r\\n\\t\\t\\tyear: \\\"numeric\\\",\\r\\n\\t\\t\\tmonth: \\\"numeric\\\",\\r\\n\\t\\t\\tday: \\\"numeric\\\",\\r\\n\\t\\t\\thour: \\\"numeric\\\",\\r\\n\\t\\t\\tminute: \\\"numeric\\\",\\r\\n\\t\\t\\tsecond: \\\"numeric\\\",\\r\\n\\t\\t\\thour12: true,\\r\\n\\t\\t})\\r\\n\\t\\tconst timeZone = formatter.resolvedOptions().timeZone\\r\\n\\t\\tconst timeZoneOffset = -now.getTimezoneOffset() / 60 // Convert to hours and invert sign to match conventional notation\\r\\n\\t\\tconst timeZoneOffsetStr = `${timeZoneOffset >= 0 ? \\\"+\\\" : \\\"\\\"}${timeZoneOffset}:00`\\r\\n\\t\\tdetails += `\\\\n\\\\n# Current Time\\\\n${formatter.format(now)} (${timeZone}, UTC${timeZoneOffsetStr})`\\r\\n\\r\\n\\t\\tif (includeFileDetails) {\\r\\n\\t\\t\\tdetails += `\\\\n\\\\n# Current Working Directory (${cwd.toPosix()}) Files\\\\n`\\r\\n\\t\\t\\tconst isDesktop = arePathsEqual(cwd, path.join(os.homedir(), \\\"Desktop\\\"))\\r\\n\\t\\t\\tif (isDesktop) {\\r\\n\\t\\t\\t\\t// don't want to immediately access desktop since it would show permission popup\\r\\n\\t\\t\\t\\tdetails += \\\"(Desktop files not shown automatically. Use list_files to explore if needed.)\\\"\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tconst [files, didHitLimit] = await listFiles(cwd, true, 200)\\r\\n\\t\\t\\t\\tconst result = formatResponse.formatFilesList(cwd, files, didHitLimit)\\r\\n\\t\\t\\t\\tdetails += result\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn `<environment_details>\\\\n${details.trim()}\\\\n</environment_details>`\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":139506,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.943Z\",\"createdTime\":\"2025-01-15T15:26:07.943Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":3183,\"nonEmptyLines\":2958,\"commentLines\":331,\"complexity\":713},\"dependencies\":[\"@anthropic-ai/sdk\",\"clone-deep\",\"delay\",\"fs/promises\",\"os\",\"p-wait-for\",\"path\",\"serialize-error\",\"vscode\",\"../api\",\"../api/transform/stream\",\"../integrations/editor/DiffViewProvider\",\"../integrations/misc/export-markdown\",\"../integrations/misc/extract-text\",\"../integrations/terminal/TerminalManager\",\"../services/browser/BrowserSession\",\"../services/browser/UrlContentFetcher\",\"../services/glob/list-files\",\"../services/ripgrep\",\"../services/tree-sitter\",\"../shared/api\",\"../shared/array\",\"../shared/AutoApprovalSettings\",\"../shared/combineApiRequests\",\"../shared/combineCommandSequences\",\"../shared/getApiMetrics\",\"../shared/HistoryItem\",\"../shared/WebviewMessage\",\"../utils/cost\",\"../utils/fs\",\"../utils/path\",\"./assistant-message\",\"./assistant-message/diff\",\"./mentions\",\"./prompts/responses\",\"./prompts/system\",\"./sliding-window\",\"./webview/ClineProvider\",\"../integrations/notifications\",\"../utils/string\",\"../api/providers/openai\",\"../integrations/checkpoints/CheckpointTracker\",\"get-folder-size\"],\"quality\":{\"score\":-6894,\"issues\":[],\"duplicateLines\":1318,\"longLines\":202,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.328Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":3183},\"analysis\":{\"metrics\":{\"lines\":3183,\"nonEmptyLines\":2958,\"commentLines\":331,\"complexity\":713},\"dependencies\":[\"@anthropic-ai/sdk\",\"clone-deep\",\"delay\",\"fs/promises\",\"os\",\"p-wait-for\",\"path\",\"serialize-error\",\"vscode\",\"../api\",\"../api/transform/stream\",\"../integrations/editor/DiffViewProvider\",\"../integrations/misc/export-markdown\",\"../integrations/misc/extract-text\",\"../integrations/terminal/TerminalManager\",\"../services/browser/BrowserSession\",\"../services/browser/UrlContentFetcher\",\"../services/glob/list-files\",\"../services/ripgrep\",\"../services/tree-sitter\",\"../shared/api\",\"../shared/array\",\"../shared/AutoApprovalSettings\",\"../shared/combineApiRequests\",\"../shared/combineCommandSequences\",\"../shared/getApiMetrics\",\"../shared/HistoryItem\",\"../shared/WebviewMessage\",\"../utils/cost\",\"../utils/fs\",\"../utils/path\",\"./assistant-message\",\"./assistant-message/diff\",\"./mentions\",\"./prompts/responses\",\"./prompts/system\",\"./sliding-window\",\"./webview/ClineProvider\",\"../integrations/notifications\",\"../utils/string\",\"../api/providers/openai\",\"../integrations/checkpoints/CheckpointTracker\",\"get-folder-size\"],\"quality\":{\"score\":-6894,\"issues\":[],\"duplicateLines\":1318,\"longLines\":202,\"complexFunctions\":0}},\"lastModified\":1737046855332,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.332Z\",\"size\":165064},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\core\\\\assistant-message\\\\parse-assistant-message.ts\",\"content\":{\"content\":\"import { AssistantMessageContent, TextContent, ToolUse, ToolParamName, toolParamNames, toolUseNames, ToolUseName } from \\\".\\\"\\r\\n\\r\\nexport function parseAssistantMessage(assistantMessage: string) {\\r\\n\\tlet contentBlocks: AssistantMessageContent[] = []\\r\\n\\tlet currentTextContent: TextContent | undefined = undefined\\r\\n\\tlet currentTextContentStartIndex = 0\\r\\n\\tlet currentToolUse: ToolUse | undefined = undefined\\r\\n\\tlet currentToolUseStartIndex = 0\\r\\n\\tlet currentParamName: ToolParamName | undefined = undefined\\r\\n\\tlet currentParamValueStartIndex = 0\\r\\n\\tlet accumulator = \\\"\\\"\\r\\n\\r\\n\\tfor (let i = 0; i < assistantMessage.length; i++) {\\r\\n\\t\\tconst char = assistantMessage[i]\\r\\n\\t\\taccumulator += char\\r\\n\\r\\n\\t\\t// there should not be a param without a tool use\\r\\n\\t\\tif (currentToolUse && currentParamName) {\\r\\n\\t\\t\\tconst currentParamValue = accumulator.slice(currentParamValueStartIndex)\\r\\n\\t\\t\\tconst paramClosingTag = `</${currentParamName}>`\\r\\n\\t\\t\\tif (currentParamValue.endsWith(paramClosingTag)) {\\r\\n\\t\\t\\t\\t// end of param value\\r\\n\\t\\t\\t\\tcurrentToolUse.params[currentParamName] = currentParamValue.slice(0, -paramClosingTag.length).trim()\\r\\n\\t\\t\\t\\tcurrentParamName = undefined\\r\\n\\t\\t\\t\\tcontinue\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t// partial param value is accumulating\\r\\n\\t\\t\\t\\tcontinue\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// no currentParamName\\r\\n\\r\\n\\t\\tif (currentToolUse) {\\r\\n\\t\\t\\tconst currentToolValue = accumulator.slice(currentToolUseStartIndex)\\r\\n\\t\\t\\tconst toolUseClosingTag = `</${currentToolUse.name}>`\\r\\n\\t\\t\\tif (currentToolValue.endsWith(toolUseClosingTag)) {\\r\\n\\t\\t\\t\\t// end of a tool use\\r\\n\\t\\t\\t\\tcurrentToolUse.partial = false\\r\\n\\t\\t\\t\\tcontentBlocks.push(currentToolUse)\\r\\n\\t\\t\\t\\tcurrentToolUse = undefined\\r\\n\\t\\t\\t\\tcontinue\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\tconst possibleParamOpeningTags = toolParamNames.map((name) => `<${name}>`)\\r\\n\\t\\t\\t\\tfor (const paramOpeningTag of possibleParamOpeningTags) {\\r\\n\\t\\t\\t\\t\\tif (accumulator.endsWith(paramOpeningTag)) {\\r\\n\\t\\t\\t\\t\\t\\t// start of a new parameter\\r\\n\\t\\t\\t\\t\\t\\tcurrentParamName = paramOpeningTag.slice(1, -1) as ToolParamName\\r\\n\\t\\t\\t\\t\\t\\tcurrentParamValueStartIndex = accumulator.length\\r\\n\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t// there's no current param, and not starting a new param\\r\\n\\r\\n\\t\\t\\t\\t// special case for write_to_file where file contents could contain the closing tag, in which case the param would have closed and we end up with the rest of the file contents here. To work around this, we get the string between the starting content tag and the LAST content tag.\\r\\n\\t\\t\\t\\tconst contentParamName: ToolParamName = \\\"content\\\"\\r\\n\\t\\t\\t\\tif (currentToolUse.name === \\\"write_to_file\\\" && accumulator.endsWith(`</${contentParamName}>`)) {\\r\\n\\t\\t\\t\\t\\tconst toolContent = accumulator.slice(currentToolUseStartIndex)\\r\\n\\t\\t\\t\\t\\tconst contentStartTag = `<${contentParamName}>`\\r\\n\\t\\t\\t\\t\\tconst contentEndTag = `</${contentParamName}>`\\r\\n\\t\\t\\t\\t\\tconst contentStartIndex = toolContent.indexOf(contentStartTag) + contentStartTag.length\\r\\n\\t\\t\\t\\t\\tconst contentEndIndex = toolContent.lastIndexOf(contentEndTag)\\r\\n\\t\\t\\t\\t\\tif (contentStartIndex !== -1 && contentEndIndex !== -1 && contentEndIndex > contentStartIndex) {\\r\\n\\t\\t\\t\\t\\t\\tcurrentToolUse.params[contentParamName] = toolContent.slice(contentStartIndex, contentEndIndex).trim()\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t// partial tool value is accumulating\\r\\n\\t\\t\\t\\tcontinue\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// no currentToolUse\\r\\n\\r\\n\\t\\tlet didStartToolUse = false\\r\\n\\t\\tconst possibleToolUseOpeningTags = toolUseNames.map((name) => `<${name}>`)\\r\\n\\t\\tfor (const toolUseOpeningTag of possibleToolUseOpeningTags) {\\r\\n\\t\\t\\tif (accumulator.endsWith(toolUseOpeningTag)) {\\r\\n\\t\\t\\t\\t// start of a new tool use\\r\\n\\t\\t\\t\\tcurrentToolUse = {\\r\\n\\t\\t\\t\\t\\ttype: \\\"tool_use\\\",\\r\\n\\t\\t\\t\\t\\tname: toolUseOpeningTag.slice(1, -1) as ToolUseName,\\r\\n\\t\\t\\t\\t\\tparams: {},\\r\\n\\t\\t\\t\\t\\tpartial: true,\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tcurrentToolUseStartIndex = accumulator.length\\r\\n\\t\\t\\t\\t// this also indicates the end of the current text content\\r\\n\\t\\t\\t\\tif (currentTextContent) {\\r\\n\\t\\t\\t\\t\\tcurrentTextContent.partial = false\\r\\n\\t\\t\\t\\t\\t// remove the partially accumulated tool use tag from the end of text (<tool)\\r\\n\\t\\t\\t\\t\\tcurrentTextContent.content = currentTextContent.content\\r\\n\\t\\t\\t\\t\\t\\t.slice(0, -toolUseOpeningTag.slice(0, -1).length)\\r\\n\\t\\t\\t\\t\\t\\t.trim()\\r\\n\\t\\t\\t\\t\\tcontentBlocks.push(currentTextContent)\\r\\n\\t\\t\\t\\t\\tcurrentTextContent = undefined\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\tdidStartToolUse = true\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (!didStartToolUse) {\\r\\n\\t\\t\\t// no tool use, so it must be text either at the beginning or between tools\\r\\n\\t\\t\\tif (currentTextContent === undefined) {\\r\\n\\t\\t\\t\\tcurrentTextContentStartIndex = i\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tcurrentTextContent = {\\r\\n\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\tcontent: accumulator.slice(currentTextContentStartIndex).trim(),\\r\\n\\t\\t\\t\\tpartial: true,\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tif (currentToolUse) {\\r\\n\\t\\t// stream did not complete tool call, add it as partial\\r\\n\\t\\tif (currentParamName) {\\r\\n\\t\\t\\t// tool call has a parameter that was not completed\\r\\n\\t\\t\\tcurrentToolUse.params[currentParamName] = accumulator.slice(currentParamValueStartIndex).trim()\\r\\n\\t\\t}\\r\\n\\t\\tcontentBlocks.push(currentToolUse)\\r\\n\\t}\\r\\n\\r\\n\\t// Note: it doesnt matter if check for currentToolUse or currentTextContent, only one of them will be defined since only one can be partial at a time\\r\\n\\tif (currentTextContent) {\\r\\n\\t\\t// stream did not complete text content, add it as partial\\r\\n\\t\\tcontentBlocks.push(currentTextContent)\\r\\n\\t}\\r\\n\\r\\n\\treturn contentBlocks\\r\\n}\\r\\n\",\"metadata\":{\"size\":5122,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.944Z\",\"createdTime\":\"2025-01-15T15:26:07.944Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":134,\"nonEmptyLines\":118,\"commentLines\":18,\"complexity\":26},\"dependencies\":[\".\"],\"quality\":{\"score\":-59,\"issues\":[],\"duplicateLines\":29,\"longLines\":7,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.323Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":134},\"analysis\":{\"metrics\":{\"lines\":134,\"nonEmptyLines\":118,\"commentLines\":18,\"complexity\":26},\"dependencies\":[\".\"],\"quality\":{\"score\":-59,\"issues\":[],\"duplicateLines\":29,\"longLines\":7,\"complexFunctions\":0}},\"lastModified\":1737046855323,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.323Z\",\"size\":6247},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\core\\\\assistant-message\\\\index.ts\",\"content\":{\"content\":\"export type AssistantMessageContent = TextContent | ToolUse\\r\\n\\r\\nexport { parseAssistantMessage } from \\\"./parse-assistant-message\\\"\\r\\n\\r\\nexport interface TextContent {\\r\\n\\ttype: \\\"text\\\"\\r\\n\\tcontent: string\\r\\n\\tpartial: boolean\\r\\n}\\r\\n\\r\\nexport const toolUseNames = [\\r\\n\\t\\\"execute_command\\\",\\r\\n\\t\\\"read_file\\\",\\r\\n\\t\\\"write_to_file\\\",\\r\\n\\t\\\"replace_in_file\\\",\\r\\n\\t\\\"search_files\\\",\\r\\n\\t\\\"list_files\\\",\\r\\n\\t\\\"list_code_definition_names\\\",\\r\\n\\t\\\"browser_action\\\",\\r\\n\\t\\\"use_mcp_tool\\\",\\r\\n\\t\\\"access_mcp_resource\\\",\\r\\n\\t\\\"ask_followup_question\\\",\\r\\n\\t\\\"attempt_completion\\\",\\r\\n] as const\\r\\n\\r\\n// Converts array of tool call names into a union type (\\\"execute_command\\\" | \\\"read_file\\\" | ...)\\r\\nexport type ToolUseName = (typeof toolUseNames)[number]\\r\\n\\r\\nexport const toolParamNames = [\\r\\n\\t\\\"command\\\",\\r\\n\\t\\\"requires_approval\\\",\\r\\n\\t\\\"path\\\",\\r\\n\\t\\\"content\\\",\\r\\n\\t\\\"diff\\\",\\r\\n\\t\\\"regex\\\",\\r\\n\\t\\\"file_pattern\\\",\\r\\n\\t\\\"recursive\\\",\\r\\n\\t\\\"action\\\",\\r\\n\\t\\\"url\\\",\\r\\n\\t\\\"coordinate\\\",\\r\\n\\t\\\"text\\\",\\r\\n\\t\\\"server_name\\\",\\r\\n\\t\\\"tool_name\\\",\\r\\n\\t\\\"arguments\\\",\\r\\n\\t\\\"uri\\\",\\r\\n\\t\\\"question\\\",\\r\\n\\t\\\"result\\\",\\r\\n] as const\\r\\n\\r\\nexport type ToolParamName = (typeof toolParamNames)[number]\\r\\n\\r\\nexport interface ToolUse {\\r\\n\\ttype: \\\"tool_use\\\"\\r\\n\\tname: ToolUseName\\r\\n\\t// params is a partial record, allowing only some or none of the possible parameters to be used\\r\\n\\tparams: Partial<Record<ToolParamName, string>>\\r\\n\\tpartial: boolean\\r\\n}\\r\\n\\r\\nexport interface ExecuteCommandToolUse extends ToolUse {\\r\\n\\tname: \\\"execute_command\\\"\\r\\n\\t// Pick<Record<ToolParamName, string>, \\\"command\\\"> makes \\\"command\\\" required, but Partial<> makes it optional\\r\\n\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\"command\\\" | \\\"requires_approval\\\">>\\r\\n}\\r\\n\\r\\nexport interface ReadFileToolUse extends ToolUse {\\r\\n\\tname: \\\"read_file\\\"\\r\\n\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\"path\\\">>\\r\\n}\\r\\n\\r\\nexport interface WriteToFileToolUse extends ToolUse {\\r\\n\\tname: \\\"write_to_file\\\"\\r\\n\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\"path\\\" | \\\"content\\\">>\\r\\n}\\r\\n\\r\\nexport interface ReplaceInFileToolUse extends ToolUse {\\r\\n\\tname: \\\"replace_in_file\\\"\\r\\n\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\"path\\\" | \\\"diff\\\">>\\r\\n}\\r\\n\\r\\nexport interface SearchFilesToolUse extends ToolUse {\\r\\n\\tname: \\\"search_files\\\"\\r\\n\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\"path\\\" | \\\"regex\\\" | \\\"file_pattern\\\">>\\r\\n}\\r\\n\\r\\nexport interface ListFilesToolUse extends ToolUse {\\r\\n\\tname: \\\"list_files\\\"\\r\\n\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\"path\\\" | \\\"recursive\\\">>\\r\\n}\\r\\n\\r\\nexport interface ListCodeDefinitionNamesToolUse extends ToolUse {\\r\\n\\tname: \\\"list_code_definition_names\\\"\\r\\n\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\"path\\\">>\\r\\n}\\r\\n\\r\\nexport interface BrowserActionToolUse extends ToolUse {\\r\\n\\tname: \\\"browser_action\\\"\\r\\n\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\"action\\\" | \\\"url\\\" | \\\"coordinate\\\" | \\\"text\\\">>\\r\\n}\\r\\n\\r\\nexport interface UseMcpToolToolUse extends ToolUse {\\r\\n\\tname: \\\"use_mcp_tool\\\"\\r\\n\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\"server_name\\\" | \\\"tool_name\\\" | \\\"arguments\\\">>\\r\\n}\\r\\n\\r\\nexport interface AccessMcpResourceToolUse extends ToolUse {\\r\\n\\tname: \\\"access_mcp_resource\\\"\\r\\n\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\"server_name\\\" | \\\"uri\\\">>\\r\\n}\\r\\n\\r\\nexport interface AskFollowupQuestionToolUse extends ToolUse {\\r\\n\\tname: \\\"ask_followup_question\\\"\\r\\n\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\"question\\\">>\\r\\n}\\r\\n\\r\\nexport interface AttemptCompletionToolUse extends ToolUse {\\r\\n\\tname: \\\"attempt_completion\\\"\\r\\n\\tparams: Partial<Pick<Record<ToolParamName, string>, \\\"result\\\" | \\\"command\\\">>\\r\\n}\\r\\n\",\"metadata\":{\"size\":3389,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.841Z\",\"createdTime\":\"2024-12-29T05:57:23.841Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":120,\"nonEmptyLines\":100,\"commentLines\":3,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":18,\"issues\":[],\"duplicateLines\":16,\"longLines\":1,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.320Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":120},\"analysis\":{\"metrics\":{\"lines\":120,\"nonEmptyLines\":100,\"commentLines\":3,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":18,\"issues\":[],\"duplicateLines\":16,\"longLines\":1,\"complexFunctions\":0}},\"lastModified\":1737046855321,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.321Z\",\"size\":4296},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\core\\\\assistant-message\\\\diff.ts\",\"content\":{\"content\":\"/**\\r\\n * Attempts a line-trimmed fallback match for the given search content in the original content.\\r\\n * It tries to match `searchContent` lines against a block of lines in `originalContent` starting\\r\\n * from `lastProcessedIndex`. Lines are matched by trimming leading/trailing whitespace and ensuring\\r\\n * they are identical afterwards.\\r\\n *\\r\\n * Returns [matchIndexStart, matchIndexEnd] if found, or false if not found.\\r\\n */\\r\\nfunction lineTrimmedFallbackMatch(originalContent: string, searchContent: string, startIndex: number): [number, number] | false {\\r\\n\\t// Split both contents into lines\\r\\n\\tconst originalLines = originalContent.split(\\\"\\\\n\\\")\\r\\n\\tconst searchLines = searchContent.split(\\\"\\\\n\\\")\\r\\n\\r\\n\\t// Trim trailing empty line if exists (from the trailing \\\\n in searchContent)\\r\\n\\tif (searchLines[searchLines.length - 1] === \\\"\\\") {\\r\\n\\t\\tsearchLines.pop()\\r\\n\\t}\\r\\n\\r\\n\\t// Find the line number where startIndex falls\\r\\n\\tlet startLineNum = 0\\r\\n\\tlet currentIndex = 0\\r\\n\\twhile (currentIndex < startIndex && startLineNum < originalLines.length) {\\r\\n\\t\\tcurrentIndex += originalLines[startLineNum].length + 1 // +1 for \\\\n\\r\\n\\t\\tstartLineNum++\\r\\n\\t}\\r\\n\\r\\n\\t// For each possible starting position in original content\\r\\n\\tfor (let i = startLineNum; i <= originalLines.length - searchLines.length; i++) {\\r\\n\\t\\tlet matches = true\\r\\n\\r\\n\\t\\t// Try to match all search lines from this position\\r\\n\\t\\tfor (let j = 0; j < searchLines.length; j++) {\\r\\n\\t\\t\\tconst originalTrimmed = originalLines[i + j].trim()\\r\\n\\t\\t\\tconst searchTrimmed = searchLines[j].trim()\\r\\n\\r\\n\\t\\t\\tif (originalTrimmed !== searchTrimmed) {\\r\\n\\t\\t\\t\\tmatches = false\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// If we found a match, calculate the exact character positions\\r\\n\\t\\tif (matches) {\\r\\n\\t\\t\\t// Find start character index\\r\\n\\t\\t\\tlet matchStartIndex = 0\\r\\n\\t\\t\\tfor (let k = 0; k < i; k++) {\\r\\n\\t\\t\\t\\tmatchStartIndex += originalLines[k].length + 1 // +1 for \\\\n\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t// Find end character index\\r\\n\\t\\t\\tlet matchEndIndex = matchStartIndex\\r\\n\\t\\t\\tfor (let k = 0; k < searchLines.length; k++) {\\r\\n\\t\\t\\t\\tmatchEndIndex += originalLines[i + k].length + 1 // +1 for \\\\n\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\treturn [matchStartIndex, matchEndIndex]\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\treturn false\\r\\n}\\r\\n\\r\\n/**\\r\\n * Attempts to match blocks of code by using the first and last lines as anchors.\\r\\n * This is a third-tier fallback strategy that helps match blocks where we can identify\\r\\n * the correct location by matching the beginning and end, even if the exact content\\r\\n * differs slightly.\\r\\n *\\r\\n * The matching strategy:\\r\\n * 1. Only attempts to match blocks of 3 or more lines to avoid false positives\\r\\n * 2. Extracts from the search content:\\r\\n * - First line as the \\\"start anchor\\\"\\r\\n * - Last line as the \\\"end anchor\\\"\\r\\n * 3. For each position in the original content:\\r\\n * - Checks if the next line matches the start anchor\\r\\n * - If it does, jumps ahead by the search block size\\r\\n * - Checks if that line matches the end anchor\\r\\n * - All comparisons are done after trimming whitespace\\r\\n *\\r\\n * This approach is particularly useful for matching blocks of code where:\\r\\n * - The exact content might have minor differences\\r\\n * - The beginning and end of the block are distinctive enough to serve as anchors\\r\\n * - The overall structure (number of lines) remains the same\\r\\n *\\r\\n * @param originalContent - The full content of the original file\\r\\n * @param searchContent - The content we're trying to find in the original file\\r\\n * @param startIndex - The character index in originalContent where to start searching\\r\\n * @returns A tuple of [startIndex, endIndex] if a match is found, false otherwise\\r\\n */\\r\\nfunction blockAnchorFallbackMatch(originalContent: string, searchContent: string, startIndex: number): [number, number] | false {\\r\\n\\tconst originalLines = originalContent.split(\\\"\\\\n\\\")\\r\\n\\tconst searchLines = searchContent.split(\\\"\\\\n\\\")\\r\\n\\r\\n\\t// Only use this approach for blocks of 3+ lines\\r\\n\\tif (searchLines.length < 3) {\\r\\n\\t\\treturn false\\r\\n\\t}\\r\\n\\r\\n\\t// Trim trailing empty line if exists\\r\\n\\tif (searchLines[searchLines.length - 1] === \\\"\\\") {\\r\\n\\t\\tsearchLines.pop()\\r\\n\\t}\\r\\n\\r\\n\\tconst firstLineSearch = searchLines[0].trim()\\r\\n\\tconst lastLineSearch = searchLines[searchLines.length - 1].trim()\\r\\n\\tconst searchBlockSize = searchLines.length\\r\\n\\r\\n\\t// Find the line number where startIndex falls\\r\\n\\tlet startLineNum = 0\\r\\n\\tlet currentIndex = 0\\r\\n\\twhile (currentIndex < startIndex && startLineNum < originalLines.length) {\\r\\n\\t\\tcurrentIndex += originalLines[startLineNum].length + 1\\r\\n\\t\\tstartLineNum++\\r\\n\\t}\\r\\n\\r\\n\\t// Look for matching start and end anchors\\r\\n\\tfor (let i = startLineNum; i <= originalLines.length - searchBlockSize; i++) {\\r\\n\\t\\t// Check if first line matches\\r\\n\\t\\tif (originalLines[i].trim() !== firstLineSearch) {\\r\\n\\t\\t\\tcontinue\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// Check if last line matches at the expected position\\r\\n\\t\\tif (originalLines[i + searchBlockSize - 1].trim() !== lastLineSearch) {\\r\\n\\t\\t\\tcontinue\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// Calculate exact character positions\\r\\n\\t\\tlet matchStartIndex = 0\\r\\n\\t\\tfor (let k = 0; k < i; k++) {\\r\\n\\t\\t\\tmatchStartIndex += originalLines[k].length + 1\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tlet matchEndIndex = matchStartIndex\\r\\n\\t\\tfor (let k = 0; k < searchBlockSize; k++) {\\r\\n\\t\\t\\tmatchEndIndex += originalLines[i + k].length + 1\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\treturn [matchStartIndex, matchEndIndex]\\r\\n\\t}\\r\\n\\r\\n\\treturn false\\r\\n}\\r\\n\\r\\n/**\\r\\n * This function reconstructs the file content by applying a streamed diff (in a\\r\\n * specialized SEARCH/REPLACE block format) to the original file content. It is designed\\r\\n * to handle both incremental updates and the final resulting file after all chunks have\\r\\n * been processed.\\r\\n *\\r\\n * The diff format is a custom structure that uses three markers to define changes:\\r\\n *\\r\\n * <<<<<<< SEARCH\\r\\n * [Exact content to find in the original file]\\r\\n * =======\\r\\n * [Content to replace with]\\r\\n * >>>>>>> REPLACE\\r\\n *\\r\\n * Behavior and Assumptions:\\r\\n * 1. The file is processed chunk-by-chunk. Each chunk of `diffContent` may contain\\r\\n * partial or complete SEARCH/REPLACE blocks. By calling this function with each\\r\\n * incremental chunk (with `isFinal` indicating the last chunk), the final reconstructed\\r\\n * file content is produced.\\r\\n *\\r\\n * 2. Matching Strategy (in order of attempt):\\r\\n * a. Exact Match: First attempts to find the exact SEARCH block text in the original file\\r\\n * b. Line-Trimmed Match: Falls back to line-by-line comparison ignoring leading/trailing whitespace\\r\\n * c. Block Anchor Match: For blocks of 3+ lines, tries to match using first/last lines as anchors\\r\\n * If all matching strategies fail, an error is thrown.\\r\\n *\\r\\n * 3. Empty SEARCH Section:\\r\\n * - If SEARCH is empty and the original file is empty, this indicates creating a new file\\r\\n * (pure insertion).\\r\\n * - If SEARCH is empty and the original file is not empty, this indicates a complete\\r\\n * file replacement (the entire original content is considered matched and replaced).\\r\\n *\\r\\n * 4. Applying Changes:\\r\\n * - Before encountering the \\\"=======\\\" marker, lines are accumulated as search content.\\r\\n * - After \\\"=======\\\" and before \\\">>>>>>> REPLACE\\\", lines are accumulated as replacement content.\\r\\n * - Once the block is complete (\\\">>>>>>> REPLACE\\\"), the matched section in the original\\r\\n * file is replaced with the accumulated replacement lines, and the position in the original\\r\\n * file is advanced.\\r\\n *\\r\\n * 5. Incremental Output:\\r\\n * - As soon as the match location is found and we are in the REPLACE section, each new\\r\\n * replacement line is appended to the result so that partial updates can be viewed\\r\\n * incrementally.\\r\\n *\\r\\n * 6. Partial Markers:\\r\\n * - If the final line of the chunk looks like it might be part of a marker but is not one\\r\\n * of the known markers, it is removed. This prevents incomplete or partial markers\\r\\n * from corrupting the output.\\r\\n *\\r\\n * 7. Finalization:\\r\\n * - Once all chunks have been processed (when `isFinal` is true), any remaining original\\r\\n * content after the last replaced section is appended to the result.\\r\\n * - Trailing newlines are not forcibly added. The code tries to output exactly what is specified.\\r\\n *\\r\\n * Errors:\\r\\n * - If the search block cannot be matched using any of the available matching strategies,\\r\\n * an error is thrown.\\r\\n */\\r\\nexport async function constructNewFileContent(diffContent: string, originalContent: string, isFinal: boolean): Promise<string> {\\r\\n\\tlet result = \\\"\\\"\\r\\n\\tlet lastProcessedIndex = 0\\r\\n\\r\\n\\tlet currentSearchContent = \\\"\\\"\\r\\n\\tlet currentReplaceContent = \\\"\\\"\\r\\n\\tlet inSearch = false\\r\\n\\tlet inReplace = false\\r\\n\\r\\n\\tlet searchMatchIndex = -1\\r\\n\\tlet searchEndIndex = -1\\r\\n\\r\\n\\tlet lines = diffContent.split(\\\"\\\\n\\\")\\r\\n\\r\\n\\t// If the last line looks like a partial marker but isn't recognized,\\r\\n\\t// remove it because it might be incomplete.\\r\\n\\tconst lastLine = lines[lines.length - 1]\\r\\n\\tif (\\r\\n\\t\\tlines.length > 0 &&\\r\\n\\t\\t(lastLine.startsWith(\\\"<\\\") || lastLine.startsWith(\\\"=\\\") || lastLine.startsWith(\\\">\\\")) &&\\r\\n\\t\\tlastLine !== \\\"<<<<<<< SEARCH\\\" &&\\r\\n\\t\\tlastLine !== \\\"=======\\\" &&\\r\\n\\t\\tlastLine !== \\\">>>>>>> REPLACE\\\"\\r\\n\\t) {\\r\\n\\t\\tlines.pop()\\r\\n\\t}\\r\\n\\r\\n\\tfor (const line of lines) {\\r\\n\\t\\tif (line === \\\"<<<<<<< SEARCH\\\") {\\r\\n\\t\\t\\tinSearch = true\\r\\n\\t\\t\\tcurrentSearchContent = \\\"\\\"\\r\\n\\t\\t\\tcurrentReplaceContent = \\\"\\\"\\r\\n\\t\\t\\tcontinue\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (line === \\\"=======\\\") {\\r\\n\\t\\t\\tinSearch = false\\r\\n\\t\\t\\tinReplace = true\\r\\n\\r\\n\\t\\t\\t// Remove trailing linebreak for adding the === marker\\r\\n\\t\\t\\t// if (currentSearchContent.endsWith(\\\"\\\\r\\\\n\\\")) {\\r\\n\\t\\t\\t// \\tcurrentSearchContent = currentSearchContent.slice(0, -2)\\r\\n\\t\\t\\t// } else if (currentSearchContent.endsWith(\\\"\\\\n\\\")) {\\r\\n\\t\\t\\t// \\tcurrentSearchContent = currentSearchContent.slice(0, -1)\\r\\n\\t\\t\\t// }\\r\\n\\r\\n\\t\\t\\tif (!currentSearchContent) {\\r\\n\\t\\t\\t\\t// Empty search block\\r\\n\\t\\t\\t\\tif (originalContent.length === 0) {\\r\\n\\t\\t\\t\\t\\t// New file scenario: nothing to match, just start inserting\\r\\n\\t\\t\\t\\t\\tsearchMatchIndex = 0\\r\\n\\t\\t\\t\\t\\tsearchEndIndex = 0\\r\\n\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t// Complete file replacement scenario: treat the entire file as matched\\r\\n\\t\\t\\t\\t\\tsearchMatchIndex = 0\\r\\n\\t\\t\\t\\t\\tsearchEndIndex = originalContent.length\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t// Add check for inefficient full-file search\\r\\n\\t\\t\\t\\t// if (currentSearchContent.trim() === originalContent.trim()) {\\r\\n\\t\\t\\t\\t// \\tthrow new Error(\\r\\n\\t\\t\\t\\t// \\t\\t\\\"The SEARCH block contains the entire file content. Please either:\\\\n\\\" +\\r\\n\\t\\t\\t\\t// \\t\\t\\t\\\"1. Use an empty SEARCH block to replace the entire file, or\\\\n\\\" +\\r\\n\\t\\t\\t\\t// \\t\\t\\t\\\"2. Make focused changes to specific parts of the file that need modification.\\\",\\r\\n\\t\\t\\t\\t// \\t)\\r\\n\\t\\t\\t\\t// }\\r\\n\\r\\n\\t\\t\\t\\t// Exact search match scenario\\r\\n\\t\\t\\t\\tconst exactIndex = originalContent.indexOf(currentSearchContent, lastProcessedIndex)\\r\\n\\t\\t\\t\\tif (exactIndex !== -1) {\\r\\n\\t\\t\\t\\t\\tsearchMatchIndex = exactIndex\\r\\n\\t\\t\\t\\t\\tsearchEndIndex = exactIndex + currentSearchContent.length\\r\\n\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t// Attempt fallback line-trimmed matching\\r\\n\\t\\t\\t\\t\\tconst lineMatch = lineTrimmedFallbackMatch(originalContent, currentSearchContent, lastProcessedIndex)\\r\\n\\t\\t\\t\\t\\tif (lineMatch) {\\r\\n\\t\\t\\t\\t\\t\\t;[searchMatchIndex, searchEndIndex] = lineMatch\\r\\n\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t// Try block anchor fallback for larger blocks\\r\\n\\t\\t\\t\\t\\t\\tconst blockMatch = blockAnchorFallbackMatch(originalContent, currentSearchContent, lastProcessedIndex)\\r\\n\\t\\t\\t\\t\\t\\tif (blockMatch) {\\r\\n\\t\\t\\t\\t\\t\\t\\t;[searchMatchIndex, searchEndIndex] = blockMatch\\r\\n\\t\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\t\\tthrow new Error(\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t`The SEARCH block:\\\\n${currentSearchContent.trimEnd()}\\\\n...does not match anything in the file.`,\\r\\n\\t\\t\\t\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t// Output everything up to the match location\\r\\n\\t\\t\\tresult += originalContent.slice(lastProcessedIndex, searchMatchIndex)\\r\\n\\t\\t\\tcontinue\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tif (line === \\\">>>>>>> REPLACE\\\") {\\r\\n\\t\\t\\t// Finished one replace block\\r\\n\\r\\n\\t\\t\\t// // Remove the artificially added linebreak in the last line of the REPLACE block\\r\\n\\t\\t\\t// if (result.endsWith(\\\"\\\\r\\\\n\\\")) {\\r\\n\\t\\t\\t// \\tresult = result.slice(0, -2)\\r\\n\\t\\t\\t// } else if (result.endsWith(\\\"\\\\n\\\")) {\\r\\n\\t\\t\\t// \\tresult = result.slice(0, -1)\\r\\n\\t\\t\\t// }\\r\\n\\r\\n\\t\\t\\t// Advance lastProcessedIndex to after the matched section\\r\\n\\t\\t\\tlastProcessedIndex = searchEndIndex\\r\\n\\r\\n\\t\\t\\t// Reset for next block\\r\\n\\t\\t\\tinSearch = false\\r\\n\\t\\t\\tinReplace = false\\r\\n\\t\\t\\tcurrentSearchContent = \\\"\\\"\\r\\n\\t\\t\\tcurrentReplaceContent = \\\"\\\"\\r\\n\\t\\t\\tsearchMatchIndex = -1\\r\\n\\t\\t\\tsearchEndIndex = -1\\r\\n\\t\\t\\tcontinue\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// Accumulate content for search or replace\\r\\n\\t\\t// (currentReplaceContent is not being used for anything right now since we directly append to result.)\\r\\n\\t\\t// (We artificially add a linebreak since we split on \\\\n at the beginning. In order to not include a trailing linebreak in the final search/result blocks we need to remove it before using them. This allows for partial line matches to be correctly identified.)\\r\\n\\t\\t// NOTE: search/replace blocks must be arranged in the order they appear in the file due to how we build the content using lastProcessedIndex. We also cannot strip the trailing newline since for non-partial lines it would remove the linebreak from the original content. (If we remove end linebreak from search, then we'd also have to remove it from replace but we can't know if it's a partial line or not since the model may be using the line break to indicate the end of the block rather than as part of the search content.) We require the model to output full lines in order for our fallbacks to work as well.\\r\\n\\t\\tif (inSearch) {\\r\\n\\t\\t\\tcurrentSearchContent += line + \\\"\\\\n\\\"\\r\\n\\t\\t} else if (inReplace) {\\r\\n\\t\\t\\tcurrentReplaceContent += line + \\\"\\\\n\\\"\\r\\n\\t\\t\\t// Output replacement lines immediately if we know the insertion point\\r\\n\\t\\t\\tif (searchMatchIndex !== -1) {\\r\\n\\t\\t\\t\\tresult += line + \\\"\\\\n\\\"\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\t// If this is the final chunk, append any remaining original content\\r\\n\\tif (isFinal && lastProcessedIndex < originalContent.length) {\\r\\n\\t\\tresult += originalContent.slice(lastProcessedIndex)\\r\\n\\t}\\r\\n\\r\\n\\treturn result\\r\\n}\\r\\n\",\"metadata\":{\"size\":13557,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.944Z\",\"createdTime\":\"2025-01-15T15:26:07.944Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":344,\"nonEmptyLines\":305,\"commentLines\":56,\"complexity\":56},\"dependencies\":[],\"quality\":{\"score\":-311,\"issues\":[],\"duplicateLines\":77,\"longLines\":13,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.319Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":344},\"analysis\":{\"metrics\":{\"lines\":344,\"nonEmptyLines\":305,\"commentLines\":56,\"complexity\":56},\"dependencies\":[],\"quality\":{\"score\":-311,\"issues\":[],\"duplicateLines\":77,\"longLines\":13,\"complexFunctions\":0}},\"lastModified\":1737046855320,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.320Z\",\"size\":15363},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\api\\\\transform\\\\stream.ts\",\"content\":{\"content\":\"export type ApiStream = AsyncGenerator<ApiStreamChunk>\\r\\nexport type ApiStreamChunk = ApiStreamTextChunk | ApiStreamUsageChunk\\r\\n\\r\\nexport interface ApiStreamTextChunk {\\r\\n\\ttype: \\\"text\\\"\\r\\n\\ttext: string\\r\\n}\\r\\n\\r\\nexport interface ApiStreamUsageChunk {\\r\\n\\ttype: \\\"usage\\\"\\r\\n\\tinputTokens: number\\r\\n\\toutputTokens: number\\r\\n\\tcacheWriteTokens?: number\\r\\n\\tcacheReadTokens?: number\\r\\n\\ttotalCost?: number // openrouter\\r\\n}\\r\\n\",\"metadata\":{\"size\":397,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.839Z\",\"createdTime\":\"2024-12-29T05:57:23.839Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":17,\"nonEmptyLines\":14,\"commentLines\":0,\"complexity\":4},\"dependencies\":[],\"quality\":{\"score\":95,\"issues\":[],\"duplicateLines\":1,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.318Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":17},\"analysis\":{\"metrics\":{\"lines\":17,\"nonEmptyLines\":14,\"commentLines\":0,\"complexity\":4},\"dependencies\":[],\"quality\":{\"score\":95,\"issues\":[],\"duplicateLines\":1,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855318,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.318Z\",\"size\":894},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\api\\\\transform\\\\openai-format.ts\",\"content\":{\"content\":\"import { Anthropic } from \\\"@anthropic-ai/sdk\\\"\\r\\nimport OpenAI from \\\"openai\\\"\\r\\n\\r\\nexport function convertToOpenAiMessages(\\r\\n\\tanthropicMessages: Anthropic.Messages.MessageParam[],\\r\\n): OpenAI.Chat.ChatCompletionMessageParam[] {\\r\\n\\tconst openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = []\\r\\n\\r\\n\\tfor (const anthropicMessage of anthropicMessages) {\\r\\n\\t\\tif (typeof anthropicMessage.content === \\\"string\\\") {\\r\\n\\t\\t\\topenAiMessages.push({\\r\\n\\t\\t\\t\\trole: anthropicMessage.role,\\r\\n\\t\\t\\t\\tcontent: anthropicMessage.content,\\r\\n\\t\\t\\t})\\r\\n\\t\\t} else {\\r\\n\\t\\t\\t// image_url.url is base64 encoded image data\\r\\n\\t\\t\\t// ensure it contains the content-type of the image: data:image/png;base64,\\r\\n\\t\\t\\t/*\\r\\n { role: \\\"user\\\", content: \\\"\\\" | { type: \\\"text\\\", text: string } | { type: \\\"image_url\\\", image_url: { url: string } } },\\r\\n // content required unless tool_calls is present\\r\\n { role: \\\"assistant\\\", content?: \\\"\\\" | null, tool_calls?: [{ id: \\\"\\\", function: { name: \\\"\\\", arguments: \\\"\\\" }, type: \\\"function\\\" }] },\\r\\n { role: \\\"tool\\\", tool_call_id: \\\"\\\", content: \\\"\\\"}\\r\\n */\\r\\n\\t\\t\\tif (anthropicMessage.role === \\\"user\\\") {\\r\\n\\t\\t\\t\\tconst { nonToolMessages, toolMessages } = anthropicMessage.content.reduce<{\\r\\n\\t\\t\\t\\t\\tnonToolMessages: (Anthropic.TextBlockParam | Anthropic.ImageBlockParam)[]\\r\\n\\t\\t\\t\\t\\ttoolMessages: Anthropic.ToolResultBlockParam[]\\r\\n\\t\\t\\t\\t}>(\\r\\n\\t\\t\\t\\t\\t(acc, part) => {\\r\\n\\t\\t\\t\\t\\t\\tif (part.type === \\\"tool_result\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\tacc.toolMessages.push(part)\\r\\n\\t\\t\\t\\t\\t\\t} else if (part.type === \\\"text\\\" || part.type === \\\"image\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\tacc.nonToolMessages.push(part)\\r\\n\\t\\t\\t\\t\\t\\t} // user cannot send tool_use messages\\r\\n\\t\\t\\t\\t\\t\\treturn acc\\r\\n\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\t{ nonToolMessages: [], toolMessages: [] },\\r\\n\\t\\t\\t\\t)\\r\\n\\r\\n\\t\\t\\t\\t// Process tool result messages FIRST since they must follow the tool use messages\\r\\n\\t\\t\\t\\tlet toolResultImages: Anthropic.Messages.ImageBlockParam[] = []\\r\\n\\t\\t\\t\\ttoolMessages.forEach((toolMessage) => {\\r\\n\\t\\t\\t\\t\\t// The Anthropic SDK allows tool results to be a string or an array of text and image blocks, enabling rich and structured content. In contrast, the OpenAI SDK only supports tool results as a single string, so we map the Anthropic tool result parts into one concatenated string to maintain compatibility.\\r\\n\\t\\t\\t\\t\\tlet content: string\\r\\n\\r\\n\\t\\t\\t\\t\\tif (typeof toolMessage.content === \\\"string\\\") {\\r\\n\\t\\t\\t\\t\\t\\tcontent = toolMessage.content\\r\\n\\t\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t\\tcontent =\\r\\n\\t\\t\\t\\t\\t\\t\\ttoolMessage.content\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t?.map((part) => {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tif (part.type === \\\"image\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\ttoolResultImages.push(part)\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\treturn \\\"(see following user message for image)\\\"\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\treturn part.text\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t.join(\\\"\\\\n\\\") ?? \\\"\\\"\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\topenAiMessages.push({\\r\\n\\t\\t\\t\\t\\t\\trole: \\\"tool\\\",\\r\\n\\t\\t\\t\\t\\t\\ttool_call_id: toolMessage.tool_use_id,\\r\\n\\t\\t\\t\\t\\t\\tcontent: content,\\r\\n\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t})\\r\\n\\r\\n\\t\\t\\t\\t// If tool results contain images, send as a separate user message\\r\\n\\t\\t\\t\\t// I ran into an issue where if I gave feedback for one of many tool uses, the request would fail.\\r\\n\\t\\t\\t\\t// \\\"Messages following `tool_use` blocks must begin with a matching number of `tool_result` blocks.\\\"\\r\\n\\t\\t\\t\\t// Therefore we need to send these images after the tool result messages\\r\\n\\t\\t\\t\\t// NOTE: it's actually okay to have multiple user messages in a row, the model will treat them as a continuation of the same input (this way works better than combining them into one message, since the tool result specifically mentions (see following user message for image)\\r\\n\\t\\t\\t\\t// UPDATE v2.0: we don't use tools anymore, but if we did it's important to note that the openrouter prompt caching mechanism requires one user message at a time, so we would need to add these images to the user content array instead.\\r\\n\\t\\t\\t\\t// if (toolResultImages.length > 0) {\\r\\n\\t\\t\\t\\t// \\topenAiMessages.push({\\r\\n\\t\\t\\t\\t// \\t\\trole: \\\"user\\\",\\r\\n\\t\\t\\t\\t// \\t\\tcontent: toolResultImages.map((part) => ({\\r\\n\\t\\t\\t\\t// \\t\\t\\ttype: \\\"image_url\\\",\\r\\n\\t\\t\\t\\t// \\t\\t\\timage_url: { url: `data:${part.source.media_type};base64,${part.source.data}` },\\r\\n\\t\\t\\t\\t// \\t\\t})),\\r\\n\\t\\t\\t\\t// \\t})\\r\\n\\t\\t\\t\\t// }\\r\\n\\r\\n\\t\\t\\t\\t// Process non-tool messages\\r\\n\\t\\t\\t\\tif (nonToolMessages.length > 0) {\\r\\n\\t\\t\\t\\t\\topenAiMessages.push({\\r\\n\\t\\t\\t\\t\\t\\trole: \\\"user\\\",\\r\\n\\t\\t\\t\\t\\t\\tcontent: nonToolMessages.map((part) => {\\r\\n\\t\\t\\t\\t\\t\\t\\tif (part.type === \\\"image\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\treturn {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"image_url\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\timage_url: {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\turl: `data:${part.source.media_type};base64,${part.source.data}`,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\treturn { type: \\\"text\\\", text: part.text }\\r\\n\\t\\t\\t\\t\\t\\t}),\\r\\n\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t} else if (anthropicMessage.role === \\\"assistant\\\") {\\r\\n\\t\\t\\t\\tconst { nonToolMessages, toolMessages } = anthropicMessage.content.reduce<{\\r\\n\\t\\t\\t\\t\\tnonToolMessages: (Anthropic.TextBlockParam | Anthropic.ImageBlockParam)[]\\r\\n\\t\\t\\t\\t\\ttoolMessages: Anthropic.ToolUseBlockParam[]\\r\\n\\t\\t\\t\\t}>(\\r\\n\\t\\t\\t\\t\\t(acc, part) => {\\r\\n\\t\\t\\t\\t\\t\\tif (part.type === \\\"tool_use\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\tacc.toolMessages.push(part)\\r\\n\\t\\t\\t\\t\\t\\t} else if (part.type === \\\"text\\\" || part.type === \\\"image\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\tacc.nonToolMessages.push(part)\\r\\n\\t\\t\\t\\t\\t\\t} // assistant cannot send tool_result messages\\r\\n\\t\\t\\t\\t\\t\\treturn acc\\r\\n\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\t{ nonToolMessages: [], toolMessages: [] },\\r\\n\\t\\t\\t\\t)\\r\\n\\r\\n\\t\\t\\t\\t// Process non-tool messages\\r\\n\\t\\t\\t\\tlet content: string | undefined\\r\\n\\t\\t\\t\\tif (nonToolMessages.length > 0) {\\r\\n\\t\\t\\t\\t\\tcontent = nonToolMessages\\r\\n\\t\\t\\t\\t\\t\\t.map((part) => {\\r\\n\\t\\t\\t\\t\\t\\t\\tif (part.type === \\\"image\\\") {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\treturn \\\"\\\" // impossible as the assistant cannot send images\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\treturn part.text\\r\\n\\t\\t\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\t\\t\\t.join(\\\"\\\\n\\\")\\r\\n\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t// Process tool use messages\\r\\n\\t\\t\\t\\tlet tool_calls: OpenAI.Chat.ChatCompletionMessageToolCall[] = toolMessages.map((toolMessage) => ({\\r\\n\\t\\t\\t\\t\\tid: toolMessage.id,\\r\\n\\t\\t\\t\\t\\ttype: \\\"function\\\",\\r\\n\\t\\t\\t\\t\\tfunction: {\\r\\n\\t\\t\\t\\t\\t\\tname: toolMessage.name,\\r\\n\\t\\t\\t\\t\\t\\t// json string\\r\\n\\t\\t\\t\\t\\t\\targuments: JSON.stringify(toolMessage.input),\\r\\n\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t}))\\r\\n\\r\\n\\t\\t\\t\\topenAiMessages.push({\\r\\n\\t\\t\\t\\t\\trole: \\\"assistant\\\",\\r\\n\\t\\t\\t\\t\\tcontent,\\r\\n\\t\\t\\t\\t\\t// Cannot be an empty array. API expects an array with minimum length 1, and will respond with an error if it's empty\\r\\n\\t\\t\\t\\t\\ttool_calls: tool_calls.length > 0 ? tool_calls : undefined,\\r\\n\\t\\t\\t\\t})\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\treturn openAiMessages\\r\\n}\\r\\n\\r\\n// Convert OpenAI response to Anthropic format\\r\\nexport function convertToAnthropicMessage(completion: OpenAI.Chat.Completions.ChatCompletion): Anthropic.Messages.Message {\\r\\n\\tconst openAiMessage = completion.choices[0].message\\r\\n\\tconst anthropicMessage: Anthropic.Messages.Message = {\\r\\n\\t\\tid: completion.id,\\r\\n\\t\\ttype: \\\"message\\\",\\r\\n\\t\\trole: openAiMessage.role, // always \\\"assistant\\\"\\r\\n\\t\\tcontent: [\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\ttext: openAiMessage.content || \\\"\\\",\\r\\n\\t\\t\\t},\\r\\n\\t\\t],\\r\\n\\t\\tmodel: completion.model,\\r\\n\\t\\tstop_reason: (() => {\\r\\n\\t\\t\\tswitch (completion.choices[0].finish_reason) {\\r\\n\\t\\t\\t\\tcase \\\"stop\\\":\\r\\n\\t\\t\\t\\t\\treturn \\\"end_turn\\\"\\r\\n\\t\\t\\t\\tcase \\\"length\\\":\\r\\n\\t\\t\\t\\t\\treturn \\\"max_tokens\\\"\\r\\n\\t\\t\\t\\tcase \\\"tool_calls\\\":\\r\\n\\t\\t\\t\\t\\treturn \\\"tool_use\\\"\\r\\n\\t\\t\\t\\tcase \\\"content_filter\\\": // Anthropic doesn't have an exact equivalent\\r\\n\\t\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\t\\treturn null\\r\\n\\t\\t\\t}\\r\\n\\t\\t})(),\\r\\n\\t\\tstop_sequence: null, // which custom stop_sequence was generated, if any (not applicable if you don't use stop_sequence)\\r\\n\\t\\tusage: {\\r\\n\\t\\t\\tinput_tokens: completion.usage?.prompt_tokens || 0,\\r\\n\\t\\t\\toutput_tokens: completion.usage?.completion_tokens || 0,\\r\\n\\t\\t},\\r\\n\\t}\\r\\n\\r\\n\\tif (openAiMessage.tool_calls && openAiMessage.tool_calls.length > 0) {\\r\\n\\t\\tanthropicMessage.content.push(\\r\\n\\t\\t\\t...openAiMessage.tool_calls.map((toolCall): Anthropic.ToolUseBlock => {\\r\\n\\t\\t\\t\\tlet parsedInput = {}\\r\\n\\t\\t\\t\\ttry {\\r\\n\\t\\t\\t\\t\\tparsedInput = JSON.parse(toolCall.function.arguments || \\\"{}\\\")\\r\\n\\t\\t\\t\\t} catch (error) {\\r\\n\\t\\t\\t\\t\\tconsole.error(\\\"Failed to parse tool arguments:\\\", error)\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\treturn {\\r\\n\\t\\t\\t\\t\\ttype: \\\"tool_use\\\",\\r\\n\\t\\t\\t\\t\\tid: toolCall.id,\\r\\n\\t\\t\\t\\t\\tname: toolCall.function.name,\\r\\n\\t\\t\\t\\t\\tinput: parsedInput,\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}),\\r\\n\\t\\t)\\r\\n\\t}\\r\\n\\treturn anthropicMessage\\r\\n}\\r\\n\",\"metadata\":{\"size\":7651,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.936Z\",\"createdTime\":\"2025-01-15T15:26:07.936Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":207,\"nonEmptyLines\":194,\"commentLines\":27,\"complexity\":45},\"dependencies\":[\"@anthropic-ai/sdk\",\"openai\"],\"quality\":{\"score\":-167,\"issues\":[],\"duplicateLines\":49,\"longLines\":11,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.316Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":207},\"analysis\":{\"metrics\":{\"lines\":207,\"nonEmptyLines\":194,\"commentLines\":27,\"complexity\":45},\"dependencies\":[\"@anthropic-ai/sdk\",\"openai\"],\"quality\":{\"score\":-167,\"issues\":[],\"duplicateLines\":49,\"longLines\":11,\"complexFunctions\":0}},\"lastModified\":1737046855317,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.317Z\",\"size\":9512},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\api\\\\transform\\\\o1-format.ts\",\"content\":{\"content\":\"import { Anthropic } from \\\"@anthropic-ai/sdk\\\"\\r\\nimport OpenAI from \\\"openai\\\"\\r\\n\\r\\nconst o1SystemPrompt = (systemPrompt: string) => `\\r\\n# System Prompt\\r\\n\\r\\n${systemPrompt}\\r\\n\\r\\n# Instructions for Formulating Your Response\\r\\n\\r\\nYou must respond to the user's request by using at least one tool call. When formulating your response, follow these guidelines:\\r\\n\\r\\n1. Begin your response with normal text, explaining your thoughts, analysis, or plan of action.\\r\\n2. If you need to use any tools, place ALL tool calls at the END of your message, after your normal text explanation.\\r\\n3. You can use multiple tool calls if needed, but they should all be grouped together at the end of your message.\\r\\n4. After placing the tool calls, do not add any additional normal text. The tool calls should be the final content in your message.\\r\\n\\r\\nHere's the general structure your responses should follow:\\r\\n\\r\\n\\\\`\\\\`\\\\`\\r\\n[Your normal text response explaining your thoughts and actions]\\r\\n\\r\\n[Tool Call 1]\\r\\n[Tool Call 2 if needed]\\r\\n[Tool Call 3 if needed]\\r\\n...\\r\\n\\\\`\\\\`\\\\`\\r\\n\\r\\nRemember:\\r\\n- Choose the most appropriate tool(s) based on the task and the tool descriptions provided.\\r\\n- Formulate your tool calls using the XML format specified for each tool.\\r\\n- Provide clear explanations in your normal text about what actions you're taking and why you're using particular tools.\\r\\n- Act as if the tool calls will be executed immediately after your message, and your next response will have access to their results.\\r\\n\\r\\n# Tool Descriptions and XML Formats\\r\\n\\r\\n1. execute_command:\\r\\n<execute_command>\\r\\n<command>Your command here</command>\\r\\n</execute_command>\\r\\nDescription: Execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory.\\r\\n\\r\\n2. list_files:\\r\\n<list_files>\\r\\n<path>Directory path here</path>\\r\\n<recursive>true or false (optional)</recursive>\\r\\n</list_files>\\r\\nDescription: List files and directories within the specified directory. If recursive is true, it will list all files and directories recursively. If recursive is false or not provided, it will only list the top-level contents.\\r\\n\\r\\n3. list_code_definition_names:\\r\\n<list_code_definition_names>\\r\\n<path>Directory path here</path>\\r\\n</list_code_definition_names>\\r\\nDescription: Lists definition names (classes, functions, methods, etc.) used in source code files at the top level of the specified directory. This tool provides insights into the codebase structure and important constructs, encapsulating high-level concepts and relationships that are crucial for understanding the overall architecture.\\r\\n\\r\\n4. search_files:\\r\\n<search_files>\\r\\n<path>Directory path here</path>\\r\\n<regex>Your regex pattern here</regex>\\r\\n<filePattern>Optional file pattern here</filePattern>\\r\\n</search_files>\\r\\nDescription: Perform a regex search across files in a specified directory, providing context-rich results. This tool searches for patterns or specific content across multiple files, displaying each match with encapsulating context.\\r\\n\\r\\n5. read_file:\\r\\n<read_file>\\r\\n<path>File path here</path>\\r\\n</read_file>\\r\\nDescription: Read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file, for example to analyze code, review text files, or extract information from configuration files. Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string.\\r\\n\\r\\n6. write_to_file:\\r\\n<write_to_file>\\r\\n<path>File path here</path>\\r\\n<content>\\r\\nYour file content here\\r\\n</content>\\r\\n</write_to_file>\\r\\nDescription: Write content to a file at the specified path. If the file exists, it will be overwritten with the provided content. If the file doesn't exist, it will be created. Always provide the full intended content of the file, without any truncation. This tool will automatically create any directories needed to write the file.\\r\\n\\r\\n7. ask_followup_question:\\r\\n<ask_followup_question>\\r\\n<question>Your question here</question>\\r\\n</ask_followup_question>\\r\\nDescription: Ask the user a question to gather additional information needed to complete the task. This tool should be used when you encounter ambiguities, need clarification, or require more details to proceed effectively. It allows for interactive problem-solving by enabling direct communication with the user. Use this tool judiciously to maintain a balance between gathering necessary information and avoiding excessive back-and-forth.\\r\\n\\r\\n8. attempt_completion:\\r\\n<attempt_completion>\\r\\n<command>Optional command to demonstrate result</command>\\r\\n<result>\\r\\nYour final result description here\\r\\n</result>\\r\\n</attempt_completion>\\r\\nDescription: Once you've completed the task, use this tool to present the result to the user. They may respond with feedback if they are not satisfied with the result, which you can use to make improvements and try again.\\r\\n\\r\\n# Examples\\r\\n\\r\\nHere are some examples of how to structure your responses with tool calls:\\r\\n\\r\\nExample 1: Using a single tool\\r\\n\\r\\nLet's run the test suite for our project. This will help us ensure that all our components are functioning correctly.\\r\\n\\r\\n<execute_command>\\r\\n<command>npm test</command>\\r\\n</execute_command>\\r\\n\\r\\nExample 2: Using multiple tools\\r\\n\\r\\nLet's create two new configuration files for the web application: one for the frontend and one for the backend.\\r\\n\\r\\n<write_to_file>\\r\\n<path>./frontend-config.json</path>\\r\\n<content>\\r\\n{\\r\\n \\\"apiEndpoint\\\": \\\"https://api.example.com\\\",\\r\\n \\\"theme\\\": {\\r\\n \\\"primaryColor\\\": \\\"#007bff\\\",\\r\\n \\\"secondaryColor\\\": \\\"#6c757d\\\",\\r\\n \\\"fontFamily\\\": \\\"Arial, sans-serif\\\"\\r\\n },\\r\\n \\\"features\\\": {\\r\\n \\\"darkMode\\\": true,\\r\\n \\\"notifications\\\": true,\\r\\n \\\"analytics\\\": false\\r\\n },\\r\\n \\\"version\\\": \\\"1.0.0\\\"\\r\\n}\\r\\n</content>\\r\\n</write_to_file>\\r\\n\\r\\n<write_to_file>\\r\\n<path>./backend-config.yaml</path>\\r\\n<content>\\r\\ndatabase:\\r\\n host: localhost\\r\\n port: 5432\\r\\n name: myapp_db\\r\\n user: admin\\r\\n\\r\\nserver:\\r\\n port: 3000\\r\\n environment: development\\r\\n logLevel: debug\\r\\n\\r\\nsecurity:\\r\\n jwtSecret: your-secret-key-here\\r\\n passwordSaltRounds: 10\\r\\n\\r\\ncaching:\\r\\n enabled: true\\r\\n provider: redis\\r\\n ttl: 3600\\r\\n\\r\\nexternalServices:\\r\\n emailProvider: sendgrid\\r\\n storageProvider: aws-s3\\r\\n</content>\\r\\n</write_to_file>\\r\\n\\r\\nExample 3: Asking a follow-up question\\r\\n\\r\\nI've analyzed the project structure, but I need more information to proceed. Let me ask the user for clarification.\\r\\n\\r\\n<ask_followup_question>\\r\\n<question>Which specific feature would you like me to implement in the example.py file?</question>\\r\\n</ask_followup_question>\\r\\n`\\r\\n\\r\\nexport function convertToO1Messages(\\r\\n\\topenAiMessages: OpenAI.Chat.ChatCompletionMessageParam[],\\r\\n\\tsystemPrompt: string,\\r\\n): OpenAI.Chat.ChatCompletionMessageParam[] {\\r\\n\\tconst toolsReplaced = openAiMessages.reduce((acc, message) => {\\r\\n\\t\\tif (message.role === \\\"tool\\\") {\\r\\n\\t\\t\\t// Convert tool messages to user messages\\r\\n\\t\\t\\tacc.push({\\r\\n\\t\\t\\t\\trole: \\\"user\\\",\\r\\n\\t\\t\\t\\tcontent: message.content || \\\"\\\",\\r\\n\\t\\t\\t})\\r\\n\\t\\t} else if (message.role === \\\"assistant\\\" && message.tool_calls) {\\r\\n\\t\\t\\t// Convert tool calls to content and remove tool_calls\\r\\n\\t\\t\\tlet content = message.content || \\\"\\\"\\r\\n\\t\\t\\tmessage.tool_calls.forEach((toolCall) => {\\r\\n\\t\\t\\t\\tif (toolCall.type === \\\"function\\\") {\\r\\n\\t\\t\\t\\t\\tcontent += `\\\\nTool Call: ${toolCall.function.name}\\\\nArguments: ${toolCall.function.arguments}`\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t})\\r\\n\\t\\t\\tacc.push({\\r\\n\\t\\t\\t\\trole: \\\"assistant\\\",\\r\\n\\t\\t\\t\\tcontent: content,\\r\\n\\t\\t\\t\\ttool_calls: undefined,\\r\\n\\t\\t\\t})\\r\\n\\t\\t} else {\\r\\n\\t\\t\\t// Keep other messages as they are\\r\\n\\t\\t\\tacc.push(message)\\r\\n\\t\\t}\\r\\n\\t\\treturn acc\\r\\n\\t}, [] as OpenAI.Chat.ChatCompletionMessageParam[])\\r\\n\\r\\n\\t// Find the index of the last assistant message\\r\\n\\t// const lastAssistantIndex = findLastIndex(toolsReplaced, (message) => message.role === \\\"assistant\\\")\\r\\n\\r\\n\\t// Create a new array to hold the modified messages\\r\\n\\tconst messagesWithSystemPrompt = [\\r\\n\\t\\t{\\r\\n\\t\\t\\trole: \\\"user\\\",\\r\\n\\t\\t\\tcontent: o1SystemPrompt(systemPrompt),\\r\\n\\t\\t} as OpenAI.Chat.ChatCompletionUserMessageParam,\\r\\n\\t\\t...toolsReplaced,\\r\\n\\t]\\r\\n\\r\\n\\t// If there's an assistant message, insert the system prompt after it\\r\\n\\t// if (lastAssistantIndex !== -1) {\\r\\n\\t// \\tconst insertIndex = lastAssistantIndex + 1\\r\\n\\t// \\tif (insertIndex < messagesWithSystemPrompt.length && messagesWithSystemPrompt[insertIndex].role === \\\"user\\\") {\\r\\n\\t// \\t\\tmessagesWithSystemPrompt.splice(insertIndex, 0, {\\r\\n\\t// \\t\\t\\trole: \\\"user\\\",\\r\\n\\t// \\t\\t\\tcontent: o1SystemPrompt(systemPrompt),\\r\\n\\t// \\t\\t})\\r\\n\\t// \\t}\\r\\n\\t// } else {\\r\\n\\t// \\t// If there were no assistant messages, prepend the system prompt\\r\\n\\t// \\tmessagesWithSystemPrompt.unshift({\\r\\n\\t// \\t\\trole: \\\"user\\\",\\r\\n\\t// \\t\\tcontent: o1SystemPrompt(systemPrompt),\\r\\n\\t// \\t})\\r\\n\\t// }\\r\\n\\r\\n\\treturn messagesWithSystemPrompt\\r\\n}\\r\\n\\r\\ninterface ToolCall {\\r\\n\\ttool: string\\r\\n\\ttool_input: Record<string, string>\\r\\n}\\r\\n\\r\\nconst toolNames = [\\r\\n\\t\\\"execute_command\\\",\\r\\n\\t\\\"list_files\\\",\\r\\n\\t\\\"list_code_definition_names\\\",\\r\\n\\t\\\"search_files\\\",\\r\\n\\t\\\"read_file\\\",\\r\\n\\t\\\"write_to_file\\\",\\r\\n\\t\\\"ask_followup_question\\\",\\r\\n\\t\\\"attempt_completion\\\",\\r\\n]\\r\\n\\r\\nfunction parseAIResponse(response: string): {\\r\\n\\tnormalText: string\\r\\n\\ttoolCalls: ToolCall[]\\r\\n} {\\r\\n\\t// Create a regex pattern to match any tool call opening tag\\r\\n\\tconst toolCallPattern = new RegExp(`<(${toolNames.join(\\\"|\\\")})`, \\\"i\\\")\\r\\n\\tconst match = response.match(toolCallPattern)\\r\\n\\r\\n\\tif (!match) {\\r\\n\\t\\t// No tool calls found\\r\\n\\t\\treturn { normalText: response.trim(), toolCalls: [] }\\r\\n\\t}\\r\\n\\r\\n\\tconst toolCallStart = match.index!\\r\\n\\tconst normalText = response.slice(0, toolCallStart).trim()\\r\\n\\tconst toolCallsText = response.slice(toolCallStart)\\r\\n\\r\\n\\tconst toolCalls = parseToolCalls(toolCallsText)\\r\\n\\r\\n\\treturn { normalText, toolCalls }\\r\\n}\\r\\n\\r\\nfunction parseToolCalls(toolCallsText: string): ToolCall[] {\\r\\n\\tconst toolCalls: ToolCall[] = []\\r\\n\\r\\n\\tlet remainingText = toolCallsText\\r\\n\\r\\n\\twhile (remainingText.length > 0) {\\r\\n\\t\\tconst toolMatch = toolNames.find((tool) => new RegExp(`<${tool}`, \\\"i\\\").test(remainingText))\\r\\n\\r\\n\\t\\tif (!toolMatch) {\\r\\n\\t\\t\\tbreak // No more tool calls found\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tconst startTag = `<${toolMatch}`\\r\\n\\t\\tconst endTag = `</${toolMatch}>`\\r\\n\\t\\tconst startIndex = remainingText.indexOf(startTag)\\r\\n\\t\\tconst endIndex = remainingText.indexOf(endTag, startIndex)\\r\\n\\r\\n\\t\\tif (endIndex === -1) {\\r\\n\\t\\t\\tbreak // Malformed XML, no closing tag found\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tconst toolCallContent = remainingText.slice(startIndex, endIndex + endTag.length)\\r\\n\\t\\tremainingText = remainingText.slice(endIndex + endTag.length).trim()\\r\\n\\r\\n\\t\\tconst toolCall = parseToolCall(toolMatch, toolCallContent)\\r\\n\\t\\tif (toolCall) {\\r\\n\\t\\t\\ttoolCalls.push(toolCall)\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\treturn toolCalls\\r\\n}\\r\\n\\r\\nfunction parseToolCall(toolName: string, content: string): ToolCall | null {\\r\\n\\tconst tool_input: Record<string, string> = {}\\r\\n\\r\\n\\t// Remove the outer tool tags\\r\\n\\tconst innerContent = content.replace(new RegExp(`^<${toolName}>|</${toolName}>$`, \\\"g\\\"), \\\"\\\").trim()\\r\\n\\r\\n\\t// Parse nested XML elements\\r\\n\\tconst paramRegex = /<(\\\\w+)>([\\\\s\\\\S]*?)<\\\\/\\\\1>/gs\\r\\n\\tlet match\\r\\n\\r\\n\\twhile ((match = paramRegex.exec(innerContent)) !== null) {\\r\\n\\t\\tconst [, paramName, paramValue] = match\\r\\n\\t\\t// Preserve newlines and trim only leading/trailing whitespace\\r\\n\\t\\ttool_input[paramName] = paramValue.replace(/^\\\\s+|\\\\s+$/g, \\\"\\\")\\r\\n\\t}\\r\\n\\r\\n\\t// Validate required parameters\\r\\n\\tif (!validateToolInput(toolName, tool_input)) {\\r\\n\\t\\tconsole.error(`Invalid tool call for ${toolName}:`, content)\\r\\n\\t\\treturn null\\r\\n\\t}\\r\\n\\r\\n\\treturn { tool: toolName, tool_input }\\r\\n}\\r\\n\\r\\nfunction validateToolInput(toolName: string, tool_input: Record<string, string>): boolean {\\r\\n\\tswitch (toolName) {\\r\\n\\t\\tcase \\\"execute_command\\\":\\r\\n\\t\\t\\treturn \\\"command\\\" in tool_input\\r\\n\\t\\tcase \\\"read_file\\\":\\r\\n\\t\\tcase \\\"list_code_definition_names\\\":\\r\\n\\t\\tcase \\\"list_files\\\":\\r\\n\\t\\t\\treturn \\\"path\\\" in tool_input\\r\\n\\t\\tcase \\\"search_files\\\":\\r\\n\\t\\t\\treturn \\\"path\\\" in tool_input && \\\"regex\\\" in tool_input\\r\\n\\t\\tcase \\\"write_to_file\\\":\\r\\n\\t\\t\\treturn \\\"path\\\" in tool_input && \\\"content\\\" in tool_input\\r\\n\\t\\tcase \\\"ask_followup_question\\\":\\r\\n\\t\\t\\treturn \\\"question\\\" in tool_input\\r\\n\\t\\tcase \\\"attempt_completion\\\":\\r\\n\\t\\t\\treturn \\\"result\\\" in tool_input\\r\\n\\t\\tdefault:\\r\\n\\t\\t\\treturn false\\r\\n\\t}\\r\\n}\\r\\n\\r\\n// Example usage:\\r\\n// const aiResponse = `Here's my analysis of the situation...\\r\\n\\r\\n// <execute_command>\\r\\n// <command>ls -la</command>\\r\\n// </execute_command>\\r\\n\\r\\n// <write_to_file>\\r\\n// <path>./example.txt</path>\\r\\n// <content>Hello, World!</content>\\r\\n// </write_to_file>`;\\r\\n//\\r\\n// const { normalText, toolCalls } = parseAIResponse(aiResponse);\\r\\n// console.log(normalText);\\r\\n// console.log(toolCalls);\\r\\n\\r\\n// Convert OpenAI response to Anthropic format\\r\\nexport function convertO1ResponseToAnthropicMessage(\\r\\n\\tcompletion: OpenAI.Chat.Completions.ChatCompletion,\\r\\n): Anthropic.Messages.Message {\\r\\n\\tconst openAiMessage = completion.choices[0].message\\r\\n\\tconst { normalText, toolCalls } = parseAIResponse(openAiMessage.content || \\\"\\\")\\r\\n\\r\\n\\tconst anthropicMessage: Anthropic.Messages.Message = {\\r\\n\\t\\tid: completion.id,\\r\\n\\t\\ttype: \\\"message\\\",\\r\\n\\t\\trole: openAiMessage.role, // always \\\"assistant\\\"\\r\\n\\t\\tcontent: [\\r\\n\\t\\t\\t{\\r\\n\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\ttext: normalText,\\r\\n\\t\\t\\t},\\r\\n\\t\\t],\\r\\n\\t\\tmodel: completion.model,\\r\\n\\t\\tstop_reason: (() => {\\r\\n\\t\\t\\tswitch (completion.choices[0].finish_reason) {\\r\\n\\t\\t\\t\\tcase \\\"stop\\\":\\r\\n\\t\\t\\t\\t\\treturn \\\"end_turn\\\"\\r\\n\\t\\t\\t\\tcase \\\"length\\\":\\r\\n\\t\\t\\t\\t\\treturn \\\"max_tokens\\\"\\r\\n\\t\\t\\t\\tcase \\\"tool_calls\\\":\\r\\n\\t\\t\\t\\t\\treturn \\\"tool_use\\\"\\r\\n\\t\\t\\t\\tcase \\\"content_filter\\\": // Anthropic doesn't have an exact equivalent\\r\\n\\t\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\t\\treturn null\\r\\n\\t\\t\\t}\\r\\n\\t\\t})(),\\r\\n\\t\\tstop_sequence: null, // which custom stop_sequence was generated, if any (not applicable if you don't use stop_sequence)\\r\\n\\t\\tusage: {\\r\\n\\t\\t\\tinput_tokens: completion.usage?.prompt_tokens || 0,\\r\\n\\t\\t\\toutput_tokens: completion.usage?.completion_tokens || 0,\\r\\n\\t\\t},\\r\\n\\t}\\r\\n\\r\\n\\tif (toolCalls.length > 0) {\\r\\n\\t\\tanthropicMessage.content.push(\\r\\n\\t\\t\\t...toolCalls.map((toolCall: ToolCall, index: number): Anthropic.ToolUseBlock => {\\r\\n\\t\\t\\t\\treturn {\\r\\n\\t\\t\\t\\t\\ttype: \\\"tool_use\\\",\\r\\n\\t\\t\\t\\t\\tid: `call_${index}_${Date.now()}`, // Generate a unique ID for each tool call\\r\\n\\t\\t\\t\\t\\tname: toolCall.tool,\\r\\n\\t\\t\\t\\t\\tinput: toolCall.tool_input,\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}),\\r\\n\\t\\t)\\r\\n\\t}\\r\\n\\r\\n\\treturn anthropicMessage\\r\\n}\\r\\n\\r\\n// Example usage:\\r\\n// const openAICompletion = {\\r\\n// id: \\\"cmpl-123\\\",\\r\\n// choices: [{\\r\\n// message: {\\r\\n// role: \\\"assistant\\\",\\r\\n// content: \\\"Here's my analysis...\\\\n\\\\n<execute_command>\\\\n <command>ls -la</command>\\\\n</execute_command>\\\"\\r\\n// },\\r\\n// finish_reason: \\\"stop\\\"\\r\\n// }],\\r\\n// model: \\\"gpt-3.5-turbo\\\",\\r\\n// usage: { prompt_tokens: 50, completion_tokens: 100 }\\r\\n// };\\r\\n// const anthropicMessage = convertO1ResponseToAnthropicMessage(openAICompletion);\\r\\n// console.log(anthropicMessage);\\r\\n\",\"metadata\":{\"size\":14688,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.936Z\",\"createdTime\":\"2025-01-15T15:26:07.936Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":433,\"nonEmptyLines\":362,\"commentLines\":57,\"complexity\":43},\"dependencies\":[\"@anthropic-ai/sdk\",\"openai\"],\"quality\":{\"score\":-192,\"issues\":[],\"duplicateLines\":50,\"longLines\":21,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.313Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":433},\"analysis\":{\"metrics\":{\"lines\":433,\"nonEmptyLines\":362,\"commentLines\":57,\"complexity\":43},\"dependencies\":[\"@anthropic-ai/sdk\",\"openai\"],\"quality\":{\"score\":-192,\"issues\":[],\"duplicateLines\":50,\"longLines\":21,\"complexFunctions\":0}},\"lastModified\":1737046855315,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.315Z\",\"size\":16614},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\api\\\\transform\\\\gemini-format.ts\",\"content\":{\"content\":\"import { Anthropic } from \\\"@anthropic-ai/sdk\\\"\\r\\nimport {\\r\\n\\tContent,\\r\\n\\tEnhancedGenerateContentResponse,\\r\\n\\tFunctionCallPart,\\r\\n\\tFunctionDeclaration,\\r\\n\\tFunctionResponsePart,\\r\\n\\tInlineDataPart,\\r\\n\\tPart,\\r\\n\\tSchemaType,\\r\\n\\tTextPart,\\r\\n} from \\\"@google/generative-ai\\\"\\r\\n\\r\\nexport function convertAnthropicContentToGemini(\\r\\n\\tcontent:\\r\\n\\t\\t| string\\r\\n\\t\\t| Array<\\r\\n\\t\\t\\t\\t| Anthropic.Messages.TextBlockParam\\r\\n\\t\\t\\t\\t| Anthropic.Messages.ImageBlockParam\\r\\n\\t\\t\\t\\t| Anthropic.Messages.ToolUseBlockParam\\r\\n\\t\\t\\t\\t| Anthropic.Messages.ToolResultBlockParam\\r\\n\\t\\t >,\\r\\n): Part[] {\\r\\n\\tif (typeof content === \\\"string\\\") {\\r\\n\\t\\treturn [{ text: content } as TextPart]\\r\\n\\t}\\r\\n\\treturn content.flatMap((block) => {\\r\\n\\t\\tswitch (block.type) {\\r\\n\\t\\t\\tcase \\\"text\\\":\\r\\n\\t\\t\\t\\treturn { text: block.text } as TextPart\\r\\n\\t\\t\\tcase \\\"image\\\":\\r\\n\\t\\t\\t\\tif (block.source.type !== \\\"base64\\\") {\\r\\n\\t\\t\\t\\t\\tthrow new Error(\\\"Unsupported image source type\\\")\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\treturn {\\r\\n\\t\\t\\t\\t\\tinlineData: {\\r\\n\\t\\t\\t\\t\\t\\tdata: block.source.data,\\r\\n\\t\\t\\t\\t\\t\\tmimeType: block.source.media_type,\\r\\n\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t} as InlineDataPart\\r\\n\\t\\t\\tcase \\\"tool_use\\\":\\r\\n\\t\\t\\t\\treturn {\\r\\n\\t\\t\\t\\t\\tfunctionCall: {\\r\\n\\t\\t\\t\\t\\t\\tname: block.name,\\r\\n\\t\\t\\t\\t\\t\\targs: block.input,\\r\\n\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t} as FunctionCallPart\\r\\n\\t\\t\\tcase \\\"tool_result\\\":\\r\\n\\t\\t\\t\\tconst name = block.tool_use_id.split(\\\"-\\\")[0]\\r\\n\\t\\t\\t\\tif (!block.content) {\\r\\n\\t\\t\\t\\t\\treturn []\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tif (typeof block.content === \\\"string\\\") {\\r\\n\\t\\t\\t\\t\\treturn {\\r\\n\\t\\t\\t\\t\\t\\tfunctionResponse: {\\r\\n\\t\\t\\t\\t\\t\\t\\tname,\\r\\n\\t\\t\\t\\t\\t\\t\\tresponse: {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tname,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tcontent: block.content,\\r\\n\\t\\t\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\t} as FunctionResponsePart\\r\\n\\t\\t\\t\\t} else {\\r\\n\\t\\t\\t\\t\\t// The only case when tool_result could be array is when the tool failed and we're providing ie user feedback potentially with images\\r\\n\\t\\t\\t\\t\\tconst textParts = block.content.filter((part) => part.type === \\\"text\\\")\\r\\n\\t\\t\\t\\t\\tconst imageParts = block.content.filter((part) => part.type === \\\"image\\\")\\r\\n\\t\\t\\t\\t\\tconst text = textParts.length > 0 ? textParts.map((part) => part.text).join(\\\"\\\\n\\\\n\\\") : \\\"\\\"\\r\\n\\t\\t\\t\\t\\tconst imageText = imageParts.length > 0 ? \\\"\\\\n\\\\n(See next part for image)\\\" : \\\"\\\"\\r\\n\\t\\t\\t\\t\\treturn [\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\tfunctionResponse: {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tname,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tresponse: {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tname,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tcontent: text + imageText,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\t\\t} as FunctionResponsePart,\\r\\n\\t\\t\\t\\t\\t\\t...imageParts.map(\\r\\n\\t\\t\\t\\t\\t\\t\\t(part) =>\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t({\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tinlineData: {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tdata: part.source.data,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tmimeType: part.source.media_type,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}) as InlineDataPart,\\r\\n\\t\\t\\t\\t\\t\\t),\\r\\n\\t\\t\\t\\t\\t]\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\tthrow new Error(`Unsupported content block type: ${(block as any).type}`)\\r\\n\\t\\t}\\r\\n\\t})\\r\\n}\\r\\n\\r\\nexport function convertAnthropicMessageToGemini(message: Anthropic.Messages.MessageParam): Content {\\r\\n\\treturn {\\r\\n\\t\\trole: message.role === \\\"assistant\\\" ? \\\"model\\\" : \\\"user\\\",\\r\\n\\t\\tparts: convertAnthropicContentToGemini(message.content),\\r\\n\\t}\\r\\n}\\r\\n\\r\\nexport function convertAnthropicToolToGemini(tool: Anthropic.Messages.Tool): FunctionDeclaration {\\r\\n\\treturn {\\r\\n\\t\\tname: tool.name,\\r\\n\\t\\tdescription: tool.description || \\\"\\\",\\r\\n\\t\\tparameters: {\\r\\n\\t\\t\\ttype: SchemaType.OBJECT,\\r\\n\\t\\t\\tproperties: Object.fromEntries(\\r\\n\\t\\t\\t\\tObject.entries(tool.input_schema.properties || {}).map(([key, value]) => [\\r\\n\\t\\t\\t\\t\\tkey,\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\ttype: (value as any).type.toUpperCase(),\\r\\n\\t\\t\\t\\t\\t\\tdescription: (value as any).description || \\\"\\\",\\r\\n\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t]),\\r\\n\\t\\t\\t),\\r\\n\\t\\t\\trequired: (tool.input_schema.required as string[]) || [],\\r\\n\\t\\t},\\r\\n\\t}\\r\\n}\\r\\n\\r\\n/*\\r\\nIt 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\\r\\n*/\\r\\nexport function unescapeGeminiContent(content: string) {\\r\\n\\treturn content.replace(/\\\\\\\\n/g, \\\"\\\\n\\\").replace(/\\\\\\\\'/g, \\\"'\\\").replace(/\\\\\\\\\\\"/g, '\\\"').replace(/\\\\\\\\r/g, \\\"\\\\r\\\").replace(/\\\\\\\\t/g, \\\"\\\\t\\\")\\r\\n}\\r\\n\\r\\nexport function convertGeminiResponseToAnthropic(response: EnhancedGenerateContentResponse): Anthropic.Messages.Message {\\r\\n\\tconst content: Anthropic.Messages.ContentBlock[] = []\\r\\n\\r\\n\\t// Add the main text response\\r\\n\\tconst text = response.text()\\r\\n\\tif (text) {\\r\\n\\t\\tcontent.push({ type: \\\"text\\\", text })\\r\\n\\t}\\r\\n\\r\\n\\t// Add function calls as tool_use blocks\\r\\n\\tconst functionCalls = response.functionCalls()\\r\\n\\tif (functionCalls) {\\r\\n\\t\\tfunctionCalls.forEach((call, index) => {\\r\\n\\t\\t\\tif (\\\"content\\\" in call.args && typeof call.args.content === \\\"string\\\") {\\r\\n\\t\\t\\t\\tcall.args.content = unescapeGeminiContent(call.args.content)\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tcontent.push({\\r\\n\\t\\t\\t\\ttype: \\\"tool_use\\\",\\r\\n\\t\\t\\t\\tid: `${call.name}-${index}-${Date.now()}`,\\r\\n\\t\\t\\t\\tname: call.name,\\r\\n\\t\\t\\t\\tinput: call.args,\\r\\n\\t\\t\\t})\\r\\n\\t\\t})\\r\\n\\t}\\r\\n\\r\\n\\t// Determine stop reason\\r\\n\\tlet stop_reason: Anthropic.Messages.Message[\\\"stop_reason\\\"] = null\\r\\n\\tconst finishReason = response.candidates?.[0]?.finishReason\\r\\n\\tif (finishReason) {\\r\\n\\t\\tswitch (finishReason) {\\r\\n\\t\\t\\tcase \\\"STOP\\\":\\r\\n\\t\\t\\t\\tstop_reason = \\\"end_turn\\\"\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\tcase \\\"MAX_TOKENS\\\":\\r\\n\\t\\t\\t\\tstop_reason = \\\"max_tokens\\\"\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\tcase \\\"SAFETY\\\":\\r\\n\\t\\t\\tcase \\\"RECITATION\\\":\\r\\n\\t\\t\\tcase \\\"OTHER\\\":\\r\\n\\t\\t\\t\\tstop_reason = \\\"stop_sequence\\\"\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t// Add more cases if needed\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\treturn {\\r\\n\\t\\tid: `msg_${Date.now()}`, // Generate a unique ID\\r\\n\\t\\ttype: \\\"message\\\",\\r\\n\\t\\trole: \\\"assistant\\\",\\r\\n\\t\\tcontent,\\r\\n\\t\\tmodel: \\\"\\\",\\r\\n\\t\\tstop_reason,\\r\\n\\t\\tstop_sequence: null, // Gemini doesn't provide this information\\r\\n\\t\\tusage: {\\r\\n\\t\\t\\tinput_tokens: response.usageMetadata?.promptTokenCount ?? 0,\\r\\n\\t\\t\\toutput_tokens: response.usageMetadata?.candidatesTokenCount ?? 0,\\r\\n\\t\\t},\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":5311,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.936Z\",\"createdTime\":\"2025-01-15T15:26:07.936Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":189,\"nonEmptyLines\":179,\"commentLines\":6,\"complexity\":36},\"dependencies\":[\"@anthropic-ai/sdk\"],\"quality\":{\"score\":-125,\"issues\":[],\"duplicateLines\":43,\"longLines\":5,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.312Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":189},\"analysis\":{\"metrics\":{\"lines\":189,\"nonEmptyLines\":179,\"commentLines\":6,\"complexity\":36},\"dependencies\":[\"@anthropic-ai/sdk\"],\"quality\":{\"score\":-125,\"issues\":[],\"duplicateLines\":43,\"longLines\":5,\"complexFunctions\":0}},\"lastModified\":1737046855312,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.312Z\",\"size\":6860},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\api\\\\providers\\\\vertex.ts\",\"content\":{\"content\":\"import { Anthropic } from \\\"@anthropic-ai/sdk\\\"\\r\\nimport { AnthropicVertex } from \\\"@anthropic-ai/vertex-sdk\\\"\\r\\nimport { ApiHandler } from \\\"../\\\"\\r\\nimport { ApiHandlerOptions, ModelInfo, vertexDefaultModelId, VertexModelId, vertexModels } from \\\"../../shared/api\\\"\\r\\nimport { ApiStream } from \\\"../transform/stream\\\"\\r\\n\\r\\n// https://docs.anthropic.com/en/api/claude-on-vertex-ai\\r\\nexport class VertexHandler implements ApiHandler {\\r\\n\\tprivate options: ApiHandlerOptions\\r\\n\\tprivate client: AnthropicVertex\\r\\n\\r\\n\\tconstructor(options: ApiHandlerOptions) {\\r\\n\\t\\tthis.options = options\\r\\n\\t\\tthis.client = new AnthropicVertex({\\r\\n\\t\\t\\tprojectId: this.options.vertexProjectId,\\r\\n\\t\\t\\t// https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-claude#regions\\r\\n\\t\\t\\tregion: this.options.vertexRegion,\\r\\n\\t\\t})\\r\\n\\t}\\r\\n\\r\\n\\tasync *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {\\r\\n\\t\\tconst stream = await this.client.messages.create({\\r\\n\\t\\t\\tmodel: this.getModel().id,\\r\\n\\t\\t\\tmax_tokens: this.getModel().info.maxTokens || 8192,\\r\\n\\t\\t\\ttemperature: 0,\\r\\n\\t\\t\\tsystem: systemPrompt,\\r\\n\\t\\t\\tmessages,\\r\\n\\t\\t\\tstream: true,\\r\\n\\t\\t})\\r\\n\\t\\tfor await (const chunk of stream) {\\r\\n\\t\\t\\tswitch (chunk.type) {\\r\\n\\t\\t\\t\\tcase \\\"message_start\\\":\\r\\n\\t\\t\\t\\t\\tconst usage = chunk.message.usage\\r\\n\\t\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"usage\\\",\\r\\n\\t\\t\\t\\t\\t\\tinputTokens: usage.input_tokens || 0,\\r\\n\\t\\t\\t\\t\\t\\toutputTokens: usage.output_tokens || 0,\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\tcase \\\"message_delta\\\":\\r\\n\\t\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"usage\\\",\\r\\n\\t\\t\\t\\t\\t\\tinputTokens: 0,\\r\\n\\t\\t\\t\\t\\t\\toutputTokens: chunk.usage.output_tokens || 0,\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\r\\n\\t\\t\\t\\tcase \\\"content_block_start\\\":\\r\\n\\t\\t\\t\\t\\tswitch (chunk.content_block.type) {\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"text\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\tif (chunk.index > 0) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttext: \\\"\\\\n\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttext: chunk.content_block.text,\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\tcase \\\"content_block_delta\\\":\\r\\n\\t\\t\\t\\t\\tswitch (chunk.delta.type) {\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"text_delta\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttext: chunk.delta.text,\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tgetModel(): { id: VertexModelId; info: ModelInfo } {\\r\\n\\t\\tconst modelId = this.options.apiModelId\\r\\n\\t\\tif (modelId && modelId in vertexModels) {\\r\\n\\t\\t\\tconst id = modelId as VertexModelId\\r\\n\\t\\t\\treturn { id, info: vertexModels[id] }\\r\\n\\t\\t}\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tid: vertexDefaultModelId,\\r\\n\\t\\t\\tinfo: vertexModels[vertexDefaultModelId],\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":2450,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.935Z\",\"createdTime\":\"2025-01-15T15:26:07.935Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":90,\"nonEmptyLines\":84,\"commentLines\":2,\"complexity\":14},\"dependencies\":[\"@anthropic-ai/sdk\",\"@anthropic-ai/vertex-sdk\",\"../\",\"../../shared/api\",\"../transform/stream\"],\"quality\":{\"score\":-44,\"issues\":[],\"duplicateLines\":28,\"longLines\":2,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.311Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":90},\"analysis\":{\"metrics\":{\"lines\":90,\"nonEmptyLines\":84,\"commentLines\":2,\"complexity\":14},\"dependencies\":[\"@anthropic-ai/sdk\",\"@anthropic-ai/vertex-sdk\",\"../\",\"../../shared/api\",\"../transform/stream\"],\"quality\":{\"score\":-44,\"issues\":[],\"duplicateLines\":28,\"longLines\":2,\"complexFunctions\":0}},\"lastModified\":1737046855311,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.311Z\",\"size\":3541},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\api\\\\providers\\\\openrouter.ts\",\"content\":{\"content\":\"import { Anthropic } from \\\"@anthropic-ai/sdk\\\"\\r\\nimport axios from \\\"axios\\\"\\r\\nimport OpenAI from \\\"openai\\\"\\r\\nimport { ApiHandler } from \\\"../\\\"\\r\\nimport { ApiHandlerOptions, ModelInfo, openRouterDefaultModelId, openRouterDefaultModelInfo } from \\\"../../shared/api\\\"\\r\\nimport { convertToOpenAiMessages } from \\\"../transform/openai-format\\\"\\r\\nimport { ApiStream } from \\\"../transform/stream\\\"\\r\\nimport delay from \\\"delay\\\"\\r\\n\\r\\nexport class OpenRouterHandler implements ApiHandler {\\r\\n\\tprivate options: ApiHandlerOptions\\r\\n\\tprivate client: OpenAI\\r\\n\\r\\n\\tconstructor(options: ApiHandlerOptions) {\\r\\n\\t\\tthis.options = options\\r\\n\\t\\tthis.client = new OpenAI({\\r\\n\\t\\t\\tbaseURL: \\\"https://openrouter.ai/api/v1\\\",\\r\\n\\t\\t\\tapiKey: this.options.openRouterApiKey,\\r\\n\\t\\t\\tdefaultHeaders: {\\r\\n\\t\\t\\t\\t\\\"HTTP-Referer\\\": \\\"https://cline.bot\\\", // Optional, for including your app on openrouter.ai rankings.\\r\\n\\t\\t\\t\\t\\\"X-Title\\\": \\\"Cline\\\", // Optional. Shows in rankings on openrouter.ai.\\r\\n\\t\\t\\t},\\r\\n\\t\\t})\\r\\n\\t}\\r\\n\\r\\n\\tasync *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {\\r\\n\\t\\t// Convert Anthropic messages to OpenAI format\\r\\n\\t\\tconst openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [\\r\\n\\t\\t\\t{ role: \\\"system\\\", content: systemPrompt },\\r\\n\\t\\t\\t...convertToOpenAiMessages(messages),\\r\\n\\t\\t]\\r\\n\\r\\n\\t\\t// prompt caching: https://openrouter.ai/docs/prompt-caching\\r\\n\\t\\t// this is specifically for claude models (some models may 'support prompt caching' automatically without this)\\r\\n\\t\\tswitch (this.getModel().id) {\\r\\n\\t\\t\\tcase \\\"anthropic/claude-3.5-sonnet\\\":\\r\\n\\t\\t\\tcase \\\"anthropic/claude-3.5-sonnet:beta\\\":\\r\\n\\t\\t\\tcase \\\"anthropic/claude-3.5-sonnet-20240620\\\":\\r\\n\\t\\t\\tcase \\\"anthropic/claude-3.5-sonnet-20240620:beta\\\":\\r\\n\\t\\t\\tcase \\\"anthropic/claude-3-5-haiku\\\":\\r\\n\\t\\t\\tcase \\\"anthropic/claude-3-5-haiku:beta\\\":\\r\\n\\t\\t\\tcase \\\"anthropic/claude-3-5-haiku-20241022\\\":\\r\\n\\t\\t\\tcase \\\"anthropic/claude-3-5-haiku-20241022:beta\\\":\\r\\n\\t\\t\\tcase \\\"anthropic/claude-3-haiku\\\":\\r\\n\\t\\t\\tcase \\\"anthropic/claude-3-haiku:beta\\\":\\r\\n\\t\\t\\tcase \\\"anthropic/claude-3-opus\\\":\\r\\n\\t\\t\\tcase \\\"anthropic/claude-3-opus:beta\\\":\\r\\n\\t\\t\\t\\topenAiMessages[0] = {\\r\\n\\t\\t\\t\\t\\trole: \\\"system\\\",\\r\\n\\t\\t\\t\\t\\tcontent: [\\r\\n\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\ttext: systemPrompt,\\r\\n\\t\\t\\t\\t\\t\\t\\t// @ts-ignore-next-line\\r\\n\\t\\t\\t\\t\\t\\t\\tcache_control: { type: \\\"ephemeral\\\" },\\r\\n\\t\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\t],\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t// Add cache_control to the last two user messages\\r\\n\\t\\t\\t\\t// (note: this works because we only ever add one user message at a time, but if we added multiple we'd need to mark the user message before the last assistant message)\\r\\n\\t\\t\\t\\tconst lastTwoUserMessages = openAiMessages.filter((msg) => msg.role === \\\"user\\\").slice(-2)\\r\\n\\t\\t\\t\\tlastTwoUserMessages.forEach((msg) => {\\r\\n\\t\\t\\t\\t\\tif (typeof msg.content === \\\"string\\\") {\\r\\n\\t\\t\\t\\t\\t\\tmsg.content = [{ type: \\\"text\\\", text: msg.content }]\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tif (Array.isArray(msg.content)) {\\r\\n\\t\\t\\t\\t\\t\\t// NOTE: this is fine since env details will always be added at the end. but if it weren't there, and the user added a image_url type message, it would pop a text part before it and then move it after to the end.\\r\\n\\t\\t\\t\\t\\t\\tlet lastTextPart = msg.content.filter((part) => part.type === \\\"text\\\").pop()\\r\\n\\r\\n\\t\\t\\t\\t\\t\\tif (!lastTextPart) {\\r\\n\\t\\t\\t\\t\\t\\t\\tlastTextPart = { type: \\\"text\\\", text: \\\"...\\\" }\\r\\n\\t\\t\\t\\t\\t\\t\\tmsg.content.push(lastTextPart)\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t// @ts-ignore-next-line\\r\\n\\t\\t\\t\\t\\t\\tlastTextPart[\\\"cache_control\\\"] = { type: \\\"ephemeral\\\" }\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// Not sure how openrouter defaults max tokens when no value is provided, but the anthropic api requires this value and since they offer both 4096 and 8192 variants, we should ensure 8192.\\r\\n\\t\\t// (models usually default to max tokens allowed)\\r\\n\\t\\tlet maxTokens: number | undefined\\r\\n\\t\\tswitch (this.getModel().id) {\\r\\n\\t\\t\\tcase \\\"anthropic/claude-3.5-sonnet\\\":\\r\\n\\t\\t\\tcase \\\"anthropic/claude-3.5-sonnet:beta\\\":\\r\\n\\t\\t\\tcase \\\"anthropic/claude-3.5-sonnet-20240620\\\":\\r\\n\\t\\t\\tcase \\\"anthropic/claude-3.5-sonnet-20240620:beta\\\":\\r\\n\\t\\t\\tcase \\\"anthropic/claude-3-5-haiku\\\":\\r\\n\\t\\t\\tcase \\\"anthropic/claude-3-5-haiku:beta\\\":\\r\\n\\t\\t\\tcase \\\"anthropic/claude-3-5-haiku-20241022\\\":\\r\\n\\t\\t\\tcase \\\"anthropic/claude-3-5-haiku-20241022:beta\\\":\\r\\n\\t\\t\\t\\tmaxTokens = 8_192\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// Removes messages in the middle when close to context window limit. Should not be applied to models that support prompt caching since it would continuously break the cache.\\r\\n\\t\\tlet shouldApplyMiddleOutTransform = !this.getModel().info.supportsPromptCache\\r\\n\\t\\t// except for deepseek (which we set supportsPromptCache to true for), where because the context window is so small our truncation algo might miss and we should use openrouter's middle-out transform as a fallback to ensure we don't exceed the context window (FIXME: once we have a more robust token estimator we should not rely on this)\\r\\n\\t\\tif (this.getModel().id === \\\"deepseek/deepseek-chat\\\") {\\r\\n\\t\\t\\tshouldApplyMiddleOutTransform = true\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\t// @ts-ignore-next-line\\r\\n\\t\\tconst stream = await this.client.chat.completions.create({\\r\\n\\t\\t\\tmodel: this.getModel().id,\\r\\n\\t\\t\\tmax_tokens: maxTokens,\\r\\n\\t\\t\\ttemperature: 0,\\r\\n\\t\\t\\tmessages: openAiMessages,\\r\\n\\t\\t\\tstream: true,\\r\\n\\t\\t\\ttransforms: shouldApplyMiddleOutTransform ? [\\\"middle-out\\\"] : undefined,\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tlet genId: string | undefined\\r\\n\\r\\n\\t\\tfor await (const chunk of stream) {\\r\\n\\t\\t\\t// openrouter returns an error object instead of the openai sdk throwing an error\\r\\n\\t\\t\\tif (\\\"error\\\" in chunk) {\\r\\n\\t\\t\\t\\tconst error = chunk.error as { message?: string; code?: number }\\r\\n\\t\\t\\t\\tconsole.error(`OpenRouter API Error: ${error?.code} - ${error?.message}`)\\r\\n\\t\\t\\t\\tthrow new Error(`OpenRouter API Error ${error?.code}: ${error?.message}`)\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif (!genId && chunk.id) {\\r\\n\\t\\t\\t\\tgenId = chunk.id\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tconst delta = chunk.choices[0]?.delta\\r\\n\\t\\t\\tif (delta?.content) {\\r\\n\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\ttext: delta.content,\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\t// if (chunk.usage) {\\r\\n\\t\\t\\t// \\tyield {\\r\\n\\t\\t\\t// \\t\\ttype: \\\"usage\\\",\\r\\n\\t\\t\\t// \\t\\tinputTokens: chunk.usage.prompt_tokens || 0,\\r\\n\\t\\t\\t// \\t\\toutputTokens: chunk.usage.completion_tokens || 0,\\r\\n\\t\\t\\t// \\t}\\r\\n\\t\\t\\t// }\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tawait delay(500) // FIXME: necessary delay to ensure generation endpoint is ready\\r\\n\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tconst response = await axios.get(`https://openrouter.ai/api/v1/generation?id=${genId}`, {\\r\\n\\t\\t\\t\\theaders: {\\r\\n\\t\\t\\t\\t\\tAuthorization: `Bearer ${this.options.openRouterApiKey}`,\\r\\n\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\ttimeout: 5_000, // this request hangs sometimes\\r\\n\\t\\t\\t})\\r\\n\\r\\n\\t\\t\\tconst generation = response.data?.data\\r\\n\\t\\t\\tconsole.log(\\\"OpenRouter generation details:\\\", response.data)\\r\\n\\t\\t\\tyield {\\r\\n\\t\\t\\t\\ttype: \\\"usage\\\",\\r\\n\\t\\t\\t\\t// cacheWriteTokens: 0,\\r\\n\\t\\t\\t\\t// cacheReadTokens: 0,\\r\\n\\t\\t\\t\\t// openrouter generation endpoint fails often\\r\\n\\t\\t\\t\\tinputTokens: generation?.native_tokens_prompt || 0,\\r\\n\\t\\t\\t\\toutputTokens: generation?.native_tokens_completion || 0,\\r\\n\\t\\t\\t\\ttotalCost: generation?.total_cost || 0,\\r\\n\\t\\t\\t}\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\t// ignore if fails\\r\\n\\t\\t\\tconsole.error(\\\"Error fetching OpenRouter generation details:\\\", error)\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tgetModel(): { id: string; info: ModelInfo } {\\r\\n\\t\\tconst modelId = this.options.openRouterModelId\\r\\n\\t\\tconst modelInfo = this.options.openRouterModelInfo\\r\\n\\t\\tif (modelId && modelInfo) {\\r\\n\\t\\t\\treturn { id: modelId, info: modelInfo }\\r\\n\\t\\t}\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tid: openRouterDefaultModelId,\\r\\n\\t\\t\\tinfo: openRouterDefaultModelInfo,\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":7102,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.935Z\",\"createdTime\":\"2025-01-15T15:26:07.935Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":185,\"nonEmptyLines\":168,\"commentLines\":25,\"complexity\":52},\"dependencies\":[\"@anthropic-ai/sdk\",\"axios\",\"openai\",\"../\",\"../../shared/api\",\"../transform/openai-format\",\"../transform/stream\",\"delay\"],\"quality\":{\"score\":-113,\"issues\":[],\"duplicateLines\":39,\"longLines\":9,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.309Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":185},\"analysis\":{\"metrics\":{\"lines\":185,\"nonEmptyLines\":168,\"commentLines\":25,\"complexity\":52},\"dependencies\":[\"@anthropic-ai/sdk\",\"axios\",\"openai\",\"../\",\"../../shared/api\",\"../transform/openai-format\",\"../transform/stream\",\"delay\"],\"quality\":{\"score\":-113,\"issues\":[],\"duplicateLines\":39,\"longLines\":9,\"complexFunctions\":0}},\"lastModified\":1737046855310,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.310Z\",\"size\":8688},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\api\\\\providers\\\\openai.ts\",\"content\":{\"content\":\"import { Anthropic } from \\\"@anthropic-ai/sdk\\\"\\r\\nimport OpenAI, { AzureOpenAI } from \\\"openai\\\"\\r\\nimport { ApiHandlerOptions, azureOpenAiDefaultApiVersion, ModelInfo, openAiModelInfoSaneDefaults } from \\\"../../shared/api\\\"\\r\\nimport { ApiHandler } from \\\"../index\\\"\\r\\nimport { convertToOpenAiMessages } from \\\"../transform/openai-format\\\"\\r\\nimport { ApiStream } from \\\"../transform/stream\\\"\\r\\n\\r\\nexport class OpenAiHandler implements ApiHandler {\\r\\n\\tprivate options: ApiHandlerOptions\\r\\n\\tprivate client: OpenAI\\r\\n\\r\\n\\tconstructor(options: ApiHandlerOptions) {\\r\\n\\t\\tthis.options = options\\r\\n\\t\\t// Azure API shape slightly differs from the core API shape: https://github.com/openai/openai-node?tab=readme-ov-file#microsoft-azure-openai\\r\\n\\t\\tif (this.options.openAiBaseUrl?.toLowerCase().includes(\\\"azure.com\\\")) {\\r\\n\\t\\t\\tthis.client = new AzureOpenAI({\\r\\n\\t\\t\\t\\tbaseURL: this.options.openAiBaseUrl,\\r\\n\\t\\t\\t\\tapiKey: this.options.openAiApiKey,\\r\\n\\t\\t\\t\\tapiVersion: this.options.azureApiVersion || azureOpenAiDefaultApiVersion,\\r\\n\\t\\t\\t})\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tthis.client = new OpenAI({\\r\\n\\t\\t\\t\\tbaseURL: this.options.openAiBaseUrl,\\r\\n\\t\\t\\t\\tapiKey: this.options.openAiApiKey,\\r\\n\\t\\t\\t})\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tasync *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {\\r\\n\\t\\tconst openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [\\r\\n\\t\\t\\t{ role: \\\"system\\\", content: systemPrompt },\\r\\n\\t\\t\\t...convertToOpenAiMessages(messages),\\r\\n\\t\\t]\\r\\n\\t\\tconst stream = await this.client.chat.completions.create({\\r\\n\\t\\t\\tmodel: this.options.openAiModelId ?? \\\"\\\",\\r\\n\\t\\t\\tmessages: openAiMessages,\\r\\n\\t\\t\\ttemperature: 0,\\r\\n\\t\\t\\tstream: true,\\r\\n\\t\\t\\tstream_options: { include_usage: true },\\r\\n\\t\\t})\\r\\n\\t\\tfor await (const chunk of stream) {\\r\\n\\t\\t\\tconst delta = chunk.choices[0]?.delta\\r\\n\\t\\t\\tif (delta?.content) {\\r\\n\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\ttext: delta.content,\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tif (chunk.usage) {\\r\\n\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\ttype: \\\"usage\\\",\\r\\n\\t\\t\\t\\t\\tinputTokens: chunk.usage.prompt_tokens || 0,\\r\\n\\t\\t\\t\\t\\toutputTokens: chunk.usage.completion_tokens || 0,\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tgetModel(): { id: string; info: ModelInfo } {\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tid: this.options.openAiModelId ?? \\\"\\\",\\r\\n\\t\\t\\tinfo: openAiModelInfoSaneDefaults,\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":2142,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.935Z\",\"createdTime\":\"2025-01-15T15:26:07.935Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":66,\"nonEmptyLines\":61,\"commentLines\":1,\"complexity\":16},\"dependencies\":[\"@anthropic-ai/sdk\",\"openai\",\"../../shared/api\",\"../index\",\"../transform/openai-format\",\"../transform/stream\"],\"quality\":{\"score\":19,\"issues\":[],\"duplicateLines\":15,\"longLines\":3,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.304Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":66},\"analysis\":{\"metrics\":{\"lines\":66,\"nonEmptyLines\":61,\"commentLines\":1,\"complexity\":16},\"dependencies\":[\"@anthropic-ai/sdk\",\"openai\",\"../../shared/api\",\"../index\",\"../transform/openai-format\",\"../transform/stream\"],\"quality\":{\"score\":19,\"issues\":[],\"duplicateLines\":15,\"longLines\":3,\"complexFunctions\":0}},\"lastModified\":1737046855307,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.307Z\",\"size\":3010},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\api\\\\providers\\\\openai-native.ts\",\"content\":{\"content\":\"import { Anthropic } from \\\"@anthropic-ai/sdk\\\"\\r\\nimport OpenAI from \\\"openai\\\"\\r\\nimport { ApiHandler } from \\\"../\\\"\\r\\nimport {\\r\\n\\tApiHandlerOptions,\\r\\n\\tModelInfo,\\r\\n\\topenAiNativeDefaultModelId,\\r\\n\\tOpenAiNativeModelId,\\r\\n\\topenAiNativeModels,\\r\\n} from \\\"../../shared/api\\\"\\r\\nimport { convertToOpenAiMessages } from \\\"../transform/openai-format\\\"\\r\\nimport { ApiStream } from \\\"../transform/stream\\\"\\r\\n\\r\\nexport class OpenAiNativeHandler implements ApiHandler {\\r\\n\\tprivate options: ApiHandlerOptions\\r\\n\\tprivate client: OpenAI\\r\\n\\r\\n\\tconstructor(options: ApiHandlerOptions) {\\r\\n\\t\\tthis.options = options\\r\\n\\t\\tthis.client = new OpenAI({\\r\\n\\t\\t\\tapiKey: this.options.openAiNativeApiKey,\\r\\n\\t\\t})\\r\\n\\t}\\r\\n\\r\\n\\tasync *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {\\r\\n\\t\\tswitch (this.getModel().id) {\\r\\n\\t\\t\\tcase \\\"o1\\\":\\r\\n\\t\\t\\tcase \\\"o1-preview\\\":\\r\\n\\t\\t\\tcase \\\"o1-mini\\\": {\\r\\n\\t\\t\\t\\t// o1 doesnt support streaming, non-1 temp, or system prompt\\r\\n\\t\\t\\t\\tconst response = await this.client.chat.completions.create({\\r\\n\\t\\t\\t\\t\\tmodel: this.getModel().id,\\r\\n\\t\\t\\t\\t\\tmessages: [{ role: \\\"user\\\", content: systemPrompt }, ...convertToOpenAiMessages(messages)],\\r\\n\\t\\t\\t\\t})\\r\\n\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\ttext: response.choices[0]?.message.content || \\\"\\\",\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\ttype: \\\"usage\\\",\\r\\n\\t\\t\\t\\t\\tinputTokens: response.usage?.prompt_tokens || 0,\\r\\n\\t\\t\\t\\t\\toutputTokens: response.usage?.completion_tokens || 0,\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tdefault: {\\r\\n\\t\\t\\t\\tconst stream = await this.client.chat.completions.create({\\r\\n\\t\\t\\t\\t\\tmodel: this.getModel().id,\\r\\n\\t\\t\\t\\t\\t// max_completion_tokens: this.getModel().info.maxTokens,\\r\\n\\t\\t\\t\\t\\ttemperature: 0,\\r\\n\\t\\t\\t\\t\\tmessages: [{ role: \\\"system\\\", content: systemPrompt }, ...convertToOpenAiMessages(messages)],\\r\\n\\t\\t\\t\\t\\tstream: true,\\r\\n\\t\\t\\t\\t\\tstream_options: { include_usage: true },\\r\\n\\t\\t\\t\\t})\\r\\n\\r\\n\\t\\t\\t\\tfor await (const chunk of stream) {\\r\\n\\t\\t\\t\\t\\tconst delta = chunk.choices[0]?.delta\\r\\n\\t\\t\\t\\t\\tif (delta?.content) {\\r\\n\\t\\t\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\ttext: delta.content,\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\t\\t\\t// contains a null value except for the last chunk which contains the token usage statistics for the entire request\\r\\n\\t\\t\\t\\t\\tif (chunk.usage) {\\r\\n\\t\\t\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\t\\t\\ttype: \\\"usage\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\tinputTokens: chunk.usage.prompt_tokens || 0,\\r\\n\\t\\t\\t\\t\\t\\t\\toutputTokens: chunk.usage.completion_tokens || 0,\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tgetModel(): { id: OpenAiNativeModelId; info: ModelInfo } {\\r\\n\\t\\tconst modelId = this.options.apiModelId\\r\\n\\t\\tif (modelId && modelId in openAiNativeModels) {\\r\\n\\t\\t\\tconst id = modelId as OpenAiNativeModelId\\r\\n\\t\\t\\treturn { id, info: openAiNativeModels[id] }\\r\\n\\t\\t}\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tid: openAiNativeDefaultModelId,\\r\\n\\t\\t\\tinfo: openAiNativeModels[openAiNativeDefaultModelId],\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":2672,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.934Z\",\"createdTime\":\"2025-01-15T15:26:07.934Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":90,\"nonEmptyLines\":83,\"commentLines\":3,\"complexity\":18},\"dependencies\":[\"@anthropic-ai/sdk\",\"openai\",\"../\",\"../transform/openai-format\",\"../transform/stream\"],\"quality\":{\"score\":-19,\"issues\":[],\"duplicateLines\":23,\"longLines\":2,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.303Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":90},\"analysis\":{\"metrics\":{\"lines\":90,\"nonEmptyLines\":83,\"commentLines\":3,\"complexity\":18},\"dependencies\":[\"@anthropic-ai/sdk\",\"openai\",\"../\",\"../transform/openai-format\",\"../transform/stream\"],\"quality\":{\"score\":-19,\"issues\":[],\"duplicateLines\":23,\"longLines\":2,\"complexFunctions\":0}},\"lastModified\":1737046855303,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.303Z\",\"size\":3692},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\api\\\\providers\\\\ollama.ts\",\"content\":{\"content\":\"import { Anthropic } from \\\"@anthropic-ai/sdk\\\"\\r\\nimport OpenAI from \\\"openai\\\"\\r\\nimport { ApiHandler } from \\\"../\\\"\\r\\nimport { ApiHandlerOptions, ModelInfo, openAiModelInfoSaneDefaults } from \\\"../../shared/api\\\"\\r\\nimport { convertToOpenAiMessages } from \\\"../transform/openai-format\\\"\\r\\nimport { ApiStream } from \\\"../transform/stream\\\"\\r\\n\\r\\nexport class OllamaHandler implements ApiHandler {\\r\\n\\tprivate options: ApiHandlerOptions\\r\\n\\tprivate client: OpenAI\\r\\n\\r\\n\\tconstructor(options: ApiHandlerOptions) {\\r\\n\\t\\tthis.options = options\\r\\n\\t\\tthis.client = new OpenAI({\\r\\n\\t\\t\\tbaseURL: (this.options.ollamaBaseUrl || \\\"http://localhost:11434\\\") + \\\"/v1\\\",\\r\\n\\t\\t\\tapiKey: \\\"ollama\\\",\\r\\n\\t\\t})\\r\\n\\t}\\r\\n\\r\\n\\tasync *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {\\r\\n\\t\\tconst openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [\\r\\n\\t\\t\\t{ role: \\\"system\\\", content: systemPrompt },\\r\\n\\t\\t\\t...convertToOpenAiMessages(messages),\\r\\n\\t\\t]\\r\\n\\r\\n\\t\\tconst stream = await this.client.chat.completions.create({\\r\\n\\t\\t\\tmodel: this.getModel().id,\\r\\n\\t\\t\\tmessages: openAiMessages,\\r\\n\\t\\t\\ttemperature: 0,\\r\\n\\t\\t\\tstream: true,\\r\\n\\t\\t})\\r\\n\\t\\tfor await (const chunk of stream) {\\r\\n\\t\\t\\tconst delta = chunk.choices[0]?.delta\\r\\n\\t\\t\\tif (delta?.content) {\\r\\n\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\ttext: delta.content,\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tgetModel(): { id: string; info: ModelInfo } {\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tid: this.options.ollamaModelId || \\\"\\\",\\r\\n\\t\\t\\tinfo: openAiModelInfoSaneDefaults,\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":1434,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.834Z\",\"createdTime\":\"2024-12-29T05:57:23.834Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":50,\"nonEmptyLines\":44,\"commentLines\":0,\"complexity\":6},\"dependencies\":[\"@anthropic-ai/sdk\",\"openai\",\"../\",\"../../shared/api\",\"../transform/openai-format\",\"../transform/stream\"],\"quality\":{\"score\":58,\"issues\":[],\"duplicateLines\":8,\"longLines\":1,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.301Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":50},\"analysis\":{\"metrics\":{\"lines\":50,\"nonEmptyLines\":44,\"commentLines\":0,\"complexity\":6},\"dependencies\":[\"@anthropic-ai/sdk\",\"openai\",\"../\",\"../../shared/api\",\"../transform/openai-format\",\"../transform/stream\"],\"quality\":{\"score\":58,\"issues\":[],\"duplicateLines\":8,\"longLines\":1,\"complexFunctions\":0}},\"lastModified\":1737046855301,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.301Z\",\"size\":2201},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\api\\\\providers\\\\lmstudio.ts\",\"content\":{\"content\":\"import { Anthropic } from \\\"@anthropic-ai/sdk\\\"\\r\\nimport OpenAI from \\\"openai\\\"\\r\\nimport { ApiHandler } from \\\"../\\\"\\r\\nimport { ApiHandlerOptions, ModelInfo, openAiModelInfoSaneDefaults } from \\\"../../shared/api\\\"\\r\\nimport { convertToOpenAiMessages } from \\\"../transform/openai-format\\\"\\r\\nimport { ApiStream } from \\\"../transform/stream\\\"\\r\\n\\r\\nexport class LmStudioHandler implements ApiHandler {\\r\\n\\tprivate options: ApiHandlerOptions\\r\\n\\tprivate client: OpenAI\\r\\n\\r\\n\\tconstructor(options: ApiHandlerOptions) {\\r\\n\\t\\tthis.options = options\\r\\n\\t\\tthis.client = new OpenAI({\\r\\n\\t\\t\\tbaseURL: (this.options.lmStudioBaseUrl || \\\"http://localhost:1234\\\") + \\\"/v1\\\",\\r\\n\\t\\t\\tapiKey: \\\"noop\\\",\\r\\n\\t\\t})\\r\\n\\t}\\r\\n\\r\\n\\tasync *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {\\r\\n\\t\\tconst openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [\\r\\n\\t\\t\\t{ role: \\\"system\\\", content: systemPrompt },\\r\\n\\t\\t\\t...convertToOpenAiMessages(messages),\\r\\n\\t\\t]\\r\\n\\r\\n\\t\\ttry {\\r\\n\\t\\t\\tconst stream = await this.client.chat.completions.create({\\r\\n\\t\\t\\t\\tmodel: this.getModel().id,\\r\\n\\t\\t\\t\\tmessages: openAiMessages,\\r\\n\\t\\t\\t\\ttemperature: 0,\\r\\n\\t\\t\\t\\tstream: true,\\r\\n\\t\\t\\t})\\r\\n\\t\\t\\tfor await (const chunk of stream) {\\r\\n\\t\\t\\t\\tconst delta = chunk.choices[0]?.delta\\r\\n\\t\\t\\t\\tif (delta?.content) {\\r\\n\\t\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\ttext: delta.content,\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t} catch (error) {\\r\\n\\t\\t\\t// LM Studio doesn't return an error code/body for now\\r\\n\\t\\t\\tthrow new Error(\\r\\n\\t\\t\\t\\t\\\"Please check the LM Studio developer logs to debug what went wrong. You may need to load the model with a larger context length to work with Cline's prompts.\\\",\\r\\n\\t\\t\\t)\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tgetModel(): { id: string; info: ModelInfo } {\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tid: this.options.lmStudioModelId || \\\"\\\",\\r\\n\\t\\t\\tinfo: openAiModelInfoSaneDefaults,\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":1739,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2024-12-29T05:57:23.834Z\",\"createdTime\":\"2024-12-29T05:57:23.834Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":57,\"nonEmptyLines\":51,\"commentLines\":1,\"complexity\":7},\"dependencies\":[\"@anthropic-ai/sdk\",\"openai\",\"../\",\"../../shared/api\",\"../transform/openai-format\",\"../transform/stream\"],\"quality\":{\"score\":51,\"issues\":[],\"duplicateLines\":9,\"longLines\":2,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.299Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":57},\"analysis\":{\"metrics\":{\"lines\":57,\"nonEmptyLines\":51,\"commentLines\":1,\"complexity\":7},\"dependencies\":[\"@anthropic-ai/sdk\",\"openai\",\"../\",\"../../shared/api\",\"../transform/openai-format\",\"../transform/stream\"],\"quality\":{\"score\":51,\"issues\":[],\"duplicateLines\":9,\"longLines\":2,\"complexFunctions\":0}},\"lastModified\":1737046855300,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.300Z\",\"size\":2556},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\api\\\\providers\\\\gemini.ts\",\"content\":{\"content\":\"import { Anthropic } from \\\"@anthropic-ai/sdk\\\"\\r\\nimport { GoogleGenerativeAI } from \\\"@google/generative-ai\\\"\\r\\nimport { ApiHandler } from \\\"../\\\"\\r\\nimport { ApiHandlerOptions, geminiDefaultModelId, GeminiModelId, geminiModels, ModelInfo } from \\\"../../shared/api\\\"\\r\\nimport { convertAnthropicMessageToGemini } from \\\"../transform/gemini-format\\\"\\r\\nimport { ApiStream } from \\\"../transform/stream\\\"\\r\\n\\r\\nexport class GeminiHandler implements ApiHandler {\\r\\n\\tprivate options: ApiHandlerOptions\\r\\n\\tprivate client: GoogleGenerativeAI\\r\\n\\r\\n\\tconstructor(options: ApiHandlerOptions) {\\r\\n\\t\\tif (!options.geminiApiKey) {\\r\\n\\t\\t\\tthrow new Error(\\\"API key is required for Google Gemini\\\")\\r\\n\\t\\t}\\r\\n\\t\\tthis.options = options\\r\\n\\t\\tthis.client = new GoogleGenerativeAI(options.geminiApiKey)\\r\\n\\t}\\r\\n\\r\\n\\tasync *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {\\r\\n\\t\\tconst model = this.client.getGenerativeModel({\\r\\n\\t\\t\\tmodel: this.getModel().id,\\r\\n\\t\\t\\tsystemInstruction: systemPrompt,\\r\\n\\t\\t})\\r\\n\\t\\tconst result = await model.generateContentStream({\\r\\n\\t\\t\\tcontents: messages.map(convertAnthropicMessageToGemini),\\r\\n\\t\\t\\tgenerationConfig: {\\r\\n\\t\\t\\t\\t// maxOutputTokens: this.getModel().info.maxTokens,\\r\\n\\t\\t\\t\\ttemperature: 0,\\r\\n\\t\\t\\t},\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tfor await (const chunk of result.stream) {\\r\\n\\t\\t\\tyield {\\r\\n\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\ttext: chunk.text(),\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tconst response = await result.response\\r\\n\\t\\tyield {\\r\\n\\t\\t\\ttype: \\\"usage\\\",\\r\\n\\t\\t\\tinputTokens: response.usageMetadata?.promptTokenCount ?? 0,\\r\\n\\t\\t\\toutputTokens: response.usageMetadata?.candidatesTokenCount ?? 0,\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tgetModel(): { id: GeminiModelId; info: ModelInfo } {\\r\\n\\t\\tconst modelId = this.options.apiModelId\\r\\n\\t\\tif (modelId && modelId in geminiModels) {\\r\\n\\t\\t\\tconst id = modelId as GeminiModelId\\r\\n\\t\\t\\treturn { id, info: geminiModels[id] }\\r\\n\\t\\t}\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tid: geminiDefaultModelId,\\r\\n\\t\\t\\tinfo: geminiModels[geminiDefaultModelId],\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":1870,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.929Z\",\"createdTime\":\"2025-01-15T15:26:07.929Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":60,\"nonEmptyLines\":53,\"commentLines\":1,\"complexity\":10},\"dependencies\":[\"@anthropic-ai/sdk\",\"@google/generative-ai\",\"../\",\"../../shared/api\",\"../transform/gemini-format\",\"../transform/stream\"],\"quality\":{\"score\":41,\"issues\":[],\"duplicateLines\":11,\"longLines\":2,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.298Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":60},\"analysis\":{\"metrics\":{\"lines\":60,\"nonEmptyLines\":53,\"commentLines\":1,\"complexity\":10},\"dependencies\":[\"@anthropic-ai/sdk\",\"@google/generative-ai\",\"../\",\"../../shared/api\",\"../transform/gemini-format\",\"../transform/stream\"],\"quality\":{\"score\":41,\"issues\":[],\"duplicateLines\":11,\"longLines\":2,\"complexFunctions\":0}},\"lastModified\":1737046855298,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.298Z\",\"size\":2686},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\api\\\\providers\\\\deepseek.ts\",\"content\":{\"content\":\"import { Anthropic } from \\\"@anthropic-ai/sdk\\\"\\r\\nimport OpenAI from \\\"openai\\\"\\r\\nimport { ApiHandler } from \\\"../\\\"\\r\\nimport { ApiHandlerOptions, DeepSeekModelId, ModelInfo, deepSeekDefaultModelId, deepSeekModels } from \\\"../../shared/api\\\"\\r\\nimport { convertToOpenAiMessages } from \\\"../transform/openai-format\\\"\\r\\nimport { ApiStream } from \\\"../transform/stream\\\"\\r\\n\\r\\nexport class DeepSeekHandler implements ApiHandler {\\r\\n\\tprivate options: ApiHandlerOptions\\r\\n\\tprivate client: OpenAI\\r\\n\\r\\n\\tconstructor(options: ApiHandlerOptions) {\\r\\n\\t\\tthis.options = options\\r\\n\\t\\tthis.client = new OpenAI({\\r\\n\\t\\t\\tbaseURL: \\\"https://api.deepseek.com/v1\\\",\\r\\n\\t\\t\\tapiKey: this.options.deepSeekApiKey,\\r\\n\\t\\t})\\r\\n\\t}\\r\\n\\r\\n\\tasync *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {\\r\\n\\t\\tconst stream = await this.client.chat.completions.create({\\r\\n\\t\\t\\tmodel: this.getModel().id,\\r\\n\\t\\t\\tmax_completion_tokens: this.getModel().info.maxTokens,\\r\\n\\t\\t\\ttemperature: 0,\\r\\n\\t\\t\\tmessages: [{ role: \\\"system\\\", content: systemPrompt }, ...convertToOpenAiMessages(messages)],\\r\\n\\t\\t\\tstream: true,\\r\\n\\t\\t\\tstream_options: { include_usage: true },\\r\\n\\t\\t})\\r\\n\\r\\n\\t\\tfor await (const chunk of stream) {\\r\\n\\t\\t\\tconst delta = chunk.choices[0]?.delta\\r\\n\\t\\t\\tif (delta?.content) {\\r\\n\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\ttext: delta.content,\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\r\\n\\t\\t\\tif (chunk.usage) {\\r\\n\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\ttype: \\\"usage\\\",\\r\\n\\t\\t\\t\\t\\tinputTokens: chunk.usage.prompt_tokens || 0, // (deepseek reports total input AND cache reads/writes, see context caching: https://api-docs.deepseek.com/guides/kv_cache) where the input tokens is the sum of the cache hits/misses, while anthropic reports them as separate tokens. This is important to know for 1) context management truncation algorithm, and 2) cost calculation (NOTE: we report both input and cache stats but for now set input price to 0 since all the cost calculation will be done using cache hits/misses)\\r\\n\\t\\t\\t\\t\\toutputTokens: chunk.usage.completion_tokens || 0,\\r\\n\\t\\t\\t\\t\\t// @ts-ignore-next-line\\r\\n\\t\\t\\t\\t\\tcacheReadTokens: chunk.usage.prompt_cache_hit_tokens || 0,\\r\\n\\t\\t\\t\\t\\t// @ts-ignore-next-line\\r\\n\\t\\t\\t\\t\\tcacheWriteTokens: chunk.usage.prompt_cache_miss_tokens || 0,\\r\\n\\t\\t\\t\\t}\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tgetModel(): { id: DeepSeekModelId; info: ModelInfo } {\\r\\n\\t\\tconst modelId = this.options.apiModelId\\r\\n\\t\\tif (modelId && modelId in deepSeekModels) {\\r\\n\\t\\t\\tconst id = modelId as DeepSeekModelId\\r\\n\\t\\t\\treturn { id, info: deepSeekModels[id] }\\r\\n\\t\\t}\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tid: deepSeekDefaultModelId,\\r\\n\\t\\t\\tinfo: deepSeekModels[deepSeekDefaultModelId],\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":2495,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.929Z\",\"createdTime\":\"2025-01-15T15:26:07.928Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":65,\"nonEmptyLines\":58,\"commentLines\":2,\"complexity\":11},\"dependencies\":[\"@anthropic-ai/sdk\",\"openai\",\"../\",\"../../shared/api\",\"../transform/openai-format\",\"../transform/stream\"],\"quality\":{\"score\":29,\"issues\":[],\"duplicateLines\":13,\"longLines\":3,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.297Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":65},\"analysis\":{\"metrics\":{\"lines\":65,\"nonEmptyLines\":58,\"commentLines\":2,\"complexity\":11},\"dependencies\":[\"@anthropic-ai/sdk\",\"openai\",\"../\",\"../../shared/api\",\"../transform/openai-format\",\"../transform/stream\"],\"quality\":{\"score\":29,\"issues\":[],\"duplicateLines\":13,\"longLines\":3,\"complexFunctions\":0}},\"lastModified\":1737046855297,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.297Z\",\"size\":3347},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\api\\\\providers\\\\bedrock.ts\",\"content\":{\"content\":\"import AnthropicBedrock from \\\"@anthropic-ai/bedrock-sdk\\\"\\r\\nimport { Anthropic } from \\\"@anthropic-ai/sdk\\\"\\r\\nimport { ApiHandler } from \\\"../\\\"\\r\\nimport { ApiHandlerOptions, bedrockDefaultModelId, BedrockModelId, bedrockModels, ModelInfo } from \\\"../../shared/api\\\"\\r\\nimport { ApiStream } from \\\"../transform/stream\\\"\\r\\n\\r\\n// https://docs.anthropic.com/en/api/claude-on-amazon-bedrock\\r\\nexport class AwsBedrockHandler implements ApiHandler {\\r\\n\\tprivate options: ApiHandlerOptions\\r\\n\\tprivate client: AnthropicBedrock\\r\\n\\r\\n\\tconstructor(options: ApiHandlerOptions) {\\r\\n\\t\\tthis.options = options\\r\\n\\t\\tthis.client = new AnthropicBedrock({\\r\\n\\t\\t\\t// Authenticate by either providing the keys below or use the default AWS credential providers, such as\\r\\n\\t\\t\\t// using ~/.aws/credentials or the \\\"AWS_SECRET_ACCESS_KEY\\\" and \\\"AWS_ACCESS_KEY_ID\\\" environment variables.\\r\\n\\t\\t\\t...(this.options.awsAccessKey ? { awsAccessKey: this.options.awsAccessKey } : {}),\\r\\n\\t\\t\\t...(this.options.awsSecretKey ? { awsSecretKey: this.options.awsSecretKey } : {}),\\r\\n\\t\\t\\t...(this.options.awsSessionToken ? { awsSessionToken: this.options.awsSessionToken } : {}),\\r\\n\\r\\n\\t\\t\\t// awsRegion changes the aws region to which the request is made. By default, we read AWS_REGION,\\r\\n\\t\\t\\t// and if that's not present, we default to us-east-1. Note that we do not read ~/.aws/config for the region.\\r\\n\\t\\t\\tawsRegion: this.options.awsRegion,\\r\\n\\t\\t})\\r\\n\\t}\\r\\n\\r\\n\\tasync *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {\\r\\n\\t\\t// cross region inference requires prefixing the model id with the region\\r\\n\\t\\tlet modelId: string\\r\\n\\t\\tif (this.options.awsUseCrossRegionInference) {\\r\\n\\t\\t\\tlet regionPrefix = (this.options.awsRegion || \\\"\\\").slice(0, 3)\\r\\n\\t\\t\\tswitch (regionPrefix) {\\r\\n\\t\\t\\t\\tcase \\\"us-\\\":\\r\\n\\t\\t\\t\\t\\tmodelId = `us.${this.getModel().id}`\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\tcase \\\"eu-\\\":\\r\\n\\t\\t\\t\\t\\tmodelId = `eu.${this.getModel().id}`\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\t\\t// cross region inference is not supported in this region, falling back to default model\\r\\n\\t\\t\\t\\t\\tmodelId = this.getModel().id\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t}\\r\\n\\t\\t} else {\\r\\n\\t\\t\\tmodelId = this.getModel().id\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tconst stream = await this.client.messages.create({\\r\\n\\t\\t\\tmodel: modelId,\\r\\n\\t\\t\\tmax_tokens: this.getModel().info.maxTokens || 8192,\\r\\n\\t\\t\\ttemperature: 0,\\r\\n\\t\\t\\tsystem: systemPrompt,\\r\\n\\t\\t\\tmessages,\\r\\n\\t\\t\\tstream: true,\\r\\n\\t\\t})\\r\\n\\t\\tfor await (const chunk of stream) {\\r\\n\\t\\t\\tswitch (chunk.type) {\\r\\n\\t\\t\\t\\tcase \\\"message_start\\\":\\r\\n\\t\\t\\t\\t\\tconst usage = chunk.message.usage\\r\\n\\t\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"usage\\\",\\r\\n\\t\\t\\t\\t\\t\\tinputTokens: usage.input_tokens || 0,\\r\\n\\t\\t\\t\\t\\t\\toutputTokens: usage.output_tokens || 0,\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\tcase \\\"message_delta\\\":\\r\\n\\t\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"usage\\\",\\r\\n\\t\\t\\t\\t\\t\\tinputTokens: 0,\\r\\n\\t\\t\\t\\t\\t\\toutputTokens: chunk.usage.output_tokens || 0,\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\r\\n\\t\\t\\t\\tcase \\\"content_block_start\\\":\\r\\n\\t\\t\\t\\t\\tswitch (chunk.content_block.type) {\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"text\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\tif (chunk.index > 0) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttext: \\\"\\\\n\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttext: chunk.content_block.text,\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\tcase \\\"content_block_delta\\\":\\r\\n\\t\\t\\t\\t\\tswitch (chunk.delta.type) {\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"text_delta\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttext: chunk.delta.text,\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tgetModel(): { id: BedrockModelId; info: ModelInfo } {\\r\\n\\t\\tconst modelId = this.options.apiModelId\\r\\n\\t\\tif (modelId && modelId in bedrockModels) {\\r\\n\\t\\t\\tconst id = modelId as BedrockModelId\\r\\n\\t\\t\\treturn { id, info: bedrockModels[id] }\\r\\n\\t\\t}\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tid: bedrockDefaultModelId,\\r\\n\\t\\t\\tinfo: bedrockModels[bedrockDefaultModelId],\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":3626,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.928Z\",\"createdTime\":\"2025-01-15T15:26:07.928Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":116,\"nonEmptyLines\":108,\"commentLines\":7,\"complexity\":22},\"dependencies\":[\"@anthropic-ai/bedrock-sdk\",\"@anthropic-ai/sdk\",\"../\",\"../../shared/api\",\"../transform/stream\"],\"quality\":{\"score\":-82,\"issues\":[],\"duplicateLines\":34,\"longLines\":6,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.295Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":116},\"analysis\":{\"metrics\":{\"lines\":116,\"nonEmptyLines\":108,\"commentLines\":7,\"complexity\":22},\"dependencies\":[\"@anthropic-ai/bedrock-sdk\",\"@anthropic-ai/sdk\",\"../\",\"../../shared/api\",\"../transform/stream\"],\"quality\":{\"score\":-82,\"issues\":[],\"duplicateLines\":34,\"longLines\":6,\"complexFunctions\":0}},\"lastModified\":1737046855296,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.296Z\",\"size\":4867},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\api\\\\providers\\\\anthropic.ts\",\"content\":{\"content\":\"import { Anthropic } from \\\"@anthropic-ai/sdk\\\"\\r\\nimport { Stream as AnthropicStream } from \\\"@anthropic-ai/sdk/streaming\\\"\\r\\nimport { anthropicDefaultModelId, AnthropicModelId, anthropicModels, ApiHandlerOptions, ModelInfo } from \\\"../../shared/api\\\"\\r\\nimport { ApiHandler } from \\\"../index\\\"\\r\\nimport { ApiStream } from \\\"../transform/stream\\\"\\r\\n\\r\\nexport class AnthropicHandler implements ApiHandler {\\r\\n\\tprivate options: ApiHandlerOptions\\r\\n\\tprivate client: Anthropic\\r\\n\\r\\n\\tconstructor(options: ApiHandlerOptions) {\\r\\n\\t\\tthis.options = options\\r\\n\\t\\tthis.client = new Anthropic({\\r\\n\\t\\t\\tapiKey: this.options.apiKey,\\r\\n\\t\\t\\tbaseURL: this.options.anthropicBaseUrl || undefined,\\r\\n\\t\\t})\\r\\n\\t}\\r\\n\\r\\n\\tasync *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {\\r\\n\\t\\tlet stream: AnthropicStream<Anthropic.Beta.PromptCaching.Messages.RawPromptCachingBetaMessageStreamEvent>\\r\\n\\t\\tconst modelId = this.getModel().id\\r\\n\\t\\tswitch (modelId) {\\r\\n\\t\\t\\t// 'latest' alias does not support cache_control\\r\\n\\t\\t\\tcase \\\"claude-3-5-sonnet-20241022\\\":\\r\\n\\t\\t\\tcase \\\"claude-3-5-haiku-20241022\\\":\\r\\n\\t\\t\\tcase \\\"claude-3-opus-20240229\\\":\\r\\n\\t\\t\\tcase \\\"claude-3-haiku-20240307\\\": {\\r\\n\\t\\t\\t\\t/*\\r\\n\\t\\t\\t\\tThe latest message will be the new user message, one before will be the assistant message from a previous request, and the user message before that will be a previously cached user message. So we need to mark the latest user message as ephemeral to cache it for the next request, and mark the second to last user message as ephemeral to let the server know the last message to retrieve from the cache for the current request..\\r\\n\\t\\t\\t\\t*/\\r\\n\\t\\t\\t\\tconst userMsgIndices = messages.reduce(\\r\\n\\t\\t\\t\\t\\t(acc, msg, index) => (msg.role === \\\"user\\\" ? [...acc, index] : acc),\\r\\n\\t\\t\\t\\t\\t[] as number[],\\r\\n\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\tconst lastUserMsgIndex = userMsgIndices[userMsgIndices.length - 1] ?? -1\\r\\n\\t\\t\\t\\tconst secondLastMsgUserIndex = userMsgIndices[userMsgIndices.length - 2] ?? -1\\r\\n\\t\\t\\t\\tstream = await this.client.beta.promptCaching.messages.create(\\r\\n\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\tmodel: modelId,\\r\\n\\t\\t\\t\\t\\t\\tmax_tokens: this.getModel().info.maxTokens || 8192,\\r\\n\\t\\t\\t\\t\\t\\ttemperature: 0,\\r\\n\\t\\t\\t\\t\\t\\tsystem: [\\r\\n\\t\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttext: systemPrompt,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tcache_control: { type: \\\"ephemeral\\\" },\\r\\n\\t\\t\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\t\\t], // setting cache breakpoint for system prompt so new tasks can reuse it\\r\\n\\t\\t\\t\\t\\t\\tmessages: messages.map((message, index) => {\\r\\n\\t\\t\\t\\t\\t\\t\\tif (index === lastUserMsgIndex || index === secondLastMsgUserIndex) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\treturn {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t...message,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\tcontent:\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\ttypeof message.content === \\\"string\\\"\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t? [\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t{\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\ttext: message.content,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tcache_control: {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"ephemeral\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t]\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t: message.content.map((content, contentIndex) =>\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tcontentIndex === message.content.length - 1\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t? {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t...content,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\tcache_control: {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"ephemeral\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t: content,\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t),\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\treturn message\\r\\n\\t\\t\\t\\t\\t\\t}),\\r\\n\\t\\t\\t\\t\\t\\t// tools, // cache breakpoints go from tools > system > messages, and since tools dont change, we can just set the breakpoint at the end of system (this avoids having to set a breakpoint at the end of tools which by itself does not meet min requirements for haiku caching)\\r\\n\\t\\t\\t\\t\\t\\t// tool_choice: { type: \\\"auto\\\" },\\r\\n\\t\\t\\t\\t\\t\\t// tools: tools,\\r\\n\\t\\t\\t\\t\\t\\tstream: true,\\r\\n\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\t(() => {\\r\\n\\t\\t\\t\\t\\t\\t// prompt caching: https://x.com/alexalbert__/status/1823751995901272068\\r\\n\\t\\t\\t\\t\\t\\t// https://github.com/anthropics/anthropic-sdk-typescript?tab=readme-ov-file#default-headers\\r\\n\\t\\t\\t\\t\\t\\t// https://github.com/anthropics/anthropic-sdk-typescript/commit/c920b77fc67bd839bfeb6716ceab9d7c9bbe7393\\r\\n\\t\\t\\t\\t\\t\\tswitch (modelId) {\\r\\n\\t\\t\\t\\t\\t\\t\\tcase \\\"claude-3-5-sonnet-20241022\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\tcase \\\"claude-3-5-haiku-20241022\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\tcase \\\"claude-3-opus-20240229\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\tcase \\\"claude-3-haiku-20240307\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\t\\treturn {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\theaders: {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t\\t\\\"anthropic-beta\\\": \\\"prompt-caching-2024-07-31\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\t},\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\tdefault:\\r\\n\\t\\t\\t\\t\\t\\t\\t\\treturn undefined\\r\\n\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t})(),\\r\\n\\t\\t\\t\\t)\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t}\\r\\n\\t\\t\\tdefault: {\\r\\n\\t\\t\\t\\tstream = (await this.client.messages.create({\\r\\n\\t\\t\\t\\t\\tmodel: modelId,\\r\\n\\t\\t\\t\\t\\tmax_tokens: this.getModel().info.maxTokens || 8192,\\r\\n\\t\\t\\t\\t\\ttemperature: 0,\\r\\n\\t\\t\\t\\t\\tsystem: [{ text: systemPrompt, type: \\\"text\\\" }],\\r\\n\\t\\t\\t\\t\\tmessages,\\r\\n\\t\\t\\t\\t\\t// tools,\\r\\n\\t\\t\\t\\t\\t// tool_choice: { type: \\\"auto\\\" },\\r\\n\\t\\t\\t\\t\\tstream: true,\\r\\n\\t\\t\\t\\t})) as any\\r\\n\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\r\\n\\t\\tfor await (const chunk of stream) {\\r\\n\\t\\t\\tswitch (chunk.type) {\\r\\n\\t\\t\\t\\tcase \\\"message_start\\\":\\r\\n\\t\\t\\t\\t\\t// tells us cache reads/writes/input/output\\r\\n\\t\\t\\t\\t\\tconst usage = chunk.message.usage\\r\\n\\t\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"usage\\\",\\r\\n\\t\\t\\t\\t\\t\\tinputTokens: usage.input_tokens || 0,\\r\\n\\t\\t\\t\\t\\t\\toutputTokens: usage.output_tokens || 0,\\r\\n\\t\\t\\t\\t\\t\\tcacheWriteTokens: usage.cache_creation_input_tokens || undefined,\\r\\n\\t\\t\\t\\t\\t\\tcacheReadTokens: usage.cache_read_input_tokens || undefined,\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\tcase \\\"message_delta\\\":\\r\\n\\t\\t\\t\\t\\t// tells us stop_reason, stop_sequence, and output tokens along the way and at the end of the message\\r\\n\\r\\n\\t\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\t\\ttype: \\\"usage\\\",\\r\\n\\t\\t\\t\\t\\t\\tinputTokens: 0,\\r\\n\\t\\t\\t\\t\\t\\toutputTokens: chunk.usage.output_tokens || 0,\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\tcase \\\"message_stop\\\":\\r\\n\\t\\t\\t\\t\\t// no usage data, just an indicator that the message is done\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\tcase \\\"content_block_start\\\":\\r\\n\\t\\t\\t\\t\\tswitch (chunk.content_block.type) {\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"text\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\t// we may receive multiple text blocks, in which case just insert a line break between them\\r\\n\\t\\t\\t\\t\\t\\t\\tif (chunk.index > 0) {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t\\ttext: \\\"\\\\n\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttext: chunk.content_block.text,\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\tcase \\\"content_block_delta\\\":\\r\\n\\t\\t\\t\\t\\tswitch (chunk.delta.type) {\\r\\n\\t\\t\\t\\t\\t\\tcase \\\"text_delta\\\":\\r\\n\\t\\t\\t\\t\\t\\t\\tyield {\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttype: \\\"text\\\",\\r\\n\\t\\t\\t\\t\\t\\t\\t\\ttext: chunk.delta.text,\\r\\n\\t\\t\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\t\\t}\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t\\tcase \\\"content_block_stop\\\":\\r\\n\\t\\t\\t\\t\\tbreak\\r\\n\\t\\t\\t}\\r\\n\\t\\t}\\r\\n\\t}\\r\\n\\r\\n\\tgetModel(): { id: AnthropicModelId; info: ModelInfo } {\\r\\n\\t\\tconst modelId = this.options.apiModelId\\r\\n\\t\\tif (modelId && modelId in anthropicModels) {\\r\\n\\t\\t\\tconst id = modelId as AnthropicModelId\\r\\n\\t\\t\\treturn { id, info: anthropicModels[id] }\\r\\n\\t\\t}\\r\\n\\t\\treturn {\\r\\n\\t\\t\\tid: anthropicDefaultModelId,\\r\\n\\t\\t\\tinfo: anthropicModels[anthropicDefaultModelId],\\r\\n\\t\\t}\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":6456,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.928Z\",\"createdTime\":\"2025-01-15T15:26:07.928Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":189,\"nonEmptyLines\":182,\"commentLines\":14,\"complexity\":39},\"dependencies\":[\"@anthropic-ai/sdk\",\"@anthropic-ai/sdk/streaming\",\"../../shared/api\",\"../index\",\"../transform/stream\"],\"quality\":{\"score\":-224,\"issues\":[],\"duplicateLines\":62,\"longLines\":7,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.292Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":189},\"analysis\":{\"metrics\":{\"lines\":189,\"nonEmptyLines\":182,\"commentLines\":14,\"complexity\":39},\"dependencies\":[\"@anthropic-ai/sdk\",\"@anthropic-ai/sdk/streaming\",\"../../shared/api\",\"../index\",\"../transform/stream\"],\"quality\":{\"score\":-224,\"issues\":[],\"duplicateLines\":62,\"longLines\":7,\"complexFunctions\":0}},\"lastModified\":1737046855293,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.293Z\",\"size\":8533},{\"key\":\"D:\\\\Projects\\\\cline\\\\src\\\\api\\\\index.ts\",\"content\":{\"content\":\"import { Anthropic } from \\\"@anthropic-ai/sdk\\\"\\r\\nimport { ApiConfiguration, ModelInfo } from \\\"../shared/api\\\"\\r\\nimport { AnthropicHandler } from \\\"./providers/anthropic\\\"\\r\\nimport { AwsBedrockHandler } from \\\"./providers/bedrock\\\"\\r\\nimport { OpenRouterHandler } from \\\"./providers/openrouter\\\"\\r\\nimport { VertexHandler } from \\\"./providers/vertex\\\"\\r\\nimport { OpenAiHandler } from \\\"./providers/openai\\\"\\r\\nimport { OllamaHandler } from \\\"./providers/ollama\\\"\\r\\nimport { LmStudioHandler } from \\\"./providers/lmstudio\\\"\\r\\nimport { GeminiHandler } from \\\"./providers/gemini\\\"\\r\\nimport { OpenAiNativeHandler } from \\\"./providers/openai-native\\\"\\r\\nimport { ApiStream } from \\\"./transform/stream\\\"\\r\\nimport { DeepSeekHandler } from \\\"./providers/deepseek\\\"\\r\\n\\r\\nexport interface ApiHandler {\\r\\n\\tcreateMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream\\r\\n\\tgetModel(): { id: string; info: ModelInfo }\\r\\n}\\r\\n\\r\\nexport function buildApiHandler(configuration: ApiConfiguration): ApiHandler {\\r\\n\\tconst { apiProvider, ...options } = configuration\\r\\n\\tswitch (apiProvider) {\\r\\n\\t\\tcase \\\"anthropic\\\":\\r\\n\\t\\t\\treturn new AnthropicHandler(options)\\r\\n\\t\\tcase \\\"openrouter\\\":\\r\\n\\t\\t\\treturn new OpenRouterHandler(options)\\r\\n\\t\\tcase \\\"bedrock\\\":\\r\\n\\t\\t\\treturn new AwsBedrockHandler(options)\\r\\n\\t\\tcase \\\"vertex\\\":\\r\\n\\t\\t\\treturn new VertexHandler(options)\\r\\n\\t\\tcase \\\"openai\\\":\\r\\n\\t\\t\\treturn new OpenAiHandler(options)\\r\\n\\t\\tcase \\\"ollama\\\":\\r\\n\\t\\t\\treturn new OllamaHandler(options)\\r\\n\\t\\tcase \\\"lmstudio\\\":\\r\\n\\t\\t\\treturn new LmStudioHandler(options)\\r\\n\\t\\tcase \\\"gemini\\\":\\r\\n\\t\\t\\treturn new GeminiHandler(options)\\r\\n\\t\\tcase \\\"openai-native\\\":\\r\\n\\t\\t\\treturn new OpenAiNativeHandler(options)\\r\\n\\t\\tcase \\\"deepseek\\\":\\r\\n\\t\\t\\treturn new DeepSeekHandler(options)\\r\\n\\t\\tdefault:\\r\\n\\t\\t\\treturn new AnthropicHandler(options)\\r\\n\\t}\\r\\n}\\r\\n\",\"metadata\":{\"size\":1711,\"mimeType\":\"video/mp2t\",\"modifiedTime\":\"2025-01-15T15:26:07.927Z\",\"createdTime\":\"2025-01-15T15:26:07.927Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":47,\"nonEmptyLines\":44,\"commentLines\":0,\"complexity\":11},\"dependencies\":[\"@anthropic-ai/sdk\",\"../shared/api\",\"./providers/anthropic\",\"./providers/bedrock\",\"./providers/openrouter\",\"./providers/vertex\",\"./providers/openai\",\"./providers/ollama\",\"./providers/lmstudio\",\"./providers/gemini\",\"./providers/openai-native\",\"./transform/stream\",\"./providers/deepseek\"],\"quality\":{\"score\":85,\"issues\":[],\"duplicateLines\":3,\"longLines\":0,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T17:00:55.290Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":47},\"analysis\":{\"metrics\":{\"lines\":47,\"nonEmptyLines\":44,\"commentLines\":0,\"complexity\":11},\"dependencies\":[\"@anthropic-ai/sdk\",\"../shared/api\",\"./providers/anthropic\",\"./providers/bedrock\",\"./providers/openrouter\",\"./providers/vertex\",\"./providers/openai\",\"./providers/ollama\",\"./providers/lmstudio\",\"./providers/gemini\",\"./providers/openai-native\",\"./transform/stream\",\"./providers/deepseek\"],\"quality\":{\"score\":85,\"issues\":[],\"duplicateLines\":3,\"longLines\":0,\"complexFunctions\":0}},\"lastModified\":1737046855290,\"hits\":0,\"lastAccessed\":\"2025-01-16T17:00:55.290Z\",\"size\":2649},{\"key\":\"d:\\\\projects\\\\mcp_docs\\\\docs\\\\tools\\\\inspector.mdx\",\"content\":{\"content\":\"---\\r\\ntitle: Inspector\\r\\ndescription: In-depth guide to using the MCP Inspector for testing and debugging Model Context Protocol servers\\r\\n---\\r\\n\\r\\nThe [MCP Inspector](https://github.com/modelcontextprotocol/inspector) is an interactive developer tool for testing and debugging MCP servers. While the [Debugging Guide](/docs/tools/debugging) covers the Inspector as part of the overall debugging toolkit, this document provides a detailed exploration of the Inspector's features and capabilities.\\r\\n\\r\\n## Getting started\\r\\n\\r\\n### Installation and basic usage\\r\\n\\r\\nThe Inspector runs directly through `npx` without requiring installation:\\r\\n\\r\\n\\r\\n```bash\\r\\nnpx @modelcontextprotocol/inspector <command>\\r\\n```\\r\\n\\r\\n```bash\\r\\nnpx @modelcontextprotocol/inspector <command> <arg1> <arg2>\\r\\n```\\r\\n\\r\\n#### Inspecting servers from NPM or PyPi\\r\\n\\r\\nA common way to start server packages from [NPM](https://npmjs.com) or [PyPi](https://pypi.com).\\r\\n\\r\\n<Tabs>\\r\\n\\r\\n <Tab title=\\\"NPM package\\\">\\r\\n ```bash\\r\\n npx -y @modelcontextprotocol/inspector npx <package-name> <args>\\r\\n # For example\\r\\n npx -y @modelcontextprotocol/inspector npx server-postgres postgres://127.0.0.1/testdb\\r\\n ```\\r\\n </Tab>\\r\\n\\r\\n <Tab title=\\\"PyPi package\\\">\\r\\n ```bash\\r\\n npx @modelcontextprotocol/inspector uvx <package-name> <args>\\r\\n # For example\\r\\n npx @modelcontextprotocol/inspector uvx mcp-server-git --repository ~/code/mcp/servers.git\\r\\n ```\\r\\n </Tab>\\r\\n</Tabs>\\r\\n\\r\\n#### Inspecting locally developed servers\\r\\n\\r\\nTo inspect servers locally developed or downloaded as a repository, the most common\\r\\nway is:\\r\\n\\r\\n<Tabs>\\r\\n <Tab title=\\\"TypeScript\\\">\\r\\n ```bash\\r\\n npx @modelcontextprotocol/inspector node path/to/server/index.js args...\\r\\n ```\\r\\n </Tab>\\r\\n <Tab title=\\\"Python\\\">\\r\\n ```bash\\r\\n npx @modelcontextprotocol/inspector \\\\\\r\\n uv \\\\\\r\\n --directory path/to/server \\\\\\r\\n run \\\\\\r\\n package-name \\\\\\r\\n args...\\r\\n ```\\r\\n </Tab>\\r\\n</Tabs>\\r\\n\\r\\nPlease carefully read any attached README for the most accurate instructions.\\r\\n\\r\\n## Feature overview\\r\\n\\r\\n<Frame caption=\\\"The MCP Inspector interface\\\">\\r\\n <img src=\\\"/images/mcp-inspector.png\\\" />\\r\\n</Frame>\\r\\n\\r\\nThe Inspector provides several features for interacting with your MCP server:\\r\\n\\r\\n### Server connection pane\\r\\n- Allows selecting the [transport](/docs/concepts/transports) for connecting to the server\\r\\n- For local servers, supports customizing the command-line arguments and environment\\r\\n\\r\\n### Resources tab\\r\\n- Lists all available resources\\r\\n- Shows resource metadata (MIME types, descriptions)\\r\\n- Allows resource content inspection\\r\\n- Supports subscription testing\\r\\n\\r\\n### Prompts tab\\r\\n- Displays available prompt templates\\r\\n- Shows prompt arguments and descriptions\\r\\n- Enables prompt testing with custom arguments\\r\\n- Previews generated messages\\r\\n\\r\\n### Tools tab\\r\\n- Lists available tools\\r\\n- Shows tool schemas and descriptions\\r\\n- Enables tool testing with custom inputs\\r\\n- Displays tool execution results\\r\\n\\r\\n### Notifications pane\\r\\n- Presents all logs recorded from the server\\r\\n- Shows notifications received from the server\\r\\n\\r\\n## Best practices\\r\\n\\r\\n### Development workflow\\r\\n\\r\\n1. Start Development\\r\\n - Launch Inspector with your server\\r\\n - Verify basic connectivity\\r\\n - Check capability negotiation\\r\\n\\r\\n2. Iterative testing\\r\\n - Make server changes\\r\\n - Rebuild the server\\r\\n - Reconnect the Inspector\\r\\n - Test affected features\\r\\n - Monitor messages\\r\\n\\r\\n3. Test edge cases\\r\\n - Invalid inputs\\r\\n - Missing prompt arguments\\r\\n - Concurrent operations\\r\\n - Verify error handling and error responses\\r\\n\\r\\n## Next steps\\r\\n\\r\\n<CardGroup cols={2}>\\r\\n <Card\\r\\n title=\\\"Inspector Repository\\\"\\r\\n icon=\\\"github\\\"\\r\\n href=\\\"https://github.com/modelcontextprotocol/inspector\\\"\\r\\n >\\r\\n Check out the MCP Inspector source code\\r\\n </Card>\\r\\n\\r\\n <Card\\r\\n title=\\\"Debugging Guide\\\"\\r\\n icon=\\\"bug\\\"\\r\\n href=\\\"/docs/tools/debugging\\\"\\r\\n >\\r\\n Learn about broader debugging strategies\\r\\n </Card>\\r\\n</CardGroup>\\r\\n\",\"metadata\":{\"size\":3932,\"mimeType\":\"text/mdx\",\"modifiedTime\":\"2024-12-17T17:14:31.726Z\",\"createdTime\":\"2024-12-17T17:14:31.726Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":146,\"nonEmptyLines\":113,\"commentLines\":0,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":-4,\"issues\":[],\"duplicateLines\":20,\"longLines\":2,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T16:59:44.477Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":146},\"analysis\":{\"metrics\":{\"lines\":146,\"nonEmptyLines\":113,\"commentLines\":0,\"complexity\":1},\"dependencies\":[],\"quality\":{\"score\":-4,\"issues\":[],\"duplicateLines\":20,\"longLines\":2,\"complexFunctions\":0}},\"lastModified\":1737046784478,\"hits\":0,\"lastAccessed\":\"2025-01-16T16:59:44.478Z\",\"size\":4707},{\"key\":\"d:\\\\projects\\\\mcp_docs\\\\docs\\\\tools\\\\debugging.mdx\",\"content\":{\"content\":\"---\\r\\ntitle: Debugging\\r\\ndescription: A comprehensive guide to debugging Model Context Protocol (MCP) integrations\\r\\n---\\r\\n\\r\\nEffective debugging is essential when developing MCP servers or integrating them with applications. This guide covers the debugging tools and approaches available in the MCP ecosystem.\\r\\n\\r\\n<Info>\\r\\n This guide is for macOS. Guides for other platforms are coming soon.\\r\\n</Info>\\r\\n\\r\\n## Debugging tools overview\\r\\n\\r\\nMCP provides several tools for debugging at different levels:\\r\\n\\r\\n1. **MCP Inspector**\\r\\n - Interactive debugging interface\\r\\n - Direct server testing\\r\\n - See the [Inspector guide](/docs/tools/inspector) for details\\r\\n\\r\\n2. **Claude Desktop Developer Tools**\\r\\n - Integration testing\\r\\n - Log collection\\r\\n - Chrome DevTools integration\\r\\n\\r\\n3. **Server Logging**\\r\\n - Custom logging implementations\\r\\n - Error tracking\\r\\n - Performance monitoring\\r\\n\\r\\n## Debugging in Claude Desktop\\r\\n\\r\\n### Checking server status\\r\\n\\r\\nThe Claude.app interface provides basic server status information:\\r\\n\\r\\n1. Click the <img src=\\\"/images/claude-desktop-mcp-plug-icon.svg\\\" style={{display: 'inline', margin: 0, height: '1.3em'}} /> icon to view:\\r\\n - Connected servers\\r\\n - Available prompts and resources\\r\\n\\r\\n2. Click the <img src=\\\"/images/claude-desktop-mcp-hammer-icon.svg\\\" style={{display: 'inline', margin: 0, height: '1.3em'}} /> icon to view:\\r\\n - Tools made available to the model\\r\\n\\r\\n### Viewing logs\\r\\n\\r\\nReview detailed MCP logs from Claude Desktop:\\r\\n\\r\\n```bash\\r\\n# Follow logs in real-time\\r\\ntail -n 20 -f ~/Library/Logs/Claude/mcp*.log\\r\\n```\\r\\n\\r\\nThe logs capture:\\r\\n- Server connection events\\r\\n- Configuration issues\\r\\n- Runtime errors\\r\\n- Message exchanges\\r\\n\\r\\n### Using Chrome DevTools\\r\\n\\r\\nAccess Chrome's developer tools inside Claude Desktop to investigate client-side errors:\\r\\n\\r\\n1. Enable DevTools:\\r\\n```bash\\r\\njq '.allowDevTools = true' ~/Library/Application\\\\ Support/Claude/developer_settings.json > tmp.json \\\\\\r\\n && mv tmp.json ~/Library/Application\\\\ Support/Claude/developer_settings.json\\r\\n```\\r\\n\\r\\n2. Open DevTools: `Command-Option-Shift-i`\\r\\n\\r\\nNote: You'll see two DevTools windows:\\r\\n- Main content window\\r\\n- App title bar window\\r\\n\\r\\nUse the Console panel to inspect client-side errors.\\r\\n\\r\\nUse the Network panel to inspect:\\r\\n- Message payloads\\r\\n- Connection timing\\r\\n\\r\\n## Common issues\\r\\n\\r\\n### Working directory\\r\\n\\r\\nWhen using MCP servers with Claude Desktop:\\r\\n\\r\\n- The working directory for servers launched via `claude_desktop_config.json` may be undefined (like `/` on macOS) since Claude Desktop could be started from anywhere\\r\\n- Always use absolute paths in your configuration and `.env` files to ensure reliable operation\\r\\n- For testing servers directly via command line, the working directory will be where you run the command\\r\\n\\r\\nFor example in `claude_desktop_config.json`, use:\\r\\n```json\\r\\n{\\r\\n \\\"command\\\": \\\"npx\\\",\\r\\n \\\"args\\\": [\\\"-y\\\", \\\"@modelcontextprotocol/server-filesystem\\\", \\\"/Users/username/data\\\"]\\r\\n}\\r\\n```\\r\\nInstead of relative paths like `./data`\\r\\n\\r\\n### Environment variables\\r\\n\\r\\nMCP servers inherit only a subset of environment variables automatically, like `USER`, `HOME`, and `PATH`.\\r\\n\\r\\nTo override the default variables or provide your own, you can specify an `env` key in `claude_desktop_config.json`:\\r\\n\\r\\n```json\\r\\n{\\r\\n \\\"myserver\\\": {\\r\\n \\\"command\\\": \\\"mcp-server-myapp\\\",\\r\\n \\\"env\\\": {\\r\\n \\\"MYAPP_API_KEY\\\": \\\"some_key\\\",\\r\\n }\\r\\n }\\r\\n}\\r\\n```\\r\\n\\r\\n### Server initialization\\r\\n\\r\\nCommon initialization problems:\\r\\n\\r\\n1. **Path Issues**\\r\\n - Incorrect server executable path\\r\\n - Missing required files\\r\\n - Permission problems\\r\\n - Try using an absolute path for `command`\\r\\n\\r\\n2. **Configuration Errors**\\r\\n - Invalid JSON syntax\\r\\n - Missing required fields\\r\\n - Type mismatches\\r\\n\\r\\n3. **Environment Problems**\\r\\n - Missing environment variables\\r\\n - Incorrect variable values\\r\\n - Permission restrictions\\r\\n\\r\\n### Connection problems\\r\\n\\r\\nWhen servers fail to connect:\\r\\n\\r\\n1. Check Claude Desktop logs\\r\\n2. Verify server process is running\\r\\n3. Test standalone with [Inspector](/docs/tools/inspector)\\r\\n4. Verify protocol compatibility\\r\\n\\r\\n## Implementing logging\\r\\n\\r\\n### Server-side logging\\r\\n\\r\\nWhen building a server that uses the local stdio [transport](/docs/concepts/transports), all messages logged to stderr (standard error) will be captured by the host application (e.g., Claude Desktop) automatically.\\r\\n\\r\\n<Warning>\\r\\n Local MCP servers should not log messages to stdout (standard out), as this will interfere with protocol operation.\\r\\n</Warning>\\r\\n\\r\\nFor all [transports](/docs/concepts/transports), you can also provide logging to the client by sending a log message notification:\\r\\n\\r\\n<Tabs>\\r\\n <Tab title=\\\"Python\\\">\\r\\n ```python\\r\\n server.request_context.session.send_log_message(\\r\\n level=\\\"info\\\",\\r\\n data=\\\"Server started successfully\\\",\\r\\n )\\r\\n ```\\r\\n </Tab>\\r\\n <Tab title=\\\"TypeScript\\\">\\r\\n ```typescript\\r\\n server.sendLoggingMessage({\\r\\n level: \\\"info\\\",\\r\\n data: \\\"Server started successfully\\\",\\r\\n });\\r\\n ```\\r\\n </Tab>\\r\\n</Tabs>\\r\\n\\r\\nImportant events to log:\\r\\n- Initialization steps\\r\\n- Resource access\\r\\n- Tool execution\\r\\n- Error conditions\\r\\n- Performance metrics\\r\\n\\r\\n### Client-side logging\\r\\n\\r\\nIn client applications:\\r\\n\\r\\n1. Enable debug logging\\r\\n2. Monitor network traffic\\r\\n3. Track message exchanges\\r\\n4. Record error states\\r\\n\\r\\n## Debugging workflow\\r\\n\\r\\n### Development cycle\\r\\n\\r\\n1. Initial Development\\r\\n - Use [Inspector](/docs/tools/inspector) for basic testing\\r\\n - Implement core functionality\\r\\n - Add logging points\\r\\n\\r\\n2. Integration Testing\\r\\n - Test in Claude Desktop\\r\\n - Monitor logs\\r\\n - Check error handling\\r\\n\\r\\n### Testing changes\\r\\n\\r\\nTo test changes efficiently:\\r\\n\\r\\n- **Configuration changes**: Restart Claude Desktop\\r\\n- **Server code changes**: Use Command-R to reload\\r\\n- **Quick iteration**: Use [Inspector](/docs/tools/inspector) during development\\r\\n\\r\\n## Best practices\\r\\n\\r\\n### Logging strategy\\r\\n\\r\\n1. **Structured Logging**\\r\\n - Use consistent formats\\r\\n - Include context\\r\\n - Add timestamps\\r\\n - Track request IDs\\r\\n\\r\\n2. **Error Handling**\\r\\n - Log stack traces\\r\\n - Include error context\\r\\n - Track error patterns\\r\\n - Monitor recovery\\r\\n\\r\\n3. **Performance Tracking**\\r\\n - Log operation timing\\r\\n - Monitor resource usage\\r\\n - Track message sizes\\r\\n - Measure latency\\r\\n\\r\\n### Security considerations\\r\\n\\r\\nWhen debugging:\\r\\n\\r\\n1. **Sensitive Data**\\r\\n - Sanitize logs\\r\\n - Protect credentials\\r\\n - Mask personal information\\r\\n\\r\\n2. **Access Control**\\r\\n - Verify permissions\\r\\n - Check authentication\\r\\n - Monitor access patterns\\r\\n\\r\\n## Getting help\\r\\n\\r\\nWhen encountering issues:\\r\\n\\r\\n1. **First Steps**\\r\\n - Check server logs\\r\\n - Test with [Inspector](/docs/tools/inspector)\\r\\n - Review configuration\\r\\n - Verify environment\\r\\n\\r\\n2. **Support Channels**\\r\\n - GitHub issues\\r\\n - GitHub discussions\\r\\n\\r\\n3. **Providing Information**\\r\\n - Log excerpts\\r\\n - Configuration files\\r\\n - Steps to reproduce\\r\\n - Environment details\\r\\n\\r\\n## Next steps\\r\\n\\r\\n<CardGroup cols={2}>\\r\\n <Card\\r\\n title=\\\"MCP Inspector\\\"\\r\\n icon=\\\"magnifying-glass\\\"\\r\\n href=\\\"/docs/tools/inspector\\\"\\r\\n >\\r\\n Learn to use the MCP Inspector\\r\\n </Card>\\r\\n</CardGroup>\\r\\n\",\"metadata\":{\"size\":7112,\"mimeType\":\"text/mdx\",\"modifiedTime\":\"2025-01-08T03:13:34.764Z\",\"createdTime\":\"2025-01-08T03:13:34.764Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":282,\"nonEmptyLines\":208,\"commentLines\":0,\"complexity\":2},\"dependencies\":[],\"quality\":{\"score\":13,\"issues\":[],\"duplicateLines\":13,\"longLines\":11,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T16:59:44.477Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":282},\"analysis\":{\"metrics\":{\"lines\":282,\"nonEmptyLines\":208,\"commentLines\":0,\"complexity\":2},\"dependencies\":[],\"quality\":{\"score\":13,\"issues\":[],\"duplicateLines\":13,\"longLines\":11,\"complexFunctions\":0}},\"lastModified\":1737046784477,\"hits\":0,\"lastAccessed\":\"2025-01-16T16:59:44.477Z\",\"size\":8180},{\"key\":\"d:\\\\projects\\\\mcp_docs\\\\docs\\\\README.md\",\"content\":{\"content\":\"# MCP TypeScript SDK ![NPM Version](https://img.shields.io/npm/v/%40modelcontextprotocol%2Fsdk)\\n\\nTypeScript implementation of the [Model Context Protocol](https://modelcontextprotocol.io) (MCP), providing both client and server capabilities for integrating with LLM surfaces.\\n\\n## Overview\\n\\nThe Model Context Protocol allows applications to provide context for LLMs in a standardized way, separating the concerns of providing context from the actual LLM interaction. This TypeScript SDK implements the full MCP specification, making it easy to:\\n\\n- Build MCP clients that can connect to any MCP server\\n- Create MCP servers that expose resources, prompts and tools\\n- Use standard transports like stdio and SSE\\n- Handle all MCP protocol messages and lifecycle events\\n\\n## Installation\\n\\n```bash\\nnpm install @modelcontextprotocol/sdk\\n```\\n\\n## Quick Start\\n\\n### Creating a Client\\n\\n```typescript\\nimport { Client } from \\\"@modelcontextprotocol/sdk/client/index.js\\\";\\nimport { StdioClientTransport } from \\\"@modelcontextprotocol/sdk/client/stdio.js\\\";\\nimport {\\n ListResourcesRequestSchema,\\n ReadResourceRequestSchema,\\n} from \\\"@modelcontextprotocol/sdk/types.js\\\";\\n\\nconst transport = new StdioClientTransport({\\n command: \\\"path/to/server\\\",\\n});\\n\\nconst client = new Client({\\n name: \\\"example-client\\\",\\n version: \\\"1.0.0\\\",\\n}, {\\n capabilities: {}\\n});\\n\\nawait client.connect(transport);\\n\\n// List available resources\\nconst resources = await client.request(\\n { method: \\\"resources/list\\\" },\\n ListResourcesResultSchema\\n);\\n\\n// Read a specific resource\\nconst resourceContent = await client.request(\\n {\\n method: \\\"resources/read\\\",\\n params: {\\n uri: \\\"file:///example.txt\\\"\\n }\\n },\\n ReadResourceResultSchema\\n);\\n```\\n\\n### Creating a Server\\n\\n```typescript\\nimport { Server } from \\\"@modelcontextprotocol/sdk/server/index.js\\\";\\nimport { StdioServerTransport } from \\\"@modelcontextprotocol/sdk/server/stdio.js\\\";\\nimport {\\n ListResourcesRequestSchema,\\n ReadResourceRequestSchema,\\n} from \\\"@modelcontextprotocol/sdk/types.js\\\";\\n\\nconst server = new Server({\\n name: \\\"example-server\\\",\\n version: \\\"1.0.0\\\",\\n}, {\\n capabilities: {\\n resources: {}\\n }\\n});\\n\\nserver.setRequestHandler(ListResourcesRequestSchema, async () => {\\n return {\\n resources: [\\n {\\n uri: \\\"file:///example.txt\\\",\\n name: \\\"Example Resource\\\",\\n },\\n ],\\n };\\n});\\n\\nserver.setRequestHandler(ReadResourceRequestSchema, async (request) => {\\n if (request.params.uri === \\\"file:///example.txt\\\") {\\n return {\\n contents: [\\n {\\n uri: \\\"file:///example.txt\\\",\\n mimeType: \\\"text/plain\\\",\\n text: \\\"This is the content of the example resource.\\\",\\n },\\n ],\\n };\\n } else {\\n throw new Error(\\\"Resource not found\\\");\\n }\\n});\\n\\nconst transport = new StdioServerTransport();\\nawait server.connect(transport);\\n```\\n\\n## Documentation\\n\\n- [Model Context Protocol documentation](https://modelcontextprotocol.io)\\n- [MCP Specification](https://spec.modelcontextprotocol.io)\\n- [Example Servers](https://github.com/modelcontextprotocol/servers)\\n\\n## Contributing\\n\\nIssues and pull requests are welcome on GitHub at https://github.com/modelcontextprotocol/typescript-sdk.\\n\\n## License\\n\\nThis project is licensed under the MIT License—see the [LICENSE](LICENSE) file for details.\\n\",\"metadata\":{\"size\":3252,\"mimeType\":\"text/markdown\",\"modifiedTime\":\"2025-01-15T15:23:48.206Z\",\"createdTime\":\"2025-01-15T15:23:59.338Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":126,\"nonEmptyLines\":99,\"commentLines\":0,\"complexity\":3},\"dependencies\":[],\"quality\":{\"score\":-26,\"issues\":[],\"duplicateLines\":24,\"longLines\":3,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T16:59:44.476Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":126},\"analysis\":{\"metrics\":{\"lines\":126,\"nonEmptyLines\":99,\"commentLines\":0,\"complexity\":3},\"dependencies\":[],\"quality\":{\"score\":-26,\"issues\":[],\"duplicateLines\":24,\"longLines\":3,\"complexFunctions\":0}},\"lastModified\":1737046784476,\"hits\":0,\"lastAccessed\":\"2025-01-16T16:59:44.476Z\",\"size\":3880},{\"key\":\"d:\\\\projects\\\\mcp_docs\\\\docs\\\\concepts\\\\transports.mdx\",\"content\":{\"content\":\"---\\r\\ntitle: \\\"Transports\\\"\\r\\ndescription: \\\"Learn about MCP's communication mechanisms\\\"\\r\\n---\\r\\n\\r\\nTransports in the Model Context Protocol (MCP) provide the foundation for communication between clients and servers. A transport handles the underlying mechanics of how messages are sent and received.\\r\\n\\r\\n## Message Format\\r\\n\\r\\nMCP uses [JSON-RPC](https://www.jsonrpc.org/) 2.0 as its wire format. The transport layer is responsible for converting MCP protocol messages into JSON-RPC format for transmission and converting received JSON-RPC messages back into MCP protocol messages.\\r\\n\\r\\nThere are three types of JSON-RPC messages used:\\r\\n\\r\\n### Requests\\r\\n```typescript\\r\\n{\\r\\n jsonrpc: \\\"2.0\\\",\\r\\n id: number | string,\\r\\n method: string,\\r\\n params?: object\\r\\n}\\r\\n```\\r\\n\\r\\n### Responses\\r\\n```typescript\\r\\n{\\r\\n jsonrpc: \\\"2.0\\\",\\r\\n id: number | string,\\r\\n result?: object,\\r\\n error?: {\\r\\n code: number,\\r\\n message: string,\\r\\n data?: unknown\\r\\n }\\r\\n}\\r\\n```\\r\\n\\r\\n### Notifications\\r\\n```typescript\\r\\n{\\r\\n jsonrpc: \\\"2.0\\\",\\r\\n method: string,\\r\\n params?: object\\r\\n}\\r\\n```\\r\\n\\r\\n## Built-in Transport Types\\r\\n\\r\\nMCP includes two standard transport implementations:\\r\\n\\r\\n### Standard Input/Output (stdio)\\r\\n\\r\\nThe stdio transport enables communication through standard input and output streams. This is particularly useful for local integrations and command-line tools.\\r\\n\\r\\nUse stdio when:\\r\\n- Building command-line tools\\r\\n- Implementing local integrations\\r\\n- Needing simple process communication\\r\\n- Working with shell scripts\\r\\n\\r\\n<Tabs>\\r\\n <Tab title=\\\"TypeScript (Server)\\\">\\r\\n ```typescript\\r\\n const server = new Server({\\r\\n name: \\\"example-server\\\",\\r\\n version: \\\"1.0.0\\\"\\r\\n }, {\\r\\n capabilities: {}\\r\\n });\\r\\n\\r\\n const transport = new StdioServerTransport();\\r\\n await server.connect(transport);\\r\\n ```\\r\\n </Tab>\\r\\n <Tab title=\\\"TypeScript (Client)\\\">\\r\\n ```typescript\\r\\n const client = new Client({\\r\\n name: \\\"example-client\\\",\\r\\n version: \\\"1.0.0\\\"\\r\\n }, {\\r\\n capabilities: {}\\r\\n });\\r\\n\\r\\n const transport = new StdioClientTransport({\\r\\n command: \\\"./server\\\",\\r\\n args: [\\\"--option\\\", \\\"value\\\"]\\r\\n });\\r\\n await client.connect(transport);\\r\\n ```\\r\\n </Tab>\\r\\n <Tab title=\\\"Python (Server)\\\">\\r\\n ```python\\r\\n app = Server(\\\"example-server\\\")\\r\\n\\r\\n async with stdio_server() as streams:\\r\\n await app.run(\\r\\n streams[0],\\r\\n streams[1],\\r\\n app.create_initialization_options()\\r\\n )\\r\\n ```\\r\\n </Tab>\\r\\n <Tab title=\\\"Python (Client)\\\">\\r\\n ```python\\r\\n params = StdioServerParameters(\\r\\n command=\\\"./server\\\",\\r\\n args=[\\\"--option\\\", \\\"value\\\"]\\r\\n )\\r\\n\\r\\n async with stdio_client(params) as streams:\\r\\n async with ClientSession(streams[0], streams[1]) as session:\\r\\n await session.initialize()\\r\\n ```\\r\\n </Tab>\\r\\n</Tabs>\\r\\n\\r\\n### Server-Sent Events (SSE)\\r\\n\\r\\nSSE transport enables server-to-client streaming with HTTP POST requests for client-to-server communication.\\r\\n\\r\\nUse SSE when:\\r\\n- Only server-to-client streaming is needed\\r\\n- Working with restricted networks\\r\\n- Implementing simple updates\\r\\n\\r\\n<Tabs>\\r\\n <Tab title=\\\"TypeScript (Server)\\\">\\r\\n ```typescript\\r\\n const server = new Server({\\r\\n name: \\\"example-server\\\",\\r\\n version: \\\"1.0.0\\\"\\r\\n }, {\\r\\n capabilities: {}\\r\\n });\\r\\n\\r\\n const transport = new SSEServerTransport(\\\"/message\\\", response);\\r\\n await server.connect(transport);\\r\\n ```\\r\\n </Tab>\\r\\n <Tab title=\\\"TypeScript (Client)\\\">\\r\\n ```typescript\\r\\n const client = new Client({\\r\\n name: \\\"example-client\\\",\\r\\n version: \\\"1.0.0\\\"\\r\\n }, {\\r\\n capabilities: {}\\r\\n });\\r\\n\\r\\n const transport = new SSEClientTransport(\\r\\n new URL(\\\"http://localhost:3000/sse\\\")\\r\\n );\\r\\n await client.connect(transport);\\r\\n ```\\r\\n </Tab>\\r\\n <Tab title=\\\"Python (Server)\\\">\\r\\n ```python\\r\\n from mcp.server.sse import SseServerTransport\\r\\n from starlette.applications import Starlette\\r\\n from starlette.routing import Route\\r\\n\\r\\n app = Server(\\\"example-server\\\")\\r\\n sse = SseServerTransport(\\\"/messages\\\")\\r\\n\\r\\n async def handle_sse(scope, receive, send):\\r\\n async with sse.connect_sse(scope, receive, send) as streams:\\r\\n await app.run(streams[0], streams[1], app.create_initialization_options())\\r\\n\\r\\n async def handle_messages(scope, receive, send):\\r\\n await sse.handle_post_message(scope, receive, send)\\r\\n\\r\\n starlette_app = Starlette(\\r\\n routes=[\\r\\n Route(\\\"/sse\\\", endpoint=handle_sse),\\r\\n Route(\\\"/messages\\\", endpoint=handle_messages, methods=[\\\"POST\\\"]),\\r\\n ]\\r\\n )\\r\\n ```\\r\\n </Tab>\\r\\n <Tab title=\\\"Python (Client)\\\">\\r\\n ```python\\r\\n async with sse_client(\\\"http://localhost:8000/sse\\\") as streams:\\r\\n async with ClientSession(streams[0], streams[1]) as session:\\r\\n await session.initialize()\\r\\n ```\\r\\n </Tab>\\r\\n</Tabs>\\r\\n\\r\\n## Custom Transports\\r\\n\\r\\nMCP makes it easy to implement custom transports for specific needs. Any transport implementation just needs to conform to the Transport interface:\\r\\n\\r\\nYou can implement custom transports for:\\r\\n- Custom network protocols\\r\\n- Specialized communication channels\\r\\n- Integration with existing systems\\r\\n- Performance optimization\\r\\n\\r\\n<Tabs>\\r\\n <Tab title=\\\"TypeScript\\\">\\r\\n ```typescript\\r\\n interface Transport {\\r\\n // Start processing messages\\r\\n start(): Promise<void>;\\r\\n\\r\\n // Send a JSON-RPC message\\r\\n send(message: JSONRPCMessage): Promise<void>;\\r\\n\\r\\n // Close the connection\\r\\n close(): Promise<void>;\\r\\n\\r\\n // Callbacks\\r\\n onclose?: () => void;\\r\\n onerror?: (error: Error) => void;\\r\\n onmessage?: (message: JSONRPCMessage) => void;\\r\\n }\\r\\n ```\\r\\n </Tab>\\r\\n <Tab title=\\\"Python\\\">\\r\\n Note that while MCP Servers are often implemented with asyncio, we recommend\\r\\n implementing low-level interfaces like transports with `anyio` for wider compatibility.\\r\\n ```python\\r\\n @contextmanager\\r\\n async def create_transport(\\r\\n read_stream: MemoryObjectReceiveStream[JSONRPCMessage | Exception],\\r\\n write_stream: MemoryObjectSendStream[JSONRPCMessage]\\r\\n ):\\r\\n \\\"\\\"\\\"\\r\\n Transport interface for MCP.\\r\\n\\r\\n Args:\\r\\n read_stream: Stream to read incoming messages from\\r\\n write_stream: Stream to write outgoing messages to\\r\\n \\\"\\\"\\\"\\r\\n async with anyio.create_task_group() as tg:\\r\\n try:\\r\\n # Start processing messages\\r\\n tg.start_soon(lambda: process_messages(read_stream))\\r\\n\\r\\n # Send messages\\r\\n async with write_stream:\\r\\n yield write_stream\\r\\n\\r\\n except Exception as exc:\\r\\n # Handle errors\\r\\n raise exc\\r\\n finally:\\r\\n # Clean up\\r\\n tg.cancel_scope.cancel()\\r\\n await write_stream.aclose()\\r\\n await read_stream.aclose()\\r\\n ```\\r\\n </Tab>\\r\\n</Tabs>\\r\\n\\r\\n## Error Handling\\r\\n\\r\\nTransport implementations should handle various error scenarios:\\r\\n\\r\\n1. Connection errors\\r\\n2. Message parsing errors\\r\\n3. Protocol errors\\r\\n4. Network timeouts\\r\\n5. Resource cleanup\\r\\n\\r\\nExample error handling:\\r\\n\\r\\n<Tabs>\\r\\n <Tab title=\\\"TypeScript\\\">\\r\\n ```typescript\\r\\n class ExampleTransport implements Transport {\\r\\n async start() {\\r\\n try {\\r\\n // Connection logic\\r\\n } catch (error) {\\r\\n this.onerror?.(new Error(`Failed to connect: ${error}`));\\r\\n throw error;\\r\\n }\\r\\n }\\r\\n\\r\\n async send(message: JSONRPCMessage) {\\r\\n try {\\r\\n // Sending logic\\r\\n } catch (error) {\\r\\n this.onerror?.(new Error(`Failed to send message: ${error}`));\\r\\n throw error;\\r\\n }\\r\\n }\\r\\n }\\r\\n ```\\r\\n </Tab>\\r\\n <Tab title=\\\"Python\\\">\\r\\n Note that while MCP Servers are often implemented with asyncio, we recommend\\r\\n implementing low-level interfaces like transports with `anyio` for wider compatibility.\\r\\n ```python\\r\\n @contextmanager\\r\\n async def example_transport(scope: Scope, receive: Receive, send: Send):\\r\\n try:\\r\\n # Create streams for bidirectional communication\\r\\n read_stream_writer, read_stream = anyio.create_memory_object_stream(0)\\r\\n write_stream, write_stream_reader = anyio.create_memory_object_stream(0)\\r\\n\\r\\n async def message_handler():\\r\\n try:\\r\\n async with read_stream_writer:\\r\\n # Message handling logic\\r\\n pass\\r\\n except Exception as exc:\\r\\n logger.error(f\\\"Failed to handle message: {exc}\\\")\\r\\n raise exc\\r\\n\\r\\n async with anyio.create_task_group() as tg:\\r\\n tg.start_soon(message_handler)\\r\\n try:\\r\\n # Yield streams for communication\\r\\n yield read_stream, write_stream\\r\\n except Exception as exc:\\r\\n logger.error(f\\\"Transport error: {exc}\\\")\\r\\n raise exc\\r\\n finally:\\r\\n tg.cancel_scope.cancel()\\r\\n await write_stream.aclose()\\r\\n await read_stream.aclose()\\r\\n except Exception as exc:\\r\\n logger.error(f\\\"Failed to initialize transport: {exc}\\\")\\r\\n raise exc\\r\\n ```\\r\\n </Tab>\\r\\n</Tabs>\\r\\n\\r\\n## Best Practices\\r\\n\\r\\nWhen implementing or using MCP transport:\\r\\n\\r\\n1. Handle connection lifecycle properly\\r\\n2. Implement proper error handling\\r\\n3. Clean up resources on connection close\\r\\n4. Use appropriate timeouts\\r\\n5. Validate messages before sending\\r\\n6. Log transport events for debugging\\r\\n7. Implement reconnection logic when appropriate\\r\\n8. Handle backpressure in message queues\\r\\n9. Monitor connection health\\r\\n10. Implement proper security measures\\r\\n\\r\\n## Security Considerations\\r\\n\\r\\nWhen implementing transport:\\r\\n\\r\\n### Authentication and Authorization\\r\\n- Implement proper authentication mechanisms\\r\\n- Validate client credentials\\r\\n- Use secure token handling\\r\\n- Implement authorization checks\\r\\n\\r\\n### Data Security\\r\\n- Use TLS for network transport\\r\\n- Encrypt sensitive data\\r\\n- Validate message integrity\\r\\n- Implement message size limits\\r\\n- Sanitize input data\\r\\n\\r\\n### Network Security\\r\\n- Implement rate limiting\\r\\n- Use appropriate timeouts\\r\\n- Handle denial of service scenarios\\r\\n- Monitor for unusual patterns\\r\\n- Implement proper firewall rules\\r\\n\\r\\n## Debugging Transport\\r\\n\\r\\nTips for debugging transport issues:\\r\\n\\r\\n1. Enable debug logging\\r\\n2. Monitor message flow\\r\\n3. Check connection states\\r\\n4. Validate message formats\\r\\n5. Test error scenarios\\r\\n6. Use network analysis tools\\r\\n7. Implement health checks\\r\\n8. Monitor resource usage\\r\\n9. Test edge cases\\r\\n10. Use proper error tracking\\r\\n\",\"metadata\":{\"size\":10633,\"mimeType\":\"text/mdx\",\"modifiedTime\":\"2024-12-17T17:14:31.725Z\",\"createdTime\":\"2024-12-17T17:14:31.725Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":383,\"nonEmptyLines\":326,\"commentLines\":0,\"complexity\":13},\"dependencies\":[],\"quality\":{\"score\":-470,\"issues\":[],\"duplicateLines\":112,\"longLines\":5,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T16:59:44.473Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":383},\"analysis\":{\"metrics\":{\"lines\":383,\"nonEmptyLines\":326,\"commentLines\":0,\"complexity\":13},\"dependencies\":[],\"quality\":{\"score\":-470,\"issues\":[],\"duplicateLines\":112,\"longLines\":5,\"complexFunctions\":0}},\"lastModified\":1737046784474,\"hits\":0,\"lastAccessed\":\"2025-01-16T16:59:44.474Z\",\"size\":11950},{\"key\":\"d:\\\\projects\\\\mcp_docs\\\\docs\\\\concepts\\\\tools.mdx\",\"content\":{\"content\":\"---\\r\\ntitle: \\\"Tools\\\"\\r\\ndescription: \\\"Enable LLMs to perform actions through your server\\\"\\r\\n---\\r\\n\\r\\nTools are a powerful primitive in the Model Context Protocol (MCP) that enable servers to expose executable functionality to clients. Through tools, LLMs can interact with external systems, perform computations, and take actions in the real world.\\r\\n\\r\\n<Note>\\r\\n Tools are designed to be **model-controlled**, meaning that tools are exposed from servers to clients with the intention of the AI model being able to automatically invoke them (with a human in the loop to grant approval).\\r\\n</Note>\\r\\n\\r\\n## Overview\\r\\n\\r\\nTools in MCP allow servers to expose executable functions that can be invoked by clients and used by LLMs to perform actions. Key aspects of tools include:\\r\\n\\r\\n- **Discovery**: Clients can list available tools through the `tools/list` endpoint\\r\\n- **Invocation**: Tools are called using the `tools/call` endpoint, where servers perform the requested operation and return results\\r\\n- **Flexibility**: Tools can range from simple calculations to complex API interactions\\r\\n\\r\\nLike [resources](/docs/concepts/resources), tools are identified by unique names and can include descriptions to guide their usage. However, unlike resources, tools represent dynamic operations that can modify state or interact with external systems.\\r\\n\\r\\n## Tool definition structure\\r\\n\\r\\nEach tool is defined with the following structure:\\r\\n\\r\\n```typescript\\r\\n{\\r\\n name: string; // Unique identifier for the tool\\r\\n description?: string; // Human-readable description\\r\\n inputSchema: { // JSON Schema for the tool's parameters\\r\\n type: \\\"object\\\",\\r\\n properties: { ... } // Tool-specific parameters\\r\\n }\\r\\n}\\r\\n```\\r\\n\\r\\n## Implementing tools\\r\\n\\r\\nHere's an example of implementing a basic tool in an MCP server:\\r\\n\\r\\n<Tabs>\\r\\n <Tab title=\\\"TypeScript\\\">\\r\\n ```typescript\\r\\n const server = new Server({\\r\\n name: \\\"example-server\\\",\\r\\n version: \\\"1.0.0\\\"\\r\\n }, {\\r\\n capabilities: {\\r\\n tools: {}\\r\\n }\\r\\n });\\r\\n\\r\\n // Define available tools\\r\\n server.setRequestHandler(ListToolsRequestSchema, async () => {\\r\\n return {\\r\\n tools: [{\\r\\n name: \\\"calculate_sum\\\",\\r\\n description: \\\"Add two numbers together\\\",\\r\\n inputSchema: {\\r\\n type: \\\"object\\\",\\r\\n properties: {\\r\\n a: { type: \\\"number\\\" },\\r\\n b: { type: \\\"number\\\" }\\r\\n },\\r\\n required: [\\\"a\\\", \\\"b\\\"]\\r\\n }\\r\\n }]\\r\\n };\\r\\n });\\r\\n\\r\\n // Handle tool execution\\r\\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\\r\\n if (request.params.name === \\\"calculate_sum\\\") {\\r\\n const { a, b } = request.params.arguments;\\r\\n return {\\r\\n content: [\\r\\n {\\r\\n type: \\\"text\\\",\\r\\n text: String(a + b)\\r\\n }\\r\\n ]\\r\\n };\\r\\n }\\r\\n throw new Error(\\\"Tool not found\\\");\\r\\n });\\r\\n ```\\r\\n </Tab>\\r\\n <Tab title=\\\"Python\\\">\\r\\n ```python\\r\\n app = Server(\\\"example-server\\\")\\r\\n\\r\\n @app.list_tools()\\r\\n async def list_tools() -> list[types.Tool]:\\r\\n return [\\r\\n types.Tool(\\r\\n name=\\\"calculate_sum\\\",\\r\\n description=\\\"Add two numbers together\\\",\\r\\n inputSchema={\\r\\n \\\"type\\\": \\\"object\\\",\\r\\n \\\"properties\\\": {\\r\\n \\\"a\\\": {\\\"type\\\": \\\"number\\\"},\\r\\n \\\"b\\\": {\\\"type\\\": \\\"number\\\"}\\r\\n },\\r\\n \\\"required\\\": [\\\"a\\\", \\\"b\\\"]\\r\\n }\\r\\n )\\r\\n ]\\r\\n\\r\\n @app.call_tool()\\r\\n async def call_tool(\\r\\n name: str,\\r\\n arguments: dict\\r\\n ) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:\\r\\n if name == \\\"calculate_sum\\\":\\r\\n a = arguments[\\\"a\\\"]\\r\\n b = arguments[\\\"b\\\"]\\r\\n result = a + b\\r\\n return [types.TextContent(type=\\\"text\\\", text=str(result))]\\r\\n raise ValueError(f\\\"Tool not found: {name}\\\")\\r\\n ```\\r\\n </Tab>\\r\\n</Tabs>\\r\\n\\r\\n## Example tool patterns\\r\\n\\r\\nHere are some examples of types of tools that a server could provide:\\r\\n\\r\\n### System operations\\r\\n\\r\\nTools that interact with the local system:\\r\\n\\r\\n```typescript\\r\\n{\\r\\n name: \\\"execute_command\\\",\\r\\n description: \\\"Run a shell command\\\",\\r\\n inputSchema: {\\r\\n type: \\\"object\\\",\\r\\n properties: {\\r\\n command: { type: \\\"string\\\" },\\r\\n args: { type: \\\"array\\\", items: { type: \\\"string\\\" } }\\r\\n }\\r\\n }\\r\\n}\\r\\n```\\r\\n\\r\\n### API integrations\\r\\n\\r\\nTools that wrap external APIs:\\r\\n\\r\\n```typescript\\r\\n{\\r\\n name: \\\"github_create_issue\\\",\\r\\n description: \\\"Create a GitHub issue\\\",\\r\\n inputSchema: {\\r\\n type: \\\"object\\\",\\r\\n properties: {\\r\\n title: { type: \\\"string\\\" },\\r\\n body: { type: \\\"string\\\" },\\r\\n labels: { type: \\\"array\\\", items: { type: \\\"string\\\" } }\\r\\n }\\r\\n }\\r\\n}\\r\\n```\\r\\n\\r\\n### Data processing\\r\\n\\r\\nTools that transform or analyze data:\\r\\n\\r\\n```typescript\\r\\n{\\r\\n name: \\\"analyze_csv\\\",\\r\\n description: \\\"Analyze a CSV file\\\",\\r\\n inputSchema: {\\r\\n type: \\\"object\\\",\\r\\n properties: {\\r\\n filepath: { type: \\\"string\\\" },\\r\\n operations: {\\r\\n type: \\\"array\\\",\\r\\n items: {\\r\\n enum: [\\\"sum\\\", \\\"average\\\", \\\"count\\\"]\\r\\n }\\r\\n }\\r\\n }\\r\\n }\\r\\n}\\r\\n```\\r\\n\\r\\n## Best practices\\r\\n\\r\\nWhen implementing tools:\\r\\n\\r\\n1. Provide clear, descriptive names and descriptions\\r\\n2. Use detailed JSON Schema definitions for parameters\\r\\n3. Include examples in tool descriptions to demonstrate how the model should use them\\r\\n4. Implement proper error handling and validation\\r\\n5. Use progress reporting for long operations\\r\\n6. Keep tool operations focused and atomic\\r\\n7. Document expected return value structures\\r\\n8. Implement proper timeouts\\r\\n9. Consider rate limiting for resource-intensive operations\\r\\n10. Log tool usage for debugging and monitoring\\r\\n\\r\\n## Security considerations\\r\\n\\r\\nWhen exposing tools:\\r\\n\\r\\n### Input validation\\r\\n\\r\\n- Validate all parameters against the schema\\r\\n- Sanitize file paths and system commands\\r\\n- Validate URLs and external identifiers\\r\\n- Check parameter sizes and ranges\\r\\n- Prevent command injection\\r\\n\\r\\n### Access control\\r\\n\\r\\n- Implement authentication where needed\\r\\n- Use appropriate authorization checks\\r\\n- Audit tool usage\\r\\n- Rate limit requests\\r\\n- Monitor for abuse\\r\\n\\r\\n### Error handling\\r\\n\\r\\n- Don't expose internal errors to clients\\r\\n- Log security-relevant errors\\r\\n- Handle timeouts appropriately\\r\\n- Clean up resources after errors\\r\\n- Validate return values\\r\\n\\r\\n## Tool discovery and updates\\r\\n\\r\\nMCP supports dynamic tool discovery:\\r\\n\\r\\n1. Clients can list available tools at any time\\r\\n2. Servers can notify clients when tools change using `notifications/tools/list_changed`\\r\\n3. Tools can be added or removed during runtime\\r\\n4. Tool definitions can be updated (though this should be done carefully)\\r\\n\\r\\n## Error handling\\r\\n\\r\\nTool errors should be reported within the result object, not as MCP protocol-level errors. This allows the LLM to see and potentially handle the error. When a tool encounters an error:\\r\\n\\r\\n1. Set `isError` to `true` in the result\\r\\n2. Include error details in the `content` array\\r\\n\\r\\nHere's an example of proper error handling for tools:\\r\\n\\r\\n<Tabs>\\r\\n <Tab title=\\\"TypeScript\\\">\\r\\n ```typescript\\r\\n try {\\r\\n // Tool operation\\r\\n const result = performOperation();\\r\\n return {\\r\\n content: [\\r\\n {\\r\\n type: \\\"text\\\",\\r\\n text: `Operation successful: ${result}`\\r\\n }\\r\\n ]\\r\\n };\\r\\n } catch (error) {\\r\\n return {\\r\\n isError: true,\\r\\n content: [\\r\\n {\\r\\n type: \\\"text\\\",\\r\\n text: `Error: ${error.message}`\\r\\n }\\r\\n ]\\r\\n };\\r\\n }\\r\\n ```\\r\\n </Tab>\\r\\n <Tab title=\\\"Python\\\">\\r\\n ```python\\r\\n try:\\r\\n # Tool operation\\r\\n result = perform_operation()\\r\\n return types.CallToolResult(\\r\\n content=[\\r\\n types.TextContent(\\r\\n type=\\\"text\\\",\\r\\n text=f\\\"Operation successful: {result}\\\"\\r\\n )\\r\\n ]\\r\\n )\\r\\n except Exception as error:\\r\\n return types.CallToolResult(\\r\\n isError=True,\\r\\n content=[\\r\\n types.TextContent(\\r\\n type=\\\"text\\\",\\r\\n text=f\\\"Error: {str(error)}\\\"\\r\\n )\\r\\n ]\\r\\n )\\r\\n ```\\r\\n </Tab>\\r\\n</Tabs>\\r\\n\\r\\nThis approach allows the LLM to see that an error occurred and potentially take corrective action or request human intervention.\\r\\n\\r\\n## Testing tools\\r\\n\\r\\nA comprehensive testing strategy for MCP tools should cover:\\r\\n\\r\\n- **Functional testing**: Verify tools execute correctly with valid inputs and handle invalid inputs appropriately\\r\\n- **Integration testing**: Test tool interaction with external systems using both real and mocked dependencies\\r\\n- **Security testing**: Validate authentication, authorization, input sanitization, and rate limiting\\r\\n- **Performance testing**: Check behavior under load, timeout handling, and resource cleanup\\r\\n- **Error handling**: Ensure tools properly report errors through the MCP protocol and clean up resources\\r\\n\",\"metadata\":{\"size\":9023,\"mimeType\":\"text/mdx\",\"modifiedTime\":\"2025-01-08T03:13:34.752Z\",\"createdTime\":\"2025-01-08T03:13:34.752Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":314,\"nonEmptyLines\":263,\"commentLines\":0,\"complexity\":4},\"dependencies\":[],\"quality\":{\"score\":-337,\"issues\":[],\"duplicateLines\":83,\"longLines\":11,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T16:59:44.472Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":314},\"analysis\":{\"metrics\":{\"lines\":314,\"nonEmptyLines\":263,\"commentLines\":0,\"complexity\":4},\"dependencies\":[],\"quality\":{\"score\":-337,\"issues\":[],\"duplicateLines\":83,\"longLines\":11,\"complexFunctions\":0}},\"lastModified\":1737046784473,\"hits\":0,\"lastAccessed\":\"2025-01-16T16:59:44.473Z\",\"size\":10240},{\"key\":\"d:\\\\projects\\\\mcp_docs\\\\docs\\\\concepts\\\\sampling.mdx\",\"content\":{\"content\":\"---\\r\\ntitle: \\\"Sampling\\\"\\r\\ndescription: \\\"Let your servers request completions from LLMs\\\"\\r\\n---\\r\\n\\r\\nSampling is a powerful MCP feature that allows servers to request LLM completions through the client, enabling sophisticated agentic behaviors while maintaining security and privacy.\\r\\n\\r\\n<Info>\\r\\n This feature of MCP is not yet supported in the Claude Desktop client.\\r\\n</Info>\\r\\n\\r\\n## How sampling works\\r\\n\\r\\nThe sampling flow follows these steps:\\r\\n\\r\\n1. Server sends a `sampling/createMessage` request to the client\\r\\n2. Client reviews the request and can modify it\\r\\n3. Client samples from an LLM\\r\\n4. Client reviews the completion\\r\\n5. Client returns the result to the server\\r\\n\\r\\nThis human-in-the-loop design ensures users maintain control over what the LLM sees and generates.\\r\\n\\r\\n## Message format\\r\\n\\r\\nSampling requests use a standardized message format:\\r\\n\\r\\n```typescript\\r\\n{\\r\\n messages: [\\r\\n {\\r\\n role: \\\"user\\\" | \\\"assistant\\\",\\r\\n content: {\\r\\n type: \\\"text\\\" | \\\"image\\\",\\r\\n\\r\\n // For text:\\r\\n text?: string,\\r\\n\\r\\n // For images:\\r\\n data?: string, // base64 encoded\\r\\n mimeType?: string\\r\\n }\\r\\n }\\r\\n ],\\r\\n modelPreferences?: {\\r\\n hints?: [{\\r\\n name?: string // Suggested model name/family\\r\\n }],\\r\\n costPriority?: number, // 0-1, importance of minimizing cost\\r\\n speedPriority?: number, // 0-1, importance of low latency\\r\\n intelligencePriority?: number // 0-1, importance of capabilities\\r\\n },\\r\\n systemPrompt?: string,\\r\\n includeContext?: \\\"none\\\" | \\\"thisServer\\\" | \\\"allServers\\\",\\r\\n temperature?: number,\\r\\n maxTokens: number,\\r\\n stopSequences?: string[],\\r\\n metadata?: Record<string, unknown>\\r\\n}\\r\\n```\\r\\n\\r\\n## Request parameters\\r\\n\\r\\n### Messages\\r\\n\\r\\nThe `messages` array contains the conversation history to send to the LLM. Each message has:\\r\\n\\r\\n- `role`: Either \\\"user\\\" or \\\"assistant\\\"\\r\\n- `content`: The message content, which can be:\\r\\n - Text content with a `text` field\\r\\n - Image content with `data` (base64) and `mimeType` fields\\r\\n\\r\\n### Model preferences\\r\\n\\r\\nThe `modelPreferences` object allows servers to specify their model selection preferences:\\r\\n\\r\\n- `hints`: Array of model name suggestions that clients can use to select an appropriate model:\\r\\n - `name`: String that can match full or partial model names (e.g. \\\"claude-3\\\", \\\"sonnet\\\")\\r\\n - Clients may map hints to equivalent models from different providers\\r\\n - Multiple hints are evaluated in preference order\\r\\n\\r\\n- Priority values (0-1 normalized):\\r\\n - `costPriority`: Importance of minimizing costs\\r\\n - `speedPriority`: Importance of low latency response\\r\\n - `intelligencePriority`: Importance of advanced model capabilities\\r\\n\\r\\nClients make the final model selection based on these preferences and their available models.\\r\\n\\r\\n### System prompt\\r\\n\\r\\nAn optional `systemPrompt` field allows servers to request a specific system prompt. The client may modify or ignore this.\\r\\n\\r\\n### Context inclusion\\r\\n\\r\\nThe `includeContext` parameter specifies what MCP context to include:\\r\\n\\r\\n- `\\\"none\\\"`: No additional context\\r\\n- `\\\"thisServer\\\"`: Include context from the requesting server\\r\\n- `\\\"allServers\\\"`: Include context from all connected MCP servers\\r\\n\\r\\nThe client controls what context is actually included.\\r\\n\\r\\n### Sampling parameters\\r\\n\\r\\nFine-tune the LLM sampling with:\\r\\n\\r\\n- `temperature`: Controls randomness (0.0 to 1.0)\\r\\n- `maxTokens`: Maximum tokens to generate\\r\\n- `stopSequences`: Array of sequences that stop generation\\r\\n- `metadata`: Additional provider-specific parameters\\r\\n\\r\\n## Response format\\r\\n\\r\\nThe client returns a completion result:\\r\\n\\r\\n```typescript\\r\\n{\\r\\n model: string, // Name of the model used\\r\\n stopReason?: \\\"endTurn\\\" | \\\"stopSequence\\\" | \\\"maxTokens\\\" | string,\\r\\n role: \\\"user\\\" | \\\"assistant\\\",\\r\\n content: {\\r\\n type: \\\"text\\\" | \\\"image\\\",\\r\\n text?: string,\\r\\n data?: string,\\r\\n mimeType?: string\\r\\n }\\r\\n}\\r\\n```\\r\\n\\r\\n## Example request\\r\\n\\r\\nHere's an example of requesting sampling from a client:\\r\\n```json\\r\\n{\\r\\n \\\"method\\\": \\\"sampling/createMessage\\\",\\r\\n \\\"params\\\": {\\r\\n \\\"messages\\\": [\\r\\n {\\r\\n \\\"role\\\": \\\"user\\\",\\r\\n \\\"content\\\": {\\r\\n \\\"type\\\": \\\"text\\\",\\r\\n \\\"text\\\": \\\"What files are in the current directory?\\\"\\r\\n }\\r\\n }\\r\\n ],\\r\\n \\\"systemPrompt\\\": \\\"You are a helpful file system assistant.\\\",\\r\\n \\\"includeContext\\\": \\\"thisServer\\\",\\r\\n \\\"maxTokens\\\": 100\\r\\n }\\r\\n}\\r\\n```\\r\\n\\r\\n## Best practices\\r\\n\\r\\nWhen implementing sampling:\\r\\n\\r\\n1. Always provide clear, well-structured prompts\\r\\n2. Handle both text and image content appropriately\\r\\n3. Set reasonable token limits\\r\\n4. Include relevant context through `includeContext`\\r\\n5. Validate responses before using them\\r\\n6. Handle errors gracefully\\r\\n7. Consider rate limiting sampling requests\\r\\n8. Document expected sampling behavior\\r\\n9. Test with various model parameters\\r\\n10. Monitor sampling costs\\r\\n\\r\\n## Human in the loop controls\\r\\n\\r\\nSampling is designed with human oversight in mind:\\r\\n\\r\\n### For prompts\\r\\n\\r\\n- Clients should show users the proposed prompt\\r\\n- Users should be able to modify or reject prompts\\r\\n- System prompts can be filtered or modified\\r\\n- Context inclusion is controlled by the client\\r\\n\\r\\n### For completions\\r\\n\\r\\n- Clients should show users the completion\\r\\n- Users should be able to modify or reject completions\\r\\n- Clients can filter or modify completions\\r\\n- Users control which model is used\\r\\n\\r\\n## Security considerations\\r\\n\\r\\nWhen implementing sampling:\\r\\n\\r\\n- Validate all message content\\r\\n- Sanitize sensitive information\\r\\n- Implement appropriate rate limits\\r\\n- Monitor sampling usage\\r\\n- Encrypt data in transit\\r\\n- Handle user data privacy\\r\\n- Audit sampling requests\\r\\n- Control cost exposure\\r\\n- Implement timeouts\\r\\n- Handle model errors gracefully\\r\\n\\r\\n## Common patterns\\r\\n\\r\\n### Agentic workflows\\r\\n\\r\\nSampling enables agentic patterns like:\\r\\n\\r\\n- Reading and analyzing resources\\r\\n- Making decisions based on context\\r\\n- Generating structured data\\r\\n- Handling multi-step tasks\\r\\n- Providing interactive assistance\\r\\n\\r\\n### Context management\\r\\n\\r\\nBest practices for context:\\r\\n\\r\\n- Request minimal necessary context\\r\\n- Structure context clearly\\r\\n- Handle context size limits\\r\\n- Update context as needed\\r\\n- Clean up stale context\\r\\n\\r\\n### Error handling\\r\\n\\r\\nRobust error handling should:\\r\\n\\r\\n- Catch sampling failures\\r\\n- Handle timeout errors\\r\\n- Manage rate limits\\r\\n- Validate responses\\r\\n- Provide fallback behaviors\\r\\n- Log errors appropriately\\r\\n\\r\\n## Limitations\\r\\n\\r\\nBe aware of these limitations:\\r\\n\\r\\n- Sampling depends on client capabilities\\r\\n- Users control sampling behavior\\r\\n- Context size has limits\\r\\n- Rate limits may apply\\r\\n- Costs should be considered\\r\\n- Model availability varies\\r\\n- Response times vary\\r\\n- Not all content types supported\\r\\n\",\"metadata\":{\"size\":6646,\"mimeType\":\"text/mdx\",\"modifiedTime\":\"2024-12-17T17:14:31.725Z\",\"createdTime\":\"2024-12-17T17:14:31.725Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":246,\"nonEmptyLines\":186,\"commentLines\":0,\"complexity\":20},\"dependencies\":[],\"quality\":{\"score\":-19,\"issues\":[],\"duplicateLines\":23,\"longLines\":2,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T16:59:44.470Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":246},\"analysis\":{\"metrics\":{\"lines\":246,\"nonEmptyLines\":186,\"commentLines\":0,\"complexity\":20},\"dependencies\":[],\"quality\":{\"score\":-19,\"issues\":[],\"duplicateLines\":23,\"longLines\":2,\"complexFunctions\":0}},\"lastModified\":1737046784471,\"hits\":0,\"lastAccessed\":\"2025-01-16T16:59:44.471Z\",\"size\":7672},{\"key\":\"d:\\\\projects\\\\mcp_docs\\\\docs\\\\concepts\\\\roots.mdx\",\"content\":{\"content\":\"---\\r\\ntitle: \\\"Roots\\\"\\r\\ndescription: \\\"Understanding roots in MCP\\\"\\r\\n---\\r\\n\\r\\nRoots are a concept in MCP that define the boundaries where servers can operate. They provide a way for clients to inform servers about relevant resources and their locations.\\r\\n\\r\\n## What are Roots?\\r\\n\\r\\nA root is a URI that a client suggests a server should focus on. When a client connects to a server, it declares which roots the server should work with. While primarily used for filesystem paths, roots can be any valid URI including HTTP URLs.\\r\\n\\r\\nFor example, roots could be:\\r\\n\\r\\n```\\r\\nfile:///home/user/projects/myapp\\r\\nhttps://api.example.com/v1\\r\\n```\\r\\n\\r\\n## Why Use Roots?\\r\\n\\r\\nRoots serve several important purposes:\\r\\n\\r\\n1. **Guidance**: They inform servers about relevant resources and locations\\r\\n2. **Clarity**: Roots make it clear which resources are part of your workspace\\r\\n3. **Organization**: Multiple roots let you work with different resources simultaneously\\r\\n\\r\\n## How Roots Work\\r\\n\\r\\nWhen a client supports roots, it:\\r\\n\\r\\n1. Declares the `roots` capability during connection\\r\\n2. Provides a list of suggested roots to the server\\r\\n3. Notifies the server when roots change (if supported)\\r\\n\\r\\nWhile roots are informational and not strictly enforcing, servers should:\\r\\n\\r\\n1. Respect the provided roots\\r\\n2. Use root URIs to locate and access resources\\r\\n3. Prioritize operations within root boundaries\\r\\n\\r\\n## Common Use Cases\\r\\n\\r\\nRoots are commonly used to define:\\r\\n\\r\\n- Project directories\\r\\n- Repository locations\\r\\n- API endpoints\\r\\n- Configuration locations\\r\\n- Resource boundaries\\r\\n\\r\\n## Best Practices\\r\\n\\r\\nWhen working with roots:\\r\\n\\r\\n1. Only suggest necessary resources\\r\\n2. Use clear, descriptive names for roots\\r\\n3. Monitor root accessibility\\r\\n4. Handle root changes gracefully\\r\\n\\r\\n## Example\\r\\n\\r\\nHere's how a typical MCP client might expose roots:\\r\\n\\r\\n```json\\r\\n{\\r\\n \\\"roots\\\": [\\r\\n {\\r\\n \\\"uri\\\": \\\"file:///home/user/projects/frontend\\\",\\r\\n \\\"name\\\": \\\"Frontend Repository\\\"\\r\\n },\\r\\n {\\r\\n \\\"uri\\\": \\\"https://api.example.com/v1\\\",\\r\\n \\\"name\\\": \\\"API Endpoint\\\"\\r\\n }\\r\\n ]\\r\\n}\\r\\n```\\r\\n\\r\\nThis configuration suggests the server focus on both a local repository and an API endpoint while keeping them logically separated.\",\"metadata\":{\"size\":2179,\"mimeType\":\"text/mdx\",\"modifiedTime\":\"2025-01-08T03:13:34.752Z\",\"createdTime\":\"2025-01-08T03:13:34.752Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":79,\"nonEmptyLines\":56,\"commentLines\":0,\"complexity\":3},\"dependencies\":[],\"quality\":{\"score\":64,\"issues\":[],\"duplicateLines\":6,\"longLines\":3,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T16:59:44.470Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":79},\"analysis\":{\"metrics\":{\"lines\":79,\"nonEmptyLines\":56,\"commentLines\":0,\"complexity\":3},\"dependencies\":[],\"quality\":{\"score\":64,\"issues\":[],\"duplicateLines\":6,\"longLines\":3,\"complexFunctions\":0}},\"lastModified\":1737046784470,\"hits\":0,\"lastAccessed\":\"2025-01-16T16:59:44.470Z\",\"size\":2809},{\"key\":\"d:\\\\projects\\\\mcp_docs\\\\docs\\\\concepts\\\\resources.mdx\",\"content\":{\"content\":\"---\\r\\ntitle: \\\"Resources\\\"\\r\\ndescription: \\\"Expose data and content from your servers to LLMs\\\"\\r\\n---\\r\\n\\r\\nResources are a core primitive in the Model Context Protocol (MCP) that allow servers to expose data and content that can be read by clients and used as context for LLM interactions.\\r\\n\\r\\n<Note>\\r\\n Resources are designed to be **application-controlled**, meaning that the client application can decide how and when they should be used.\\r\\n Different MCP clients may handle resources differently. For example:\\r\\n - Claude Desktop currently requires users to explicitly select resources before they can be used\\r\\n - Other clients might automatically select resources based on heuristics\\r\\n - Some implementations may even allow the AI model itself to determine which resources to use\\r\\n\\r\\n Server authors should be prepared to handle any of these interaction patterns when implementing resource support. In order to expose data to models automatically, server authors should use a **model-controlled** primitive such as [Tools](./tools).\\r\\n</Note>\\r\\n\\r\\n## Overview\\r\\n\\r\\nResources represent any kind of data that an MCP server wants to make available to clients. This can include:\\r\\n\\r\\n- File contents\\r\\n- Database records\\r\\n- API responses\\r\\n- Live system data\\r\\n- Screenshots and images\\r\\n- Log files\\r\\n- And more\\r\\n\\r\\nEach resource is identified by a unique URI and can contain either text or binary data.\\r\\n\\r\\n## Resource URIs\\r\\n\\r\\nResources are identified using URIs that follow this format:\\r\\n\\r\\n```\\r\\n[protocol]://[host]/[path]\\r\\n```\\r\\n\\r\\nFor example:\\r\\n- `file:///home/user/documents/report.pdf`\\r\\n- `postgres://database/customers/schema`\\r\\n- `screen://localhost/display1`\\r\\n\\r\\nThe protocol and path structure is defined by the MCP server implementation. Servers can define their own custom URI schemes.\\r\\n\\r\\n## Resource types\\r\\n\\r\\nResources can contain two types of content:\\r\\n\\r\\n### Text resources\\r\\n\\r\\nText resources contain UTF-8 encoded text data. These are suitable for:\\r\\n- Source code\\r\\n- Configuration files\\r\\n- Log files\\r\\n- JSON/XML data\\r\\n- Plain text\\r\\n\\r\\n### Binary resources\\r\\n\\r\\nBinary resources contain raw binary data encoded in base64. These are suitable for:\\r\\n- Images\\r\\n- PDFs\\r\\n- Audio files\\r\\n- Video files\\r\\n- Other non-text formats\\r\\n\\r\\n## Resource discovery\\r\\n\\r\\nClients can discover available resources through two main methods:\\r\\n\\r\\n### Direct resources\\r\\n\\r\\nServers expose a list of concrete resources via the `resources/list` endpoint. Each resource includes:\\r\\n\\r\\n```typescript\\r\\n{\\r\\n uri: string; // Unique identifier for the resource\\r\\n name: string; // Human-readable name\\r\\n description?: string; // Optional description\\r\\n mimeType?: string; // Optional MIME type\\r\\n}\\r\\n```\\r\\n\\r\\n### Resource templates\\r\\n\\r\\nFor dynamic resources, servers can expose [URI templates](https://datatracker.ietf.org/doc/html/rfc6570) that clients can use to construct valid resource URIs:\\r\\n\\r\\n```typescript\\r\\n{\\r\\n uriTemplate: string; // URI template following RFC 6570\\r\\n name: string; // Human-readable name for this type\\r\\n description?: string; // Optional description\\r\\n mimeType?: string; // Optional MIME type for all matching resources\\r\\n}\\r\\n```\\r\\n\\r\\n## Reading resources\\r\\n\\r\\nTo read a resource, clients make a `resources/read` request with the resource URI.\\r\\n\\r\\nThe server responds with a list of resource contents:\\r\\n\\r\\n```typescript\\r\\n{\\r\\n contents: [\\r\\n {\\r\\n uri: string; // The URI of the resource\\r\\n mimeType?: string; // Optional MIME type\\r\\n\\r\\n // One of:\\r\\n text?: string; // For text resources\\r\\n blob?: string; // For binary resources (base64 encoded)\\r\\n }\\r\\n ]\\r\\n}\\r\\n```\\r\\n\\r\\n<Tip>\\r\\n Servers may return multiple resources in response to one `resources/read` request. This could be used, for example, to return a list of files inside a directory when the directory is read.\\r\\n</Tip>\\r\\n\\r\\n## Resource updates\\r\\n\\r\\nMCP supports real-time updates for resources through two mechanisms:\\r\\n\\r\\n### List changes\\r\\n\\r\\nServers can notify clients when their list of available resources changes via the `notifications/resources/list_changed` notification.\\r\\n\\r\\n### Content changes\\r\\n\\r\\nClients can subscribe to updates for specific resources:\\r\\n\\r\\n1. Client sends `resources/subscribe` with resource URI\\r\\n2. Server sends `notifications/resources/updated` when the resource changes\\r\\n3. Client can fetch latest content with `resources/read`\\r\\n4. Client can unsubscribe with `resources/unsubscribe`\\r\\n\\r\\n## Example implementation\\r\\n\\r\\nHere's a simple example of implementing resource support in an MCP server:\\r\\n\\r\\n<Tabs>\\r\\n <Tab title=\\\"TypeScript\\\">\\r\\n ```typescript\\r\\n const server = new Server({\\r\\n name: \\\"example-server\\\",\\r\\n version: \\\"1.0.0\\\"\\r\\n }, {\\r\\n capabilities: {\\r\\n resources: {}\\r\\n }\\r\\n });\\r\\n\\r\\n // List available resources\\r\\n server.setRequestHandler(ListResourcesRequestSchema, async () => {\\r\\n return {\\r\\n resources: [\\r\\n {\\r\\n uri: \\\"file:///logs/app.log\\\",\\r\\n name: \\\"Application Logs\\\",\\r\\n mimeType: \\\"text/plain\\\"\\r\\n }\\r\\n ]\\r\\n };\\r\\n });\\r\\n\\r\\n // Read resource contents\\r\\n server.setRequestHandler(ReadResourceRequestSchema, async (request) => {\\r\\n const uri = request.params.uri;\\r\\n\\r\\n if (uri === \\\"file:///logs/app.log\\\") {\\r\\n const logContents = await readLogFile();\\r\\n return {\\r\\n contents: [\\r\\n {\\r\\n uri,\\r\\n mimeType: \\\"text/plain\\\",\\r\\n text: logContents\\r\\n }\\r\\n ]\\r\\n };\\r\\n }\\r\\n\\r\\n throw new Error(\\\"Resource not found\\\");\\r\\n });\\r\\n ```\\r\\n </Tab>\\r\\n <Tab title=\\\"Python\\\">\\r\\n ```python\\r\\n app = Server(\\\"example-server\\\")\\r\\n\\r\\n @app.list_resources()\\r\\n async def list_resources() -> list[types.Resource]:\\r\\n return [\\r\\n types.Resource(\\r\\n uri=\\\"file:///logs/app.log\\\",\\r\\n name=\\\"Application Logs\\\",\\r\\n mimeType=\\\"text/plain\\\"\\r\\n )\\r\\n ]\\r\\n\\r\\n @app.read_resource()\\r\\n async def read_resource(uri: AnyUrl) -> str:\\r\\n if str(uri) == \\\"file:///logs/app.log\\\":\\r\\n log_contents = await read_log_file()\\r\\n return log_contents\\r\\n\\r\\n raise ValueError(\\\"Resource not found\\\")\\r\\n\\r\\n # Start server\\r\\n async with stdio_server() as streams:\\r\\n await app.run(\\r\\n streams[0],\\r\\n streams[1],\\r\\n app.create_initialization_options()\\r\\n )\\r\\n ```\\r\\n </Tab>\\r\\n</Tabs>\\r\\n\\r\\n## Best practices\\r\\n\\r\\nWhen implementing resource support:\\r\\n\\r\\n1. Use clear, descriptive resource names and URIs\\r\\n2. Include helpful descriptions to guide LLM understanding\\r\\n3. Set appropriate MIME types when known\\r\\n4. Implement resource templates for dynamic content\\r\\n5. Use subscriptions for frequently changing resources\\r\\n6. Handle errors gracefully with clear error messages\\r\\n7. Consider pagination for large resource lists\\r\\n8. Cache resource contents when appropriate\\r\\n9. Validate URIs before processing\\r\\n10. Document your custom URI schemes\\r\\n\\r\\n## Security considerations\\r\\n\\r\\nWhen exposing resources:\\r\\n\\r\\n- Validate all resource URIs\\r\\n- Implement appropriate access controls\\r\\n- Sanitize file paths to prevent directory traversal\\r\\n- Be cautious with binary data handling\\r\\n- Consider rate limiting for resource reads\\r\\n- Audit resource access\\r\\n- Encrypt sensitive data in transit\\r\\n- Validate MIME types\\r\\n- Implement timeouts for long-running reads\\r\\n- Handle resource cleanup appropriately\\r\\n\",\"metadata\":{\"size\":7399,\"mimeType\":\"text/mdx\",\"modifiedTime\":\"2024-12-17T17:14:31.724Z\",\"createdTime\":\"2024-12-17T17:14:31.724Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":253,\"nonEmptyLines\":196,\"commentLines\":0,\"complexity\":9},\"dependencies\":[],\"quality\":{\"score\":-88,\"issues\":[],\"duplicateLines\":34,\"longLines\":9,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T16:59:44.469Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":253},\"analysis\":{\"metrics\":{\"lines\":253,\"nonEmptyLines\":196,\"commentLines\":0,\"complexity\":9},\"dependencies\":[],\"quality\":{\"score\":-88,\"issues\":[],\"duplicateLines\":34,\"longLines\":9,\"complexFunctions\":0}},\"lastModified\":1737046784469,\"hits\":0,\"lastAccessed\":\"2025-01-16T16:59:44.469Z\",\"size\":8396},{\"key\":\"d:\\\\projects\\\\mcp_docs\\\\docs\\\\concepts\\\\prompts.mdx\",\"content\":{\"content\":\"---\\r\\ntitle: \\\"Prompts\\\"\\r\\ndescription: \\\"Create reusable prompt templates and workflows\\\"\\r\\n---\\r\\n\\r\\nPrompts enable servers to define reusable prompt templates and workflows that clients can easily surface to users and LLMs. They provide a powerful way to standardize and share common LLM interactions.\\r\\n\\r\\n<Note>\\r\\n Prompts are designed to be **user-controlled**, meaning they are exposed from servers to clients with the intention of the user being able to explicitly select them for use.\\r\\n</Note>\\r\\n\\r\\n## Overview\\r\\n\\r\\nPrompts in MCP are predefined templates that can:\\r\\n- Accept dynamic arguments\\r\\n- Include context from resources\\r\\n- Chain multiple interactions\\r\\n- Guide specific workflows\\r\\n- Surface as UI elements (like slash commands)\\r\\n\\r\\n## Prompt structure\\r\\n\\r\\nEach prompt is defined with:\\r\\n\\r\\n```typescript\\r\\n{\\r\\n name: string; // Unique identifier for the prompt\\r\\n description?: string; // Human-readable description\\r\\n arguments?: [ // Optional list of arguments\\r\\n {\\r\\n name: string; // Argument identifier\\r\\n description?: string; // Argument description\\r\\n required?: boolean; // Whether argument is required\\r\\n }\\r\\n ]\\r\\n}\\r\\n```\\r\\n\\r\\n## Discovering prompts\\r\\n\\r\\nClients can discover available prompts through the `prompts/list` endpoint:\\r\\n\\r\\n```typescript\\r\\n// Request\\r\\n{\\r\\n method: \\\"prompts/list\\\"\\r\\n}\\r\\n\\r\\n// Response\\r\\n{\\r\\n prompts: [\\r\\n {\\r\\n name: \\\"analyze-code\\\",\\r\\n description: \\\"Analyze code for potential improvements\\\",\\r\\n arguments: [\\r\\n {\\r\\n name: \\\"language\\\",\\r\\n description: \\\"Programming language\\\",\\r\\n required: true\\r\\n }\\r\\n ]\\r\\n }\\r\\n ]\\r\\n}\\r\\n```\\r\\n\\r\\n## Using prompts\\r\\n\\r\\nTo use a prompt, clients make a `prompts/get` request:\\r\\n\\r\\n```typescript\\r\\n// Request\\r\\n{\\r\\n method: \\\"prompts/get\\\",\\r\\n params: {\\r\\n name: \\\"analyze-code\\\",\\r\\n arguments: {\\r\\n language: \\\"python\\\"\\r\\n }\\r\\n }\\r\\n}\\r\\n\\r\\n// Response\\r\\n{\\r\\n description: \\\"Analyze Python code for potential improvements\\\",\\r\\n messages: [\\r\\n {\\r\\n role: \\\"user\\\",\\r\\n content: {\\r\\n type: \\\"text\\\",\\r\\n text: \\\"Please analyze the following Python code for potential improvements:\\\\n\\\\n```python\\\\ndef calculate_sum(numbers):\\\\n total = 0\\\\n for num in numbers:\\\\n total = total + num\\\\n return total\\\\n\\\\nresult = calculate_sum([1, 2, 3, 4, 5])\\\\nprint(result)\\\\n```\\\"\\r\\n }\\r\\n }\\r\\n ]\\r\\n}\\r\\n```\\r\\n\\r\\n## Dynamic prompts\\r\\n\\r\\nPrompts can be dynamic and include:\\r\\n\\r\\n### Embedded resource context\\r\\n\\r\\n```json\\r\\n{\\r\\n \\\"name\\\": \\\"analyze-project\\\",\\r\\n \\\"description\\\": \\\"Analyze project logs and code\\\",\\r\\n \\\"arguments\\\": [\\r\\n {\\r\\n \\\"name\\\": \\\"timeframe\\\",\\r\\n \\\"description\\\": \\\"Time period to analyze logs\\\",\\r\\n \\\"required\\\": true\\r\\n },\\r\\n {\\r\\n \\\"name\\\": \\\"fileUri\\\",\\r\\n \\\"description\\\": \\\"URI of code file to review\\\",\\r\\n \\\"required\\\": true\\r\\n }\\r\\n ]\\r\\n}\\r\\n```\\r\\n\\r\\nWhen handling the `prompts/get` request:\\r\\n\\r\\n```json\\r\\n{\\r\\n \\\"messages\\\": [\\r\\n {\\r\\n \\\"role\\\": \\\"user\\\",\\r\\n \\\"content\\\": {\\r\\n \\\"type\\\": \\\"text\\\",\\r\\n \\\"text\\\": \\\"Analyze these system logs and the code file for any issues:\\\"\\r\\n }\\r\\n },\\r\\n {\\r\\n \\\"role\\\": \\\"user\\\",\\r\\n \\\"content\\\": {\\r\\n \\\"type\\\": \\\"resource\\\",\\r\\n \\\"resource\\\": {\\r\\n \\\"uri\\\": \\\"logs://recent?timeframe=1h\\\",\\r\\n \\\"text\\\": \\\"[2024-03-14 15:32:11] ERROR: Connection timeout in network.py:127\\\\n[2024-03-14 15:32:15] WARN: Retrying connection (attempt 2/3)\\\\n[2024-03-14 15:32:20] ERROR: Max retries exceeded\\\",\\r\\n \\\"mimeType\\\": \\\"text/plain\\\"\\r\\n }\\r\\n }\\r\\n },\\r\\n {\\r\\n \\\"role\\\": \\\"user\\\",\\r\\n \\\"content\\\": {\\r\\n \\\"type\\\": \\\"resource\\\",\\r\\n \\\"resource\\\": {\\r\\n \\\"uri\\\": \\\"file:///path/to/code.py\\\",\\r\\n \\\"text\\\": \\\"def connect_to_service(timeout=30):\\\\n retries = 3\\\\n for attempt in range(retries):\\\\n try:\\\\n return establish_connection(timeout)\\\\n except TimeoutError:\\\\n if attempt == retries - 1:\\\\n raise\\\\n time.sleep(5)\\\\n\\\\ndef establish_connection(timeout):\\\\n # Connection implementation\\\\n pass\\\",\\r\\n \\\"mimeType\\\": \\\"text/x-python\\\"\\r\\n }\\r\\n }\\r\\n }\\r\\n ]\\r\\n}\\r\\n```\\r\\n\\r\\n### Multi-step workflows\\r\\n\\r\\n```typescript\\r\\nconst debugWorkflow = {\\r\\n name: \\\"debug-error\\\",\\r\\n async getMessages(error: string) {\\r\\n return [\\r\\n {\\r\\n role: \\\"user\\\",\\r\\n content: {\\r\\n type: \\\"text\\\",\\r\\n text: `Here's an error I'm seeing: ${error}`\\r\\n }\\r\\n },\\r\\n {\\r\\n role: \\\"assistant\\\",\\r\\n content: {\\r\\n type: \\\"text\\\",\\r\\n text: \\\"I'll help analyze this error. What have you tried so far?\\\"\\r\\n }\\r\\n },\\r\\n {\\r\\n role: \\\"user\\\",\\r\\n content: {\\r\\n type: \\\"text\\\",\\r\\n text: \\\"I've tried restarting the service, but the error persists.\\\"\\r\\n }\\r\\n }\\r\\n ];\\r\\n }\\r\\n};\\r\\n```\\r\\n\\r\\n## Example implementation\\r\\n\\r\\nHere's a complete example of implementing prompts in an MCP server:\\r\\n\\r\\n<Tabs>\\r\\n <Tab title=\\\"TypeScript\\\">\\r\\n ```typescript\\r\\n import { Server } from \\\"@modelcontextprotocol/sdk/server\\\";\\r\\n import {\\r\\n ListPromptsRequestSchema,\\r\\n GetPromptRequestSchema\\r\\n } from \\\"@modelcontextprotocol/sdk/types\\\";\\r\\n\\r\\n const PROMPTS = {\\r\\n \\\"git-commit\\\": {\\r\\n name: \\\"git-commit\\\",\\r\\n description: \\\"Generate a Git commit message\\\",\\r\\n arguments: [\\r\\n {\\r\\n name: \\\"changes\\\",\\r\\n description: \\\"Git diff or description of changes\\\",\\r\\n required: true\\r\\n }\\r\\n ]\\r\\n },\\r\\n \\\"explain-code\\\": {\\r\\n name: \\\"explain-code\\\",\\r\\n description: \\\"Explain how code works\\\",\\r\\n arguments: [\\r\\n {\\r\\n name: \\\"code\\\",\\r\\n description: \\\"Code to explain\\\",\\r\\n required: true\\r\\n },\\r\\n {\\r\\n name: \\\"language\\\",\\r\\n description: \\\"Programming language\\\",\\r\\n required: false\\r\\n }\\r\\n ]\\r\\n }\\r\\n };\\r\\n\\r\\n const server = new Server({\\r\\n name: \\\"example-prompts-server\\\",\\r\\n version: \\\"1.0.0\\\"\\r\\n }, {\\r\\n capabilities: {\\r\\n prompts: {}\\r\\n }\\r\\n });\\r\\n\\r\\n // List available prompts\\r\\n server.setRequestHandler(ListPromptsRequestSchema, async () => {\\r\\n return {\\r\\n prompts: Object.values(PROMPTS)\\r\\n };\\r\\n });\\r\\n\\r\\n // Get specific prompt\\r\\n server.setRequestHandler(GetPromptRequestSchema, async (request) => {\\r\\n const prompt = PROMPTS[request.params.name];\\r\\n if (!prompt) {\\r\\n throw new Error(`Prompt not found: ${request.params.name}`);\\r\\n }\\r\\n\\r\\n if (request.params.name === \\\"git-commit\\\") {\\r\\n return {\\r\\n messages: [\\r\\n {\\r\\n role: \\\"user\\\",\\r\\n content: {\\r\\n type: \\\"text\\\",\\r\\n text: `Generate a concise but descriptive commit message for these changes:\\\\n\\\\n${request.params.arguments?.changes}`\\r\\n }\\r\\n }\\r\\n ]\\r\\n };\\r\\n }\\r\\n\\r\\n if (request.params.name === \\\"explain-code\\\") {\\r\\n const language = request.params.arguments?.language || \\\"Unknown\\\";\\r\\n return {\\r\\n messages: [\\r\\n {\\r\\n role: \\\"user\\\",\\r\\n content: {\\r\\n type: \\\"text\\\",\\r\\n text: `Explain how this ${language} code works:\\\\n\\\\n${request.params.arguments?.code}`\\r\\n }\\r\\n }\\r\\n ]\\r\\n };\\r\\n }\\r\\n\\r\\n throw new Error(\\\"Prompt implementation not found\\\");\\r\\n });\\r\\n ```\\r\\n </Tab>\\r\\n <Tab title=\\\"Python\\\">\\r\\n ```python\\r\\n from mcp.server import Server\\r\\n import mcp.types as types\\r\\n\\r\\n # Define available prompts\\r\\n PROMPTS = {\\r\\n \\\"git-commit\\\": types.Prompt(\\r\\n name=\\\"git-commit\\\",\\r\\n description=\\\"Generate a Git commit message\\\",\\r\\n arguments=[\\r\\n types.PromptArgument(\\r\\n name=\\\"changes\\\",\\r\\n description=\\\"Git diff or description of changes\\\",\\r\\n required=True\\r\\n )\\r\\n ],\\r\\n ),\\r\\n \\\"explain-code\\\": types.Prompt(\\r\\n name=\\\"explain-code\\\",\\r\\n description=\\\"Explain how code works\\\",\\r\\n arguments=[\\r\\n types.PromptArgument(\\r\\n name=\\\"code\\\",\\r\\n description=\\\"Code to explain\\\",\\r\\n required=True\\r\\n ),\\r\\n types.PromptArgument(\\r\\n name=\\\"language\\\",\\r\\n description=\\\"Programming language\\\",\\r\\n required=False\\r\\n )\\r\\n ],\\r\\n )\\r\\n }\\r\\n\\r\\n # Initialize server\\r\\n app = Server(\\\"example-prompts-server\\\")\\r\\n\\r\\n @app.list_prompts()\\r\\n async def list_prompts() -> list[types.Prompt]:\\r\\n return list(PROMPTS.values())\\r\\n\\r\\n @app.get_prompt()\\r\\n async def get_prompt(\\r\\n name: str, arguments: dict[str, str] | None = None\\r\\n ) -> types.GetPromptResult:\\r\\n if name not in PROMPTS:\\r\\n raise ValueError(f\\\"Prompt not found: {name}\\\")\\r\\n\\r\\n if name == \\\"git-commit\\\":\\r\\n changes = arguments.get(\\\"changes\\\") if arguments else \\\"\\\"\\r\\n return types.GetPromptResult(\\r\\n messages=[\\r\\n types.PromptMessage(\\r\\n role=\\\"user\\\",\\r\\n content=types.TextContent(\\r\\n type=\\\"text\\\",\\r\\n text=f\\\"Generate a concise but descriptive commit message \\\"\\r\\n f\\\"for these changes:\\\\n\\\\n{changes}\\\"\\r\\n )\\r\\n )\\r\\n ]\\r\\n )\\r\\n\\r\\n if name == \\\"explain-code\\\":\\r\\n code = arguments.get(\\\"code\\\") if arguments else \\\"\\\"\\r\\n language = arguments.get(\\\"language\\\", \\\"Unknown\\\") if arguments else \\\"Unknown\\\"\\r\\n return types.GetPromptResult(\\r\\n messages=[\\r\\n types.PromptMessage(\\r\\n role=\\\"user\\\",\\r\\n content=types.TextContent(\\r\\n type=\\\"text\\\",\\r\\n text=f\\\"Explain how this {language} code works:\\\\n\\\\n{code}\\\"\\r\\n )\\r\\n )\\r\\n ]\\r\\n )\\r\\n\\r\\n raise ValueError(\\\"Prompt implementation not found\\\")\\r\\n ```\\r\\n </Tab>\\r\\n</Tabs>\\r\\n\\r\\n## Best practices\\r\\n\\r\\nWhen implementing prompts:\\r\\n\\r\\n1. Use clear, descriptive prompt names\\r\\n2. Provide detailed descriptions for prompts and arguments\\r\\n3. Validate all required arguments\\r\\n4. Handle missing arguments gracefully\\r\\n5. Consider versioning for prompt templates\\r\\n6. Cache dynamic content when appropriate\\r\\n7. Implement error handling\\r\\n8. Document expected argument formats\\r\\n9. Consider prompt composability\\r\\n10. Test prompts with various inputs\\r\\n\\r\\n## UI integration\\r\\n\\r\\nPrompts can be surfaced in client UIs as:\\r\\n\\r\\n- Slash commands\\r\\n- Quick actions\\r\\n- Context menu items\\r\\n- Command palette entries\\r\\n- Guided workflows\\r\\n- Interactive forms\\r\\n\\r\\n## Updates and changes\\r\\n\\r\\nServers can notify clients about prompt changes:\\r\\n\\r\\n1. Server capability: `prompts.listChanged`\\r\\n2. Notification: `notifications/prompts/list_changed`\\r\\n3. Client re-fetches prompt list\\r\\n\\r\\n## Security considerations\\r\\n\\r\\nWhen implementing prompts:\\r\\n\\r\\n- Validate all arguments\\r\\n- Sanitize user input\\r\\n- Consider rate limiting\\r\\n- Implement access controls\\r\\n- Audit prompt usage\\r\\n- Handle sensitive data appropriately\\r\\n- Validate generated content\\r\\n- Implement timeouts\\r\\n- Consider prompt injection risks\\r\\n- Document security requirements\\r\\n\",\"metadata\":{\"size\":11392,\"mimeType\":\"text/mdx\",\"modifiedTime\":\"2024-12-17T17:14:31.724Z\",\"createdTime\":\"2024-12-17T17:14:31.724Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":426,\"nonEmptyLines\":373,\"commentLines\":0,\"complexity\":17},\"dependencies\":[],\"quality\":{\"score\":-674,\"issues\":[],\"duplicateLines\":152,\"longLines\":7,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T16:59:44.467Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":426},\"analysis\":{\"metrics\":{\"lines\":426,\"nonEmptyLines\":373,\"commentLines\":0,\"complexity\":17},\"dependencies\":[],\"quality\":{\"score\":-674,\"issues\":[],\"duplicateLines\":152,\"longLines\":7,\"complexFunctions\":0}},\"lastModified\":1737046784468,\"hits\":0,\"lastAccessed\":\"2025-01-16T16:59:44.468Z\",\"size\":12990},{\"key\":\"d:\\\\projects\\\\mcp_docs\\\\docs\\\\concepts\\\\architecture.mdx\",\"content\":{\"content\":\"---\\r\\ntitle: \\\"Core architecture\\\"\\r\\ndescription: \\\"Understand how MCP connects clients, servers, and LLMs\\\"\\r\\n---\\r\\n\\r\\nThe Model Context Protocol (MCP) is built on a flexible, extensible architecture that enables seamless communication between LLM applications and integrations. This document covers the core architectural components and concepts.\\r\\n\\r\\n## Overview\\r\\n\\r\\nMCP follows a client-server architecture where:\\r\\n\\r\\n- **Hosts** are LLM applications (like Claude Desktop or IDEs) that initiate connections\\r\\n- **Clients** maintain 1:1 connections with servers, inside the host application\\r\\n- **Servers** provide context, tools, and prompts to clients\\r\\n\\r\\n```mermaid\\r\\nflowchart LR\\r\\n subgraph \\\"&nbsp;Host (e.g., Claude Desktop)&nbsp;\\\"\\r\\n client1[MCP Client]\\r\\n client2[MCP Client]\\r\\n end\\r\\n subgraph \\\"Server Process\\\"\\r\\n server1[MCP Server]\\r\\n end\\r\\n subgraph \\\"Server Process\\\"\\r\\n server2[MCP Server]\\r\\n end\\r\\n\\r\\n client1 <-->|Transport Layer| server1\\r\\n client2 <-->|Transport Layer| server2\\r\\n```\\r\\n\\r\\n## Core components\\r\\n\\r\\n### Protocol layer\\r\\n\\r\\nThe protocol layer handles message framing, request/response linking, and high-level communication patterns.\\r\\n\\r\\n<Tabs>\\r\\n <Tab title=\\\"TypeScript\\\">\\r\\n ```typescript\\r\\n class Protocol<Request, Notification, Result> {\\r\\n // Handle incoming requests\\r\\n setRequestHandler<T>(schema: T, handler: (request: T, extra: RequestHandlerExtra) => Promise<Result>): void\\r\\n\\r\\n // Handle incoming notifications\\r\\n setNotificationHandler<T>(schema: T, handler: (notification: T) => Promise<void>): void\\r\\n\\r\\n // Send requests and await responses\\r\\n request<T>(request: Request, schema: T, options?: RequestOptions): Promise<T>\\r\\n\\r\\n // Send one-way notifications\\r\\n notification(notification: Notification): Promise<void>\\r\\n }\\r\\n ```\\r\\n </Tab>\\r\\n <Tab title=\\\"Python\\\">\\r\\n ```python\\r\\n class Session(BaseSession[RequestT, NotificationT, ResultT]):\\r\\n async def send_request(\\r\\n self,\\r\\n request: RequestT,\\r\\n result_type: type[Result]\\r\\n ) -> Result:\\r\\n \\\"\\\"\\\"\\r\\n Send request and wait for response. Raises McpError if response contains error.\\r\\n \\\"\\\"\\\"\\r\\n # Request handling implementation\\r\\n\\r\\n async def send_notification(\\r\\n self,\\r\\n notification: NotificationT\\r\\n ) -> None:\\r\\n \\\"\\\"\\\"Send one-way notification that doesn't expect response.\\\"\\\"\\\"\\r\\n # Notification handling implementation\\r\\n\\r\\n async def _received_request(\\r\\n self,\\r\\n responder: RequestResponder[ReceiveRequestT, ResultT]\\r\\n ) -> None:\\r\\n \\\"\\\"\\\"Handle incoming request from other side.\\\"\\\"\\\"\\r\\n # Request handling implementation\\r\\n\\r\\n async def _received_notification(\\r\\n self,\\r\\n notification: ReceiveNotificationT\\r\\n ) -> None:\\r\\n \\\"\\\"\\\"Handle incoming notification from other side.\\\"\\\"\\\"\\r\\n # Notification handling implementation\\r\\n ```\\r\\n </Tab>\\r\\n</Tabs>\\r\\n\\r\\nKey classes include:\\r\\n\\r\\n* `Protocol`\\r\\n* `Client`\\r\\n* `Server`\\r\\n\\r\\n### Transport layer\\r\\n\\r\\nThe transport layer handles the actual communication between clients and servers. MCP supports multiple transport mechanisms:\\r\\n\\r\\n1. **Stdio transport**\\r\\n - Uses standard input/output for communication\\r\\n - Ideal for local processes\\r\\n\\r\\n2. **HTTP with SSE transport**\\r\\n - Uses Server-Sent Events for server-to-client messages\\r\\n - HTTP POST for client-to-server messages\\r\\n\\r\\nAll transports use [JSON-RPC](https://www.jsonrpc.org/) 2.0 to exchange messages. See the [specification](https://spec.modelcontextprotocol.io) for detailed information about the Model Context Protocol message format.\\r\\n\\r\\n### Message types\\r\\n\\r\\nMCP has these main types of messages:\\r\\n\\r\\n1. **Requests** expect a response from the other side:\\r\\n ```typescript\\r\\n interface Request {\\r\\n method: string;\\r\\n params?: { ... };\\r\\n }\\r\\n ```\\r\\n\\r\\n2. **Results** are successful responses to requests:\\r\\n ```typescript\\r\\n interface Result {\\r\\n [key: string]: unknown;\\r\\n }\\r\\n ```\\r\\n\\r\\n3. **Errors** indicate that a request failed:\\r\\n ```typescript\\r\\n interface Error {\\r\\n code: number;\\r\\n message: string;\\r\\n data?: unknown;\\r\\n }\\r\\n ```\\r\\n\\r\\n4. **Notifications** are one-way messages that don't expect a response:\\r\\n ```typescript\\r\\n interface Notification {\\r\\n method: string;\\r\\n params?: { ... };\\r\\n }\\r\\n ```\\r\\n\\r\\n## Connection lifecycle\\r\\n\\r\\n### 1. Initialization\\r\\n\\r\\n```mermaid\\r\\nsequenceDiagram\\r\\n participant Client\\r\\n participant Server\\r\\n\\r\\n Client->>Server: initialize request\\r\\n Server->>Client: initialize response\\r\\n Client->>Server: initialized notification\\r\\n\\r\\n Note over Client,Server: Connection ready for use\\r\\n```\\r\\n\\r\\n1. Client sends `initialize` request with protocol version and capabilities\\r\\n2. Server responds with its protocol version and capabilities\\r\\n3. Client sends `initialized` notification as acknowledgment\\r\\n4. Normal message exchange begins\\r\\n\\r\\n### 2. Message exchange\\r\\n\\r\\nAfter initialization, the following patterns are supported:\\r\\n\\r\\n- **Request-Response**: Client or server sends requests, the other responds\\r\\n- **Notifications**: Either party sends one-way messages\\r\\n\\r\\n### 3. Termination\\r\\n\\r\\nEither party can terminate the connection:\\r\\n- Clean shutdown via `close()`\\r\\n- Transport disconnection\\r\\n- Error conditions\\r\\n\\r\\n## Error handling\\r\\n\\r\\nMCP defines these standard error codes:\\r\\n\\r\\n```typescript\\r\\nenum ErrorCode {\\r\\n // Standard JSON-RPC error codes\\r\\n ParseError = -32700,\\r\\n InvalidRequest = -32600,\\r\\n MethodNotFound = -32601,\\r\\n InvalidParams = -32602,\\r\\n InternalError = -32603\\r\\n}\\r\\n```\\r\\n\\r\\nSDKs and applications can define their own error codes above -32000.\\r\\n\\r\\nErrors are propagated through:\\r\\n- Error responses to requests\\r\\n- Error events on transports\\r\\n- Protocol-level error handlers\\r\\n\\r\\n## Implementation example\\r\\n\\r\\nHere's a basic example of implementing an MCP server:\\r\\n\\r\\n<Tabs>\\r\\n <Tab title=\\\"TypeScript\\\">\\r\\n ```typescript\\r\\n import { Server } from \\\"@modelcontextprotocol/sdk/server/index.js\\\";\\r\\n import { StdioServerTransport } from \\\"@modelcontextprotocol/sdk/server/stdio.js\\\";\\r\\n\\r\\n const server = new Server({\\r\\n name: \\\"example-server\\\",\\r\\n version: \\\"1.0.0\\\"\\r\\n }, {\\r\\n capabilities: {\\r\\n resources: {}\\r\\n }\\r\\n });\\r\\n\\r\\n // Handle requests\\r\\n server.setRequestHandler(ListResourcesRequestSchema, async () => {\\r\\n return {\\r\\n resources: [\\r\\n {\\r\\n uri: \\\"example://resource\\\",\\r\\n name: \\\"Example Resource\\\"\\r\\n }\\r\\n ]\\r\\n };\\r\\n });\\r\\n\\r\\n // Connect transport\\r\\n const transport = new StdioServerTransport();\\r\\n await server.connect(transport);\\r\\n ```\\r\\n </Tab>\\r\\n <Tab title=\\\"Python\\\">\\r\\n ```python\\r\\n import asyncio\\r\\n import mcp.types as types\\r\\n from mcp.server import Server\\r\\n from mcp.server.stdio import stdio_server\\r\\n\\r\\n app = Server(\\\"example-server\\\")\\r\\n\\r\\n @app.list_resources()\\r\\n async def list_resources() -> list[types.Resource]:\\r\\n return [\\r\\n types.Resource(\\r\\n uri=\\\"example://resource\\\",\\r\\n name=\\\"Example Resource\\\"\\r\\n )\\r\\n ]\\r\\n\\r\\n async def main():\\r\\n async with stdio_server() as streams:\\r\\n await app.run(\\r\\n streams[0],\\r\\n streams[1],\\r\\n app.create_initialization_options()\\r\\n )\\r\\n\\r\\n if __name__ == \\\"__main__\\\":\\r\\n asyncio.run(main)\\r\\n ```\\r\\n </Tab>\\r\\n</Tabs>\\r\\n\\r\\n## Best practices\\r\\n\\r\\n### Transport selection\\r\\n\\r\\n1. **Local communication**\\r\\n - Use stdio transport for local processes\\r\\n - Efficient for same-machine communication\\r\\n - Simple process management\\r\\n\\r\\n2. **Remote communication**\\r\\n - Use SSE for scenarios requiring HTTP compatibility\\r\\n - Consider security implications including authentication and authorization\\r\\n\\r\\n### Message handling\\r\\n\\r\\n1. **Request processing**\\r\\n - Validate inputs thoroughly\\r\\n - Use type-safe schemas\\r\\n - Handle errors gracefully\\r\\n - Implement timeouts\\r\\n\\r\\n2. **Progress reporting**\\r\\n - Use progress tokens for long operations\\r\\n - Report progress incrementally\\r\\n - Include total progress when known\\r\\n\\r\\n3. **Error management**\\r\\n - Use appropriate error codes\\r\\n - Include helpful error messages\\r\\n - Clean up resources on errors\\r\\n\\r\\n## Security considerations\\r\\n\\r\\n1. **Transport security**\\r\\n - Use TLS for remote connections\\r\\n - Validate connection origins\\r\\n - Implement authentication when needed\\r\\n\\r\\n2. **Message validation**\\r\\n - Validate all incoming messages\\r\\n - Sanitize inputs\\r\\n - Check message size limits\\r\\n - Verify JSON-RPC format\\r\\n\\r\\n3. **Resource protection**\\r\\n - Implement access controls\\r\\n - Validate resource paths\\r\\n - Monitor resource usage\\r\\n - Rate limit requests\\r\\n\\r\\n4. **Error handling**\\r\\n - Don't leak sensitive information\\r\\n - Log security-relevant errors\\r\\n - Implement proper cleanup\\r\\n - Handle DoS scenarios\\r\\n\\r\\n## Debugging and monitoring\\r\\n\\r\\n1. **Logging**\\r\\n - Log protocol events\\r\\n - Track message flow\\r\\n - Monitor performance\\r\\n - Record errors\\r\\n\\r\\n2. **Diagnostics**\\r\\n - Implement health checks\\r\\n - Monitor connection state\\r\\n - Track resource usage\\r\\n - Profile performance\\r\\n\\r\\n3. **Testing**\\r\\n - Test different transports\\r\\n - Verify error handling\\r\\n - Check edge cases\\r\\n - Load test servers\\r\\n\",\"metadata\":{\"size\":9391,\"mimeType\":\"text/mdx\",\"modifiedTime\":\"2024-12-17T17:14:31.724Z\",\"createdTime\":\"2024-12-17T17:14:31.724Z\",\"isDirectory\":false,\"analysis\":{\"metrics\":{\"lines\":350,\"nonEmptyLines\":277,\"commentLines\":0,\"complexity\":5},\"dependencies\":[],\"quality\":{\"score\":-155,\"issues\":[],\"duplicateLines\":49,\"longLines\":5,\"complexFunctions\":0}},\"lastAnalyzed\":\"2025-01-16T16:59:44.460Z\"},\"encoding\":\"utf8\",\"truncated\":false,\"totalLines\":350},\"analysis\":{\"metrics\":{\"lines\":350,\"nonEmptyLines\":277,\"commentLines\":0,\"complexity\":5},\"dependencies\":[],\"quality\":{\"score\":-155,\"issues\":[],\"duplicateLines\":49,\"longLines\":5,\"complexFunctions\":0}},\"lastModified\":1737046784465,\"hits\":0,\"lastAccessed\":\"2025-01-16T16:59:44.465Z\",\"size\":10609}]","metadata":{"size":0,"mimeType":"application/json","modifiedTime":"2025-01-16T17:01:47.362Z","createdTime":"2025-01-16T17:01:47.362Z","isDirectory":false},"encoding":"utf8","truncated":false,"totalLines":0},"size":0,"hits":0},"timestamp":1737046907362}

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/bsmi021/mcp-file-context-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server