From 82eba4d66822097b85a4214da8bd23f16af9e488 Mon Sep 17 00:00:00 2001 From: supermomonga Date: Mon, 10 Feb 2025 17:13:48 +0900 Subject: [PATCH 001/100] Add extension setting for chromium executable path (#1721) * Add extension setting for chromium executable path * apply changeset * Update src/services/browser/BrowserSession.ts Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * format code --------- Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> --- .changeset/poor-cobras-destroy.md | 5 +++++ package.json | 5 +++++ src/services/browser/BrowserSession.ts | 13 ++++++++----- 3 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 .changeset/poor-cobras-destroy.md diff --git a/.changeset/poor-cobras-destroy.md b/.changeset/poor-cobras-destroy.md new file mode 100644 index 0000000000..991c7324cc --- /dev/null +++ b/.changeset/poor-cobras-destroy.md @@ -0,0 +1,5 @@ +--- +"claude-dev": minor +--- + +Add extension setting for chromium executable path diff --git a/package.json b/package.json index 7276e45b8d..898e2b9edf 100644 --- a/package.json +++ b/package.json @@ -171,6 +171,11 @@ ], "default": "medium", "description": "Controls the reasoning effort when using the o3-mini model. Higher values may result in more thorough but slower responses." + }, + "cline.chromeExecutablePath": { + "type": "string", + "default": null, + "description": "Path to Chrome executable for browser use functionality. If not set, the extension will attempt to find or download it automatically." } } } diff --git a/src/services/browser/BrowserSession.ts b/src/services/browser/BrowserSession.ts index cbc7734d36..14b6ecef8b 100644 --- a/src/services/browser/BrowserSession.ts +++ b/src/services/browser/BrowserSession.ts @@ -42,11 +42,14 @@ export class BrowserSession { await fs.mkdir(puppeteerDir, { recursive: true }) } - // if chromium doesn't exist, this will download it to path.join(puppeteerDir, ".chromium-browser-snapshots") - // if it does exist it will return the path to existing chromium - const stats: PCRStats = await PCR({ - downloadPath: puppeteerDir, - }) + const chromeExecutablePath = vscode.workspace.getConfiguration("cline").get("chromeExecutablePath") + if (chromeExecutablePath && !(await fileExistsAtPath(chromeExecutablePath))) + throw new Error(`Chrome executable not found at path: ${chromeExecutablePath}`) + const stats: PCRStats = chromeExecutablePath + ? { puppeteer: require("puppeteer-core"), executablePath: chromeExecutablePath } + : // if chromium doesn't exist, this will download it to path.join(puppeteerDir, ".chromium-browser-snapshots") + // if it does exist it will return the path to existing chromium + await PCR({ downloadPath: puppeteerDir }) return stats } From 7b6a3d25e5711e935ba2c611c7a5f9e1bc56ba53 Mon Sep 17 00:00:00 2001 From: brownrw8 Date: Mon, 10 Feb 2025 18:02:00 -1000 Subject: [PATCH 002/100] Advanced Setting to enable browser session (#1736) * feat: add Advanced Setting to enable browser session * ensure browser tool is removed from system prompt * fix test * Update BrowserSession.ts * Update BrowserSession.ts --------- Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> --- .changeset/long-masks-notice.md | 5 +++++ package.json | 5 +++++ src/core/Cline.ts | 12 ++++++------ src/test/suite/extension.test.js | 10 ++++++++++ 4 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 .changeset/long-masks-notice.md diff --git a/.changeset/long-masks-notice.md b/.changeset/long-masks-notice.md new file mode 100644 index 0000000000..76079fbed6 --- /dev/null +++ b/.changeset/long-masks-notice.md @@ -0,0 +1,5 @@ +--- +"claude-dev": minor +--- + +Advanced Setting to disable browser tool diff --git a/package.json b/package.json index 898e2b9edf..f78ffd3870 100644 --- a/package.json +++ b/package.json @@ -162,6 +162,11 @@ "default": true, "description": "Enables extension to save checkpoints of workspace throughout the task." }, + "cline.disableBrowserTool": { + "type": "boolean", + "default": false, + "description": "Disables extension from spawning browser session." + }, "cline.modelSettings.o3Mini.reasoningEffort": { "type": "string", "enum": [ diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 7d40127edf..127960f073 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -1259,12 +1259,12 @@ export class Cline { throw new Error("MCP hub not available") } - let systemPrompt = await SYSTEM_PROMPT( - cwd, - this.api.getModel().info.supportsComputerUse ?? false, - mcpHub, - this.browserSettings, - ) + const disableBrowserTool = vscode.workspace.getConfiguration("cline").get("disableBrowserTool") ?? false + const modelSupportsComputerUse = this.api.getModel().info.supportsComputerUse ?? false + + const supportsComputerUse = modelSupportsComputerUse && !disableBrowserTool // only enable computer use if the model supports it and the user hasn't disabled it + + let systemPrompt = await SYSTEM_PROMPT(cwd, supportsComputerUse, mcpHub, this.browserSettings) let settingsCustomInstructions = this.customInstructions?.trim() const clineRulesFilePath = path.resolve(cwd, GlobalFileNames.clineRules) diff --git a/src/test/suite/extension.test.js b/src/test/suite/extension.test.js index f9d3305db0..a450fafa2a 100644 --- a/src/test/suite/extension.test.js +++ b/src/test/suite/extension.test.js @@ -34,4 +34,14 @@ describe("Extension Tests", function () { await vscode.commands.executeCommand("cline.historyButtonClicked") // Success if no error thrown }) + + it("should handle advanced settings configuration", async () => { + // Test browser session setting + await vscode.workspace.getConfiguration().update("cline.disableBrowserTool", true, true) + const updatedConfig = vscode.workspace.getConfiguration("cline") + expect(updatedConfig.get("disableBrowserTool")).to.be.true + + // Reset settings + await vscode.workspace.getConfiguration().update("cline.disableBrowserTool", undefined, true) + }) }) From e8a2e88fa0ec601d7105d2da3c49e3fd778186aa Mon Sep 17 00:00:00 2001 From: Waylon <63188898+abandon-jw3@users.noreply.github.com> Date: Tue, 11 Feb 2025 12:04:16 +0800 Subject: [PATCH 003/100] feat: qwen platform adds deepseek-r1/v3 support (#1729) Co-authored-by: fine --- .changeset/stale-lizards-poke.md | 5 +++++ src/api/providers/qwen.ts | 7 ++++++- src/shared/api.ts | 20 ++++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 .changeset/stale-lizards-poke.md diff --git a/.changeset/stale-lizards-poke.md b/.changeset/stale-lizards-poke.md new file mode 100644 index 0000000000..40e3308e8b --- /dev/null +++ b/.changeset/stale-lizards-poke.md @@ -0,0 +1,5 @@ +--- +"claude-dev": minor +--- + +qwen platform adds deepseek-r1/v3 support diff --git a/src/api/providers/qwen.ts b/src/api/providers/qwen.ts index aa4138d09c..26ca03a898 100644 --- a/src/api/providers/qwen.ts +++ b/src/api/providers/qwen.ts @@ -4,6 +4,7 @@ import { ApiHandler } from "../" import { ApiHandlerOptions, QwenModelId, ModelInfo, qwenDefaultModelId, qwenModels } from "../../shared/api" import { convertToOpenAiMessages } from "../transform/openai-format" import { ApiStream } from "../transform/stream" +import { convertToR1Format } from "../transform/r1-format" export class QwenHandler implements ApiHandler { private options: ApiHandlerOptions @@ -34,17 +35,21 @@ export class QwenHandler implements ApiHandler { async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { const model = this.getModel() + const isDeepseekReasoner = model.id.includes("deepseek-r1") let openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [ { role: "system", content: systemPrompt }, ...convertToOpenAiMessages(messages), ] - + if (isDeepseekReasoner) { + openAiMessages = convertToR1Format([{ role: "user", content: systemPrompt }, ...messages]) + } const stream = await this.client.chat.completions.create({ model: model.id, max_completion_tokens: model.info.maxTokens, messages: openAiMessages, stream: true, stream_options: { include_usage: true }, + ...(model.id === "deepseek-r1" ? {} : { temperature: 0 }), }) for await (const chunk of stream) { diff --git a/src/shared/api.ts b/src/shared/api.ts index 748fb54e09..1297e6b155 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -529,6 +529,26 @@ export const qwenModels = { cacheWritesPrice: 0.0056, cacheReadsPrice: 0.0224, }, + "deepseek-v3": { + maxTokens: 8_000, + contextWindow: 64_000, + supportsImages: false, + supportsPromptCache: true, + inputPrice: 0, + outputPrice: 0.28, + cacheWritesPrice: 0.14, + cacheReadsPrice: 0.014, + }, + "deepseek-r1": { + maxTokens: 8_000, + contextWindow: 64_000, + supportsImages: false, + supportsPromptCache: true, + inputPrice: 0, + outputPrice: 2.19, + cacheWritesPrice: 0.55, + cacheReadsPrice: 0.14, + }, } as const satisfies Record // Mistral From 57ae7c05d665189830b5f8fb48bf490509396dd9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 20:04:47 -0800 Subject: [PATCH 004/100] Bump esbuild from 0.21.5 to 0.25.0 in the npm_and_yarn group (#1740) Bumps the npm_and_yarn group with 1 update: [esbuild](https://github.com/evanw/esbuild). Updates `esbuild` from 0.21.5 to 0.25.0 - [Release notes](https://github.com/evanw/esbuild/releases) - [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG-2024.md) - [Commits](https://github.com/evanw/esbuild/compare/v0.21.5...v0.25.0) --- updated-dependencies: - dependency-name: esbuild dependency-type: direct:development dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 478 ++++++++++++++++++++++++++++++++++++++++++---- package.json | 2 +- 2 files changed, 445 insertions(+), 35 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3591d6bd3b..7a4b80d77b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "claude-dev", - "version": "3.3.1", + "version": "3.3.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "claude-dev", - "version": "3.3.1", + "version": "3.3.2", "license": "Apache-2.0", "dependencies": { "@anthropic-ai/bedrock-sdk": "^0.10.2", @@ -63,7 +63,7 @@ "@vscode/test-cli": "^0.0.9", "@vscode/test-electron": "^2.4.0", "chai": "^4.3.10", - "esbuild": "^0.21.5", + "esbuild": "^0.25.0", "eslint": "^8.57.0", "husky": "^9.1.7", "npm-run-all": "^4.1.5", @@ -2533,10 +2533,78 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", + "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", + "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", + "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", + "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", + "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", "cpu": [ "arm64" ], @@ -2547,7 +2615,347 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", + "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", + "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", + "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", + "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", + "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", + "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", + "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", + "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", + "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", + "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", + "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", + "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", + "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", + "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", + "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", + "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", + "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", + "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", + "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", + "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { @@ -7472,9 +7880,9 @@ } }, "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", + "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -7482,32 +7890,34 @@ "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" + "@esbuild/aix-ppc64": "0.25.0", + "@esbuild/android-arm": "0.25.0", + "@esbuild/android-arm64": "0.25.0", + "@esbuild/android-x64": "0.25.0", + "@esbuild/darwin-arm64": "0.25.0", + "@esbuild/darwin-x64": "0.25.0", + "@esbuild/freebsd-arm64": "0.25.0", + "@esbuild/freebsd-x64": "0.25.0", + "@esbuild/linux-arm": "0.25.0", + "@esbuild/linux-arm64": "0.25.0", + "@esbuild/linux-ia32": "0.25.0", + "@esbuild/linux-loong64": "0.25.0", + "@esbuild/linux-mips64el": "0.25.0", + "@esbuild/linux-ppc64": "0.25.0", + "@esbuild/linux-riscv64": "0.25.0", + "@esbuild/linux-s390x": "0.25.0", + "@esbuild/linux-x64": "0.25.0", + "@esbuild/netbsd-arm64": "0.25.0", + "@esbuild/netbsd-x64": "0.25.0", + "@esbuild/openbsd-arm64": "0.25.0", + "@esbuild/openbsd-x64": "0.25.0", + "@esbuild/sunos-x64": "0.25.0", + "@esbuild/win32-arm64": "0.25.0", + "@esbuild/win32-ia32": "0.25.0", + "@esbuild/win32-x64": "0.25.0" } }, "node_modules/escalade": { diff --git a/package.json b/package.json index f78ffd3870..0c9e7e41b0 100644 --- a/package.json +++ b/package.json @@ -223,7 +223,7 @@ "@vscode/test-cli": "^0.0.9", "@vscode/test-electron": "^2.4.0", "chai": "^4.3.10", - "esbuild": "^0.21.5", + "esbuild": "^0.25.0", "eslint": "^8.57.0", "husky": "^9.1.7", "npm-run-all": "^4.1.5", From 9c8254cf9251f2f497e0512afad01d46e2cba253 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Mon, 10 Feb 2025 20:29:41 -0800 Subject: [PATCH 005/100] Revert context progress bar removal --- webview-ui/src/components/chat/TaskHeader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webview-ui/src/components/chat/TaskHeader.tsx b/webview-ui/src/components/chat/TaskHeader.tsx index f6b245d5f6..92e44e2e4d 100644 --- a/webview-ui/src/components/chat/TaskHeader.tsx +++ b/webview-ui/src/components/chat/TaskHeader.tsx @@ -422,7 +422,7 @@ const TaskHeader: React.FC = ({ )} - {/* {ContextWindowComponent} */} + {ContextWindowComponent} {isCostAvailable && (
Date: Tue, 11 Feb 2025 04:26:35 -0600 Subject: [PATCH 006/100] feat: Add SendMessage capability during Mode Switch --- src/core/Cline.ts | 5 ++-- src/core/webview/ClineProvider.ts | 3 ++- src/shared/ChatContent.ts | 4 +++ src/shared/WebviewMessage.ts | 2 ++ src/test/webview/chat-native.test.ts | 27 ++++++++++++++++++- .../src/components/chat/ChatTextArea.tsx | 6 ++++- 6 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 src/shared/ChatContent.ts diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 127960f073..e088f0073b 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -2707,14 +2707,15 @@ export class Cline { this.isAwaitingPlanResponse = false if (this.didRespondToPlanAskBySwitchingMode) { - // await this.say("user_feedback", text ?? "", images) pushToolResult( formatResponse.toolResult( `[The user has switched to ACT MODE, so you may now proceed with the task.]`, images, ), ) - } else { + } + + if (text) { await this.say("user_feedback", text ?? "", images) pushToolResult(formatResponse.toolResult(`\n${text}\n`, images)) } diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 397e11eb80..6fb3f415ab 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -618,7 +618,8 @@ export class ClineProvider implements vscode.WebviewViewProvider { await this.postMessageToWebview({ type: "invoke", invoke: "sendMessage", - text: "[Proceeding with the task...]", + text: message.chatContent?.message || "[Proceeding with the task...]", + images: message.chatContent?.images, }) } else { this.cancelTask() diff --git a/src/shared/ChatContent.ts b/src/shared/ChatContent.ts new file mode 100644 index 0000000000..fe209de363 --- /dev/null +++ b/src/shared/ChatContent.ts @@ -0,0 +1,4 @@ +export interface ChatContent { + message?: string + images?: string[] +} diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index a18a3c405a..447193dd18 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -2,6 +2,7 @@ import { ApiConfiguration } from "./api" import { AutoApprovalSettings } from "./AutoApprovalSettings" import { BrowserSettings } from "./BrowserSettings" import { ChatSettings } from "./ChatSettings" +import { ChatContent } from "./ChatContent" export interface WebviewMessage { type: @@ -53,6 +54,7 @@ export interface WebviewMessage { autoApprovalSettings?: AutoApprovalSettings browserSettings?: BrowserSettings chatSettings?: ChatSettings + chatContent?: ChatContent // For toggleToolAutoApprove serverName?: string diff --git a/src/test/webview/chat-native.test.ts b/src/test/webview/chat-native.test.ts index 775af4b7ae..d3fe630a94 100644 --- a/src/test/webview/chat-native.test.ts +++ b/src/test/webview/chat-native.test.ts @@ -28,7 +28,13 @@ describe("Chat Integration Tests", () => { vscode.postMessage({ type: 'newTask', text: message.text }); break; case 'toggleMode': - vscode.postMessage({ type: 'chatSettings', chatSettings: { mode: 'act' } }); + vscode.postMessage({ + type: 'chatSettings', + chatSettings: { mode: 'act' }, + chatContent: { + message: "message test", + } + }); break; case 'invoke': if (message.invoke === 'primaryButtonClick') { @@ -92,6 +98,25 @@ describe("Chat Integration Tests", () => { assert.equal(stateChange.chatSettings.mode, "act") }) + it("should toggle between plan and act modes with messages", async () => { + // Set up state change listener + const stateChangePromise = new Promise((resolve) => { + panel.webview.onDidReceiveMessage((message) => { + if (message.type === "chatSettings") { + resolve(message) + } + }) + }) + + // Trigger mode toggle + await panel.webview.postMessage({ type: "toggleMode" }) + + // Verify mode changed + const stateChange = await stateChangePromise + assert.equal(stateChange.chatSettings.mode, "act") + assert.equal(stateChange.chatContent.message, "message test") + }) + it("should handle tool approval flow", async () => { // Set up approval listener const approvalPromise = new Promise((resolve) => { diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index 0255ef0301..e86697405f 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -616,13 +616,17 @@ const ChatTextArea = forwardRef( chatSettings: { mode: newMode, }, + chatContent: { + message: inputValue.trim() ? inputValue : undefined, + images: selectedImages.length > 0 ? selectedImages : undefined, + }, }) // Focus the textarea after mode toggle with slight delay setTimeout(() => { textAreaRef.current?.focus() }, 100) }, changeModeDelay) - }, [chatSettings.mode, showModelSelector, submitApiConfig]) + }, [chatSettings.mode, showModelSelector, submitApiConfig, inputValue, selectedImages]) useShortcut("Meta+Shift+a", onModeToggle, { disableTextInputs: false }) // important that we don't disable the text input here From 8793c2c6c59713775bd07be36865453d69935d2c Mon Sep 17 00:00:00 2001 From: Dennis Bartlett Date: Tue, 11 Feb 2025 04:29:09 -0600 Subject: [PATCH 007/100] fix: ESLint issue in BrowserSession --- src/services/browser/BrowserSession.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/browser/BrowserSession.ts b/src/services/browser/BrowserSession.ts index 14b6ecef8b..183979d92a 100644 --- a/src/services/browser/BrowserSession.ts +++ b/src/services/browser/BrowserSession.ts @@ -43,8 +43,9 @@ export class BrowserSession { } const chromeExecutablePath = vscode.workspace.getConfiguration("cline").get("chromeExecutablePath") - if (chromeExecutablePath && !(await fileExistsAtPath(chromeExecutablePath))) + if (chromeExecutablePath && !(await fileExistsAtPath(chromeExecutablePath))) { throw new Error(`Chrome executable not found at path: ${chromeExecutablePath}`) + } const stats: PCRStats = chromeExecutablePath ? { puppeteer: require("puppeteer-core"), executablePath: chromeExecutablePath } : // if chromium doesn't exist, this will download it to path.join(puppeteerDir, ".chromium-browser-snapshots") From 2a894d8cf14b1702765e4ce4d884ed084b502a51 Mon Sep 17 00:00:00 2001 From: Dennis Bartlett Date: Tue, 11 Feb 2025 04:34:43 -0600 Subject: [PATCH 008/100] Add Changeset --- .changeset/silly-cats-appear.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/silly-cats-appear.md diff --git a/.changeset/silly-cats-appear.md b/.changeset/silly-cats-appear.md new file mode 100644 index 0000000000..6e6925a4c9 --- /dev/null +++ b/.changeset/silly-cats-appear.md @@ -0,0 +1,5 @@ +--- +"claude-dev": minor +--- + +Add new ability to send message that is in Input field during Plan/Act Mode Change to Act. From 7da74bd9d8b2f716c13ac6902b3902e3477f86db Mon Sep 17 00:00:00 2001 From: Evan Date: Tue, 11 Feb 2025 13:20:54 -0800 Subject: [PATCH 009/100] add accept feedback --- src/core/Cline.ts | 13 +- src/core/prompts/responses.ts | 3 + webview-ui/src/components/chat/ChatView.tsx | 158 ++++++++++++-------- 3 files changed, 110 insertions(+), 64 deletions(-) diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 7d40127edf..faa04cf110 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -1542,7 +1542,9 @@ export class Cline { const askApproval = async (type: ClineAsk, partialMessage?: string) => { const { response, text, images } = await this.ask(type, partialMessage, false) if (response !== "yesButtonClicked") { + // User did NOT approve (rejected) if (response === "messageResponse") { + // Rejection WITH feedback await this.say("user_feedback", text, images) pushToolResult(formatResponse.toolResult(formatResponse.toolDeniedWithFeedback(text), images)) // this.userMessageContent.push({ @@ -1560,15 +1562,24 @@ export class Cline { this.didRejectTool = true return false } + // Rejection WITHOUT explicit feedback pushToolResult(formatResponse.toolDenied()) // this.toolResults.push({ // type: "tool_result", // tool_use_id: toolUseId, // content: await this.formatToolDenied(), // }) - this.didRejectTool = true + this.didRejectTool = true // Prevent further tool uses in this message return false } + + // Handle yesButtonClicked with text (Acceptance WITH feedback) + if (text) { + await this.say("user_feedback", text, images) + pushToolResult(formatResponse.toolResult(formatResponse.toolApprovedWithFeedback(text), images)) // Structured feedback to model on approval + } + + // User approved without feedback return true } diff --git a/src/core/prompts/responses.ts b/src/core/prompts/responses.ts index 623e3d8806..8a14acc02b 100644 --- a/src/core/prompts/responses.ts +++ b/src/core/prompts/responses.ts @@ -9,6 +9,9 @@ export const formatResponse = { toolDeniedWithFeedback: (feedback?: string) => `The user denied this operation and provided the following feedback:\n\n${feedback}\n`, + toolApprovedWithFeedback: (feedback?: string) => + `The user approved this operation and provided the following feedback:\n\n${feedback}\n`, + toolError: (error?: string) => `The tool execution failed with the following error:\n\n${error}\n`, clineIgnoreError: (path: string) => diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index cad6e84aff..8b4290cd2a 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -324,67 +324,99 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie /* This logic depends on the useEffect[messages] above to set clineAsk, after which buttons are shown and we then send an askResponse to the extension. */ - const handlePrimaryButtonClick = useCallback(() => { - switch (clineAsk) { - case "api_req_failed": - case "command": - case "command_output": - case "tool": - case "browser_action_launch": - case "use_mcp_server": - case "resume_task": - case "mistake_limit_reached": - case "auto_approval_max_req_reached": - vscode.postMessage({ - type: "askResponse", - askResponse: "yesButtonClicked", - }) - break - case "completion_result": - case "resume_completed_task": - // extension waiting for feedback. but we can just present a new task button - startNewTask() - break - } - setTextAreaDisabled(true) - setClineAsk(undefined) - setEnableButtons(false) - // setPrimaryButtonText(undefined) - // setSecondaryButtonText(undefined) - disableAutoScrollRef.current = false - }, [clineAsk, startNewTask]) + const handlePrimaryButtonClick = useCallback( + (text?: string, images?: string[]) => { + const trimmedInput = text?.trim() + switch (clineAsk) { + case "api_req_failed": + case "command": + case "command_output": + case "tool": + case "browser_action_launch": + case "use_mcp_server": + case "resume_task": + case "mistake_limit_reached": + case "auto_approval_max_req_reached": + if (trimmedInput || (images && images.length > 0)) { + vscode.postMessage({ + type: "askResponse", + askResponse: "yesButtonClicked", + text: trimmedInput, + images: images, + }) + } else { + vscode.postMessage({ + type: "askResponse", + askResponse: "yesButtonClicked", + }) + } + // Clear input state after sending + setInputValue("") + setSelectedImages([]) + break + case "completion_result": + case "resume_completed_task": + // extension waiting for feedback. but we can just present a new task button + startNewTask() + break + } + setTextAreaDisabled(true) + setClineAsk(undefined) + setEnableButtons(false) + // setPrimaryButtonText(undefined) + // setSecondaryButtonText(undefined) + disableAutoScrollRef.current = false + }, + [clineAsk, startNewTask], + ) - const handleSecondaryButtonClick = useCallback(() => { - if (isStreaming) { - vscode.postMessage({ type: "cancelTask" }) - setDidClickCancel(true) - return - } + const handleSecondaryButtonClick = useCallback( + (text?: string, images?: string[]) => { + const trimmedInput = text?.trim() + if (isStreaming) { + vscode.postMessage({ type: "cancelTask" }) + setDidClickCancel(true) + return + } - switch (clineAsk) { - case "api_req_failed": - case "mistake_limit_reached": - case "auto_approval_max_req_reached": - startNewTask() - break - case "command": - case "tool": - case "browser_action_launch": - case "use_mcp_server": - // responds to the API with a "This operation failed" and lets it try again - vscode.postMessage({ - type: "askResponse", - askResponse: "noButtonClicked", - }) - break - } - setTextAreaDisabled(true) - setClineAsk(undefined) - setEnableButtons(false) - // setPrimaryButtonText(undefined) - // setSecondaryButtonText(undefined) - disableAutoScrollRef.current = false - }, [clineAsk, startNewTask, isStreaming]) + switch (clineAsk) { + case "api_req_failed": + case "mistake_limit_reached": + case "auto_approval_max_req_reached": + startNewTask() + break + case "command": + case "tool": + case "browser_action_launch": + case "use_mcp_server": + if (trimmedInput || (images && images.length > 0)) { + vscode.postMessage({ + type: "askResponse", + askResponse: "noButtonClicked", + text: trimmedInput, + images: images, + }) + } else { + // responds to the API with a "This operation failed" and lets it try again + vscode.postMessage({ + type: "askResponse", + askResponse: "noButtonClicked", + }) + } + // Clear input state after sending + setInputValue("") + setSelectedImages([]) + break + } + setTextAreaDisabled(true) + setClineAsk(undefined) + setEnableButtons(false) + // setPrimaryButtonText(undefined) + // setSecondaryButtonText(undefined) + disableAutoScrollRef.current = false + }, + [clineAsk, startNewTask, isStreaming], + ) const handleTaskCloseButtonClick = useCallback(() => { startNewTask() @@ -426,10 +458,10 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie handleSendMessage(message.text ?? "", message.images ?? []) break case "primaryButtonClick": - handlePrimaryButtonClick() + handlePrimaryButtonClick(message.text ?? "", message.images ?? []) break case "secondaryButtonClick": - handleSecondaryButtonClick() + handleSecondaryButtonClick(message.text ?? "", message.images ?? []) break } } @@ -869,7 +901,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie flex: secondaryButtonText ? 1 : 2, marginRight: secondaryButtonText ? "6px" : "0", }} - onClick={handlePrimaryButtonClick}> + onClick={() => handlePrimaryButtonClick(inputValue, selectedImages)}> {primaryButtonText} )} @@ -881,7 +913,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie flex: isStreaming ? 2 : 1, marginLeft: isStreaming ? 0 : "6px", }} - onClick={handleSecondaryButtonClick}> + onClick={() => handleSecondaryButtonClick(inputValue, selectedImages)}> {isStreaming ? "Cancel" : secondaryButtonText} )} From 96540ddf1e64b7fccf708014be95640e4c0e47ef Mon Sep 17 00:00:00 2001 From: Evan Date: Tue, 11 Feb 2025 13:21:22 -0800 Subject: [PATCH 010/100] fix linting error --- src/services/browser/BrowserSession.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/browser/BrowserSession.ts b/src/services/browser/BrowserSession.ts index 14b6ecef8b..183979d92a 100644 --- a/src/services/browser/BrowserSession.ts +++ b/src/services/browser/BrowserSession.ts @@ -43,8 +43,9 @@ export class BrowserSession { } const chromeExecutablePath = vscode.workspace.getConfiguration("cline").get("chromeExecutablePath") - if (chromeExecutablePath && !(await fileExistsAtPath(chromeExecutablePath))) + if (chromeExecutablePath && !(await fileExistsAtPath(chromeExecutablePath))) { throw new Error(`Chrome executable not found at path: ${chromeExecutablePath}`) + } const stats: PCRStats = chromeExecutablePath ? { puppeteer: require("puppeteer-core"), executablePath: chromeExecutablePath } : // if chromium doesn't exist, this will download it to path.join(puppeteerDir, ".chromium-browser-snapshots") From f096d73813a947e94cb81e9424b01ff91d5a7747 Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 12 Feb 2025 08:46:23 -0800 Subject: [PATCH 011/100] approve with feedback for write_to_file/replace_in_file --- src/core/Cline.ts | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/core/Cline.ts b/src/core/Cline.ts index faa04cf110..4a6609d84c 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -1547,28 +1547,13 @@ export class Cline { // Rejection WITH feedback await this.say("user_feedback", text, images) pushToolResult(formatResponse.toolResult(formatResponse.toolDeniedWithFeedback(text), images)) - // this.userMessageContent.push({ - // type: "text", - // text: `${toolDescription()}`, - // }) - // this.toolResults.push({ - // type: "tool_result", - // tool_use_id: toolUseId, - // content: this.formatToolResponseWithImages( - // await this.formatToolDeniedFeedback(text), - // images - // ), - // }) + this.didRejectTool = true return false } // Rejection WITHOUT explicit feedback pushToolResult(formatResponse.toolDenied()) - // this.toolResults.push({ - // type: "tool_result", - // tool_use_id: toolUseId, - // content: await this.formatToolDenied(), - // }) + this.didRejectTool = true // Prevent further tool uses in this message return false } @@ -1819,11 +1804,14 @@ export class Cline { let didApprove = true const { response, text, images } = await this.ask("tool", completeMessage, false) if (response !== "yesButtonClicked") { + // User did NOT approve (rejected) + // TODO: add similar context for other tool denial responses, to emphasize ie that a command was not run const fileDeniedNote = fileExists ? "The file was not updated, and maintains its original contents." : "The file was not created." if (response === "messageResponse") { + // Rejection WITH feedback await this.say("user_feedback", text, images) pushToolResult( formatResponse.toolResult( @@ -1838,6 +1826,16 @@ export class Cline { this.didRejectTool = true didApprove = false } + } else { + // User approved + + // Handle yesButtonClicked with text (Acceptance WITH feedback) + if (text) { + await this.say("user_feedback", text, images) + pushToolResult( + formatResponse.toolResult(formatResponse.toolApprovedWithFeedback(text), images), + ) + } } if (!didApprove) { From 69ff71b051f1d967abf5cd05ec54a4aa2ac3a5c5 Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 12 Feb 2025 08:56:29 -0800 Subject: [PATCH 012/100] add changeset --- .changeset/wise-phones-mate.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/wise-phones-mate.md diff --git a/.changeset/wise-phones-mate.md b/.changeset/wise-phones-mate.md new file mode 100644 index 0000000000..7be57b25e9 --- /dev/null +++ b/.changeset/wise-phones-mate.md @@ -0,0 +1,5 @@ +--- +"claude-dev": minor +--- + +Allowing the user to give feedback when approving a tool use. From e534c3dbc7b6417ae07954795198baf34ccb2449 Mon Sep 17 00:00:00 2001 From: Sanjaykumar S <45158568+SSK-14@users.noreply.github.com> Date: Thu, 13 Feb 2025 00:26:13 +0530 Subject: [PATCH 013/100] Add api key for litellm api provider #1766 (#1767) * Add api key for litellm api provider * Added Changeset * Update litellm.ts * Update ApiOptions.tsx --------- Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> --- .changeset/slimy-toes-wait.md | 5 +++++ src/api/providers/litellm.ts | 2 +- src/core/webview/ClineProvider.ts | 19 +++++++++++++------ src/shared/api.ts | 1 + .../src/components/settings/ApiOptions.tsx | 8 ++++++++ .../src/context/ExtensionStateContext.tsx | 1 + 6 files changed, 29 insertions(+), 7 deletions(-) create mode 100644 .changeset/slimy-toes-wait.md diff --git a/.changeset/slimy-toes-wait.md b/.changeset/slimy-toes-wait.md new file mode 100644 index 0000000000..059978604f --- /dev/null +++ b/.changeset/slimy-toes-wait.md @@ -0,0 +1,5 @@ +--- +"claude-dev": minor +--- + +Added api key field for litellm api provider in settings diff --git a/src/api/providers/litellm.ts b/src/api/providers/litellm.ts index 80ad5e2c75..2e69c43880 100644 --- a/src/api/providers/litellm.ts +++ b/src/api/providers/litellm.ts @@ -13,7 +13,7 @@ export class LiteLlmHandler implements ApiHandler { this.options = options this.client = new OpenAI({ baseURL: this.options.liteLlmBaseUrl || "http://localhost:4000", - apiKey: "not-needed", + apiKey: this.options.liteLlmApiKey || "noop", }) } diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 397e11eb80..1977dfaf99 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -49,6 +49,7 @@ type SecretKey = | "togetherApiKey" | "qwenApiKey" | "mistralApiKey" + | "liteLlmApiKey" | "authToken" | "authNonce" type GlobalStateKey = @@ -334,15 +335,15 @@ export class ClineProvider implements vscode.WebviewViewProvider { // Use a nonce to only allow a specific script to be run. /* - content security policy of your webview to only allow scripts that have a specific nonce - create a content security policy meta tag so that only loading scripts with a nonce is allowed - 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. - + content security policy of your webview to only allow scripts that have a specific nonce + create a content security policy meta tag so that only loading scripts with a nonce is allowed + 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. + - 'unsafe-inline' is required for styles due to vscode-webview-toolkit's dynamic style injection - since we pass base64 images to the webview, we need to specify img-src ${webview.cspSource} data:; - 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. - */ + 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. + */ const nonce = getNonce() // Tip: Install the es6-string-html VS Code extension to enable code highlighting below @@ -462,6 +463,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { vsCodeLmModelSelector, liteLlmBaseUrl, liteLlmModelId, + liteLlmApiKey, qwenApiLine, } = message.apiConfiguration await this.updateGlobalState("apiProvider", apiProvider) @@ -492,6 +494,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { await this.storeSecret("togetherApiKey", togetherApiKey) await this.storeSecret("qwenApiKey", qwenApiKey) await this.storeSecret("mistralApiKey", mistralApiKey) + await this.storeSecret("liteLlmApiKey", liteLlmApiKey) await this.updateGlobalState("azureApiVersion", azureApiVersion) await this.updateGlobalState("openRouterModelId", openRouterModelId) await this.updateGlobalState("openRouterModelInfo", openRouterModelInfo) @@ -1416,6 +1419,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { previousModeModelId, previousModeModelInfo, qwenApiLine, + liteLlmApiKey, ] = await Promise.all([ this.getGlobalState("apiProvider") as Promise, this.getGlobalState("apiModelId") as Promise, @@ -1465,6 +1469,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { this.getGlobalState("previousModeModelId") as Promise, this.getGlobalState("previousModeModelInfo") as Promise, this.getGlobalState("qwenApiLine") as Promise, + this.getSecret("liteLlmApiKey") as Promise, ]) let apiProvider: ApiProvider @@ -1525,6 +1530,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { o3MiniReasoningEffort, liteLlmBaseUrl, liteLlmModelId, + liteLlmApiKey, }, lastShownAnnouncementId, customInstructions, @@ -1617,6 +1623,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { "togetherApiKey", "qwenApiKey", "mistralApiKey", + "liteLlmApiKey", "authToken", ] for (const key of secretKeys) { diff --git a/src/shared/api.ts b/src/shared/api.ts index 1297e6b155..0eee68b19e 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -21,6 +21,7 @@ export interface ApiHandlerOptions { apiKey?: string // anthropic liteLlmBaseUrl?: string liteLlmModelId?: string + liteLlmApiKey?: string anthropicBaseUrl?: string openRouterApiKey?: string openRouterModelId?: string diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 5e39320092..1633e4abea 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -900,6 +900,14 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage, is {selectedProvider === "litellm" && (
+ + API Key + Date: Wed, 12 Feb 2025 12:09:55 -1000 Subject: [PATCH 014/100] Advanced configuration for OpenAI Compatible Providers (#1737) * feat: advanced configuration for OpenAI Compatible Providers * Update .changeset/thirty-eyes-appear.md Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * Update webview-ui/src/components/settings/ApiOptions.tsx Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * dropdown menu * Show pricing if user entered model info --------- Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> --- .changeset/thirty-eyes-appear.md | 5 + src/api/providers/openai.ts | 2 +- src/core/webview/ClineProvider.ts | 8 ++ src/shared/api.ts | 1 + webview-ui/src/components/chat/TaskHeader.tsx | 10 +- .../src/components/settings/ApiOptions.tsx | 123 ++++++++++++++++++ .../settings/__tests__/APIOptions.spec.tsx | 58 ++++++++- webview-ui/src/utils/__tests__/hooks.spec.ts | 4 +- .../src/utils/__tests__/platformUtils.spec.ts | 4 +- 9 files changed, 207 insertions(+), 8 deletions(-) create mode 100644 .changeset/thirty-eyes-appear.md diff --git a/.changeset/thirty-eyes-appear.md b/.changeset/thirty-eyes-appear.md new file mode 100644 index 0000000000..2cfb8405d6 --- /dev/null +++ b/.changeset/thirty-eyes-appear.md @@ -0,0 +1,5 @@ +--- +"claude-dev": minor +--- + +Advanced Configuration for OpenAI Compatible Providers diff --git a/src/api/providers/openai.ts b/src/api/providers/openai.ts index c03b1d13ec..7309d5bb98 100644 --- a/src/api/providers/openai.ts +++ b/src/api/providers/openai.ts @@ -78,7 +78,7 @@ export class OpenAiHandler implements ApiHandler { getModel(): { id: string; info: ModelInfo } { return { id: this.options.openAiModelId ?? "", - info: openAiModelInfoSaneDefaults, + info: this.options.openAiModelInfo ?? openAiModelInfoSaneDefaults, } } } diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 1977dfaf99..87803eceed 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -66,6 +66,7 @@ type GlobalStateKey = | "taskHistory" | "openAiBaseUrl" | "openAiModelId" + | "openAiModelInfo" | "ollamaModelId" | "ollamaBaseUrl" | "lmStudioModelId" @@ -443,6 +444,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { openAiBaseUrl, openAiApiKey, openAiModelId, + openAiModelInfo, ollamaModelId, ollamaBaseUrl, lmStudioModelId, @@ -482,6 +484,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { await this.updateGlobalState("openAiBaseUrl", openAiBaseUrl) await this.storeSecret("openAiApiKey", openAiApiKey) await this.updateGlobalState("openAiModelId", openAiModelId) + await this.updateGlobalState("openAiModelInfo", openAiModelInfo) await this.updateGlobalState("ollamaModelId", ollamaModelId) await this.updateGlobalState("ollamaBaseUrl", ollamaBaseUrl) await this.updateGlobalState("lmStudioModelId", lmStudioModelId) @@ -561,6 +564,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { break case "openai": await this.updateGlobalState("previousModeModelId", apiConfiguration.openAiModelId) + await this.updateGlobalState("previousModeModelInfo", apiConfiguration.openAiModelInfo) break case "ollama": await this.updateGlobalState("previousModeModelId", apiConfiguration.ollamaModelId) @@ -592,6 +596,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { break case "openai": await this.updateGlobalState("openAiModelId", newModelId) + await this.updateGlobalState("openAiModelInfo", newModelInfo) break case "ollama": await this.updateGlobalState("ollamaModelId", newModelId) @@ -1387,6 +1392,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { openAiBaseUrl, openAiApiKey, openAiModelId, + openAiModelInfo, ollamaModelId, ollamaBaseUrl, lmStudioModelId, @@ -1437,6 +1443,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { this.getGlobalState("openAiBaseUrl") as Promise, this.getSecret("openAiApiKey") as Promise, this.getGlobalState("openAiModelId") as Promise, + this.getGlobalState("openAiModelInfo") as Promise, this.getGlobalState("ollamaModelId") as Promise, this.getGlobalState("ollamaBaseUrl") as Promise, this.getGlobalState("lmStudioModelId") as Promise, @@ -1508,6 +1515,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { openAiBaseUrl, openAiApiKey, openAiModelId, + openAiModelInfo, ollamaModelId, ollamaBaseUrl, lmStudioModelId, diff --git a/src/shared/api.ts b/src/shared/api.ts index 0eee68b19e..c7f6d218d6 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -38,6 +38,7 @@ export interface ApiHandlerOptions { openAiBaseUrl?: string openAiApiKey?: string openAiModelId?: string + openAiModelInfo?: ModelInfo ollamaModelId?: string ollamaBaseUrl?: string lmStudioModelId?: string diff --git a/webview-ui/src/components/chat/TaskHeader.tsx b/webview-ui/src/components/chat/TaskHeader.tsx index 92e44e2e4d..51780ea895 100644 --- a/webview-ui/src/components/chat/TaskHeader.tsx +++ b/webview-ui/src/components/chat/TaskHeader.tsx @@ -100,14 +100,20 @@ const TaskHeader: React.FC = ({ }, [task.text, windowWidth]) const isCostAvailable = useMemo(() => { + const openAiCompatHasPricing = + apiConfiguration?.apiProvider === "openai" && + apiConfiguration?.openAiModelInfo?.inputPrice && + apiConfiguration?.openAiModelInfo?.outputPrice + if (openAiCompatHasPricing) { + return true + } return ( - apiConfiguration?.apiProvider !== "openai" && apiConfiguration?.apiProvider !== "vscode-lm" && apiConfiguration?.apiProvider !== "ollama" && apiConfiguration?.apiProvider !== "lmstudio" && apiConfiguration?.apiProvider !== "gemini" ) - }, [apiConfiguration?.apiProvider]) + }, [apiConfiguration?.apiProvider, apiConfiguration?.openAiModelInfo]) const shouldShowPromptCacheInfo = doesModelSupportPromptCache && apiConfiguration?.apiProvider !== "openrouter" diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 1633e4abea..4f54cc0978 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -41,6 +41,7 @@ import VSCodeButtonLink from "../common/VSCodeButtonLink" import OpenRouterModelPicker, { ModelDescriptionMarkdown } from "./OpenRouterModelPicker" import styled from "styled-components" import * as vscodemodels from "vscode" +import { getAsVar, VSC_DESCRIPTION_FOREGROUND } from "../../utils/vscStyles" interface ApiOptionsProps { showModelOptions: boolean @@ -80,6 +81,7 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage, is const [vsCodeLmModels, setVsCodeLmModels] = useState([]) const [anthropicBaseUrlSelected, setAnthropicBaseUrlSelected] = useState(!!apiConfiguration?.anthropicBaseUrl) const [azureApiVersionSelected, setAzureApiVersionSelected] = useState(!!apiConfiguration?.azureApiVersion) + const [modelConfigurationSelected, setModelConfigurationSelected] = useState(false) const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false) const handleInputChange = (field: keyof ApiConfiguration) => (event: any) => { @@ -694,6 +696,127 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage, is placeholder={`Default: ${azureOpenAiDefaultApiVersion}`} /> )} +
setModelConfigurationSelected((val) => !val)}> + + + Model Configuration + +
+ {modelConfigurationSelected && ( + <> + { + const isChecked = e.target.checked === true + let modelInfo = apiConfiguration?.openAiModelInfo + ? apiConfiguration.openAiModelInfo + : { ...openAiModelInfoSaneDefaults } + modelInfo.supportsImages = isChecked + setApiConfiguration({ + ...apiConfiguration, + openAiModelInfo: modelInfo, + }) + }}> + Supports Images + +
+ { + let modelInfo = apiConfiguration?.openAiModelInfo + ? apiConfiguration.openAiModelInfo + : { ...openAiModelInfoSaneDefaults } + modelInfo.contextWindow = Number(input.target.value) + setApiConfiguration({ + ...apiConfiguration, + openAiModelInfo: modelInfo, + }) + }}> + Context Window Size + + { + let modelInfo = apiConfiguration?.openAiModelInfo + ? apiConfiguration.openAiModelInfo + : { ...openAiModelInfoSaneDefaults } + modelInfo.maxTokens = input.target.value + setApiConfiguration({ + ...apiConfiguration, + openAiModelInfo: modelInfo, + }) + }}> + Max Output Tokens + +
+
+ { + let modelInfo = apiConfiguration?.openAiModelInfo + ? apiConfiguration.openAiModelInfo + : { ...openAiModelInfoSaneDefaults } + modelInfo.inputPrice = input.target.value + setApiConfiguration({ + ...apiConfiguration, + openAiModelInfo: modelInfo, + }) + }}> + Input Price / 1M tokens + + { + let modelInfo = apiConfiguration?.openAiModelInfo + ? apiConfiguration.openAiModelInfo + : { ...openAiModelInfoSaneDefaults } + modelInfo.outputPrice = input.target.value + setApiConfiguration({ + ...apiConfiguration, + openAiModelInfo: modelInfo, + }) + }}> + Output Price / 1M tokens + +
+ + )}

{ expect(modelIdInput).toBeInTheDocument() }) }) + +vi.mock("../../../context/ExtensionStateContext", async (importOriginal) => { + const actual = await importOriginal() + return { + ...actual, + // your mocked methods + useExtensionState: vi.fn(() => ({ + apiConfiguration: { + apiProvider: "openai", + requestyApiKey: "", + requestyModelId: "", + }, + setApiConfiguration: vi.fn(), + uriScheme: "vscode", + })), + } +}) + +describe("OpenApiInfoOptions", () => { + const mockPostMessage = vi.fn() + + beforeEach(() => { + vi.clearAllMocks() + global.vscode = { postMessage: mockPostMessage } + }) + + it("renders OpenAI Supports Images input", () => { + render( + + + , + ) + const apiKeyInput = screen.getByText("Supports Images") + expect(apiKeyInput).toBeInTheDocument() + }) + + it("renders OpenAI Context Window Size input", () => { + render( + + + , + ) + const orgIdInput = screen.getByText("Context Window Size") + expect(orgIdInput).toBeInTheDocument() + }) + + it("renders OpenAI Max Output Tokens input", () => { + render( + + + , + ) + const modelInput = screen.getByText("Max Output Tokens") + expect(modelInput).toBeInTheDocument() + }) +}) diff --git a/webview-ui/src/utils/__tests__/hooks.spec.ts b/webview-ui/src/utils/__tests__/hooks.spec.ts index c613db72b3..2a1466f132 100644 --- a/webview-ui/src/utils/__tests__/hooks.spec.ts +++ b/webview-ui/src/utils/__tests__/hooks.spec.ts @@ -45,14 +45,14 @@ describe("useMetaKeyDetection", () => { // mock the detect functions const { result } = renderHook(() => useMetaKeyDetection("win32")) expect(result.current[0]).toBe("windows") - expect(result.current[1]).toBe("⊞ Win") + expect(result.current[1]).toBe("Win") }) it("should detect Mac OS and metaKey from platform", () => { // mock the detect functions const { result } = renderHook(() => useMetaKeyDetection("darwin")) expect(result.current[0]).toBe("mac") - expect(result.current[1]).toBe("⌘ Command") + expect(result.current[1]).toBe("CMD") }) it("should detect Linux OS and metaKey from platform", () => { diff --git a/webview-ui/src/utils/__tests__/platformUtils.spec.ts b/webview-ui/src/utils/__tests__/platformUtils.spec.ts index 9ec19ba3d0..2938280ab4 100644 --- a/webview-ui/src/utils/__tests__/platformUtils.spec.ts +++ b/webview-ui/src/utils/__tests__/platformUtils.spec.ts @@ -4,12 +4,12 @@ import { detectMetaKeyChar } from "../platformUtils" describe("detectMetaKeyChar", () => { it("should return ⌘ Command for darwin platform", () => { const result = detectMetaKeyChar("darwin") - expect(result).toBe("⌘ Command") + expect(result).toBe("CMD") }) it("should return ⊞ Win for win32 platform", () => { const result = detectMetaKeyChar("win32") - expect(result).toBe("⊞ Win") + expect(result).toBe("Win") }) it("should return Alt for linux platform", () => { From a99648884a6c98da9eb933a407e4bedb31afffa5 Mon Sep 17 00:00:00 2001 From: Shawn Smith Date: Wed, 12 Feb 2025 17:56:20 -0800 Subject: [PATCH 015/100] Fix: Bedrock Profiles (#1751) * fix-bedrock-profiles * fix-bedrock-profiles * resolve linting issue * fixes * resolve using var * add changeset * update changeset --- .changeset/orange-eels-unite.md | 5 +++ src/api/providers/bedrock.ts | 57 +++++++++++++++++++++------------ 2 files changed, 41 insertions(+), 21 deletions(-) create mode 100644 .changeset/orange-eels-unite.md diff --git a/.changeset/orange-eels-unite.md b/.changeset/orange-eels-unite.md new file mode 100644 index 0000000000..e0eab5d394 --- /dev/null +++ b/.changeset/orange-eels-unite.md @@ -0,0 +1,5 @@ +--- +"claude-dev": patch +--- + +Fix AWS Bedrock Profiles. When configuring the AnthropicBedrock Client you must pass AWS credentials in a specific way, otherwise the client will default to reading credentials from the default AWS profile. diff --git a/src/api/providers/bedrock.ts b/src/api/providers/bedrock.ts index 448190291d..04de4af2b2 100644 --- a/src/api/providers/bedrock.ts +++ b/src/api/providers/bedrock.ts @@ -8,35 +8,50 @@ import { fromIni } from "@aws-sdk/credential-providers" // https://docs.anthropic.com/en/api/claude-on-amazon-bedrock export class AwsBedrockHandler implements ApiHandler { private options: ApiHandlerOptions - private client: AnthropicBedrock + private client: AnthropicBedrock | any + private initializationPromise: Promise constructor(options: ApiHandlerOptions) { this.options = options + this.initializationPromise = this.initializeClient() + } - const clientConfig: any = { + private async initializeClient() { + let clientConfig: any = { awsRegion: this.options.awsRegion || "us-east-1", } - - if (this.options.awsUseProfile) { - // Use profile-based credentials if enabled - if (this.options.awsProfile) { - clientConfig.credentials = fromIni({ - profile: this.options.awsProfile, - }) - } else { - // Use default profile if no specific profile is set - clientConfig.credentials = fromIni() - } - } else if (this.options.awsAccessKey && this.options.awsSecretKey) { - // Use direct credentials if provided - clientConfig.awsAccessKey = this.options.awsAccessKey - clientConfig.awsSecretKey = this.options.awsSecretKey - if (this.options.awsSessionToken) { - clientConfig.awsSessionToken = this.options.awsSessionToken + try { + if (this.options.awsUseProfile) { + // Use profile-based credentials if enabled + // Use named profile, defaulting to 'default' if not specified + var credentials: any + if (this.options.awsProfile) { + credentials = await fromIni({ + profile: this.options.awsProfile, + ignoreCache: true, + })() + } else { + credentials = await fromIni({ + ignoreCache: true, + })() + } + clientConfig.awsAccessKey = credentials.accessKeyId + clientConfig.awsSecretKey = credentials.secretAccessKey + clientConfig.awsSessionToken = credentials.sessionToken + } else if (this.options.awsAccessKey && this.options.awsSecretKey) { + // Use direct credentials if provided + clientConfig.awsAccessKey = this.options.awsAccessKey + clientConfig.awsSecretKey = this.options.awsSecretKey + if (this.options.awsSessionToken) { + clientConfig.awsSessionToken = this.options.awsSessionToken + } } + } catch (error) { + console.error("Failed to initialize Bedrock client:", error) + throw error + } finally { + this.client = new AnthropicBedrock(clientConfig) } - - this.client = new AnthropicBedrock(clientConfig) } async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { From 66d5a320b50e43a87f7ed07f5c002db2aa48157c Mon Sep 17 00:00:00 2001 From: Gustavo de Oliveira Date: Wed, 12 Feb 2025 23:18:46 -0300 Subject: [PATCH 016/100] docs: Add translation to portuguese pt-BR (#1713) * docs: Translate CONTRIBUTING to portuguese * docs: Translate CODE_OF_CONDUCT to portuguese * docs: Translate README to portuguese * fix: typo errors * fix: ellipses requested changes * fix: Add changeset * docs: add portuguese label in README.md --- .changeset/angry-lions-sneeze.md | 5 + README.md | 2 +- locales/pt-BR/CODE_OF_CONDUCT.md | 51 ++++++++++ locales/pt-BR/CONTRIBUTING.md | 83 ++++++++++++++++ locales/pt-BR/README.md | 161 +++++++++++++++++++++++++++++++ 5 files changed, 301 insertions(+), 1 deletion(-) create mode 100644 .changeset/angry-lions-sneeze.md create mode 100644 locales/pt-BR/CODE_OF_CONDUCT.md create mode 100644 locales/pt-BR/CONTRIBUTING.md create mode 100644 locales/pt-BR/README.md diff --git a/.changeset/angry-lions-sneeze.md b/.changeset/angry-lions-sneeze.md new file mode 100644 index 0000000000..091739140b --- /dev/null +++ b/.changeset/angry-lions-sneeze.md @@ -0,0 +1,5 @@ +--- +"claude-dev": patch +--- + +Add translation to CODE_OF_CONDUCT, CONTRIBUTING and README to portuguese pt-BR. diff --git a/README.md b/README.md index 4f48c5ad41..f1c48eebfd 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

# Cline – \#1 on OpenRouter diff --git a/locales/pt-BR/CODE_OF_CONDUCT.md b/locales/pt-BR/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..5721aba41e --- /dev/null +++ b/locales/pt-BR/CODE_OF_CONDUCT.md @@ -0,0 +1,51 @@ +# Código de Conduta para Contribuidores + +## Nosso Compromisso + + +Com o objetivo de promover um ambiente aberto e acolhedor, nós, como contribuidores e mantenedores, nos comprometemos a tornar a participação em nosso projeto e comunidade uma experiência livre de assédio para todos, independentemente de idade, tamanho corporal, deficiência, etnia, características sexuais, identidade e expressão de gênero, nível de experiência, educação, status socioeconômico, nacionalidade, aparência pessoal, raça, religião ou orientação sexual. + +## Nossos Padrões + +Exemplos de comportamentos que contribuem para criar um ambiente positivo incluem: + +- Uso de linguagem acolhedora e inclusiva +- Respeito por diferentes pontos de vista e experiências +- Aceitar críticas de maneira construtiva +- Foco no que é melhor para a comunidade +- Ser empático com outros membros da comunidade + + +Exemplos de comportamentos inaceitáveis por parte dos participantes incluem: + +- Uso de linguagem ou imagens sexualizadas e atenção ou avanços sexuais indesejados +- Trollar, insultar, fazer comentários depreciativos, ataques pessoais ou políticos +- Assédio público ou privado +- Divulgar informações privadas sem autorização, como endereços físicos ou eletrônicos, sem permissão explícita +- Outras condutas que poderiam ser consideradas inadequadas em um ambiente profissional + +## Nossas Responsabilidades + +Os mantenedores do projeto são responsáveis por esclarecer os padrões de comportamento aceitáveis e devem tomar ações corretivas apropriadas e justas em resposta a qualquer instância de comportamento inaceitável. + +Os mantenedores têm o direito e a responsabilidade de remover, editar ou rejeitar comentários, commits, códigos, edições no wiki, issues e outras contribuições que não estejam alinhadas com este Código de Conduta. Também podem banir temporária ou permanentemente qualquer colaborador cujo comportamento seja considerado inapropriado, ameaçador, ofensivo ou prejudicial. + +## Escopo + +Este Código de Conduta se aplica tanto aos espaços do projeto quanto aos espaços públicos +quando uma pessoa representa o projeto ou sua comunidade. Exemplos de +representação de um projeto ou comunidade incluem o uso de um endereço de e-mail oficial do projeto, +publicar em uma conta oficial de mídia social ou atuar como representante designado +em um evento online ou offline. A representação de um projeto pode +ser mais especificamente definido e esclarecido pelos mantenedores do projeto. + +## Aplicação + +Casos de comportamento abusivo, assediador ou inaceitáveis podem ser reportados entrando em contato com a equipe do projeto pelo email hi@cline.bot. Todas as queixas serão revisadas e investigadas confidencialmente. Mais detalhes sobre políticas específicas podem ser publicados separadamente. + +Os mantenedores que não seguirem ou aplicarem este Código de Conduta de boa fé podem enfrentar repercussões temporárias ou permanentes determinadas por outros membros da liderança do projeto. + +## Atribuição + +Este Código de Conduta é adaptado do [Contributor Covenant](https://www.contributor-covenant.org), versão 1.4, disponível em https://www.contributor-covenant.org/version/1/4/code-of-conduct.html. + diff --git a/locales/pt-BR/CONTRIBUTING.md b/locales/pt-BR/CONTRIBUTING.md new file mode 100644 index 0000000000..34cea9a124 --- /dev/null +++ b/locales/pt-BR/CONTRIBUTING.md @@ -0,0 +1,83 @@ +# Contribuir para o Cline + +Estamos felizes por você estar interessado em contribuir com o Cline. Seja corrigindo um erro, adicionando uma funcionalidade ou melhorando nossa documentação, cada contribuição torna o Cline mais inteligente! Para manter nossa comunidade viva e acolhedora, todos os membros devem cumprir nosso Código de Conduta [Código de Conduta](CODE_OF_CONDUCT.md). + +## Relatar erros ou problemas + +Relatar erros ajuda a melhorar o Cline para todos! Antes de criar um novo issue, revise as [issues existentes](https://github.com/cline/cline/issues) para evitar duplicações. Quando estiver pronto para relatar um erro, vá até nossa [página de Issues](https://github.com/cline/cline/issues/new/choose), onde você encontrará um modelo que ajudará a preencher as informações relevantes. + +
+ 🔐 Importante: Se você descobrir uma vulnerabilidade de segurança, utilize a ferramenta de segurança do GitHub para relatá-la de forma privada. +
+ +## Escolher no que trabalhar + +Procurando uma boa primeira contribuição? Consulte os problemas marcados com ["good first issue"](https://github.com/cline/cline/labels/good%20first%20issue) ou ["help wanted"](https://github.com/cline/cline/labels/help%20wanted). Estes foram especialmente selecionados para novos colaboradores e são áreas em que adoraríamos receber ajuda! + +Também damos boas-vindas a contribuições para nossa [documentação](https://github.com/cline/cline/tree/main/docs). Seja corrigindo erros de digitação, melhorando guias existentes ou criando novos conteúdos educativos, queremos construir um repositório de recursos gerido pela comunidade que ajude todos a tirar o máximo proveito do Cline. Você pode começar explorando `/docs` e procurando áreas que precisam de melhorias. + +Se planeja trabalhar em uma funcionalidade maior, crie primeiro uma [solicitação de funcionalidade](https://github.com/cline/cline/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) para que possamos discutir se ela se alinha à visão do Cline. + +## Configurar o ambiente de desenvolvimento + +1. **Extensões do VS Code** + + - Ao abrir o projeto, o VS Code solicitará que você instale as extensões recomendadas. + - Essas extensões são necessárias para o desenvolvimento – aceite todas as solicitações de instalação. + - Caso tenha rejeitado as solicitações, você pode instalá-las manualmente na seção de extensões. + +2. **Desenvolvimento local** + - Execute `npm run install:all` para instalar as dependências. + - Execute `npm run test` para rodar os testes localmente. + - Antes de enviar um PR, execute `npm run format:fix` para formatar seu código. + +## Escrever e enviar código + +Qualquer pessoa pode contribuir com código para o Cline, mas pedimos que siga estas diretrizes para garantir que suas contribuições sejam integradas sem problemas: + +1. **Mantenha os Pull Requests focados** + + - Limite os PRs a uma única funcionalidade ou correção de erro. + - Divida alterações maiores em PRs menores e coerentes. + - Divida as alterações em commits lógicos que possam ser revisados independentemente. + +2. **Qualidade do código** + + - Execute `npm run lint` para verificar o estilo do código. + - Execute `npm run format` para formatar automaticamente o código. + - Todos os PRs devem passar nas verificações do CI, que incluem linting e formatação. + - Resolva todos os avisos ou erros do ESLint antes de enviar. + - Siga as melhores práticas para TypeScript e mantenha a segurança dos tipos. + +3. **Testes** + + - Adicione testes para novas funcionalidades. + - Execute `npm test` para garantir que todos os testes passem. + - Atualize testes existentes caso suas alterações os afetem. + - Inclua tanto testes unitários quanto de integração onde for apropriado. + +4. **Diretrizes de commits** + + - Escreva mensagens de commit claras e descritivas. + - Use o formato convencional (por exemplo, "feat:", "fix:", "docs:"). + - Faça referência aos issues relevantes nos commits usando #número-do-issue. + +5. **Antes de enviar** + + - Faça rebase com sua branch com a última versão da branch principal (main). + - Certifique-se de que sua branch seja construída corretamente. + - Verifique se todos os testes passam. + - Revise suas alterações para remover qualquer código de depuração ou logs desnecessários. + +6. **Descrição do Pull Request** + - Descreva claramente o que suas alterações fazem. + - Inclua passos para testar as alterações. + - Liste quaisquer mudanças importantes. + - Adicione capturas de tela para mudanças na interface do usuário. + +## Acordo de contribuição + +Ao enviar um Pull Request, você concorda que suas contribuições serão licenciadas sob a mesma licença do projeto ([Apache 2.0](LICENSE)). + +Lembre-se: Contribuir com o Cline não é apenas escrever código – é fazer parte de uma comunidade que está moldando o futuro do desenvolvimento assistido por IA. Vamos criar algo incrível juntos! 🚀 + diff --git a/locales/pt-BR/README.md b/locales/pt-BR/README.md new file mode 100644 index 0000000000..bdcae339e5 --- /dev/null +++ b/locales/pt-BR/README.md @@ -0,0 +1,161 @@ +# Cline – #1 no OpenRouter + +

+ +

+ + + +Conheça o Cline: um assistente de IA que pode usar seu **CLI** e **Editor**. + +Graças às [habilidades avançadas do Claude 3.5 Sonnet](https://www-cdn.anthropic.com/fed9cc193a14b84131812372d8d5857f8f304c52/Model_Card_Claude_3_Addendum.pdf), o Cline pode lidar com tarefas complexas de desenvolvimento de software passo a passo. Com ferramentas que permitem criar e editar arquivos, explorar grandes projetos, usar o navegador e executar comandos no terminal (com sua aprovação), ele pode ajudar você de maneiras que vão além da inclusão de código ou suporte técnico. O Cline pode é capaz inclusive de usar o Model Context Protocol (MCP) para criar novas ferramentas e expandir seus próprios recursos. Embora os scripts de IA autônomas tradicionalmente sejam executados em ambientes isolados, esta extensão oferece uma GUI com um humano no circuito para aprovar cada alteração de arquivo e comando de terminal, fornecendo uma maneira segura e acessível de explorar todo o potencial da IA. + +1. Insira sua tarefa e adicione imagens para transformar mockups em aplicativos funcionais ou corrigir erros através de capturas de tela. + +2. O Cline começará analisando a estrutura do seu arquivo e os ASTs do código-fonte, fazendo pesquisas com Regex e lendo arquivos relevantes para se orientar em projetos existentes. Ao gerenciar cuidadosamente as informações agregadas, o Cline pode fornecer assistência valiosa mesmo em projetos grandes e complexos, sem sobrecarregar a janela de contexto. +3. Assim que ele tiver as informações necessárias, o Cline poderá: + - Criar e editar arquivos + monitorar erros de Linter/Compilador, para que você possa corrigir proativamente problemas como importações ausentes e erros de sintaxe. + - Executar comandos diretamente no terminal e monitorar o resultado, para que você possa responder a problemas do servidor de desenvolvimento após editar um arquivo. + - Para tarefas de desenvolvimento web, o Cline pode iniciar o site em um navegador headless, clicar, digitar, fazer scroll e capturar capturas de tela + registros de console, para que você possa corrigir erros em tempo de execução e erros visuais. + +> [!TIP] +> Use o atalho de teclado `CMD/CTRL + Shift + P` para abrir a lista de comandos possiveis e digite "Cline: Abrir em nova aba" para abrir a extensão como uma aba no seu editor. Dessa forma, você pode usar o Cline junto com seu explorador de arquivos e ver mais claramente como seu espaço de trabalho muda. + +--- + + + +### Use qualquer API ou modelo + +O Cline oferece suporte a provedores de API como OpenRouter, Anthropic, OpenAI, Google Gemini, AWS Bedrock, Azure e GCP Vertex. Você também pode configurar qualquer API compatível com OpenAI ou usar um modelo local via LM Studio/Ollama. Se você usar o OpenRouter, a extensão recuperará sua lista de modelos mais recentes, para que você possa usar os modelos mais novos assim que estiverem disponíveis. + +A extensão também rastreia o uso total de tokens e os custos da API para todo o ciclo de tarefas e solicitações individuais, para que você seja informado sobre as despesas em cada etapa. + + + +
+ + + +### Executar comandos no terminal + +Graças às novas [atualizações de integração do Shell no VSCode v1.93](https://code.visualstudio.com/updates/v1_93#_terminal-shell-integration-api), o Cline pode executar comandos diretamente no seu terminal e receber o resultado. Isso permite que você execute uma variedade de tarefas, desde instalar pacotes e executar build scripts para fazer deploy de aplicações, gerenciar bancos de dados e executar testes, adaptando-se ao seu ambiente de desenvolvimento e ferramentas para fazer o trabalho corretamente. + +Para processos de longa duração, como servidores de desenvolvimento, use o botão "Continuar durante a execução" para permitir que o Cline continue a tarefa enquanto o comando é executado em segundo plano. Enquanto Cline trabalha, você será notificado sobre novas saídas do terminal, para que possa responder a problemas que possam surgir, como erros de compilação ao editar arquivos. + + + +
+ + + +### Criar e editar arquivos + +Cline pode criar e editar arquivos diretamente no seu editor, apresentando um diff com as alterações. Você pode editar ou reverter as alterações do Cline diretamente no editor de diff ou fornecer feedback no chat até ficar satisfeito com o resultado. Cline também monitora erros de linter/compilador (importações ausentes, erros de sintaxe, etc.) para que possa corrigir problemas que surgem ao longo do caminho por conta própria. + +Todas as alterações feitas pelo Cline são registradas na Linha do tempo do arquivo, fornecendo uma maneira fácil de rastrear e reverter modificações, caso seja necessário. + + + +
+ + + +### Uso do navegador + +Com a nova habilidade de [uso de computador](https://www.anthropic.com/news/3-5-models-and-computer-use) do Claude Sonnet 3.5, Cline pode abrir um navegador, clicar em elementos, digitar texto e rolar, capturando a tela e logs de console. Isso permite depurar de maneira interativa, testes end-to-end e até mesmo uso geral da web. Isso lhe dá autonomia para solucionar erros visuais e problemas em tempo de execução sem precisar copiar e colar logs dos erros. + +Tente pedir a Cline para "testar o aplicativo" e observe enquanto o Cline executa um comando como `npm run dev`, inicia seu servidor de desenvolvimento local em um navegador e executa uma série de testes para confirmar se tudo funciona. [Veja uma demonstração aqui.](https://x.com/sdrzn/status/1850880547825823989) + + + +
+ + + +### "adicione uma ferramenta que..." + +Graças ao [Model Context Protocol](https://github.com/modelcontextprotocol), o Cline pode expandir seus recursos por meio de ferramentas personalizadas. Embora você possa usar [servidores criados pela comunidade](https://github.com/modelcontextprotocol/servers), Cline pode criar e instalar ferramentas especificamente para seu fluxo de trabalho. Basta pedir ao Cline para "adicionar uma ferramenta" e ele cuidará de tudo, desde a criação de um novo servidor MCP até a instalação na extensão. Essas ferramentas personalizadas se tornam parte do conjunto de ferramentas da Cline e estão prontas para serem usadas em tarefas futuras. + +- "adicione uma ferramenta que recupere tickets do Jira": Recupere ACs de tickets e coloque Cline para trabalhar +- "adicione uma ferramenta que gerencie AWS EC2s": verifique as métricas do servidor e aumente ou diminua as instâncias +- "adicione uma ferramenta para recuperar os últimos incidentes do PagerDuty": Recupere detalhes e peça ao Cline para corrigir erros + + + +
+ + + +### Adicione contexto + +**`@url`:** Insira uma URL para a extensão recuperar e converter para Markdown, que é útil quando você deseja fornecer ao Cline documentos mais recentes + +**`@problems`:** Adicionar erros e avisos do espaço de trabalho (painel 'Problemas') que o Cline deve corrigir + +**`@file`:** Adicione o conteúdo de um arquivo para que você não precise desperdiçar solicitações de API para aprovar a leitura do arquivo (+ para pesquisar arquivos) + +**`@folder`:** Adicione arquivos de uma pasta por vez para acelerar ainda mais seu fluxo de trabalho + + + +
+ + + +### Checkpoints: Comparar e Restaurar + +Enquanto Cline trabalha em uma tarefa, a extensão cria um instantâneo de seu espaço de trabalho em cada etapa. Você pode usar o botão "Comparar" para ver a diferença entre o instantâneo e seu espaço de trabalho atual, e o botão "Restaurar" para retornar a esse ponto. + +Por exemplo, se estiver trabalhando com um servidor web local, você pode usar 'Restaurar somente o espaço de trabalho' para testar rapidamente diferentes versões do seu aplicativo e, em seguida, 'Restaurar tarefa e espaço de trabalho' quando encontrar a versão na qual deseja continuar trabalhando. Isso permite que você explore diferentes abordagens com segurança sem perder o progresso. + + + +
+ +## Contribuições + +Para contribuir com o projeto, comece com nosso [Guia de Contribuição](CONTRIBUTING.md) para aprender o básico. Você também pode entrar no nosso [Discord](https://discord.gg/cline) para bater papo com outros colaboradores no canal `#contributors`. Se você está procurando um emprego de período integral, confira nossas vagas em aberto na nossa [página de carreiras](https://cline.bot/join-us). + +
+Instruções para desenvolvimento local + +1. Clone o repositório _(Necessário [git-lfs](https://git-lfs.com/))_: + ```bash + git clone https://github.com/cline/cline.git + ``` +2. Abra o projeto no VSCode: + ```bash + code cline + ``` +3. Instale as dependências necessárias para a extensão e webview-gui: + ```bash + npm run install:all + ``` +4. Inicie pressionando `F5` (ou `Executar`->`Iniciar Depuração`) para abrir uma nova janela do VSCode com a extensão carregada. (Pode ser necessário instalar a [extensão esbuild problem matchers](https://marketplace.visualstudio.com/items?itemName=connor4312.esbuild-problem-matchers) se você encontrar problemas ao compilar seu projeto.) + +
+ +## Licença + +[Apache 2.0 © 2024 Cline Bot Inc.](./LICENSE) From 83a28001be957568d14e087dd365ac4b1b02cfdf Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Wed, 12 Feb 2025 20:18:36 -0800 Subject: [PATCH 017/100] Fix plan/act response prompt --- src/core/Cline.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/core/Cline.ts b/src/core/Cline.ts index b868efd285..1e9ea6bbb4 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -2716,19 +2716,20 @@ export class Cline { this.isAwaitingPlanResponse = false if (this.didRespondToPlanAskBySwitchingMode) { + if (text) { + await this.say("user_feedback", text ?? "", images) + } pushToolResult( formatResponse.toolResult( - `[The user has switched to ACT MODE, so you may now proceed with the task.]`, + `[The user has switched to ACT MODE, so you may now proceed with the task.]` + + (text + ? `\n\nThe user also provided the following message when switching to ACT MODE:\n\n${text}\n` + : ""), images, ), ) } - if (text) { - await this.say("user_feedback", text ?? "", images) - pushToolResult(formatResponse.toolResult(`\n${text}\n`, images)) - } - // break } From 283d7d6928755ce5f62827e25c437a7a77964549 Mon Sep 17 00:00:00 2001 From: Hiroki Nakashima Date: Thu, 13 Feb 2025 16:01:01 +0900 Subject: [PATCH 018/100] fix: adjust litellm default context window settings (#1774) --- src/shared/api.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/shared/api.ts b/src/shared/api.ts index c7f6d218d6..4794327437 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -637,9 +637,9 @@ export const mistralModels = { export type LiteLLMModelId = string export const liteLlmDefaultModelId = "gpt-3.5-turbo" export const liteLlmModelInfoSaneDefaults: ModelInfo = { - maxTokens: 4096, - contextWindow: 8192, - supportsImages: false, + maxTokens: -1, + contextWindow: 128_000, + supportsImages: true, supportsPromptCache: false, inputPrice: 0, outputPrice: 0, From 7d67000d1f5b9eb4f012cca08c1a641945b864a8 Mon Sep 17 00:00:00 2001 From: pashpashpash Date: Thu, 13 Feb 2025 17:23:22 -0800 Subject: [PATCH 019/100] marketplace wip --- src/core/webview/ClineProvider.ts | 113 ++++++++++++ src/shared/ExtensionMessage.ts | 7 +- src/shared/WebviewMessage.ts | 3 + src/shared/mcp.ts | 24 +++ webview-ui/src/components/mcp/McpView.tsx | 164 +++++++++++------- .../mcp/marketplace/McpMarketplaceCard.tsx | 141 +++++++++++++++ .../mcp/marketplace/McpMarketplaceView.tsx | 117 +++++++++++++ 7 files changed, 506 insertions(+), 63 deletions(-) create mode 100644 webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx create mode 100644 webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 3e59dfad2a..50a08bd532 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -14,6 +14,7 @@ import { selectImages } from "../../integrations/misc/process-images" import { getTheme } from "../../integrations/theme/getTheme" import WorkspaceTracker from "../../integrations/workspace/WorkspaceTracker" import { McpHub } from "../../services/mcp/McpHub" +import { McpMarketplaceCatalog, McpMarketplaceItem } from "../../shared/mcp" import { FirebaseAuthManager, UserInfo } from "../../services/auth/FirebaseAuthManager" import { ApiProvider, ModelInfo } from "../../shared/api" import { findLast } from "../../shared/array" @@ -88,6 +89,7 @@ type GlobalStateKey = | "qwenApiLine" | "requestyModelId" | "togetherModelId" + | "mcpMarketplaceCatalog" export const GlobalFileNames = { apiConversationHistory: "api_conversation_history.json", @@ -375,6 +377,107 @@ export class ClineProvider implements vscode.WebviewViewProvider { * * @param webview A reference to the extension webview */ + private async fetchMcpMarketplace(forceRefresh: boolean = false) { + try { + // Check if we have cached data + const cachedCatalog = (await this.getGlobalState("mcpMarketplaceCatalog")) as McpMarketplaceCatalog | undefined + if (!forceRefresh && cachedCatalog?.items) { + await this.postMessageToWebview({ + type: "mcpMarketplaceCatalog", + mcpMarketplaceCatalog: cachedCatalog, + }) + return + } + + try { + const response = await axios.get("https://api.cline.bot/v1/mcp/marketplace", { + headers: { + "Content-Type": "application/json", + }, + }) + + if (!response.data) { + throw new Error("Invalid response from MCP marketplace API") + } + + const catalog: McpMarketplaceCatalog = { + items: (response.data || []).map((item: any) => ({ + ...item, + githubStars: item.githubStars ?? 0, + downloads: item.downloads ?? 0, + tags: item.tags ?? [], + })), + } + + // Store in global state + await this.updateGlobalState("mcpMarketplaceCatalog", catalog) + + await this.postMessageToWebview({ + type: "mcpMarketplaceCatalog", + mcpMarketplaceCatalog: catalog, + }) + } catch (error) { + console.error("Failed to fetch MCP marketplace:", error) + const errorMessage = error instanceof Error ? error.message : "Failed to fetch MCP marketplace" + await this.postMessageToWebview({ + type: "mcpMarketplaceCatalog", + error: errorMessage, + }) + vscode.window.showErrorMessage(errorMessage) + } + } catch (error) { + console.error("Failed to handle cached MCP marketplace:", error) + const errorMessage = error instanceof Error ? error.message : "Failed to handle cached MCP marketplace" + await this.postMessageToWebview({ + type: "mcpMarketplaceCatalog", + error: errorMessage, + }) + vscode.window.showErrorMessage(errorMessage) + } + } + + private async downloadMcp(mcpId: string) { + try { + const response = await axios.post( + "https://api.cline.bot/v1/mcp/download", + { + mcpId, + }, + { + headers: { + "Content-Type": "application/json", + }, + }, + ) + + if (!response.data) { + throw new Error("Invalid response from MCP download API") + } + + const mcpDetails = response.data + await this.postMessageToWebview({ + type: "mcpDownloadDetails", + mcpDownloadDetails: mcpDetails, + }) + + // Create a new task for Cline to set up the MCP server + const task = `Set up the MCP server from ${mcpDetails.githubUrl}. Here's some additional context from the README:\n\n${mcpDetails.readmeContent}` + await this.initClineWithTask(task) + await this.postMessageToWebview({ + type: "action", + action: "chatButtonClicked", + }) + } catch (error) { + console.error("Failed to download MCP:", error) + const errorMessage = error instanceof Error ? error.message : "Failed to download MCP" + vscode.window.showErrorMessage(errorMessage) + await this.postMessageToWebview({ + type: "mcpDownloadDetails", + error: errorMessage, + }) + } + } + private setWebviewMessageListener(webview: vscode.Webview) { webview.onDidReceiveMessage( async (message: WebviewMessage) => { @@ -779,6 +882,16 @@ export class ClineProvider implements vscode.WebviewViewProvider { } break } + case "fetchMcpMarketplace": { + await this.fetchMcpMarketplace(message.bool) + break + } + case "downloadMcp": { + if (message.mcpId) { + await this.downloadMcp(message.mcpId) + } + break + } case "toggleMcpServer": { try { await this.mcpHub?.toggleServerDisabled(message.serverName!, message.disabled!) diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 70a58fbfd8..ee2d1a4d9c 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -5,7 +5,7 @@ import { AutoApprovalSettings } from "./AutoApprovalSettings" import { BrowserSettings } from "./BrowserSettings" import { ChatSettings } from "./ChatSettings" import { HistoryItem } from "./HistoryItem" -import { McpServer } from "./mcp" +import { McpServer, McpMarketplaceCatalog, McpMarketplaceItem } from "./mcp" // webview will hold state export interface ExtensionMessage { @@ -26,6 +26,8 @@ export interface ExtensionMessage { | "vsCodeLmModels" | "requestVsCodeLmModels" | "emailSubscribed" + | "mcpMarketplaceCatalog" + | "mcpDownloadDetails" text?: string action?: | "chatButtonClicked" @@ -46,6 +48,9 @@ export interface ExtensionMessage { openRouterModels?: Record openAiModels?: string[] mcpServers?: McpServer[] + mcpMarketplaceCatalog?: McpMarketplaceCatalog + error?: string + mcpDownloadDetails?: McpMarketplaceItem } export type Platform = "aix" | "darwin" | "freebsd" | "linux" | "openbsd" | "sunos" | "win32" | "unknown" diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 447193dd18..b691315d45 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -43,6 +43,8 @@ export interface WebviewMessage { | "accountLoginClicked" | "accountLogoutClicked" | "subscribeEmail" + | "fetchMcpMarketplace" + | "downloadMcp" // | "relaunchChromeDebugMode" text?: string disabled?: boolean @@ -55,6 +57,7 @@ export interface WebviewMessage { browserSettings?: BrowserSettings chatSettings?: ChatSettings chatContent?: ChatContent + mcpId?: string // For toggleToolAutoApprove serverName?: string diff --git a/src/shared/mcp.ts b/src/shared/mcp.ts index a8ae7f70f6..d859dc7b8f 100644 --- a/src/shared/mcp.ts +++ b/src/shared/mcp.ts @@ -66,3 +66,27 @@ export type McpToolCallResponse = { > isError?: boolean } + +export interface McpMarketplaceItem { + mcpId: string + githubUrl: string + name: string + author: string + description: string + codegenIcon: string + logoUrl: string + category: string + tags: string[] + requiresApiKey: boolean + readmeContent?: string + isRecommended: boolean + githubStars: number + downloads: number + createdAt: string + updatedAt: string + lastGithubSync: string +} + +export interface McpMarketplaceCatalog { + items: McpMarketplaceItem[] +} diff --git a/webview-ui/src/components/mcp/McpView.tsx b/webview-ui/src/components/mcp/McpView.tsx index b8afdbb05f..c2e64d24b7 100644 --- a/webview-ui/src/components/mcp/McpView.tsx +++ b/webview-ui/src/components/mcp/McpView.tsx @@ -1,10 +1,18 @@ -import { VSCodeButton, VSCodeLink, VSCodePanels, VSCodePanelTab, VSCodePanelView } from "@vscode/webview-ui-toolkit/react" +import { + VSCodeButton, + VSCodeLink, + VSCodePanels, + VSCodePanelTab, + VSCodePanelView, + VSCodeDivider, +} from "@vscode/webview-ui-toolkit/react" import { useState } from "react" import { vscode } from "../../utils/vscode" import { useExtensionState } from "../../context/ExtensionStateContext" import { McpServer } from "../../../../src/shared/mcp" import McpToolRow from "./McpToolRow" import McpResourceRow from "./McpResourceRow" +import McpMarketplaceView from "./marketplace/McpMarketplaceView" type McpViewProps = { onDone: () => void @@ -12,6 +20,7 @@ type McpViewProps = { const McpView = ({ onDone }: McpViewProps) => { const { mcpServers: servers } = useExtensionState() + const [activeTab, setActiveTab] = useState(servers.length === 0 ? "marketplace" : "installed") // const [servers, setServers] = useState([ // // Add some mock servers for testing @@ -96,72 +105,103 @@ const McpView = ({ onDone }: McpViewProps) => { Done
-
-
- The{" "} - - Model Context Protocol - {" "} - enables communication with locally running MCP servers that provide additional tools and resources to extend - Cline's capabilities. You can use{" "} - - community-made servers - {" "} - or ask Cline to create new tools specific to your workflow (e.g., "add a tool that gets the latest npm docs").{" "} - - See a demo here. - -
+
+ setActiveTab(e.target.activeid)}> + Installed + Marketplace + Settings - {servers.length > 0 && ( -
- {servers.map((server) => ( - - ))} -
- )} + +
+
+ The{" "} + + Model Context Protocol + {" "} + enables communication with locally running MCP servers that provide additional tools and resources + to extend Cline's capabilities. You can use{" "} + + community-made servers + {" "} + or ask Cline to create new tools specific to your workflow (e.g., "add a tool that gets the latest + npm docs").{" "} + + See a demo here. + +
- {/* Server Configuration Button */} + {servers.length > 0 ? ( +
+ {servers.map((server) => ( + + ))} +
+ ) : ( +
+
No MCP servers installed yet
+ setActiveTab("marketplace")}> + + Browse Marketplace + +
+ )} +
+
-
- { - vscode.postMessage({ type: "openMcpSettings" }) - }}> - - Configure MCP Servers - -
+ + + - {/* Advanced Settings Link */} -
- { - vscode.postMessage({ - type: "openExtensionSettings", - text: "cline.mcp", - }) - }} - style={{ fontSize: "12px" }}> - Advanced MCP Settings - -
+ +
+ {/* Server Configuration Button */} +
+ { + vscode.postMessage({ type: "openMcpSettings" }) + }}> + + Configure MCP Servers + +
- {/* Bottom padding */} -
+ {/* Advanced Settings Link */} +
+ { + vscode.postMessage({ + type: "openExtensionSettings", + text: "cline.mcp", + }) + }} + style={{ fontSize: "12px" }}> + Advanced MCP Settings + +
+
+ +
) diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx new file mode 100644 index 0000000000..2eebaa6da5 --- /dev/null +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx @@ -0,0 +1,141 @@ +import { useEffect, useState } from "react" +import { VSCodeButton } from "@vscode/webview-ui-toolkit/react" +import { McpMarketplaceItem, McpServer } from "../../../../../src/shared/mcp" +import { vscode } from "../../../utils/vscode" + +interface McpMarketplaceCardProps { + item: McpMarketplaceItem + installedServers: McpServer[] +} + +const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps) => { + const isInstalled = installedServers.some((server) => server.name === item.mcpId) + const [isDownloading, setIsDownloading] = useState(false) + + useEffect(() => { + const handleMessage = (event: MessageEvent) => { + const message = event.data + if (message.type === "mcpDownloadDetails") { + setIsDownloading(false) + } + } + + window.addEventListener("message", handleMessage) + return () => { + window.removeEventListener("message", handleMessage) + } + }, []) + + return ( +
+
+ {item.logoUrl && ( + {`${item.name} + )} +
+
+
+

{item.name}

+
by {item.author}
+
+ { + if (!isInstalled && !isDownloading) { + setIsDownloading(true) + vscode.postMessage({ + type: "downloadMcp", + mcpId: item.mcpId, + }) + } + }}> + + {isInstalled ? "Installed" : isDownloading ? "Downloading..." : "Download"} + +
+

{item.description}

+
+ vscode.postMessage({ type: "openFile", text: item.githubUrl })} + title="View on GitHub"> + + +
+ + {item.githubStars?.toLocaleString() ?? 0} +
+
+ + {item.downloads?.toLocaleString() ?? 0} +
+ {item.requiresApiKey && ( +
+ +
+ )} + {item.isRecommended && ( +
+ +
+ )} +
+
+ + {item.category} + + {item.tags.map((tag) => ( + + {tag} + + ))} +
+
+
+
+ ) +} + +export default McpMarketplaceCard diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx new file mode 100644 index 0000000000..0c9301a151 --- /dev/null +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx @@ -0,0 +1,117 @@ +import { useEffect, useState } from "react" +import { VSCodeButton, VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react" +import { McpMarketplaceItem } from "../../../../../src/shared/mcp" +import { useExtensionState } from "../../../context/ExtensionStateContext" +import { vscode } from "../../../utils/vscode" +import McpMarketplaceCard from "./McpMarketplaceCard" + +const McpMarketplaceView = () => { + const { mcpServers } = useExtensionState() + const [items, setItems] = useState([]) + const [isLoading, setIsLoading] = useState(true) + const [error, setError] = useState(null) + const [isRefreshing, setIsRefreshing] = useState(false) + + useEffect(() => { + const handleMessage = (event: MessageEvent) => { + const message = event.data + if (message.type === "mcpMarketplaceCatalog") { + if (message.error) { + setError(message.error) + } else { + setItems(message.mcpMarketplaceCatalog?.items || []) + setError(null) + } + setIsLoading(false) + setIsRefreshing(false) + } else if (message.type === "mcpDownloadDetails") { + if (message.error) { + setError(message.error) + } + } + } + + window.addEventListener("message", handleMessage) + + // Fetch marketplace catalog + fetchMarketplace() + + return () => { + window.removeEventListener("message", handleMessage) + } + }, []) + + const fetchMarketplace = (forceRefresh: boolean = false) => { + if (forceRefresh) { + setIsRefreshing(true) + } else { + setIsLoading(true) + } + setError(null) + vscode.postMessage({ type: "fetchMcpMarketplace", bool: forceRefresh }) + } + + if (isLoading || isRefreshing) { + return ( +
+ +
+ ) + } + + if (error) { + return ( +
+
{error}
+ fetchMarketplace(true)}> + + Retry + +
+ ) + } + + return ( +
+
+ fetchMarketplace(true)} disabled={isRefreshing}> + + Refresh + +
+ {items.length === 0 ? ( +
+ No MCP servers found in the marketplace +
+ ) : ( + items.map((item) => ) + )} +
+ ) +} + +export default McpMarketplaceView From 8e12bdb00d5b913dbb390db7c549aacc283e7645 Mon Sep 17 00:00:00 2001 From: wen-jy <36290410+WEN-JY@users.noreply.github.com> Date: Fri, 14 Feb 2025 10:03:20 +0800 Subject: [PATCH 020/100] Add support for qwen vl models (#1776) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add support for qwen vl models * feat: add support for qwen vl models * feat: updated the price of the Qianwen model following the BaiLian platform documentation --------- Co-authored-by: 执无 --- .changeset/five-flies-breathe.md | 5 ++ src/shared/api.ts | 104 +++++++++++++++++++++---------- 2 files changed, 77 insertions(+), 32 deletions(-) create mode 100644 .changeset/five-flies-breathe.md diff --git a/.changeset/five-flies-breathe.md b/.changeset/five-flies-breathe.md new file mode 100644 index 0000000000..aa13a9b71f --- /dev/null +++ b/.changeset/five-flies-breathe.md @@ -0,0 +1,5 @@ +--- +"claude-dev": patch +--- + +Add support for qwen vl models diff --git a/src/shared/api.ts b/src/shared/api.ts index 4794327437..0494f2df1f 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -456,80 +456,80 @@ export const qwenModels = { contextWindow: 131_072, supportsImages: false, supportsPromptCache: false, - inputPrice: 0.0035, - outputPrice: 0.007, - cacheWritesPrice: 0.0035, - cacheReadsPrice: 0.007, + inputPrice: 3.5, + outputPrice: 7, + cacheWritesPrice: 3.5, + cacheReadsPrice: 7, }, "qwen-plus-latest": { maxTokens: 129_024, contextWindow: 131_072, supportsImages: false, supportsPromptCache: false, - inputPrice: 0.0008, - outputPrice: 0.002, - cacheWritesPrice: 0.0004, - cacheReadsPrice: 0.001, + inputPrice: 0.8, + outputPrice: 2, + cacheWritesPrice: 0.8, + cacheReadsPrice: 0.2, }, "qwen-turbo-latest": { maxTokens: 1_000_000, contextWindow: 1_000_000, supportsImages: false, supportsPromptCache: false, - inputPrice: 0.0003, - outputPrice: 0.0006, - cacheWritesPrice: 0.00015, - cacheReadsPrice: 0.0003, + inputPrice: 0.8, + outputPrice: 2, + cacheWritesPrice: 0.8, + cacheReadsPrice: 2, }, "qwen-max-latest": { maxTokens: 30_720, contextWindow: 32_768, supportsImages: false, supportsPromptCache: false, - inputPrice: 0.0112, - outputPrice: 0.0448, - cacheWritesPrice: 0.0056, - cacheReadsPrice: 0.0224, + inputPrice: 2.4, + outputPrice: 9.6, + cacheWritesPrice: 2.4, + cacheReadsPrice: 9.6, }, "qwen-coder-plus": { maxTokens: 129_024, contextWindow: 131_072, supportsImages: false, supportsPromptCache: false, - inputPrice: 0.0035, - outputPrice: 0.007, - cacheWritesPrice: 0.0035, - cacheReadsPrice: 0.007, + inputPrice: 3.5, + outputPrice: 7, + cacheWritesPrice: 3.5, + cacheReadsPrice: 7, }, "qwen-plus": { maxTokens: 129_024, contextWindow: 131_072, supportsImages: false, supportsPromptCache: false, - inputPrice: 0.0008, - outputPrice: 0.002, - cacheWritesPrice: 0.0004, - cacheReadsPrice: 0.001, + inputPrice: 0.8, + outputPrice: 2, + cacheWritesPrice: 0.8, + cacheReadsPrice: 0.2, }, "qwen-turbo": { maxTokens: 1_000_000, contextWindow: 1_000_000, supportsImages: false, supportsPromptCache: false, - inputPrice: 0.0003, - outputPrice: 0.0006, - cacheWritesPrice: 0.00015, - cacheReadsPrice: 0.0003, + inputPrice: 0.3, + outputPrice: 0.6, + cacheWritesPrice: 0.3, + cacheReadsPrice: 0.6, }, "qwen-max": { maxTokens: 30_720, contextWindow: 32_768, supportsImages: false, supportsPromptCache: false, - inputPrice: 0.0112, - outputPrice: 0.0448, - cacheWritesPrice: 0.0056, - cacheReadsPrice: 0.0224, + inputPrice: 2.4, + outputPrice: 9.6, + cacheWritesPrice: 2.4, + cacheReadsPrice: 9.6, }, "deepseek-v3": { maxTokens: 8_000, @@ -551,6 +551,46 @@ export const qwenModels = { cacheWritesPrice: 0.55, cacheReadsPrice: 0.14, }, + "qwen-vl-max": { + maxTokens: 30_720, + contextWindow: 32_768, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 3, + outputPrice: 9, + cacheWritesPrice: 3, + cacheReadsPrice: 9, + }, + "qwen-vl-max-latest": { + maxTokens: 129_024, + contextWindow: 131_072, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 3, + outputPrice: 9, + cacheWritesPrice: 3, + cacheReadsPrice: 9, + }, + "qwen-vl-plus": { + maxTokens: 6_000, + contextWindow: 8_000, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 1.5, + outputPrice: 4.5, + cacheWritesPrice: 1.5, + cacheReadsPrice: 4.5, + }, + "qwen-vl-plus-latest": { + maxTokens: 129_024, + contextWindow: 131_072, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 1.5, + outputPrice: 4.5, + cacheWritesPrice: 1.5, + cacheReadsPrice: 4.5, + }, } as const satisfies Record // Mistral From e3cfb405fbfc7bcc6b278c08d49640e2e2ed84a7 Mon Sep 17 00:00:00 2001 From: pashpashpash Date: Thu, 13 Feb 2025 18:22:33 -0800 Subject: [PATCH 021/100] added some logging + error handling --- src/core/webview/ClineProvider.ts | 69 +++++++-- src/shared/ExtensionMessage.ts | 4 +- src/shared/mcp.ts | 12 +- .../mcp/marketplace/McpMarketplaceCard.tsx | 14 +- .../mcp/marketplace/McpMarketplaceView.tsx | 132 +++++++++++++++++- 5 files changed, 209 insertions(+), 22 deletions(-) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 50a08bd532..0b6a2c2424 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -14,7 +14,7 @@ import { selectImages } from "../../integrations/misc/process-images" import { getTheme } from "../../integrations/theme/getTheme" import WorkspaceTracker from "../../integrations/workspace/WorkspaceTracker" import { McpHub } from "../../services/mcp/McpHub" -import { McpMarketplaceCatalog, McpMarketplaceItem } from "../../shared/mcp" +import { McpDownloadResponse, McpMarketplaceCatalog, McpMarketplaceItem, McpServer } from "../../shared/mcp" import { FirebaseAuthManager, UserInfo } from "../../services/auth/FirebaseAuthManager" import { ApiProvider, ModelInfo } from "../../shared/api" import { findLast } from "../../shared/array" @@ -404,7 +404,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { items: (response.data || []).map((item: any) => ({ ...item, githubStars: item.githubStars ?? 0, - downloads: item.downloads ?? 0, + downloadCount: item.downloadCount ?? 0, tags: item.tags ?? [], })), } @@ -438,30 +438,59 @@ export class ClineProvider implements vscode.WebviewViewProvider { private async downloadMcp(mcpId: string) { try { - const response = await axios.post( + // First check if we already have this MCP server installed + const servers = this.mcpHub?.getServers() || [] + const isInstalled = servers.some((server: McpServer) => { + try { + const config = JSON.parse(server.config) + const serverConfig = config.mcpServers[server.name] + const githubUrl = serverConfig.args?.find((arg: string) => arg.includes("github.com")) + return githubUrl?.includes(mcpId) + } catch { + return false + } + }) + + if (isInstalled) { + throw new Error("This MCP server is already installed") + } + + // Fetch server details from marketplace + const response = await axios.post( "https://api.cline.bot/v1/mcp/download", + { mcpId }, { - mcpId, - }, - { - headers: { - "Content-Type": "application/json", - }, + headers: { "Content-Type": "application/json" }, + timeout: 10000, }, ) if (!response.data) { - throw new Error("Invalid response from MCP download API") + throw new Error("Invalid response from MCP marketplace API") } + console.log("[downloadMcp] Response from download API", { response }) + const mcpDetails = response.data + + // Validate required fields + if (!mcpDetails.githubUrl) { + throw new Error("Missing GitHub URL in MCP download response") + } + if (!mcpDetails.readmeContent) { + throw new Error("Missing README content in MCP download response") + } + + // Send details to webview await this.postMessageToWebview({ type: "mcpDownloadDetails", mcpDownloadDetails: mcpDetails, }) - // Create a new task for Cline to set up the MCP server + // Create task with context from README const task = `Set up the MCP server from ${mcpDetails.githubUrl}. Here's some additional context from the README:\n\n${mcpDetails.readmeContent}` + + // Initialize task and show chat view await this.initClineWithTask(task) await this.postMessageToWebview({ type: "action", @@ -469,7 +498,23 @@ export class ClineProvider implements vscode.WebviewViewProvider { }) } catch (error) { console.error("Failed to download MCP:", error) - const errorMessage = error instanceof Error ? error.message : "Failed to download MCP" + let errorMessage = "Failed to download MCP" + + if (axios.isAxiosError(error)) { + if (error.code === "ECONNABORTED") { + errorMessage = "Request timed out. Please try again." + } else if (error.response?.status === 404) { + errorMessage = "MCP server not found in marketplace." + } else if (error.response?.status === 500) { + errorMessage = "Internal server error. Please try again later." + } else if (!error.response && error.request) { + errorMessage = "Network error. Please check your internet connection." + } + } else if (error instanceof Error) { + errorMessage = error.message + } + + // Show error in both notification and marketplace UI vscode.window.showErrorMessage(errorMessage) await this.postMessageToWebview({ type: "mcpDownloadDetails", diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index ee2d1a4d9c..8e00540534 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -5,7 +5,7 @@ import { AutoApprovalSettings } from "./AutoApprovalSettings" import { BrowserSettings } from "./BrowserSettings" import { ChatSettings } from "./ChatSettings" import { HistoryItem } from "./HistoryItem" -import { McpServer, McpMarketplaceCatalog, McpMarketplaceItem } from "./mcp" +import { McpServer, McpMarketplaceCatalog, McpMarketplaceItem, McpDownloadResponse } from "./mcp" // webview will hold state export interface ExtensionMessage { @@ -50,7 +50,7 @@ export interface ExtensionMessage { mcpServers?: McpServer[] mcpMarketplaceCatalog?: McpMarketplaceCatalog error?: string - mcpDownloadDetails?: McpMarketplaceItem + mcpDownloadDetails?: McpDownloadResponse } export type Platform = "aix" | "darwin" | "freebsd" | "linux" | "openbsd" | "sunos" | "win32" | "unknown" diff --git a/src/shared/mcp.ts b/src/shared/mcp.ts index d859dc7b8f..28f4692c93 100644 --- a/src/shared/mcp.ts +++ b/src/shared/mcp.ts @@ -81,7 +81,7 @@ export interface McpMarketplaceItem { readmeContent?: string isRecommended: boolean githubStars: number - downloads: number + downloadCount: number createdAt: string updatedAt: string lastGithubSync: string @@ -90,3 +90,13 @@ export interface McpMarketplaceItem { export interface McpMarketplaceCatalog { items: McpMarketplaceItem[] } + +export interface McpDownloadResponse { + mcpId: string + githubUrl: string + name: string + author: string + description: string + readmeContent: string + requiresApiKey: boolean +} diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx index 2eebaa6da5..b5471bc1e7 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx @@ -9,7 +9,17 @@ interface McpMarketplaceCardProps { } const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps) => { - const isInstalled = installedServers.some((server) => server.name === item.mcpId) + const isInstalled = installedServers.some((server) => { + try { + const config = JSON.parse(server.config) + const serverConfig = config.mcpServers[server.name] + // Extract GitHub URL from args if it's an npm package + const githubUrl = serverConfig.args?.find((arg: string) => arg.includes("github.com")) + return githubUrl?.includes(item.mcpId) || githubUrl?.includes(item.githubUrl) + } catch { + return false + } + }) const [isDownloading, setIsDownloading] = useState(false) useEffect(() => { @@ -94,7 +104,7 @@ const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps)
- {item.downloads?.toLocaleString() ?? 0} + {item.downloadCount?.toLocaleString() ?? 0}
{item.requiresApiKey && (
diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx index 0c9301a151..e346db3cea 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx @@ -1,16 +1,70 @@ -import { useEffect, useState } from "react" +import { useEffect, useMemo, useState } from "react" import { VSCodeButton, VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react" import { McpMarketplaceItem } from "../../../../../src/shared/mcp" import { useExtensionState } from "../../../context/ExtensionStateContext" import { vscode } from "../../../utils/vscode" import McpMarketplaceCard from "./McpMarketplaceCard" +const searchInputStyles = { + width: "100%", + padding: "4px 8px 4px 28px", + background: "var(--vscode-input-background)", + border: "1px solid var(--vscode-input-border)", + color: "var(--vscode-input-foreground)", + borderRadius: "2px", + outline: "none", + transition: "border-color 0.1s ease-in-out, opacity 0.1s ease-in-out", +} + +const selectStyles = { + padding: "4px 8px", + background: "var(--vscode-dropdown-background)", + border: "1px solid var(--vscode-dropdown-border)", + color: "var(--vscode-dropdown-foreground)", + borderRadius: "2px", + outline: "none", + transition: "border-color 0.1s ease-in-out, opacity 0.1s ease-in-out", +} + const McpMarketplaceView = () => { const { mcpServers } = useExtensionState() const [items, setItems] = useState([]) const [isLoading, setIsLoading] = useState(true) const [error, setError] = useState(null) const [isRefreshing, setIsRefreshing] = useState(false) + const [searchQuery, setSearchQuery] = useState("") + const [selectedCategory, setSelectedCategory] = useState(null) + const [sortBy, setSortBy] = useState<"downloadCount" | "stars" | "name">("downloadCount") + + const categories = useMemo(() => { + const uniqueCategories = new Set(items.map((item) => item.category)) + return Array.from(uniqueCategories).sort() + }, [items]) + + const filteredItems = useMemo(() => { + return items + .filter((item) => { + const matchesSearch = + searchQuery === "" || + item.name.toLowerCase().includes(searchQuery.toLowerCase()) || + item.description.toLowerCase().includes(searchQuery.toLowerCase()) || + item.tags.some((tag) => tag.toLowerCase().includes(searchQuery.toLowerCase())) + const matchesCategory = !selectedCategory || item.category === selectedCategory + return matchesSearch && matchesCategory + }) + .sort((a, b) => { + switch (sortBy) { + case "downloadCount": + return b.downloadCount - a.downloadCount + case "stars": + return b.githubStars - a.githubStars + case "name": + return a.name.localeCompare(b.name) + default: + return 0 + } + }) + }, [items, searchQuery, selectedCategory, sortBy]) useEffect(() => { const handleMessage = (event: MessageEvent) => { @@ -89,13 +143,79 @@ const McpMarketplaceView = () => { return (
-
+
+
+
+ setSearchQuery(e.target.value)} + className="mcp-search-input" + style={searchInputStyles} + /> + +
+ + +
fetchMarketplace(true)} disabled={isRefreshing}> Refresh
- {items.length === 0 ? ( + + {filteredItems.length === 0 ? (
{ padding: "20px", color: "var(--vscode-descriptionForeground)", }}> - No MCP servers found in the marketplace + {searchQuery || selectedCategory + ? "No matching MCP servers found" + : "No MCP servers found in the marketplace"}
) : ( - items.map((item) => ) + filteredItems.map((item) => ) )}
) From b8024497f2e556b0ee48409f28e5ce88ebaa7708 Mon Sep 17 00:00:00 2001 From: pashpashpash Date: Thu, 13 Feb 2025 19:11:08 -0800 Subject: [PATCH 022/100] mcp marketplace working --- src/core/webview/ClineProvider.ts | 13 ++----------- .../mcp/marketplace/McpMarketplaceCard.tsx | 12 +----------- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 0b6a2c2424..d4603e0322 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -440,16 +440,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { try { // First check if we already have this MCP server installed const servers = this.mcpHub?.getServers() || [] - const isInstalled = servers.some((server: McpServer) => { - try { - const config = JSON.parse(server.config) - const serverConfig = config.mcpServers[server.name] - const githubUrl = serverConfig.args?.find((arg: string) => arg.includes("github.com")) - return githubUrl?.includes(mcpId) - } catch { - return false - } - }) + const isInstalled = servers.some((server: McpServer) => server.name === mcpId) if (isInstalled) { throw new Error("This MCP server is already installed") @@ -488,7 +479,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { }) // Create task with context from README - const task = `Set up the MCP server from ${mcpDetails.githubUrl}. Here's some additional context from the README:\n\n${mcpDetails.readmeContent}` + const task = `Set up the MCP server from ${mcpDetails.githubUrl}. Use "${mcpDetails.mcpId}" as the server name in cline_mcp_settings.json. Here's some additional context from the README:\n\n${mcpDetails.readmeContent}` // Initialize task and show chat view await this.initClineWithTask(task) diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx index b5471bc1e7..bbe3ace015 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx @@ -9,17 +9,7 @@ interface McpMarketplaceCardProps { } const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps) => { - const isInstalled = installedServers.some((server) => { - try { - const config = JSON.parse(server.config) - const serverConfig = config.mcpServers[server.name] - // Extract GitHub URL from args if it's an npm package - const githubUrl = serverConfig.args?.find((arg: string) => arg.includes("github.com")) - return githubUrl?.includes(item.mcpId) || githubUrl?.includes(item.githubUrl) - } catch { - return false - } - }) + const isInstalled = installedServers.some((server) => server.name === item.mcpId) const [isDownloading, setIsDownloading] = useState(false) useEffect(() => { From ee06c0811c6f72bdba0f48fe13a56cd995d72218 Mon Sep 17 00:00:00 2001 From: stephen8339 <55537353@qq.com> Date: Sat, 15 Feb 2025 03:10:37 +0800 Subject: [PATCH 023/100] add all qwen2.5 coder models (#1797) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add alibaba qwen-max qwen-plus qwen-turbo qwen-coder-plus stable/latest models * add alibaba qwen-max qwen-plus qwen-turbo qwen-coder-plus stable/latest models * Provide the api line choice for international user * Remove redundant code * Copy fixes * Create dry-socks-talk.md * fix problem what is when you use Qwen api provider and then you want to change the api provider ,the apiline dropdown will obscure your api provider drop-down options * feat: add qwen2.5-coder models Description Add new models as list: qwen2.5-coder-32b-instruct qwen2.5-coder-14b-instruct qwen2.5-coder-7b-instruct qwen2.5-coder-3b-instruct Doc: https://help.aliyun.com/zh/model-studio/getting-started/models#9f8890ce29g5u * add changeset * feat: add all alibaba qwen2.5 coder models --------- Co-authored-by: yaojunWang Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Co-authored-by: 刘耸 --- .changeset/gentle-glasses-flow.md | 5 +++ .changeset/slimy-roses-dance.md | 5 +++ src/shared/api.ts | 60 +++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 .changeset/gentle-glasses-flow.md create mode 100644 .changeset/slimy-roses-dance.md diff --git a/.changeset/gentle-glasses-flow.md b/.changeset/gentle-glasses-flow.md new file mode 100644 index 0000000000..c3d9ed997c --- /dev/null +++ b/.changeset/gentle-glasses-flow.md @@ -0,0 +1,5 @@ +--- +"claude-dev": minor +--- + +add alibaba qwen2.5 coder models diff --git a/.changeset/slimy-roses-dance.md b/.changeset/slimy-roses-dance.md new file mode 100644 index 0000000000..e88ef3ca57 --- /dev/null +++ b/.changeset/slimy-roses-dance.md @@ -0,0 +1,5 @@ +--- +"claude-dev": minor +--- + +add alibaba qwen2.5-coder models diff --git a/src/shared/api.ts b/src/shared/api.ts index 0494f2df1f..444242433d 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -451,6 +451,66 @@ export const deepSeekModels = { export type QwenModelId = keyof typeof qwenModels export const qwenDefaultModelId: QwenModelId = "qwen-coder-plus-latest" export const qwenModels = { + "qwen2.5-coder-32b-instruct": { + maxTokens: 8_192, + contextWindow: 131_072, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0.002, + outputPrice: 0.006, + cacheWritesPrice: 0.002, + cacheReadsPrice: 0.006, + }, + "qwen2.5-coder-14b-instruct": { + maxTokens: 8_192, + contextWindow: 131_072, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0.002, + outputPrice: 0.006, + cacheWritesPrice: 0.002, + cacheReadsPrice: 0.006, + }, + "qwen2.5-coder-7b-instruct": { + maxTokens: 8_192, + contextWindow: 131_072, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0.001, + outputPrice: 0.002, + cacheWritesPrice: 0.001, + cacheReadsPrice: 0.002, + }, + "qwen2.5-coder-3b-instruct": { + maxTokens: 8_192, + contextWindow: 32_768, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0.0, + outputPrice: 0.0, + cacheWritesPrice: 0.0, + cacheReadsPrice: 0.0, + }, + "qwen2.5-coder-1.5b-instruct": { + maxTokens: 8_192, + contextWindow: 32_768, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0.0, + outputPrice: 0.0, + cacheWritesPrice: 0.0, + cacheReadsPrice: 0.0, + }, + "qwen2.5-coder-0.5b-instruct": { + maxTokens: 8_192, + contextWindow: 32_768, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0.0, + outputPrice: 0.0, + cacheWritesPrice: 0.0, + cacheReadsPrice: 0.0, + }, "qwen-coder-plus-latest": { maxTokens: 129_024, contextWindow: 131_072, From 267b60c09e55e2c4c84572f4fb63adc081ecd80d Mon Sep 17 00:00:00 2001 From: pashpashpash Date: Fri, 14 Feb 2025 13:48:26 -0800 Subject: [PATCH 024/100] ui polish --- webview-ui/src/components/mcp/McpView.tsx | 8 ++- .../mcp/marketplace/McpMarketplaceView.tsx | 69 +++++++++++++------ 2 files changed, 52 insertions(+), 25 deletions(-) diff --git a/webview-ui/src/components/mcp/McpView.tsx b/webview-ui/src/components/mcp/McpView.tsx index c2e64d24b7..9450140f2f 100644 --- a/webview-ui/src/components/mcp/McpView.tsx +++ b/webview-ui/src/components/mcp/McpView.tsx @@ -105,7 +105,7 @@ const McpView = ({ onDone }: McpViewProps) => { Done
-
+
setActiveTab(e.target.activeid)}> Installed Marketplace @@ -168,11 +168,13 @@ const McpView = ({ onDone }: McpViewProps) => { - +
+ +
-
+
{/* Server Configuration Button */}
{ const { mcpServers } = useExtensionState() @@ -142,16 +154,19 @@ const McpMarketplaceView = () => { } return ( -
+
-
+
+ {" "} + {/* Added minWidth: 0 to prevent flex item from overflowing */}
{ className="codicon codicon-search" style={{ position: "absolute", - left: "8px", + left: "10px", top: "50%", transform: "translateY(-50%)", color: "var(--vscode-input-placeholderForeground)", + pointerEvents: "none", + fontSize: "14px", // Match input text size + lineHeight: 1, // Ensure icon is centered properly }} />
@@ -194,26 +212,33 @@ const McpMarketplaceView = () => {
- fetchMarketplace(true)} disabled={isRefreshing}> + fetchMarketplace(true)} + disabled={isRefreshing} + style={refreshStyles}> Refresh
{filteredItems.length === 0 ? (
Date: Fri, 14 Feb 2025 14:25:58 -0800 Subject: [PATCH 025/100] ui polish --- .../mcp/marketplace/McpMarketplaceCard.tsx | 181 +++++++++--------- 1 file changed, 95 insertions(+), 86 deletions(-) diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx index bbe3ace015..da11b6f78b 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from "react" -import { VSCodeButton } from "@vscode/webview-ui-toolkit/react" +import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react" import { McpMarketplaceItem, McpServer } from "../../../../../src/shared/mcp" import { vscode } from "../../../utils/vscode" @@ -36,101 +36,110 @@ const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps) flexDirection: "column", gap: "12px", }}> -
- {item.logoUrl && ( - {`${item.name} - )} -
-
-
-

{item.name}

-
by {item.author}
-
- { - if (!isInstalled && !isDownloading) { - setIsDownloading(true) - vscode.postMessage({ - type: "downloadMcp", - mcpId: item.mcpId, - }) - } - }}> - - {isInstalled ? "Installed" : isDownloading ? "Downloading..." : "Download"} - -
-

{item.description}

-
- vscode.postMessage({ type: "openFile", text: item.githubUrl })} - title="View on GitHub"> - - -
- - {item.githubStars?.toLocaleString() ?? 0} -
-
- - {item.downloadCount?.toLocaleString() ?? 0} -
- {item.requiresApiKey && ( -
- -
- )} - {item.isRecommended && ( -
- -
- )} -
-
- +
+ {item.logoUrl && ( + {`${item.name} + )} +
+
+
+

{item.name}

+
+ by {item.author} +
+
+ { + if (!isInstalled && !isDownloading) { + setIsDownloading(true) + vscode.postMessage({ + type: "downloadMcp", + mcpId: item.mcpId, + }) + } + }}> + + {isInstalled ? "Installed" : isDownloading ? "Downloading..." : "Download"} + +
+

{item.description}

+
- {item.category} - - {item.tags.map((tag) => ( + + + +
+ + {item.githubStars?.toLocaleString() ?? 0} +
+
+ + {item.downloadCount?.toLocaleString() ?? 0} +
+ {item.requiresApiKey && ( +
+ +
+ )} + {item.isRecommended && ( +
+ +
+ )} +
+
- {tag} + {item.category} - ))} + {item.tags.map((tag) => ( + + {tag} + + ))} +
From 584af643344b28ac878d2315797df1feb9dc1da7 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Fri, 14 Feb 2025 18:48:47 -0800 Subject: [PATCH 026/100] Add terminal context mention (#1805) * Add terminal context mention * Create fuzzy-moose-punch.md --- .changeset/fuzzy-moose-punch.md | 5 +++ src/core/mentions/index.ts | 12 +++++ .../terminal/get-latest-output.ts | 45 +++++++++++++++++++ src/shared/context-mentions.ts | 6 ++- .../src/components/chat/ChatTextArea.tsx | 3 ++ .../src/components/chat/ContextMenu.tsx | 5 +++ webview-ui/src/utils/context-mentions.ts | 6 ++- 7 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 .changeset/fuzzy-moose-punch.md create mode 100644 src/integrations/terminal/get-latest-output.ts diff --git a/.changeset/fuzzy-moose-punch.md b/.changeset/fuzzy-moose-punch.md new file mode 100644 index 0000000000..7034341375 --- /dev/null +++ b/.changeset/fuzzy-moose-punch.md @@ -0,0 +1,5 @@ +--- +"claude-dev": patch +--- + +Add terminal context mention diff --git a/src/core/mentions/index.ts b/src/core/mentions/index.ts index 1c9c122d1d..ea9e2afd79 100644 --- a/src/core/mentions/index.ts +++ b/src/core/mentions/index.ts @@ -7,6 +7,7 @@ import fs from "fs/promises" import { extractTextFromFile } from "../../integrations/misc/extract-text" import { isBinaryFile } from "isbinaryfile" import { diagnosticsToProblemsString } from "../../integrations/diagnostics" +import { getLatestTerminalOutput } from "../../integrations/terminal/get-latest-output" export function openMention(mention?: string): void { if (!mention) { @@ -28,6 +29,8 @@ export function openMention(mention?: string): void { } } else if (mention === "problems") { vscode.commands.executeCommand("workbench.actions.view.problems") + } else if (mention === "terminal") { + vscode.commands.executeCommand("workbench.action.terminal.focus") } else if (mention.startsWith("http")) { vscode.env.openExternal(vscode.Uri.parse(mention)) } @@ -46,6 +49,8 @@ export async function parseMentions(text: string, cwd: string, urlContentFetcher : `'${mentionPath}' (see below for file content)` } else if (mention === "problems") { return `Workspace Problems (see below for diagnostics)` + } else if (mention === "terminal") { + return `Terminal Output (see below for output)` } return match }) @@ -99,6 +104,13 @@ export async function parseMentions(text: string, cwd: string, urlContentFetcher } catch (error) { parsedText += `\n\n\nError fetching diagnostics: ${error.message}\n` } + } else if (mention === "terminal") { + try { + const terminalOutput = await getLatestTerminalOutput() + parsedText += `\n\n\n${terminalOutput}\n` + } catch (error) { + parsedText += `\n\n\nError fetching terminal output: ${error.message}\n` + } } } diff --git a/src/integrations/terminal/get-latest-output.ts b/src/integrations/terminal/get-latest-output.ts new file mode 100644 index 0000000000..0c869e7fad --- /dev/null +++ b/src/integrations/terminal/get-latest-output.ts @@ -0,0 +1,45 @@ +import * as vscode from "vscode" + +/** + * Gets the contents of the active terminal + * @returns The terminal contents as a string + */ +export async function getLatestTerminalOutput(): Promise { + // Store original clipboard content to restore later + const originalClipboard = await vscode.env.clipboard.readText() + + try { + // Select terminal content + await vscode.commands.executeCommand("workbench.action.terminal.selectAll") + + // Copy selection to clipboard + await vscode.commands.executeCommand("workbench.action.terminal.copySelection") + + // Clear the selection + await vscode.commands.executeCommand("workbench.action.terminal.clearSelection") + + // Get terminal contents from clipboard + let terminalContents = (await vscode.env.clipboard.readText()).trim() + + // Check if there's actually a terminal open + if (terminalContents === originalClipboard) { + return "" + } + + // Clean up command separation + const lines = terminalContents.split("\n") + const lastLine = lines.pop()?.trim() + if (lastLine) { + let i = lines.length - 1 + while (i >= 0 && !lines[i].trim().startsWith(lastLine)) { + i-- + } + terminalContents = lines.slice(Math.max(i, 0)).join("\n") + } + + return terminalContents + } finally { + // Restore original clipboard content + await vscode.env.clipboard.writeText(originalClipboard) + } +} diff --git a/src/shared/context-mentions.ts b/src/shared/context-mentions.ts index 3912868b10..5444b903ef 100644 --- a/src/shared/context-mentions.ts +++ b/src/shared/context-mentions.ts @@ -25,6 +25,9 @@ Mention regex: - `problems\b`: - **Exact Word ('problems')**: Matches the exact word 'problems'. - **Word Boundary (`\b`)**: Ensures that 'problems' is matched as a whole word and not as part of another word (e.g., 'problematic'). + - `terminal\b`: + - **Exact Word ('terminal')**: Matches the exact word 'terminal'. + - **Word Boundary (`\b`)**: Ensures that 'terminal' is matched as a whole word and not as part of another word (e.g., 'terminals'). - `(?=[.,;:!?]?(?=[\s\r\n]|$))`: - **Positive Lookahead (`(?=...)`)**: Ensures that the match is followed by specific patterns without including them in the match. @@ -38,11 +41,12 @@ Mention regex: - Mentions that are file or folder paths starting with '/' and containing any non-whitespace characters (including periods within the path). - URLs that start with a protocol (like 'http://') followed by any non-whitespace characters (including query parameters). - The exact word 'problems'. + - The exact word 'terminal'. - 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. - **Global Regex**: - `mentionRegexGlobal`: Creates a global version of the `mentionRegex` to find all matches within a given string. */ -export const mentionRegex = /@((?:\/|\w+:\/\/)[^\s]+?|problems\b)(?=[.,;:!?]?(?=[\s\r\n]|$))/ +export const mentionRegex = /@((?:\/|\w+:\/\/)[^\s]+?|problems\b|terminal\b)(?=[.,;:!?]?(?=[\s\r\n]|$))/ export const mentionRegexGlobal = new RegExp(mentionRegex.source, "g") diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index e86697405f..180332028b 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -243,6 +243,7 @@ const ChatTextArea = forwardRef( const queryItems = useMemo(() => { return [ { type: ContextMenuOptionType.Problems, value: "problems" }, + { type: ContextMenuOptionType.Terminal, value: "terminal" }, ...filePaths .map((file) => "/" + file) .map((path) => ({ @@ -293,6 +294,8 @@ const ChatTextArea = forwardRef( insertValue = value || "" } else if (type === ContextMenuOptionType.Problems) { insertValue = "problems" + } else if (type === ContextMenuOptionType.Terminal) { + insertValue = "terminal" } const { newValue, mentionIndex } = insertMention(textAreaRef.current.value, cursorPosition, insertValue) diff --git a/webview-ui/src/components/chat/ContextMenu.tsx b/webview-ui/src/components/chat/ContextMenu.tsx index 25a7c0902f..8c0b28c513 100644 --- a/webview-ui/src/components/chat/ContextMenu.tsx +++ b/webview-ui/src/components/chat/ContextMenu.tsx @@ -48,6 +48,8 @@ const ContextMenu: React.FC = ({ switch (option.type) { case ContextMenuOptionType.Problems: return Problems + case ContextMenuOptionType.Terminal: + return Terminal case ContextMenuOptionType.URL: return Paste URL to fetch contents case ContextMenuOptionType.NoResults: @@ -85,6 +87,8 @@ const ContextMenu: React.FC = ({ return "folder" case ContextMenuOptionType.Problems: return "warning" + case ContextMenuOptionType.Terminal: + return "terminal" case ContextMenuOptionType.URL: return "link" case ContextMenuOptionType.NoResults: @@ -173,6 +177,7 @@ const ContextMenu: React.FC = ({ /> )} {(option.type === ContextMenuOptionType.Problems || + option.type === ContextMenuOptionType.Terminal || ((option.type === ContextMenuOptionType.File || option.type === ContextMenuOptionType.Folder) && option.value)) && ( Date: Fri, 14 Feb 2025 19:03:11 -0800 Subject: [PATCH 027/100] Add git context mention (#1806) * Add git context mention * Fix context mention highlight * Create long-guests-occur.md --- .changeset/long-guests-occur.md | 5 + src/core/mentions/index.ts | 30 ++- src/core/webview/ClineProvider.ts | 16 ++ src/shared/ExtensionMessage.ts | 3 + src/shared/WebviewMessage.ts | 1 + src/shared/context-mentions.ts | 50 ++--- src/utils/git.ts | 177 ++++++++++++++++++ webview-ui/package-lock.json | 7 + webview-ui/package.json | 1 + .../src/components/chat/ChatTextArea.tsx | 52 ++++- .../src/components/chat/ContextMenu.tsx | 31 ++- webview-ui/src/utils/context-mentions.ts | 100 +++++++++- 12 files changed, 426 insertions(+), 47 deletions(-) create mode 100644 .changeset/long-guests-occur.md create mode 100644 src/utils/git.ts diff --git a/.changeset/long-guests-occur.md b/.changeset/long-guests-occur.md new file mode 100644 index 0000000000..34f6598335 --- /dev/null +++ b/.changeset/long-guests-occur.md @@ -0,0 +1,5 @@ +--- +"claude-dev": patch +--- + +Add git context mention diff --git a/src/core/mentions/index.ts b/src/core/mentions/index.ts index ea9e2afd79..146e823f34 100644 --- a/src/core/mentions/index.ts +++ b/src/core/mentions/index.ts @@ -8,22 +8,24 @@ import { extractTextFromFile } from "../../integrations/misc/extract-text" import { isBinaryFile } from "isbinaryfile" import { diagnosticsToProblemsString } from "../../integrations/diagnostics" import { getLatestTerminalOutput } from "../../integrations/terminal/get-latest-output" +import { getCommitInfo } from "../../utils/git" +import { getWorkingState } from "../../utils/git" export function openMention(mention?: string): void { if (!mention) { return } + const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0) + if (!cwd) { + return + } + if (mention.startsWith("/")) { const relPath = mention.slice(1) - const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0) - if (!cwd) { - return - } const absPath = path.resolve(cwd, relPath) if (mention.endsWith("/")) { vscode.commands.executeCommand("revealInExplorer", vscode.Uri.file(absPath)) - // vscode.commands.executeCommand("vscode.openFolder", , { forceNewWindow: false }) opens in new window } else { openFile(absPath) } @@ -51,6 +53,10 @@ export async function parseMentions(text: string, cwd: string, urlContentFetcher return `Workspace Problems (see below for diagnostics)` } else if (mention === "terminal") { return `Terminal Output (see below for output)` + } else if (mention === "git-changes") { + return `Working directory changes (see below for details)` + } else if (/^[a-f0-9]{7,40}$/.test(mention)) { + return `Git commit '${mention}' (see below for commit info)` } return match }) @@ -111,6 +117,20 @@ export async function parseMentions(text: string, cwd: string, urlContentFetcher } catch (error) { parsedText += `\n\n\nError fetching terminal output: ${error.message}\n` } + } else if (mention === "git-changes") { + try { + const workingState = await getWorkingState(cwd) + parsedText += `\n\n\n${workingState}\n` + } catch (error) { + parsedText += `\n\n\nError fetching working state: ${error.message}\n` + } + } else if (/^[a-f0-9]{7,40}$/.test(mention)) { + try { + const commitInfo = await getCommitInfo(mention, cwd) + parsedText += `\n\n\n${commitInfo}\n` + } catch (error) { + parsedText += `\n\n\nError fetching commit info: ${error.message}\n` + } } } diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 3e59dfad2a..09a735ae1c 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -28,6 +28,7 @@ import { getUri } from "./getUri" import { AutoApprovalSettings, DEFAULT_AUTO_APPROVAL_SETTINGS } from "../../shared/AutoApprovalSettings" import { BrowserSettings, DEFAULT_BROWSER_SETTINGS } from "../../shared/BrowserSettings" import { ChatSettings, DEFAULT_CHAT_SETTINGS } from "../../shared/ChatSettings" +import { searchCommits } from "../../utils/git" /* https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts @@ -803,6 +804,21 @@ export class ClineProvider implements vscode.WebviewViewProvider { } break } + case "searchCommits": { + const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0) + if (cwd) { + try { + const commits = await searchCommits(message.text || "", cwd) + await this.postMessageToWebview({ + type: "commitSearchResults", + commits, + }) + } catch (error) { + console.error(`Error searching commits: ${JSON.stringify(error)}`) + } + } + break + } case "openExtensionSettings": { const settingsFilter = message.text || "" await vscode.commands.executeCommand( diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 70a58fbfd8..6350ed466d 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -1,5 +1,6 @@ // 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' +import { GitCommit } from "../utils/git" import { ApiConfiguration, ModelInfo } from "./api" import { AutoApprovalSettings } from "./AutoApprovalSettings" import { BrowserSettings } from "./BrowserSettings" @@ -26,6 +27,7 @@ export interface ExtensionMessage { | "vsCodeLmModels" | "requestVsCodeLmModels" | "emailSubscribed" + | "commitSearchResults" text?: string action?: | "chatButtonClicked" @@ -46,6 +48,7 @@ export interface ExtensionMessage { openRouterModels?: Record openAiModels?: string[] mcpServers?: McpServer[] + commits?: GitCommit[] } export type Platform = "aix" | "darwin" | "freebsd" | "linux" | "openbsd" | "sunos" | "win32" | "unknown" diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 447193dd18..9b0f43ee9b 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -43,6 +43,7 @@ export interface WebviewMessage { | "accountLoginClicked" | "accountLogoutClicked" | "subscribeEmail" + | "searchCommits" // | "relaunchChromeDebugMode" text?: string disabled?: boolean diff --git a/src/shared/context-mentions.ts b/src/shared/context-mentions.ts index 5444b903ef..eff8f0a5c0 100644 --- a/src/shared/context-mentions.ts +++ b/src/shared/context-mentions.ts @@ -7,22 +7,22 @@ Mention regex: - **Regex Breakdown**: - `/@`: - - **@**: The mention must start with the '@' symbol. + - **@**: The mention must start with the '@' symbol. - `((?:\/|\w+:\/\/)[^\s]+?|problems\b)`: - - **Capturing Group (`(...)`)**: Captures the part of the string that matches one of the specified patterns. - - `(?:\/|\w+:\/\/)`: - - **Non-Capturing Group (`(?:...)`)**: Groups the alternatives without capturing them for back-referencing. - - `\/`: - - **Slash (`/`)**: Indicates that the mention is a file or folder path starting with a '/'. - - `|`: Logical OR. - - `\w+:\/\/`: - - **Protocol (`\w+://`)**: Matches URLs that start with a word character sequence followed by '://', such as 'http://', 'https://', 'ftp://', etc. - - `[^\s]+?`: - - **Non-Whitespace Characters (`[^\s]+`)**: Matches one or more characters that are not whitespace. - - **Non-Greedy (`+?`)**: Ensures the smallest possible match, preventing the inclusion of trailing punctuation. - - `|`: Logical OR. - - `problems\b`: + - **Capturing Group (`(...)`)**: Captures the part of the string that matches one of the specified patterns. + - `(?:\/|\w+:\/\/)`: + - **Non-Capturing Group (`(?:...)`)**: Groups the alternatives without capturing them for back-referencing. + - `\/`: + - **Slash (`/`)**: Indicates that the mention is a file or folder path starting with a '/'. + - `|`: Logical OR. + - `\w+:\/\/`: + - **Protocol (`\w+://`)**: Matches URLs that start with a word character sequence followed by '://', such as 'http://', 'https://', 'ftp://', etc. + - `[^\s]+?`: + - **Non-Whitespace Characters (`[^\s]+`)**: Matches one or more characters that are not whitespace. + - **Non-Greedy (`+?`)**: Ensures the smallest possible match, preventing the inclusion of trailing punctuation. + - `|`: Logical OR. + - `problems\b`: - **Exact Word ('problems')**: Matches the exact word 'problems'. - **Word Boundary (`\b`)**: Ensures that 'problems' is matched as a whole word and not as part of another word (e.g., 'problematic'). - `terminal\b`: @@ -30,23 +30,25 @@ Mention regex: - **Word Boundary (`\b`)**: Ensures that 'terminal' is matched as a whole word and not as part of another word (e.g., 'terminals'). - `(?=[.,;:!?]?(?=[\s\r\n]|$))`: - - **Positive Lookahead (`(?=...)`)**: Ensures that the match is followed by specific patterns without including them in the match. - - `[.,;:!?]?`: - - **Optional Punctuation (`[.,;:!?]?`)**: Matches zero or one of the specified punctuation marks. - - `(?=[\s\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. + - **Positive Lookahead (`(?=...)`)**: Ensures that the match is followed by specific patterns without including them in the match. + - `[.,;:!?]?`: + - **Optional Punctuation (`[.,;:!?]?`)**: Matches zero or one of the specified punctuation marks. + - `(?=[\s\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. - **Summary**: - The regex effectively matches: - - Mentions that are file or folder paths starting with '/' and containing any non-whitespace characters (including periods within the path). - - URLs that start with a protocol (like 'http://') followed by any non-whitespace characters (including query parameters). - - The exact word 'problems'. - - The exact word 'terminal'. + - Mentions that are file or folder paths starting with '/' and containing any non-whitespace characters (including periods within the path). + - URLs that start with a protocol (like 'http://') followed by any non-whitespace characters (including query parameters). + - The exact word 'problems'. + - The exact word 'terminal'. + - The exact word 'git-changes'. - 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. - **Global Regex**: - `mentionRegexGlobal`: Creates a global version of the `mentionRegex` to find all matches within a given string. */ -export const mentionRegex = /@((?:\/|\w+:\/\/)[^\s]+?|problems\b|terminal\b)(?=[.,;:!?]?(?=[\s\r\n]|$))/ +export const mentionRegex = + /@((?:\/|\w+:\/\/)[^\s]+?|[a-f0-9]{7,40}\b|problems\b|terminal\b|git-changes\b)(?=[.,;:!?]?(?=[\s\r\n]|$))/ export const mentionRegexGlobal = new RegExp(mentionRegex.source, "g") diff --git a/src/utils/git.ts b/src/utils/git.ts new file mode 100644 index 0000000000..7ab6d67a07 --- /dev/null +++ b/src/utils/git.ts @@ -0,0 +1,177 @@ +import { exec } from "child_process" +import { promisify } from "util" + +const execAsync = promisify(exec) +const GIT_OUTPUT_LINE_LIMIT = 500 + +export interface GitCommit { + hash: string + shortHash: string + subject: string + author: string + date: string +} + +async function checkGitRepo(cwd: string): Promise { + try { + await execAsync("git rev-parse --git-dir", { cwd }) + return true + } catch (error) { + return false + } +} + +async function checkGitInstalled(): Promise { + try { + await execAsync("git --version") + return true + } catch (error) { + return false + } +} + +export async function searchCommits(query: string, cwd: string): Promise { + try { + const isInstalled = await checkGitInstalled() + if (!isInstalled) { + console.error("Git is not installed") + return [] + } + + const isRepo = await checkGitRepo(cwd) + if (!isRepo) { + console.error("Not a git repository") + return [] + } + + // Search commits by hash or message, limiting to 10 results + const { stdout } = await execAsync( + `git log -n 10 --format="%H%n%h%n%s%n%an%n%ad" --date=short ` + `--grep="${query}" --regexp-ignore-case`, + { cwd }, + ) + + let output = stdout + if (!output.trim() && /^[a-f0-9]+$/i.test(query)) { + // If no results from grep search and query looks like a hash, try searching by hash + const { stdout: hashStdout } = await execAsync( + `git log -n 10 --format="%H%n%h%n%s%n%an%n%ad" --date=short ` + `--author-date-order ${query}`, + { cwd }, + ).catch(() => ({ stdout: "" })) + + if (!hashStdout.trim()) { + return [] + } + + output = hashStdout + } + + const commits: GitCommit[] = [] + const lines = output + .trim() + .split("\n") + .filter((line) => line !== "--") + + for (let i = 0; i < lines.length; i += 5) { + commits.push({ + hash: lines[i], + shortHash: lines[i + 1], + subject: lines[i + 2], + author: lines[i + 3], + date: lines[i + 4], + }) + } + + return commits + } catch (error) { + console.error("Error searching commits:", error) + return [] + } +} + +export async function getCommitInfo(hash: string, cwd: string): Promise { + try { + const isInstalled = await checkGitInstalled() + if (!isInstalled) { + return "Git is not installed" + } + + const isRepo = await checkGitRepo(cwd) + if (!isRepo) { + return "Not a git repository" + } + + // Get commit info, stats, and diff separately + const { stdout: info } = await execAsync(`git show --format="%H%n%h%n%s%n%an%n%ad%n%b" --no-patch ${hash}`, { + cwd, + }) + const [fullHash, shortHash, subject, author, date, body] = info.trim().split("\n") + + const { stdout: stats } = await execAsync(`git show --stat --format="" ${hash}`, { cwd }) + + const { stdout: diff } = await execAsync(`git show --format="" ${hash}`, { cwd }) + + const summary = [ + `Commit: ${shortHash} (${fullHash})`, + `Author: ${author}`, + `Date: ${date}`, + `\nMessage: ${subject}`, + body ? `\nDescription:\n${body}` : "", + "\nFiles Changed:", + stats.trim(), + "\nFull Changes:", + ].join("\n") + + const output = summary + "\n\n" + diff.trim() + return truncateOutput(output) + } catch (error) { + console.error("Error getting commit info:", error) + return `Failed to get commit info: ${error instanceof Error ? error.message : String(error)}` + } +} + +export async function getWorkingState(cwd: string): Promise { + try { + const isInstalled = await checkGitInstalled() + if (!isInstalled) { + return "Git is not installed" + } + + const isRepo = await checkGitRepo(cwd) + if (!isRepo) { + return "Not a git repository" + } + + // Get status of working directory + const { stdout: status } = await execAsync("git status --short", { cwd }) + if (!status.trim()) { + return "No changes in working directory" + } + + // Get all changes (both staged and unstaged) compared to HEAD + const { stdout: diff } = await execAsync("git diff HEAD", { cwd }) + const output = `Working directory changes:\n\n${status}\n\n${diff}`.trim() + return truncateOutput(output) + } catch (error) { + console.error("Error getting working state:", error) + return `Failed to get working state: ${error instanceof Error ? error.message : String(error)}` + } +} + +function truncateOutput(content: string): string { + if (!GIT_OUTPUT_LINE_LIMIT) { + return content + } + + const lines = content.split("\n") + if (lines.length <= GIT_OUTPUT_LINE_LIMIT) { + return content + } + + const beforeLimit = Math.floor(GIT_OUTPUT_LINE_LIMIT * 0.2) // 20% of lines before + const afterLimit = GIT_OUTPUT_LINE_LIMIT - beforeLimit // remaining 80% after + return [ + ...lines.slice(0, beforeLimit), + `\n[...${lines.length - GIT_OUTPUT_LINE_LIMIT} lines omitted...]\n`, + ...lines.slice(-afterLimit), + ].join("\n") +} diff --git a/webview-ui/package-lock.json b/webview-ui/package-lock.json index 4a06dc2e45..e1015aca29 100644 --- a/webview-ui/package-lock.json +++ b/webview-ui/package-lock.json @@ -13,6 +13,7 @@ "debounce": "^2.1.1", "fast-deep-equal": "^3.1.3", "fuse.js": "^7.0.0", + "fzf": "^0.5.2", "pretty-bytes": "^6.1.1", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -9825,6 +9826,12 @@ "node": ">=10" } }, + "node_modules/fzf": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fzf/-/fzf-0.5.2.tgz", + "integrity": "sha512-Tt4kuxLXFKHy8KT40zwsUPUkg1CrsgY25FxA2U/j/0WgEDCk3ddc/zLTCCcbSHX9FcKtLuVaDGtGE/STWC+j3Q==", + "license": "BSD-3-Clause" + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", diff --git a/webview-ui/package.json b/webview-ui/package.json index 2f023747d1..8f15b44187 100644 --- a/webview-ui/package.json +++ b/webview-ui/package.json @@ -8,6 +8,7 @@ "debounce": "^2.1.1", "fast-deep-equal": "^3.1.3", "fuse.js": "^7.0.0", + "fzf": "^0.5.2", "pretty-bytes": "^6.1.1", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index 180332028b..2e642f9a46 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -1,9 +1,10 @@ import { VSCodeButton } from "@vscode/webview-ui-toolkit/react" import React, { forwardRef, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react" import DynamicTextArea from "react-textarea-autosize" -import { useClickAway, useWindowSize } from "react-use" +import { useClickAway, useEvent, useWindowSize } from "react-use" import styled from "styled-components" import { mentionRegex, mentionRegexGlobal } from "../../../../src/shared/context-mentions" +import { ExtensionMessage } from "../../../../src/shared/ExtensionMessage" import { useExtensionState } from "../../context/ExtensionStateContext" import { ContextMenuOptionType, @@ -12,16 +13,15 @@ import { removeMention, shouldShowContextMenu, } from "../../utils/context-mentions" +import { useMetaKeyDetection, useShortcut } from "../../utils/hooks" import { validateApiConfiguration, validateModelId } from "../../utils/validate" import { vscode } from "../../utils/vscode" import { CODE_BLOCK_BG_COLOR } from "../common/CodeBlock" import Thumbnails from "../common/Thumbnails" +import Tooltip from "../common/Tooltip" import ApiOptions, { normalizeApiConfiguration } from "../settings/ApiOptions" import { MAX_IMAGES_PER_MESSAGE } from "./ChatView" import ContextMenu from "./ContextMenu" -import { useShortcut } from "../../utils/hooks" -import Tooltip from "../common/Tooltip" -import { useMetaKeyDetection } from "../../utils/hooks" interface ChatTextAreaProps { inputValue: string @@ -215,6 +215,8 @@ const ChatTextArea = forwardRef( ) => { const { filePaths, chatSettings, apiConfiguration, openRouterModels, platform } = useExtensionState() const [isTextAreaFocused, setIsTextAreaFocused] = useState(false) + const [gitCommits, setGitCommits] = useState([]) + const [thumbnailsHeight, setThumbnailsHeight] = useState(0) const [textAreaBaseHeight, setTextAreaBaseHeight] = useState(undefined) const [showContextMenu, setShowContextMenu] = useState(false) @@ -240,10 +242,40 @@ const ChatTextArea = forwardRef( // Add a ref to track previous menu state const prevShowModelSelector = useRef(showModelSelector) + // Fetch git commits when Git is selected or when typing a hash + useEffect(() => { + if (selectedType === ContextMenuOptionType.Git || /^[a-f0-9]+$/i.test(searchQuery)) { + vscode.postMessage({ + type: "searchCommits", + text: searchQuery || "", + }) + } + }, [selectedType, searchQuery]) + + const handleMessage = useCallback((event: MessageEvent) => { + const message: ExtensionMessage = event.data + switch (message.type) { + case "commitSearchResults": { + const commits = + message.commits?.map((commit: any) => ({ + type: ContextMenuOptionType.Git, + value: commit.hash, + label: commit.subject, + description: `${commit.shortHash} by ${commit.author} on ${commit.date}`, + })) || [] + setGitCommits(commits) + break + } + } + }, []) + + useEvent("message", handleMessage) + const queryItems = useMemo(() => { return [ { type: ContextMenuOptionType.Problems, value: "problems" }, { type: ContextMenuOptionType.Terminal, value: "terminal" }, + ...gitCommits, ...filePaths .map((file) => "/" + file) .map((path) => ({ @@ -251,7 +283,7 @@ const ChatTextArea = forwardRef( value: path, })), ] - }, [filePaths]) + }, [filePaths, gitCommits]) useEffect(() => { const handleClickOutside = (event: MouseEvent) => { @@ -275,7 +307,11 @@ const ChatTextArea = forwardRef( return } - if (type === ContextMenuOptionType.File || type === ContextMenuOptionType.Folder) { + if ( + type === ContextMenuOptionType.File || + type === ContextMenuOptionType.Folder || + type === ContextMenuOptionType.Git + ) { if (!value) { setSelectedType(type) setSearchQuery("") @@ -296,6 +332,8 @@ const ChatTextArea = forwardRef( insertValue = "problems" } else if (type === ContextMenuOptionType.Terminal) { insertValue = "terminal" + } else if (type === ContextMenuOptionType.Git) { + insertValue = value || "" } const { newValue, mentionIndex } = insertMention(textAreaRef.current.value, cursorPosition, insertValue) @@ -898,7 +936,7 @@ const ChatTextArea = forwardRef( borderTop: 0, borderColor: "transparent", borderBottom: `${thumbnailsHeight + 6}px solid transparent`, - padding: "9px 49px 3px 9px", + padding: "9px 28px 3px 9px", }} /> = ({ return Paste URL to fetch contents case ContextMenuOptionType.NoResults: return No results found + case ContextMenuOptionType.Git: + if (option.value) { + return ( +
+ {option.label} + + {option.description} + +
+ ) + } else { + return Git Commits + } case ContextMenuOptionType.File: case ContextMenuOptionType.Folder: if (option.value) { @@ -91,6 +112,8 @@ const ContextMenu: React.FC = ({ return "terminal" case ContextMenuOptionType.URL: return "link" + case ContextMenuOptionType.Git: + return "git-commit" case ContextMenuOptionType.NoResults: return "info" default: @@ -165,7 +188,9 @@ const ContextMenu: React.FC = ({ /> {renderOptionContent(option)}
- {(option.type === ContextMenuOptionType.File || option.type === ContextMenuOptionType.Folder) && + {(option.type === ContextMenuOptionType.File || + option.type === ContextMenuOptionType.Folder || + option.type === ContextMenuOptionType.Git) && !option.value && ( = ({ )} {(option.type === ContextMenuOptionType.Problems || option.type === ContextMenuOptionType.Terminal || - ((option.type === ContextMenuOptionType.File || option.type === ContextMenuOptionType.Folder) && + ((option.type === ContextMenuOptionType.File || + option.type === ContextMenuOptionType.Folder || + option.type === ContextMenuOptionType.Git) && option.value)) && ( 0 ? folders : [{ type: ContextMenuOptionType.NoResults }] } + if (selectedType === ContextMenuOptionType.Git) { + const commits = queryItems.filter((item) => item.type === ContextMenuOptionType.Git) + return commits.length > 0 ? [workingChanges, ...commits] : [workingChanges] + } + return [ { type: ContextMenuOptionType.URL }, { type: ContextMenuOptionType.Problems }, { type: ContextMenuOptionType.Terminal }, + { type: ContextMenuOptionType.Git }, { type: ContextMenuOptionType.Folder }, { type: ContextMenuOptionType.File }, ] } const lowerQuery = query.toLowerCase() + const suggestions: ContextMenuQueryItem[] = [] + // Check for top-level option matches + if ("git".startsWith(lowerQuery)) { + suggestions.push({ + type: ContextMenuOptionType.Git, + label: "Git Commits", + description: "Search repository history", + }) + } else if ("git-changes".startsWith(lowerQuery)) { + suggestions.push(workingChanges) + } + if ("problems".startsWith(lowerQuery)) { + suggestions.push({ type: ContextMenuOptionType.Problems }) + } if (query.startsWith("http")) { - return [{ type: ContextMenuOptionType.URL, value: query }] - } else { - const matchingItems = queryItems.filter((item) => item.value?.toLowerCase().includes(lowerQuery)) + suggestions.push({ type: ContextMenuOptionType.URL, value: query }) + } - if (matchingItems.length > 0) { - return matchingItems.map((item) => ({ - type: item.type, - value: item.value, - })) + // Add exact SHA matches to suggestions + if (/^[a-f0-9]{7,40}$/i.test(lowerQuery)) { + const exactMatches = queryItems.filter( + (item) => item.type === ContextMenuOptionType.Git && item.value?.toLowerCase() === lowerQuery, + ) + if (exactMatches.length > 0) { + suggestions.push(...exactMatches) } else { - return [{ type: ContextMenuOptionType.NoResults }] + // If no exact match but valid SHA format, add as option + suggestions.push({ + type: ContextMenuOptionType.Git, + value: lowerQuery, + label: `Commit ${lowerQuery}`, + description: "Git commit hash", + }) } } + + // Create searchable strings array for fzf + const searchableItems = queryItems.map((item) => ({ + original: item, + searchStr: [item.value, item.label, item.description].filter(Boolean).join(" "), + })) + + // Initialize fzf instance for fuzzy search + const fzf = new Fzf(searchableItems, { + selector: (item) => item.searchStr, + }) + + // Get fuzzy matching items + const matchingItems = query ? fzf.find(query).map((result) => result.item.original) : [] + + // Separate matches by type + const fileMatches = matchingItems.filter( + (item) => item.type === ContextMenuOptionType.File || item.type === ContextMenuOptionType.Folder, + ) + const gitMatches = matchingItems.filter((item) => item.type === ContextMenuOptionType.Git) + const otherMatches = matchingItems.filter( + (item) => + item.type !== ContextMenuOptionType.File && + item.type !== ContextMenuOptionType.Folder && + item.type !== ContextMenuOptionType.Git, + ) + + // Combine suggestions with matching items in the desired order + if (suggestions.length > 0 || matchingItems.length > 0) { + const allItems = [...suggestions, ...fileMatches, ...gitMatches, ...otherMatches] + + // Remove duplicates based on type and value + const seen = new Set() + const deduped = allItems.filter((item) => { + const key = `${item.type}-${item.value}` + if (seen.has(key)) return false + seen.add(key) + return true + }) + + return deduped + } + + return [{ type: ContextMenuOptionType.NoResults }] } export function shouldShowContextMenu(text: string, position: number): boolean { From 7ae437fb9442b02dbedb82d8ceb0ecac3a6e097d Mon Sep 17 00:00:00 2001 From: AlwaleedAlwabel Date: Sat, 15 Feb 2025 13:33:49 +0300 Subject: [PATCH 028/100] Add Arabic language **Add CODE_OF_CONDUCT in ar-sa. **Add CONTRIBUTING in ar-sa. **Add README in ar-sa. --- locales/ar-sa/CODE_OF_CONDUCT.md | 47 ++++++++ locales/ar-sa/CONTRIBUTING.md | 93 +++++++++++++++ locales/ar-sa/README.md | 189 +++++++++++++++++++++++++++++++ 3 files changed, 329 insertions(+) create mode 100644 locales/ar-sa/CODE_OF_CONDUCT.md create mode 100644 locales/ar-sa/CONTRIBUTING.md create mode 100644 locales/ar-sa/README.md diff --git a/locales/ar-sa/CODE_OF_CONDUCT.md b/locales/ar-sa/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..d41eea1f96 --- /dev/null +++ b/locales/ar-sa/CODE_OF_CONDUCT.md @@ -0,0 +1,47 @@ +# ميثاق المساهمين + +## تعهدنا + +نحن المساهمون والقائمون على هذا المشروع، نتعهد بتوفير بيئة مفتوحة ومرحبة، ونجعل المشاركة في مشروعنا ومجتمعنا تجربة خالية من التحرش للجميع، بغض النظر عن العمر، أو حجم الجسم، أو الإعاقة، أو العرق، أو الخصائص الجنسية، أو الهوية الجنسية والتعبير عنها، أو مستوى الخبرة، أو التعليم، أو الوضع الاجتماعي والاقتصادي، أو الجنسية، أو المظهر الشخصي، أو العرق، أو الدين، أو الهوية الجنسية والتوجه الجنسي. + +## معاييرنا + +أمثلة على السلوك الذي يساهم في خلق بيئة إيجابية تشمل: + +- استخدام لغة ترحيبية وشاملة +- احترام وجهات النظر والخبرات المختلفة +- تقبل النقد البناء برحابة صدر +- التركيز على ما هو الأفضل للمجتمع +- إظهار التعاطف تجاه أعضاء المجتمع الآخرين + +أمثلة على السلوك غير المقبول من قبل المشاركين تشمل: + +- استخدام لغة أو صور جنسية والاهتمام الجنسي غير المرغوب فيه أو التحرش الجنسي +- التصيد، والتعليقات المهينة/المسيئة، والهجمات الشخصية أو السياسية +- التحرش العلني أو الخاص +- نشر معلومات الآخرين الخاصة، مثل العنوان الفعلي أو الإلكتروني، دون إذن صريح +- أي سلوك آخر يمكن اعتباره غير لائق في بيئة مهنية + +## مسؤولياتنا + +يتحمل القائمون على المشروع مسؤولية توضيح معايير السلوك المقبول، ومن المتوقع أن يتخذوا إجراءات تصحيحية مناسبة وعادلة استجابة لأي حالات سلوك غير مقبول. + +يحق للقائمين على المشروع إزالة أو تعديل أو رفض التعليقات والالتزامات والتعليمات البرمجية وتعديلات wiki والمشكلات والمساهمات الأخرى التي لا تتماشى مع مدونة قواعد السلوك هذه، أو حظر أي مساهم بشكل مؤقت أو دائم بسبب سلوكيات أخرى يعتبرونها غير لائقة أو مهددة أو مسيئة أو ضارة، كما أنهم يتحملون مسؤولية ذلك. + +## النطاق + +تنطبق مدونة قواعد السلوك هذه داخل مساحات المشروع وفي الأماكن العامة عندما يمثل الفرد المشروع أو مجتمعه. تتضمن أمثلة تمثيل مشروع أو مجتمع استخدام عنوان بريد إلكتروني رسمي للمشروع، أو النشر عبر حساب رسمي على وسائل التواصل الاجتماعي، أو العمل كممثل معين في حدث عبر الإنترنت أو خارجه. يمكن للقائمين على المشروع تحديد وتوضيح تمثيل المشروع بشكل أكبر. + +## التنفيذ + +يمكن الإبلاغ عن حالات السلوك المسيء أو التحرش أو السلوك غير المقبول عن طريق الاتصال بفريق المشروع على hi@cline.bot. ستتم مراجعة جميع الشكاوى والتحقيق فيها وستؤدي إلى استجابة تعتبر ضرورية ومناسبة للظروف. يلتزم فريق المشروع بالحفاظ على السرية فيما يتعلق بالمبلغ عن الحادث. يمكن نشر مزيد من التفاصيل حول سياسات التنفيذ المحددة بشكل منفصل. + +قد يواجه القائمون على المشروع الذين لا يتبعون أو يفرضون مدونة قواعد السلوك بحسن نية تداعيات مؤقتة أو دائمة على النحو الذي يحدده الأعضاء الآخرون في قيادة المشروع. + +## الإسناد + +تم اقتباس مدونة قواعد السلوك هذه من [تعهد المساهم][homepage]، الإصدار 1.4، متاح على https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +للحصول على إجابات للأسئلة الشائعة حول مدونة قواعد السلوك هذه، راجع https://www.contributor-covenant.org/faq \ No newline at end of file diff --git a/locales/ar-sa/CONTRIBUTING.md b/locales/ar-sa/CONTRIBUTING.md new file mode 100644 index 0000000000..8d56263fd2 --- /dev/null +++ b/locales/ar-sa/CONTRIBUTING.md @@ -0,0 +1,93 @@ +# المساهمة في Cline + +نحن سعداء لاهتمامك بالمساهمة في Cline. سواء كنت تصلح خطأً أو تضيف ميزة أو تحسن الوثائق لدينا، فإن كل مساهمة تجعل Cline أذكى! للحفاظ على مجتمعنا نابضًا بالحياة وترحيبيًا، يجب على جميع الأعضاء الالتزام بـ [مدونة قواعد السلوك](CODE_OF_CONDUCT.md) لدينا. + +## الإبلاغ عن الأخطاء أو المشكلات + +تساعد تقارير الأخطاء على جعل Cline أفضل للجميع! قبل إنشاء مشكلة جديدة، يرجى [البحث عن المشكلات الموجودة](https://github.com/cline/cline/issues) لتجنب الازدواجية. عندما تكون جاهزًا للإبلاغ عن خطأ، انتقل إلى [صفحة المشكلات](https://github.com/cline/cline/issues/new/choose) حيث ستجد قالبًا لمساعدتك في ملء المعلومات ذات الصلة. + +
+ 🔐 مهم: إذا اكتشفت ثغرة أمنية، فيرجى استخدام أداة الأمان على Github للإبلاغ عنها بشكل خاص. +
+ +## تحديد ما يجب العمل عليه + +تبحث عن مساهمة أولى جيدة؟ تحقق من المشكلات المميزة بـ ["good first issue"](https://github.com/cline/cline/labels/good%20first%20issue) أو ["help wanted"](https://github.com/cline/cline/labels/help%20wanted). تم تحديد هذه المشكلات خصيصًا للمساهمين الجدد والمجالات التي نرحب فيها بالمساعدة! + +نرحب أيضًا بالمساهمات في [الوثائق](https://github.com/cline/cline/tree/main/docs) لدينا! سواء كان تصحيح أخطاء إملائية، أو تحسين الأدلة الحالية، أو إنشاء محتوى تعليمي جديد - نود بناء مستودع موارد مدفوع من المجتمع يساعد الجميع على الاستفادة القصوى من Cline. يمكنك البدء بالغوص في `/docs` والبحث عن مجالات تحتاج إلى تحسين. + +إذا كنت تخطط للعمل على ميزة أكبر، فيرجى إنشاء [طلب ميزة](https://github.com/cline/cline/discussions/categories/feature-requests?discussions_q=is%3Aopen+category%3A%22Feature+Requests%22+sort%3Atop) أولاً حتى نتمكن من مناقشة ما إذا كان ذلك يتماشى مع رؤية Cline. + +## إعداد التطوير + +1. **إضافات VS Code** + + - عند فتح المشروع، سيطالبك VS Code بتثبيت الإضافات الموصى بها + - هذه الإضافات مطلوبة للتطوير - يرجى قبول جميع مطالبات التثبيت + - إذا تجاهلت المطالبات، يمكنك تثبيتها يدويًا من لوحة الإضافات + +2. **التطوير المحلي** + - قم بتشغيل `npm run install:all` لتثبيت التبعيات + - قم بتشغيل `npm run test` لتشغيل الاختبارات محليًا + - قبل تقديم طلب السحب، قم بتشغيل `npm run format:fix` لتنسيق التعليمات البرمجية الخاصة بك + +## كتابة وتقديم التعليمات البرمجية + +يمكن لأي شخص المساهمة بالتعليمات البرمجية في Cline، لكننا نطلب منك اتباع هذه الإرشادات لضمان دمج مساهماتك بسلاسة: + +1. **احتفظ بطلبات السحب مركزة** + + - قيد طلبات السحب بميزة واحدة أو إصلاح خطأ + - قسم التغييرات الأكبر إلى طلبات سحب أصغر ومتصلة + - قسم التغييرات إلى التزامات منطقية يمكن مراجعتها بشكل مستقل + +2. **جودة التعليمات البرمجية** + + - قم بتشغيل `npm run lint` للتحقق من نمط التعليمات البرمجية + - قم بتشغيل `npm run format` لتنسيق التعليمات البرمجية تلقائيًا + - يجب أن تجتاز جميع طلبات السحب عمليات التحقق المستمر التي تشمل كلاً من التنضيد والتنسيق + - تعامل مع أي تحذيرات أو أخطاء ESLint قبل التقديم + - اتبع أفضل ممارسات TypeScript والحفاظ على سلامة النوع + +3. **الاختبار** + + - أضف اختبارات للميزات الجديدة + - قم بتشغيل `npm test` للتأكد من اجتياز جميع الاختبارات + - قم بتحديث الاختبارات الحالية إذا كانت تغييراتك تؤثر عليها + - تضمين كل من اختبارات الوحدة واختبارات التكامل حيثما كان ذلك مناسبًا + +4. **إدارة الإصدار مع Changesets** + + - أنشئ changeset لأي تغييرات واجهة المستخدم باستخدام `npm run changeset` + - اختر زيادة الإصدار المناسبة: + - `major` للتغييرات الكبيرة (1.0.0 → 2.0.0) + - `minor` للميزات الجديدة (1.0.0 → 1.1.0) + - `patch` لإصلاحات الأخطاء (1.0.0 → 1.0.1) + - اكتب رسائل changeset واضحة ووصفية تشرح التأثير + - لا تتطلب التغييرات في الوثائق فقط changesets + +5. **إرشادات الالتزام (Commit Guidelines)** + + - اكتب رسائل التزام واضحة وواصفة + - استخدم تنسيق الالتزام التقليدي (مثل: "feat:", "fix:", "docs:") + - أشر إلى القضايا ذات الصلة في الالتزامات باستخدام #رقم-القضية + +6. **قبل الإرسال** + + - قم بإعادة دمج فرعك مع أحدث إصدار من الفرع الرئيسي + - تأكد من أن الفرع الخاص بك يُبنى بنجاح + - تحقق من اجتياز جميع الاختبارات + - راجع التغييرات الخاصة بك للتأكد من عدم وجود تعليمات تصحيح الأخطاء أو سجلات وحدة التحكم + +7. **وصف طلب السحب (Pull Request Description)** + + - صف بوضوح ما تقوم به التغييرات + - قم بتضمين خطوات لاختبار التغييرات + - أدرج أي تغييرات غير متوافقة + - أضف لقطات شاشة للتغييرات في واجهة المستخدم + +## اتفاقية المساهمة + +من خلال إرسال طلب سحب، فإنك توافق على أن مساهماتك سيتم ترخيصها بنفس ترخيص المشروع ([Apache 2.0](LICENSE)). + +تذكر: المساهمة في Cline لا تقتصر فقط على كتابة الكود - إنها تتعلق بأن تكون جزءًا من مجتمع يُشكل مستقبل التطوير بمساعدة الذكاء الاصطناعي. لنبنِ شيئًا رائعًا معًا! 🚀 \ No newline at end of file diff --git a/locales/ar-sa/README.md b/locales/ar-sa/README.md new file mode 100644 index 0000000000..05360b9b1c --- /dev/null +++ b/locales/ar-sa/README.md @@ -0,0 +1,189 @@ + + +# Cline – \#1 على OpenRouter + +

+ +

+ + + +التقى Cline، مساعد الذكاء الاصطناعي الذي يمكنه استخدام **سطر الأوامر** و **محرر النصوص** الخاص بك. + +بفضل [قدرات Claude 3.5 Sonnet على التعليمات البرمجية الوكيلة](https://www-cdn.anthropic.com/fed9cc193a14b84131812372d8d5857f8f304c52/Model_Card_Claude_3_Addendum.pdf)، يمكن لـ Cline التعامل مع مهام تطوير البرامج المعقدة خطوة بخطوة. مع الأدوات التي تسمح له بإنشاء وتعديل الملفات، واستكشاف المشاريع الكبيرة، واستخدام المتصفح، وتنفيذ أوامر الطرفية (بعد منحك الإذن)، يمكنه مساعدتك بطرق تتجاوز إكمال الكود أو الدعم الفني. يمكن لـ Cline أيضًا استخدام بروتوكول سياق النموذج (MCP) لإنشاء أدوات جديدة وتوسيع قدراته الخاصة. في حين تعمل النصوص البرمجية الآلية المستقلة تقليديًا في بيئات محاصرة، توفر هذه الإضافة واجهة رسومية لموافقة المستخدم على كل تغيير في الملف وأمر طرفية، مما يوفر طريقة آمنة وسهلة الاستخدام لاستكشاف إمكانات الذكاء الاصطناعي الوكيل. + +1. أدخل مهمتك وأضف الصور لتحويل المحاكاة إلى تطبيقات وظيفية أو إصلاح الأخطاء مع لقطات الشاشة. +2. يبدأ Cline بتحليل هيكل الملفات الخاصة بك وشجرة التعريف المصدرية، وإجراء عمليات بحث regex، وقراءة الملفات ذات الصلة للاطلاع على المشاريع الحالية. من خلال إدارة المعلومات التي يتم إضافتها إلى السياق بعناية، يمكن لـ Cline تقديم مساعدة قيمة حتى للمشاريع الكبيرة والمعقدة دون إرهاق نافذة السياق. +3. بمجرد حصول Cline على المعلومات التي يحتاجها، يمكنه: + - إنشاء وتعديل الملفات + مراقبة أخطاء Linter/Compiler أثناء السير، مما يسمح له بإصلاح المشكلات مثل الواردات المفقودة وأخطاء البناء النحوي بمفرده. + - تنفيذ الأوامر مباشرة في الطرفية الخاصة بك ومراقبة إخراجها أثناء العمل، مما يسمح له على سبيل المثال بالاستجابة لمشكلات خادم التطوير بعد تعديل ملف. + - بالنسبة لمهام تطوير الويب، يمكن لـ Cline إطلاق الموقع في متصفح بلا رأس، والنقر، وكتابة النص، والتمرير، والتقاط لقطات الشاشة + سجلات وحدة التحكم، مما يسمح له بإصلاح أخطاء وقت التشغيل والأخطاء البصرية. +4. عند اكتمال المهمة، سيقدم Cline النتيجة لك مع أمر طرفية مثل `open -a "Google Chrome" index.html`، والذي تقوم بتشغيله بنقرة زر. + +> [!TIP] +> استخدم اختصار `CMD/CTRL + Shift + P` لفتح لوحة الأوامر واكتب "Cline: Open In New Tab" لفتح الإضافة كعلامة تبويب في محرر النصوص الخاص بك. يتيح لك هذا استخدام Cline جنبًا إلى جنب مع مستكشف الملفات الخاص بك، ورؤية كيف يغير مساحة العمل الخاصة بك بوضوح أكبر. + +--- + + + +### استخدم أي واجهة برمجة تطبيقات ونموذج + +يدعم Cline مقدمي واجهات برمجة التطبيقات مثل OpenRouter و Anthropic و OpenAI و Google Gemini و AWS Bedrock و Azure و GCP Vertex. يمكنك أيضًا تكوين أي واجهة برمجة تطبيقات متوافقة مع OpenAI، أو استخدام نموذج محلي من خلال LM Studio/Ollama. إذا كنت تستخدم OpenRouter، فستقوم الإضافة بجلب قائمة النماذج الأحدث الخاصة بهم، مما يسمح لك باستخدام أحدث النماذج بمجرد توفرها. + +تتتبع الإضافة أيضًا إجمالي الرموز والاستخدام الخاص بواجهة برمجة التطبيقات لدورة المهمة بأكملها وطلبات فردية، مما يبقيك على اطلاع بالإنفاق في كل خطوة. + + + +
+ + + +### تشغيل الأوامر في الطرفية + +بفضل [تحديثات تكامل الشل الجديدة في VSCode v1.93](https://code.visualstudio.com/updates/v1_93#_terminal-shell-integration-api)، يمكن لـ Cline تنفيذ الأوامر مباشرة في الطرفية الخاصة بك وتلقي الإخراج. يسمح له هذا بأداء مجموعة واسعة من المهام، من تثبيت الحزم وتشغيل سكربتات البناء إلى نشر التطبيقات، وإدارة قواعد البيانات، وتنفيذ الاختبارات، وذلك بالتكيف مع بيئة التطوير الخاصة بك وسلسلة الأدوات للقيام بالعمل على النحو الصحيح. + +بالنسبة للعمليات الطويلة المدى مثل خوادم التطوير، استخدم زر "المتابعة أثناء التشغيل" للسماح لـ Cline بالاستمرار في المهمة بينما يعمل الأمر في الخلفية. أثناء عمل Cline، سيتم إخباره بأي إخراج طرفية جديد على الطريق، مما يسمح له بالاستجابة للمشكلات التي قد تنشأ، مثل أخطاء وقت الإنشاء عند تعديل الملفات. + + + +
+ + + +### إنشاء وتعديل الملفات + +يمكن لـ Cline إنشاء وتعديل الملفات مباشرة في محرر النصوص الخاص بك، وعرض الاختلافات. يمكنك تعديل أو إلغاء تغييرات Cline مباشرة في محرر الاختلافات، أو تقديم ملاحظات في الدردشة حتى تكون راضيًا عن النتيجة. يراقب Cline أيضًا أخطاء Linter/Compiler (الواردات المفقودة، أخطاء البناء النحوي، إلخ) حتى يتمكن من إصلاح المشكلات التي تنشأ أثناء السير بمفرده. + +يتم تسجيل جميع التغييرات التي أجراها Cline في جدول زمني للملف، مما يوفر طريقة سهلة لتتبع وإلغاء التعديلات إذا لزم الأمر. + + + +
+ + + +### استخدم المتصفح + +مع قدرة [استخدام الكمبيوتر](https://www.anthropic.com/news/3-5-models-and-computer-use) الجديدة لـ Claude 3.5 Sonnet، يمكن لـ Cline إطلاق متصفح، والنقر على العناصر، وكتابة النص، والتمرير، والتقاط لقطات الشاشة وسجلات وحدة التحكم في كل خطوة. يسمح له هذا بالتصحيح التفاعلي، واختبار نهاية إلى نهاية، وحتى الاستخدام العام للويب! يمنحه هذا الاستقلالية لإصلاح الأخطاء البصرية وأخطاء وقت التشغيل دون الحاجة إلى نسخ ولصق سجلات الأخطاء بنفسك. + +حاول طلب من Cline "اختبار التطبيق"، وشاهده يشغل أمرًا مثل `npm run dev`، ويطلق خادم التطوير المحلي في متصفح، ويجري سلسلة من الاختبارات للتأكد من أن كل شيء يعمل. [شاهد عرضًا توضيحيًا هنا.](https://x.com/sdrzn/status/1850880547825823989) + + + +
+ + + +### "add a tool that..." + +شكراً لـ [بروتوكول سياق النموذج](https://github.com/modelcontextprotocol)، يمكن لـ Cline توسيع قدراته من خلال الأدوات المخصصة. بينما يمكنك استخدام [الخوادم التي أنشأها المجتمع](https://github.com/modelcontextprotocol/servers)، يمكن لـ Cline بدلاً من ذلك إنشاء أدوات وتثبيتها مصممة خصيصًا لتناسب سير عملك. ما عليك سوى أن تطلب من Cline "إضافة أداة"، وسيتولى كل شيء، من إنشاء خادم MCP جديد إلى تثبيته في الامتداد. تصبح هذه الأدوات المخصصة بعد ذلك جزءًا من مجموعة أدوات Cline، جاهزة للاستخدام في المهام المستقبلية. + +- **"أضف أداة تجلب تذاكر Jira"**: استرجع تذاكر AC وقم بتشغيل Cline +- **"أضف أداة تدير AWS EC2s"**: تحقق من مقاييس الخادم وقم بتوسيع أو تقليص عدد الحالات +- **"أضف أداة تجلب أحدث حوادث PagerDuty"**: استرجع التفاصيل واطلب من Cline إصلاح الأخطاء + + + +
+ + + +### إضافة السياق + +**`@url`**: الصق رابط URL ليقوم الامتداد بجلبه وتحويله إلى Markdown، مفيد عندما تريد تزويد Cline بأحدث الوثائق + +**`@problems`**: أضف أخطاء وتحذيرات بيئة العمل ('لوحة المشكلات') ليتمكن Cline من إصلاحها + +**`@file`**: يضيف محتويات ملف حتى لا تضطر إلى إهدار طلبات API بالموافقة على قراءة الملف (+ البحث في الملفات) + +**`@folder`**: يضيف جميع ملفات المجلد دفعة واحدة لتسريع سير العمل بشكل أكبر + + + +
+ + + +### نقاط التحقق: المقارنة والاستعادة + +أثناء عمل Cline على مهمة، يأخذ الامتداد لقطة من بيئة العمل في كل خطوة. يمكنك استخدام زر "Compare" لرؤية الفرق بين اللقطة وبيئة العمل الحالية، وزر "Restore" للعودة إلى تلك النقطة. + +على سبيل المثال، عند العمل مع خادم ويب محلي، يمكنك استخدام "استعادة بيئة العمل فقط" لاختبار إصدارات مختلفة من تطبيقك بسرعة، ثم استخدام "استعادة المهمة وبيئة العمل" عندما تجد الإصدار الذي تريد المتابعة منه. يتيح لك ذلك استكشاف أساليب مختلفة بأمان دون فقدان التقدم. + + + +
+ +## المساهمة + +للمساهمة في المشروع، ابدأ بـ [دليل المساهمة](CONTRIBUTING.md) لتعلم الأساسيات. يمكنك أيضًا الانضمام إلى [خادم Discord](https://discord.gg/cline) للدردشة مع المساهمين الآخرين في قناة `#contributors`. إذا كنت تبحث عن عمل بدوام كامل، تحقق من الوظائف المتاحة على [صفحة التوظيف](https://cline.bot/join-us)! + +
+تعليمات التطوير المحلي + +1. استنساخ المستودع _(يتطلب [git-lfs](https://git-lfs.com/))_: + ```bash + git clone https://github.com/cline/cline.git + ``` +2. افتح المشروع في VSCode: + ```bash + code cline + ``` +3. قم بتثبيت التبعيات اللازمة للامتداد وواجهة الويب: + ```bash + npm run install:all + ``` +4. قم بالتشغيل بالضغط على `F5` (أو من `Run` -> `Start Debugging`) لفتح نافذة VSCode جديدة مع تحميل الامتداد. (قد تحتاج إلى تثبيت [إضافة esbuild problem matchers](https://marketplace.visualstudio.com/items?itemName=connor4312.esbuild-problem-matchers) إذا واجهت مشكلات في بناء المشروع.) + +
+ +
+إنشاء طلب سحب (Pull Request) + +1. قبل إنشاء PR، قم بإنشاء إدخال للتغييرات: + ```bash + npm run changeset + ``` + سيطلب منك تحديد: + - نوع التغيير (رئيسي، ثانوي، إصلاح) + - `رئيسي` → تغييرات غير متوافقة (1.0.0 → 2.0.0) + - `ثانوي` → ميزات جديدة (1.0.0 → 1.1.0) + - `إصلاح` → إصلاحات للأخطاء (1.0.0 → 1.0.1) + - وصف التغييرات التي قمت بها + +2. قم بحفظ التغييرات وملف `.changeset` الذي تم إنشاؤه + +3. ادفع فرعك وأنشئ PR على GitHub. سيقوم CI بـ: + - تشغيل الاختبارات والفحوصات + - سيقوم Changesetbot بإنشاء تعليق يوضح تأثير الإصدار + - عند الدمج مع الفرع الرئيسي، سيقوم Changesetbot بإنشاء PR لحزم الإصدار + - عند دمج PR لحزم الإصدار، سيتم نشر إصدار جديد + +
+ +## الرخصة + +[Apache 2.0 © 2025 Cline Bot Inc.](./LICENSE) \ No newline at end of file From b059b3ac1ef2fcbe733e7eb59e797eceb27b8c0e Mon Sep 17 00:00:00 2001 From: AlwaleedAlwabel Date: Sat, 15 Feb 2025 13:41:51 +0300 Subject: [PATCH 029/100] Update README.md **Fix mistakes in the translation. --- locales/ar-sa/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/ar-sa/README.md b/locales/ar-sa/README.md index 05360b9b1c..7febd9a26b 100644 --- a/locales/ar-sa/README.md +++ b/locales/ar-sa/README.md @@ -97,7 +97,7 @@ -### "add a tool that..." +### "إضافة أداة التي..." شكراً لـ [بروتوكول سياق النموذج](https://github.com/modelcontextprotocol)، يمكن لـ Cline توسيع قدراته من خلال الأدوات المخصصة. بينما يمكنك استخدام [الخوادم التي أنشأها المجتمع](https://github.com/modelcontextprotocol/servers)، يمكن لـ Cline بدلاً من ذلك إنشاء أدوات وتثبيتها مصممة خصيصًا لتناسب سير عملك. ما عليك سوى أن تطلب من Cline "إضافة أداة"، وسيتولى كل شيء، من إنشاء خادم MCP جديد إلى تثبيته في الامتداد. تصبح هذه الأدوات المخصصة بعد ذلك جزءًا من مجموعة أدوات Cline، جاهزة للاستخدام في المهام المستقبلية. From c756470c512fafc1693bc9d37d49a0d9bcfb7288 Mon Sep 17 00:00:00 2001 From: AlwaleedAlwabel <41131762+Nick390@users.noreply.github.com> Date: Sat, 15 Feb 2025 13:43:20 +0300 Subject: [PATCH 030/100] Update locales/ar-sa/CODE_OF_CONDUCT.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit العرق = Race الدين = Religion Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> --- locales/ar-sa/CODE_OF_CONDUCT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/ar-sa/CODE_OF_CONDUCT.md b/locales/ar-sa/CODE_OF_CONDUCT.md index d41eea1f96..5ec34d8567 100644 --- a/locales/ar-sa/CODE_OF_CONDUCT.md +++ b/locales/ar-sa/CODE_OF_CONDUCT.md @@ -2,7 +2,7 @@ ## تعهدنا -نحن المساهمون والقائمون على هذا المشروع، نتعهد بتوفير بيئة مفتوحة ومرحبة، ونجعل المشاركة في مشروعنا ومجتمعنا تجربة خالية من التحرش للجميع، بغض النظر عن العمر، أو حجم الجسم، أو الإعاقة، أو العرق، أو الخصائص الجنسية، أو الهوية الجنسية والتعبير عنها، أو مستوى الخبرة، أو التعليم، أو الوضع الاجتماعي والاقتصادي، أو الجنسية، أو المظهر الشخصي، أو العرق، أو الدين، أو الهوية الجنسية والتوجه الجنسي. +نحن المساهمون والقائمون على هذا المشروع، نتعهد بتوفير بيئة مفتوحة ومرحبة، ونجعل المشاركة في مشروعنا ومجتمعنا تجربة خالية من التحرش للجميع، بغض النظر عن العمر، أو حجم الجسم، أو الإعاقة، أو العرق، أو الخصائص الجنسية، أو الهوية الجنسية والتعبير عنها، أو مستوى الخبرة، أو التعليم، أو الوضع الاجتماعي والاقتصادي، أو الجنسية، أو المظهر الشخصي، أو الدين، أو الهوية الجنسية والتوجه الجنسي. ## معاييرنا From 9b66eac9fe39a231976611fafd73f93fa0b11972 Mon Sep 17 00:00:00 2001 From: AlwaleedAlwabel Date: Sat, 15 Feb 2025 13:54:59 +0300 Subject: [PATCH 031/100] Create distance-fire-world.md --- .changeset/distance-fire-world.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/distance-fire-world.md diff --git a/.changeset/distance-fire-world.md b/.changeset/distance-fire-world.md new file mode 100644 index 0000000000..078b17bd9d --- /dev/null +++ b/.changeset/distance-fire-world.md @@ -0,0 +1,5 @@ +--- +"claude-dev": patch +--- + +Add translation to CODE_OF_CONDUCT, CONTRIBUTING and README to Arabic ar-sa. \ No newline at end of file From 0cbd6b321f2175298421ff4beea2f96ede62a6a0 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Sat, 15 Feb 2025 14:33:58 -0800 Subject: [PATCH 032/100] update ui to look like vs marketplace --- webview-ui/src/components/mcp/McpView.tsx | 100 ++++--- .../mcp/marketplace/McpMarketplaceCard.tsx | 261 ++++++++++++------ .../mcp/marketplace/McpMarketplaceView.tsx | 183 +++++++----- 3 files changed, 360 insertions(+), 184 deletions(-) diff --git a/webview-ui/src/components/mcp/McpView.tsx b/webview-ui/src/components/mcp/McpView.tsx index 9450140f2f..c3f615efaa 100644 --- a/webview-ui/src/components/mcp/McpView.tsx +++ b/webview-ui/src/components/mcp/McpView.tsx @@ -13,6 +13,7 @@ import { McpServer } from "../../../../src/shared/mcp" import McpToolRow from "./McpToolRow" import McpResourceRow from "./McpResourceRow" import McpMarketplaceView from "./marketplace/McpMarketplaceView" +import styled from "styled-components" type McpViewProps = { onDone: () => void @@ -20,7 +21,7 @@ type McpViewProps = { const McpView = ({ onDone }: McpViewProps) => { const { mcpServers: servers } = useExtensionState() - const [activeTab, setActiveTab] = useState(servers.length === 0 ? "marketplace" : "installed") + const [activeTab, setActiveTab] = useState("marketplace") // const [servers, setServers] = useState([ // // Add some mock servers for testing @@ -99,20 +100,34 @@ const McpView = ({ onDone }: McpViewProps) => { display: "flex", justifyContent: "space-between", alignItems: "center", - padding: "10px 17px 10px 20px", + padding: "10px 17px 5px 20px", }}>

MCP Servers

Done
-
- setActiveTab(e.target.activeid)}> - Installed - Marketplace - Settings +
+ {/* Tabs container */} +
+ setActiveTab("marketplace")}> + Marketplace + + setActiveTab("installed")}> + Installed + +
- -
+ {/* Content container */} +
+ {activeTab === "marketplace" && } + {activeTab === "installed" && ( +
{
)} -
- - -
- -
-
- - -
- {/* Server Configuration Button */} -
+ {/* Settings Section */} +
{ vscode.postMessage({ type: "openMcpSettings" }) }}> Configure MCP Servers -
- {/* Advanced Settings Link */} -
- { - vscode.postMessage({ - type: "openExtensionSettings", - text: "cline.mcp", - }) - }} - style={{ fontSize: "12px" }}> - Advanced MCP Settings - +
+ { + vscode.postMessage({ + type: "openExtensionSettings", + text: "cline.mcp", + }) + }} + style={{ fontSize: "12px" }}> + Advanced MCP Settings + +
- - + )} +
) } +const StyledTabButton = styled.button<{ isActive: boolean }>` + background: none; + border: none; + border-bottom: 2px solid ${(props) => (props.isActive ? "var(--vscode-foreground)" : "transparent")}; + color: ${(props) => (props.isActive ? "var(--vscode-foreground)" : "var(--vscode-descriptionForeground)")}; + padding: 8px 16px; + cursor: pointer; + font-size: 13px; + margin-bottom: -1px; + font-family: inherit; + + &:hover { + color: var(--vscode-foreground); + } +` + +const TabButton = ({ children, isActive, onClick }: { children: React.ReactNode; isActive: boolean; onClick: () => void }) => ( + + {children} + +) + // Server Row Component const ServerRow = ({ server }: { server: McpServer }) => { const [isExpanded, setIsExpanded] = useState(false) diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx index da11b6f78b..2e6ec6a708 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx @@ -1,5 +1,6 @@ -import { useEffect, useState } from "react" -import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react" +import { VSCodeLink } from "@vscode/webview-ui-toolkit/react" +import { CSSProperties, useEffect, useState } from "react" +import styled from "styled-components" import { McpMarketplaceItem, McpServer } from "../../../../../src/shared/mcp" import { vscode } from "../../../utils/vscode" @@ -27,40 +28,62 @@ const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps) }, []) return ( -
-
-
+ <> + +
{ + console.log("Card clicked:", item.mcpId) + }} + style={{ + borderBottom: "1px solid var(--vscode-textCodeBlock-background)", + padding: "12px 16px", + }}> + {/* Main container with logo and content */} +
+ {/* Logo */} {item.logoUrl && ( {`${item.name} )} -
-
-
-

{item.name}

-
- by {item.author} -
-
- { + + {/* Content section */} +
+ {/* First row: name and install button */} +
+

+ {item.name} +

+
{ + e.stopPropagation() // Prevent card click when clicking install if (!isInstalled && !isDownloading) { setIsDownloading(true) vscode.postMessage({ @@ -68,22 +91,25 @@ const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps) mcpId: item.mcpId, }) } - }}> - - {isInstalled ? "Installed" : isDownloading ? "Downloading..." : "Download"} - + }} + style={{}}> + + {isInstalled ? "Installed" : isDownloading ? "Installing..." : "Install"} + +
-

{item.description}

+ + {/* Second row: metadata */}
- + -
- - {item.githubStars?.toLocaleString() ?? 0} -
-
- - {item.downloadCount?.toLocaleString() ?? 0} -
- {item.requiresApiKey && ( -
- -
- )} - {item.isRecommended && ( -
- -
- )} -
-
- {item.category} + {item.author} - {item.tags.map((tag) => ( - - {tag} - - ))} + | +
+ + {item.githubStars?.toLocaleString() ?? 0} +
+ | +
+ + {item.downloadCount?.toLocaleString() ?? 0} +
+ {item.requiresApiKey && ( + + )} + {item.isRecommended && ( + + )}
+ + {/* Description and tags */} +
+

{item.description}

+
+ + {item.category} + + {item.tags.map((tag, index) => ( + + {tag} + {index === item.tags.length - 1 ? "" : ""} + + ))} +
+
+
-
+ ) } +const StyledInstallButton = styled.button<{ $isInstalled?: boolean }>` + font-size: 12px; + font-weight: 500; + padding: 2px 6px; + border-radius: 2px; + border: none; + cursor: pointer; + background: ${(props) => + props.$isInstalled ? "var(--vscode-button-secondaryBackground)" : "var(--vscode-button-background)"}; + color: var(--vscode-button-foreground); + + &:hover:not(:disabled) { + background: ${(props) => + props.$isInstalled ? "var(--vscode-button-secondaryHoverBackground)" : "var(--vscode-button-hoverBackground)"}; + } + + &:active:not(:disabled) { + background: ${(props) => + props.$isInstalled ? "var(--vscode-button-secondaryBackground)" : "var(--vscode-button-background)"}; + opacity: 0.7; + } + + &:disabled { + opacity: 0.5; + cursor: default; + } +` + export default McpMarketplaceCard diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx index 966dd5bd47..2b34c90194 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx @@ -1,5 +1,13 @@ import { useEffect, useMemo, useState } from "react" -import { VSCodeButton, VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react" +import { + VSCodeButton, + VSCodeProgressRing, + VSCodeRadioGroup, + VSCodeRadio, + VSCodeDropdown, + VSCodeOption, + VSCodeTextField, +} from "@vscode/webview-ui-toolkit/react" import { McpMarketplaceItem } from "../../../../../src/shared/mcp" import { useExtensionState } from "../../../context/ExtensionStateContext" import { vscode } from "../../../utils/vscode" @@ -33,11 +41,6 @@ const selectStyles = { cursor: "pointer", // Show pointer cursor on hover } as const -const refreshStyles = { - height: controlHeight, - alignSelf: "flex-end", -} as const - const McpMarketplaceView = () => { const { mcpServers } = useExtensionState() const [items, setItems] = useState([]) @@ -46,7 +49,7 @@ const McpMarketplaceView = () => { const [isRefreshing, setIsRefreshing] = useState(false) const [searchQuery, setSearchQuery] = useState("") const [selectedCategory, setSelectedCategory] = useState(null) - const [sortBy, setSortBy] = useState<"downloadCount" | "stars" | "name">("downloadCount") + const [sortBy, setSortBy] = useState<"downloadCount" | "stars" | "name" | "newest">("downloadCount") const categories = useMemo(() => { const uniqueCategories = new Set(items.map((item) => item.category)) @@ -72,6 +75,8 @@ const McpMarketplaceView = () => { return b.githubStars - a.githubStars case "name": return a.name.localeCompare(b.name) + case "newest": + return b.githubStars - a.githubStars // FIXME: b.createdAt - a.createdAt // Assuming there's a createdAt field default: return 0 } @@ -154,73 +159,115 @@ const McpMarketplaceView = () => { } return ( -
-
-
- {" "} - {/* Added minWidth: 0 to prevent flex item from overflowing */} -
- setSearchQuery(e.target.value)} - className="mcp-search-input" - style={searchInputStyles} - /> - +
+ {/* Search row */} + setSearchQuery((e.target as HTMLInputElement).value)}> +
+ {searchQuery && ( +
setSearchQuery("")} + slot="end" style={{ - position: "absolute", - left: "10px", - top: "50%", - transform: "translateY(-50%)", - color: "var(--vscode-input-placeholderForeground)", - pointerEvents: "none", - fontSize: "14px", // Match input text size - lineHeight: 1, // Ensure icon is centered properly + display: "flex", + justifyContent: "center", + alignItems: "center", + height: "100%", + cursor: "pointer", }} /> + )} + + + {/* Filter row */} +
+ + Filter: + +
+ setSelectedCategory((e.target as HTMLSelectElement).value || null)}> + All Categories + {categories.map((category) => ( + + {category} + + ))} +
- -
- fetchMarketplace(true)} - disabled={isRefreshing} - style={refreshStyles}> - - Refresh - + + {/* Sort row */} +
+ + Sort: + + setSortBy((e.target as HTMLInputElement).value as typeof sortBy)}> + Most Installs + Most Stars + Newest + Name + +
+
{ + onClick={(e) => { + if (githubLinkRef.current?.contains(e.target as Node)) { + return + } + console.log("Card clicked:", item.mcpId) setIsLoading(true) vscode.postMessage({ @@ -121,18 +124,27 @@ const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps) flexWrap: "wrap", minWidth: 0, }}> - - - +
+ { + e.currentTarget.style.opacity = "0.8" + }} + onMouseLeave={(e) => { + e.currentTarget.style.opacity = "0.5" + }}> + + +
Date: Sat, 15 Feb 2025 16:49:06 -0800 Subject: [PATCH 037/100] including llmsInstall + removed borders between items --- src/core/webview/ClineProvider.ts | 2 +- src/shared/mcp.ts | 2 ++ .../src/components/mcp/marketplace/McpMarketplaceCard.tsx | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 74d30edbfe..16f827d0ac 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -480,7 +480,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { }) // Create task with context from README - const task = `Set up the MCP server from ${mcpDetails.githubUrl}. Use "${mcpDetails.mcpId}" as the server name in cline_mcp_settings.json. Here's some additional context from the README:\n\n${mcpDetails.readmeContent}` + const task = `Set up the MCP server from ${mcpDetails.githubUrl}. Use "${mcpDetails.mcpId}" as the server name in cline_mcp_settings.json. Here's some additional context from the README:\n\n${mcpDetails.readmeContent}\n${mcpDetails.llmsInstallationContent}` // Initialize task and show chat view await this.initClineWithTask(task) diff --git a/src/shared/mcp.ts b/src/shared/mcp.ts index 28f4692c93..fec640d1ae 100644 --- a/src/shared/mcp.ts +++ b/src/shared/mcp.ts @@ -79,6 +79,7 @@ export interface McpMarketplaceItem { tags: string[] requiresApiKey: boolean readmeContent?: string + llmsInstallationContent?: string isRecommended: boolean githubStars: number downloadCount: number @@ -98,5 +99,6 @@ export interface McpDownloadResponse { author: string description: string readmeContent: string + llmsInstallationContent: string requiresApiKey: boolean } diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx index bfafb722b3..32c566f8a4 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx @@ -57,7 +57,6 @@ const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps) }) }} style={{ - borderBottom: "1px solid var(--vscode-textCodeBlock-background)", padding: "12px 16px", cursor: isLoading ? "wait" : "pointer", }}> From 338116fbd42fa12aad7c61fbdc5224b4916ecbb3 Mon Sep 17 00:00:00 2001 From: pashpashpash Date: Sat, 15 Feb 2025 17:23:08 -0800 Subject: [PATCH 038/100] consistent spacing with flexbox gap --- webview-ui/src/components/mcp/McpView.tsx | 2 +- .../mcp/marketplace/McpMarketplaceCard.tsx | 84 +++++++++++-------- .../mcp/marketplace/McpMarketplaceView.tsx | 5 +- 3 files changed, 50 insertions(+), 41 deletions(-) diff --git a/webview-ui/src/components/mcp/McpView.tsx b/webview-ui/src/components/mcp/McpView.tsx index c3f615efaa..f05f4d0546 100644 --- a/webview-ui/src/components/mcp/McpView.tsx +++ b/webview-ui/src/components/mcp/McpView.tsx @@ -112,7 +112,7 @@ const McpView = ({ onDone }: McpViewProps) => { style={{ display: "flex", gap: "1px", - padding: "0 8px 0 10px", + padding: "0 20px 0 20px", borderBottom: "1px solid var(--vscode-panel-border)", }}> setActiveTab("marketplace")}> diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx index 32c566f8a4..dae8fa70ff 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx @@ -57,33 +57,43 @@ const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps) }) }} style={{ - padding: "12px 16px", + padding: "16px 20px", + display: "flex", + flexDirection: "column", + gap: 16, cursor: isLoading ? "wait" : "pointer", }}> {/* Main container with logo and content */} -
+
{/* Logo */} {item.logoUrl && ( {`${item.name} )} {/* Content section */} -
+
{/* First row: name and install button */}

-
- +
+ { + e.currentTarget.style.opacity = "0.8" + }} + onMouseLeave={(e) => { + e.currentTarget.style.opacity = "0.5" + }}> + + +
+ { - e.currentTarget.style.opacity = "0.8" - }} - onMouseLeave={(e) => { - e.currentTarget.style.opacity = "0.5" }}> - -
+ {item.author} +
- - {item.author} - |
{/* Description and tags */} -
-

{item.description}

+
+

{item.description}

{ flexDirection: "column", width: "100%", }}> -
+
{/* Search row */} { className="codicon codicon-search" style={{ fontSize: 13, - marginTop: 2.5, opacity: 0.8, }} /> @@ -204,7 +203,6 @@ const McpMarketplaceView = () => { display: "flex", alignItems: "center", gap: "8px", - marginTop: "8px", }}> { style={{ display: "flex", gap: "8px", - marginTop: "8px", }}> Date: Sat, 15 Feb 2025 17:37:02 -0800 Subject: [PATCH 039/100] more cleanup --- .../mcp/marketplace/McpMarketplaceCard.tsx | 57 +++++++++---------- 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx index dae8fa70ff..787e9fbe4b 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx @@ -129,43 +129,39 @@ const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps) gap: "12px", fontSize: "12px", color: "var(--vscode-descriptionForeground)", - marginTop: "2px", flexWrap: "wrap", minWidth: 0, }}> -
-
- { + e.currentTarget.style.opacity = "0.8" + }} + onMouseLeave={(e) => { + e.currentTarget.style.opacity = "0.5" + }}> +
+ + { - e.currentTarget.style.opacity = "0.8" - }} - onMouseLeave={(e) => { - e.currentTarget.style.opacity = "0.5" }}> - - + {item.author} +
- - {item.author} - -
- | +
{item.githubStars?.toLocaleString() ?? 0}
- |
Date: Sat, 15 Feb 2025 18:20:10 -0800 Subject: [PATCH 040/100] cleanup --- .../components/mcp/marketplace/McpMarketplaceCard.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx index 787e9fbe4b..12555244f2 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx @@ -40,6 +40,15 @@ const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps) .mcp-card:hover { background-color: var(--vscode-list-hoverBackground); } + vscode-link::part(control) { + text-decoration: none !important; + border: none !important; + } + vscode-link:hover::part(control) { + color: var(--link-active-foreground); + text-decoration: none !important; + border: none !important; + } `}
{ e.currentTarget.style.opacity = "0.8" }} From a4d1ae1af44cc7c01fa1f72c36e407998b8f4f3b Mon Sep 17 00:00:00 2001 From: pashpashpash Date: Sat, 15 Feb 2025 19:00:08 -0800 Subject: [PATCH 041/100] codegenicon renamed to codiconIcon --- src/shared/mcp.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/mcp.ts b/src/shared/mcp.ts index fec640d1ae..f0a09afa2d 100644 --- a/src/shared/mcp.ts +++ b/src/shared/mcp.ts @@ -73,7 +73,7 @@ export interface McpMarketplaceItem { name: string author: string description: string - codegenIcon: string + codiconIcon: string logoUrl: string category: string tags: string[] From a6e812824c77b0332abbbe34a5d82b2c5873f6ad Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Sat, 15 Feb 2025 23:42:12 -0800 Subject: [PATCH 042/100] Give attribution to Szpadel for reasoning update --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1146e374ae..549d4ff9f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,7 +34,7 @@ ## [3.2.10] -- Improve support for DeepSeek-R1 (deepseek-reasoner) model for OpenRouter, OpenAI-compatible, and DeepSeek direct +- Improve support for DeepSeek-R1 (deepseek-reasoner) model for OpenRouter, OpenAI-compatible, and DeepSeek direct (thanks @Szpadel!) - Show Reasoning tokens for models that support it - Fix issues with switching models between Plan/Act modes From 3873908efbacd94cfb7951e5953bcdb587f0bfb8 Mon Sep 17 00:00:00 2001 From: Evan <58194240+celestial-vault@users.noreply.github.com> Date: Sun, 16 Feb 2025 14:20:27 -0800 Subject: [PATCH 043/100] Webpack Single Bundle (#1821) * formatting * changeset --------- Co-authored-by: Evan Fannin --- .changeset/smart-cycles-promise.md | 5 +++++ webview-ui/scripts/build-react-no-split.js | 26 ++++++++++++++-------- 2 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 .changeset/smart-cycles-promise.md diff --git a/.changeset/smart-cycles-promise.md b/.changeset/smart-cycles-promise.md new file mode 100644 index 0000000000..f3df1948bb --- /dev/null +++ b/.changeset/smart-cycles-promise.md @@ -0,0 +1,5 @@ +--- +"claude-dev": patch +--- + +Force all JS code into a single bundle for better VS Code compatability diff --git a/webview-ui/scripts/build-react-no-split.js b/webview-ui/scripts/build-react-no-split.js index f021a12104..28f37108be 100644 --- a/webview-ui/scripts/build-react-no-split.js +++ b/webview-ui/scripts/build-react-no-split.js @@ -98,18 +98,26 @@ config.module.rules[1].oneOf.forEach((rule) => { } }) -// Disable code splitting -config.optimization.splitChunks = { - cacheGroups: { - default: false, +// Force all code into a single bundle for VS Code webview compatibility. +// This is necessary for: +// 1. Mermaid.js to work properly (prevents async chunk loading) +// 2. Consistent CSP nonce handling (single bundle = single nonce) +config.optimization = { + ...config.optimization, + splitChunks: { + cacheGroups: { + default: false, + }, + name: "main", // Forces all chunks (dynamic import() calls, for example those used by Mermaid) into one bundle - this is what actually prevents code splitting }, + runtimeChunk: false, } -// Disable code chunks -config.optimization.runtimeChunk = false - -// Rename main.{hash}.js to main.js -config.output.filename = "static/js/[name].js" +// Ensure all chunks are named 'main' to match our CSP nonce setup +config.output = { + ...config.output, + filename: "static/js/[name].js", +} // Rename main.{hash}.css to main.css config.plugins[5].options.filename = "static/css/[name].css" From 796eac558caf0408970a8902885fc76729529ff4 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Sun, 16 Feb 2025 15:15:27 -0800 Subject: [PATCH 044/100] Fix sending message when toggling plan/act mode --- src/core/Cline.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 1e9ea6bbb4..3632f25c1b 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -2716,9 +2716,6 @@ export class Cline { this.isAwaitingPlanResponse = false if (this.didRespondToPlanAskBySwitchingMode) { - if (text) { - await this.say("user_feedback", text ?? "", images) - } pushToolResult( formatResponse.toolResult( `[The user has switched to ACT MODE, so you may now proceed with the task.]` + @@ -2728,6 +2725,13 @@ export class Cline { images, ), ) + } else { + // if we didn't switch to ACT MODE, then we can just send the user_feedback message + pushToolResult(formatResponse.toolResult(`\n${text}\n`, images)) + } + + if (text || images) { + await this.say("user_feedback", text ?? "", images) } // From aab26e9a386d5465bf4d833d29e66e34776d5be2 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Sun, 16 Feb 2025 15:38:06 -0800 Subject: [PATCH 045/100] Fix empty message bubble after switching plan/act with a message --- src/core/Cline.ts | 9 +++++++-- src/core/webview/ClineProvider.ts | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 3632f25c1b..4bfcf59520 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -2712,9 +2712,14 @@ export class Cline { // } this.isAwaitingPlanResponse = true - const { text, images } = await this.ask("plan_mode_response", response, false) + let { text, images } = await this.ask("plan_mode_response", response, false) this.isAwaitingPlanResponse = false + // webview invoke sendMessage will send this marker in order to put webview into the proper state (responding to an ask) and as a flag to extension that the user switched to ACT mode. + if (text === "PLAN_MODE_TOGGLE_RESPONSE") { + text = "" + } + if (this.didRespondToPlanAskBySwitchingMode) { pushToolResult( formatResponse.toolResult( @@ -2730,7 +2735,7 @@ export class Cline { pushToolResult(formatResponse.toolResult(`\n${text}\n`, images)) } - if (text || images) { + if (text || images?.length) { await this.say("user_feedback", text ?? "", images) } diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 09a735ae1c..8ef786f4dc 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -627,7 +627,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { await this.postMessageToWebview({ type: "invoke", invoke: "sendMessage", - text: message.chatContent?.message || "[Proceeding with the task...]", + text: message.chatContent?.message || "PLAN_MODE_TOGGLE_RESPONSE", images: message.chatContent?.images, }) } else { From a98269f6d5031db212dee9303badf771b57d999c Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Sun, 16 Feb 2025 20:38:31 -0800 Subject: [PATCH 046/100] Fix sending feedback with approve --- src/core/Cline.ts | 83 +++++++++++++++++------------------ src/core/prompts/responses.ts | 6 --- 2 files changed, 40 insertions(+), 49 deletions(-) diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 4bfcf59520..acecbd4fe6 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -1539,33 +1539,44 @@ export class Cline { this.didAlreadyUseTool = true } + // The user can approve, reject, or provide feedback (rejection). However the user may also send a message along with an approval, in which case we add a separate user message with this feedback. + const pushAdditionalToolFeedback = (feedback?: string, images?: string[]) => { + if (!feedback && !images) { + return + } + const content = formatResponse.toolResult( + `The user provided the following feedback:\n\n${feedback}\n`, + images, + ) + if (typeof content === "string") { + this.userMessageContent.push({ + type: "text", + text: content, + }) + } else { + this.userMessageContent.push(...content) + } + } + const askApproval = async (type: ClineAsk, partialMessage?: string) => { const { response, text, images } = await this.ask(type, partialMessage, false) if (response !== "yesButtonClicked") { - // User did NOT approve (rejected) - if (response === "messageResponse") { - // Rejection WITH feedback - await this.say("user_feedback", text, images) - pushToolResult(formatResponse.toolResult(formatResponse.toolDeniedWithFeedback(text), images)) - - this.didRejectTool = true - return false - } - // Rejection WITHOUT explicit feedback + // User pressed reject button or responded with a message, which we treat as a rejection pushToolResult(formatResponse.toolDenied()) - + if (text || images?.length) { + pushAdditionalToolFeedback(text, images) + await this.say("user_feedback", text, images) + } this.didRejectTool = true // Prevent further tool uses in this message return false + } else { + // User hit the approve button, and may have provided feedback + if (text || images?.length) { + pushAdditionalToolFeedback(text, images) + await this.say("user_feedback", text, images) + } + return true } - - // Handle yesButtonClicked with text (Acceptance WITH feedback) - if (text) { - await this.say("user_feedback", text, images) - pushToolResult(formatResponse.toolResult(formatResponse.toolApprovedWithFeedback(text), images)) // Structured feedback to model on approval - } - - // User approved without feedback - return true } const showNotificationForApprovalIfAutoApprovalEnabled = (message: string) => { @@ -1804,37 +1815,23 @@ export class Cline { let didApprove = true const { response, text, images } = await this.ask("tool", completeMessage, false) if (response !== "yesButtonClicked") { - // User did NOT approve (rejected) - + // User either sent a message or pressed reject button // TODO: add similar context for other tool denial responses, to emphasize ie that a command was not run const fileDeniedNote = fileExists ? "The file was not updated, and maintains its original contents." : "The file was not created." - if (response === "messageResponse") { - // Rejection WITH feedback + pushToolResult(`The user denied this operation. ${fileDeniedNote}`) + if (text || images?.length) { + pushAdditionalToolFeedback(text, images) await this.say("user_feedback", text, images) - pushToolResult( - formatResponse.toolResult( - `The user denied this operation. ${fileDeniedNote}\nThe user provided the following feedback:\n\n${text}\n`, - images, - ), - ) - this.didRejectTool = true - didApprove = false - } else { - pushToolResult(`The user denied this operation. ${fileDeniedNote}`) - this.didRejectTool = true - didApprove = false } + this.didRejectTool = true + didApprove = false } else { - // User approved - - // Handle yesButtonClicked with text (Acceptance WITH feedback) - if (text) { + // User hit the approve button, and may have provided feedback + if (text || images?.length) { + pushAdditionalToolFeedback(text, images) await this.say("user_feedback", text, images) - pushToolResult( - formatResponse.toolResult(formatResponse.toolApprovedWithFeedback(text), images), - ) } } diff --git a/src/core/prompts/responses.ts b/src/core/prompts/responses.ts index 8a14acc02b..bf5db6bbe7 100644 --- a/src/core/prompts/responses.ts +++ b/src/core/prompts/responses.ts @@ -6,12 +6,6 @@ import { ClineIgnoreController, LOCK_TEXT_SYMBOL } from "../ignore/ClineIgnoreCo export const formatResponse = { toolDenied: () => `The user denied this operation.`, - toolDeniedWithFeedback: (feedback?: string) => - `The user denied this operation and provided the following feedback:\n\n${feedback}\n`, - - toolApprovedWithFeedback: (feedback?: string) => - `The user approved this operation and provided the following feedback:\n\n${feedback}\n`, - toolError: (error?: string) => `The tool execution failed with the following error:\n\n${error}\n`, clineIgnoreError: (path: string) => From 217162ccba404a87fc780f4586fdab3ed872a2ce Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Sun, 16 Feb 2025 21:01:52 -0800 Subject: [PATCH 047/100] Prepare for release --- .changeset/angry-lions-sneeze.md | 5 ----- .changeset/five-flies-breathe.md | 5 ----- .changeset/fuzzy-moose-punch.md | 5 ----- .changeset/gentle-glasses-flow.md | 5 ----- .changeset/long-guests-occur.md | 5 ----- .changeset/long-masks-notice.md | 5 ----- .changeset/orange-eels-unite.md | 5 ----- .changeset/poor-cobras-destroy.md | 5 ----- .changeset/silly-cats-appear.md | 5 ----- .changeset/slimy-roses-dance.md | 5 ----- .changeset/slimy-toes-wait.md | 5 ----- .changeset/smart-cycles-promise.md | 5 ----- .changeset/stale-lizards-poke.md | 5 ----- .changeset/ten-books-act.md | 5 ----- .changeset/thirty-eyes-appear.md | 5 ----- .changeset/wise-phones-mate.md | 5 ----- CHANGELOG.md | 13 ++++++++++++- README.md | 2 +- package.json | 2 +- 19 files changed, 14 insertions(+), 83 deletions(-) delete mode 100644 .changeset/angry-lions-sneeze.md delete mode 100644 .changeset/five-flies-breathe.md delete mode 100644 .changeset/fuzzy-moose-punch.md delete mode 100644 .changeset/gentle-glasses-flow.md delete mode 100644 .changeset/long-guests-occur.md delete mode 100644 .changeset/long-masks-notice.md delete mode 100644 .changeset/orange-eels-unite.md delete mode 100644 .changeset/poor-cobras-destroy.md delete mode 100644 .changeset/silly-cats-appear.md delete mode 100644 .changeset/slimy-roses-dance.md delete mode 100644 .changeset/slimy-toes-wait.md delete mode 100644 .changeset/smart-cycles-promise.md delete mode 100644 .changeset/stale-lizards-poke.md delete mode 100644 .changeset/ten-books-act.md delete mode 100644 .changeset/thirty-eyes-appear.md delete mode 100644 .changeset/wise-phones-mate.md diff --git a/.changeset/angry-lions-sneeze.md b/.changeset/angry-lions-sneeze.md deleted file mode 100644 index 091739140b..0000000000 --- a/.changeset/angry-lions-sneeze.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"claude-dev": patch ---- - -Add translation to CODE_OF_CONDUCT, CONTRIBUTING and README to portuguese pt-BR. diff --git a/.changeset/five-flies-breathe.md b/.changeset/five-flies-breathe.md deleted file mode 100644 index aa13a9b71f..0000000000 --- a/.changeset/five-flies-breathe.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"claude-dev": patch ---- - -Add support for qwen vl models diff --git a/.changeset/fuzzy-moose-punch.md b/.changeset/fuzzy-moose-punch.md deleted file mode 100644 index 7034341375..0000000000 --- a/.changeset/fuzzy-moose-punch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"claude-dev": patch ---- - -Add terminal context mention diff --git a/.changeset/gentle-glasses-flow.md b/.changeset/gentle-glasses-flow.md deleted file mode 100644 index c3d9ed997c..0000000000 --- a/.changeset/gentle-glasses-flow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"claude-dev": minor ---- - -add alibaba qwen2.5 coder models diff --git a/.changeset/long-guests-occur.md b/.changeset/long-guests-occur.md deleted file mode 100644 index 34f6598335..0000000000 --- a/.changeset/long-guests-occur.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"claude-dev": patch ---- - -Add git context mention diff --git a/.changeset/long-masks-notice.md b/.changeset/long-masks-notice.md deleted file mode 100644 index 76079fbed6..0000000000 --- a/.changeset/long-masks-notice.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"claude-dev": minor ---- - -Advanced Setting to disable browser tool diff --git a/.changeset/orange-eels-unite.md b/.changeset/orange-eels-unite.md deleted file mode 100644 index e0eab5d394..0000000000 --- a/.changeset/orange-eels-unite.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"claude-dev": patch ---- - -Fix AWS Bedrock Profiles. When configuring the AnthropicBedrock Client you must pass AWS credentials in a specific way, otherwise the client will default to reading credentials from the default AWS profile. diff --git a/.changeset/poor-cobras-destroy.md b/.changeset/poor-cobras-destroy.md deleted file mode 100644 index 991c7324cc..0000000000 --- a/.changeset/poor-cobras-destroy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"claude-dev": minor ---- - -Add extension setting for chromium executable path diff --git a/.changeset/silly-cats-appear.md b/.changeset/silly-cats-appear.md deleted file mode 100644 index 6e6925a4c9..0000000000 --- a/.changeset/silly-cats-appear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"claude-dev": minor ---- - -Add new ability to send message that is in Input field during Plan/Act Mode Change to Act. diff --git a/.changeset/slimy-roses-dance.md b/.changeset/slimy-roses-dance.md deleted file mode 100644 index e88ef3ca57..0000000000 --- a/.changeset/slimy-roses-dance.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"claude-dev": minor ---- - -add alibaba qwen2.5-coder models diff --git a/.changeset/slimy-toes-wait.md b/.changeset/slimy-toes-wait.md deleted file mode 100644 index 059978604f..0000000000 --- a/.changeset/slimy-toes-wait.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"claude-dev": minor ---- - -Added api key field for litellm api provider in settings diff --git a/.changeset/smart-cycles-promise.md b/.changeset/smart-cycles-promise.md deleted file mode 100644 index f3df1948bb..0000000000 --- a/.changeset/smart-cycles-promise.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"claude-dev": patch ---- - -Force all JS code into a single bundle for better VS Code compatability diff --git a/.changeset/stale-lizards-poke.md b/.changeset/stale-lizards-poke.md deleted file mode 100644 index 40e3308e8b..0000000000 --- a/.changeset/stale-lizards-poke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"claude-dev": minor ---- - -qwen platform adds deepseek-r1/v3 support diff --git a/.changeset/ten-books-act.md b/.changeset/ten-books-act.md deleted file mode 100644 index f37fcf0e9a..0000000000 --- a/.changeset/ten-books-act.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"claude-dev": minor ---- - -Adding .clineignore guide diff --git a/.changeset/thirty-eyes-appear.md b/.changeset/thirty-eyes-appear.md deleted file mode 100644 index 2cfb8405d6..0000000000 --- a/.changeset/thirty-eyes-appear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"claude-dev": minor ---- - -Advanced Configuration for OpenAI Compatible Providers diff --git a/.changeset/wise-phones-mate.md b/.changeset/wise-phones-mate.md deleted file mode 100644 index 7be57b25e9..0000000000 --- a/.changeset/wise-phones-mate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"claude-dev": minor ---- - -Allowing the user to give feedback when approving a tool use. diff --git a/CHANGELOG.md b/CHANGELOG.md index 549d4ff9f3..0e9ef115f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,20 @@ # Changelog +## [3.4.0] + +- Send current textfield contents as additional feedback when toggling from Plan to Act Mode, or when hitting 'Approve' button +- Add 'Terminal' context mention to reference the active terminal's contents +- Add 'Git Commits' context mention to reference current working changes or specific commits (thanks @mrubens!) +- Add advanced configuration options for OpenAI Compatible (context window, max output, pricing, etc.) +- Add Alibaba Qwen 2.5 coder models, VL models, and DeepSeek-R1/V3 support +- Improve support for AWS Bedrock Profiles +- Add advanced setting to disable browser tool +- Add advanced setting to set chromium executable path for browser tool + ## [3.3.2] - Fix bug where OpenRouter requests would periodically not return cost/token stats, leading to context window limit errors -- Make checkpoints more visible and keep track of restored checkpoints +- Make checkpoints more visible and keep track of restored checkpoints ## [3.3.0] diff --git a/README.md b/README.md index f1c48eebfd..4f48c5ad41 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Cline – \#1 on OpenRouter diff --git a/package.json b/package.json index 0c9e7e41b0..a7dd3770d7 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "claude-dev", "displayName": "Cline", "description": "Autonomous coding agent right in your IDE, capable of creating/editing files, running commands, using the browser, and more with your permission every step of the way.", - "version": "3.3.2", + "version": "3.4.0", "icon": "assets/icons/icon.png", "galleryBanner": { "color": "#617A91", From 5dea79c65f1abdc15ae7162091210ee8169be235 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Mon, 17 Feb 2025 10:35:21 -0800 Subject: [PATCH 048/100] Fix Mistral provider server URL --- CHANGELOG.md | 1 + package-lock.json | 24 ++++++++++++++++++------ package.json | 2 +- src/api/providers/mistral.ts | 1 - 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e9ef115f3..a1eaebc759 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Add advanced configuration options for OpenAI Compatible (context window, max output, pricing, etc.) - Add Alibaba Qwen 2.5 coder models, VL models, and DeepSeek-R1/V3 support - Improve support for AWS Bedrock Profiles +- Fix Mistral provider support for non-codestral models - Add advanced setting to disable browser tool - Add advanced setting to set chromium executable path for browser tool diff --git a/package-lock.json b/package-lock.json index 7a4b80d77b..d89b7dc8dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,19 @@ { "name": "claude-dev", - "version": "3.3.2", + "version": "3.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "claude-dev", - "version": "3.3.2", + "version": "3.4.0", "license": "Apache-2.0", "dependencies": { "@anthropic-ai/bedrock-sdk": "^0.10.2", "@anthropic-ai/sdk": "^0.26.0", "@anthropic-ai/vertex-sdk": "^0.4.1", "@google/generative-ai": "^0.18.0", - "@mistralai/mistralai": "^1.3.6", + "@mistralai/mistralai": "^1.5.0", "@modelcontextprotocol/sdk": "^1.0.1", "@types/clone-deep": "^4.0.4", "@types/get-folder-size": "^3.0.4", @@ -4098,13 +4098,25 @@ } }, "node_modules/@mistralai/mistralai": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-1.4.0.tgz", - "integrity": "sha512-xA3DAtIDh4Qgr1EoSuiGVE+2ABNrxpcTeC0kSXYbkDNUGdthalLAH7DgbG0fkKZ7TN8xdWXQq2WiIghp/O96Eg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-1.5.0.tgz", + "integrity": "sha512-AIn8pwAwA/fDvEUvmkt+40zH1ZmfaG3Q7oUWl17GUEC1tU7ZPwYz8Cv9P59lyS1SisHdDSu81oknO7f1ywkz8Q==", + "dependencies": { + "zod-to-json-schema": "^3.24.1" + }, "peerDependencies": { "zod": ">= 3" } }, + "node_modules/@mistralai/mistralai/node_modules/zod-to-json-schema": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz", + "integrity": "sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } + }, "node_modules/@mixmark-io/domino": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@mixmark-io/domino/-/domino-2.2.0.tgz", diff --git a/package.json b/package.json index a7dd3770d7..5decc223d0 100644 --- a/package.json +++ b/package.json @@ -236,7 +236,7 @@ "@anthropic-ai/sdk": "^0.26.0", "@anthropic-ai/vertex-sdk": "^0.4.1", "@google/generative-ai": "^0.18.0", - "@mistralai/mistralai": "^1.3.6", + "@mistralai/mistralai": "^1.5.0", "@modelcontextprotocol/sdk": "^1.0.1", "@types/clone-deep": "^4.0.4", "@types/get-folder-size": "^3.0.4", diff --git a/src/api/providers/mistral.ts b/src/api/providers/mistral.ts index 28c2331c60..4be68e2e5e 100644 --- a/src/api/providers/mistral.ts +++ b/src/api/providers/mistral.ts @@ -22,7 +22,6 @@ export class MistralHandler implements ApiHandler { constructor(options: ApiHandlerOptions) { this.options = options this.client = new Mistral({ - serverURL: "https://api.mistral.ai", apiKey: this.options.mistralApiKey, }) } From cc166f41443bdda215eede4995f2fef3507f534b Mon Sep 17 00:00:00 2001 From: Daniel Trugman Date: Mon, 17 Feb 2025 18:37:23 +0000 Subject: [PATCH 049/100] Requesty provider improvements (#1829) * Requesty: Add cline headers to requests * Requesty: Add support for cache read/write tokens * Requesty: Remove deepseek reasoner conversions that are handled in-flight by Requesty * Requesty: Read o3-mini reasoning effort from Cline settings * Requesty: Add total cost per interaction * Add changeset --- .changeset/fair-snails-confess.md | 10 ++++++++++ src/api/providers/requesty.ts | 31 +++++++++++++++++++++++-------- 2 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 .changeset/fair-snails-confess.md diff --git a/.changeset/fair-snails-confess.md b/.changeset/fair-snails-confess.md new file mode 100644 index 0000000000..f54c839141 --- /dev/null +++ b/.changeset/fair-snails-confess.md @@ -0,0 +1,10 @@ +--- +"claude-dev": patch +--- + +Improve Requesty provider integration + +- Adding Cline headers to API requests, to enable targeted optimizations +- Read o3 reasoning effort from Cline config, not model name +- Show token information in task header +- Get total cost from response when available diff --git a/src/api/providers/requesty.ts b/src/api/providers/requesty.ts index 34dd2b4d67..218d4785ad 100644 --- a/src/api/providers/requesty.ts +++ b/src/api/providers/requesty.ts @@ -5,7 +5,6 @@ import { ApiHandlerOptions, ModelInfo, openAiModelInfoSaneDefaults } from "../.. import { ApiHandler } from "../index" import { convertToOpenAiMessages } from "../transform/openai-format" import { ApiStream } from "../transform/stream" -import { convertToR1Format } from "../transform/r1-format" export class RequestyHandler implements ApiHandler { private options: ApiHandlerOptions @@ -16,30 +15,32 @@ export class RequestyHandler implements ApiHandler { this.client = new OpenAI({ baseURL: "https://router.requesty.ai/v1", apiKey: this.options.requestyApiKey, + defaultHeaders: { + "HTTP-Referer": "https://cline.bot", + "X-Title": "Cline", + }, }) } @withRetry() async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { const modelId = this.options.requestyModelId ?? "" - const isDeepseekReasoner = modelId.includes("deepseek-reasoner") let openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [ { role: "system", content: systemPrompt }, ...convertToOpenAiMessages(messages), ] - if (isDeepseekReasoner) { - openAiMessages = convertToR1Format([{ role: "user", content: systemPrompt }, ...messages]) - } - + // @ts-ignore-next-line const stream = await this.client.chat.completions.create({ model: modelId, messages: openAiMessages, temperature: 0, stream: true, stream_options: { include_usage: true }, + ...(modelId === "openai/o3-mini" ? { reasoning_effort: this.options.o3MiniReasoningEffort || "medium" } : {}), }) + for await (const chunk of stream) { const delta = chunk.choices[0]?.delta if (delta?.content) { @@ -56,11 +57,25 @@ export class RequestyHandler implements ApiHandler { } } + // Requesty usage includes an extra field for Anthropic use cases. + // Safely cast the prompt token details section to the appropriate structure. + interface RequestyUsage extends OpenAI.CompletionUsage { + prompt_tokens_details?: { + caching_tokens?: number + cached_tokens?: number + } + total_cost?: number + } + if (chunk.usage) { + const usage = chunk.usage as RequestyUsage yield { type: "usage", - inputTokens: chunk.usage.prompt_tokens || 0, - outputTokens: chunk.usage.completion_tokens || 0, + inputTokens: usage.prompt_tokens || 0, + outputTokens: usage.completion_tokens || 0, + cacheWriteTokens: usage.prompt_tokens_details?.caching_tokens || undefined, + cacheReadTokens: usage.prompt_tokens_details?.cached_tokens || undefined, + totalCost: usage.total_cost || undefined, } } } From 31eb212beef4d8c3542a2de4e9227ceca3272e7e Mon Sep 17 00:00:00 2001 From: pashpashpash Date: Mon, 17 Feb 2025 15:03:10 -0800 Subject: [PATCH 050/100] faster detail view --- src/core/webview/ClineProvider.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 16f827d0ac..fe38269249 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -931,14 +931,6 @@ export class ClineProvider implements vscode.WebviewViewProvider { } case "openMcpMarketplaceServerDetails": { if (message.mcpId) { - // close existing - const tabs = vscode.window.tabGroups.all - .flatMap((tg) => tg.tabs) - .filter((tab) => tab.label && tab.label.includes("README") && tab.label.includes("Preview")) - for (const tab of tabs) { - await vscode.window.tabGroups.close(tab) - } - const response = await fetch(`https://api.cline.bot/v1/mcp/marketplace/item?mcpId=${message.mcpId}`) const details: McpDownloadResponse = await response.json() @@ -952,6 +944,14 @@ export class ClineProvider implements vscode.WebviewViewProvider { `${DIFF_VIEW_URI_SCHEME}:${details.name} README?${Buffer.from(details.readmeContent).toString("base64")}`, ) + // close existing + const tabs = vscode.window.tabGroups.all + .flatMap((tg) => tg.tabs) + .filter((tab) => tab.label && tab.label.includes("README") && tab.label.includes("Preview")) + for (const tab of tabs) { + await vscode.window.tabGroups.close(tab) + } + // Show only the preview await vscode.commands.executeCommand("markdown.showPreview", uri, { sideBySide: true, From 09c4202cb216a4945f5ff5b5376378f87e14435e Mon Sep 17 00:00:00 2001 From: pashpashpash Date: Mon, 17 Feb 2025 16:14:36 -0800 Subject: [PATCH 051/100] added submit card --- .../mcp/marketplace/McpMarketplaceView.tsx | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx index 28e1e440a6..d07cdb4e1a 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx @@ -12,6 +12,7 @@ import { McpMarketplaceItem } from "../../../../../src/shared/mcp" import { useExtensionState } from "../../../context/ExtensionStateContext" import { vscode } from "../../../utils/vscode" import McpMarketplaceCard from "./McpMarketplaceCard" +import McpSubmitCard from "./McpSubmitCard" const controlHeight = "28px" @@ -284,23 +285,26 @@ const McpMarketplaceView = () => { } `} - {filteredItems.length === 0 ? ( -
- {searchQuery || selectedCategory - ? "No matching MCP servers found" - : "No MCP servers found in the marketplace"} -
- ) : ( - filteredItems.map((item) => ) - )} +
+ {filteredItems.length === 0 ? ( +
+ {searchQuery || selectedCategory + ? "No matching MCP servers found" + : "No MCP servers found in the marketplace"} +
+ ) : ( + filteredItems.map((item) => ) + )} + +
) } From 7722234761c93cabfe2c85ee90313471d929a596 Mon Sep 17 00:00:00 2001 From: pashpashpash Date: Mon, 17 Feb 2025 16:21:22 -0800 Subject: [PATCH 052/100] submit card --- .../mcp/marketplace/McpSubmitCard.tsx | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 webview-ui/src/components/mcp/marketplace/McpSubmitCard.tsx diff --git a/webview-ui/src/components/mcp/marketplace/McpSubmitCard.tsx b/webview-ui/src/components/mcp/marketplace/McpSubmitCard.tsx new file mode 100644 index 0000000000..3373a96783 --- /dev/null +++ b/webview-ui/src/components/mcp/marketplace/McpSubmitCard.tsx @@ -0,0 +1,79 @@ +import { VSCodeLink } from "@vscode/webview-ui-toolkit/react" +import styled from "styled-components" + +const McpSubmitCard = () => { + return ( +
+ {/* Logo */} + Cline bot logo + + {/* Content */} +
+

+ Is something missing? +

+

+ Submit your own MCP servers to the marketplace by{" "} + submitting an issue on the official MCP Marketplace + repo on GitHub. +

+
+
+ ) +} + +const StyledGitHubButton = styled.div` + font-size: 13px; + font-weight: 500; + padding: 8px; + border-radius: 2px; + border: 1px solid var(--vscode-button-border, transparent); + cursor: pointer; + background: var(--vscode-button-background); + color: var(--vscode-button-foreground); + display: flex; + align-items: center; + justify-content: center; + width: 100%; + + &:hover { + background: var(--vscode-button-hoverBackground); + } + + &:active { + background: var(--vscode-button-background); + opacity: 0.7; + } +` + +export default McpSubmitCard From 2d05dfd986a1231a86ece4a27906c47baac062ae Mon Sep 17 00:00:00 2001 From: pashpashpash Date: Mon, 17 Feb 2025 16:34:02 -0800 Subject: [PATCH 053/100] silent prefetching --- src/core/webview/ClineProvider.ts | 125 ++++++++++++++-------- src/shared/WebviewMessage.ts | 1 + webview-ui/src/components/mcp/McpView.tsx | 11 +- 3 files changed, 92 insertions(+), 45 deletions(-) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index fe38269249..7a87370a44 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -378,6 +378,66 @@ export class ClineProvider implements vscode.WebviewViewProvider { * * @param webview A reference to the extension webview */ + private async fetchMcpMarketplaceFromApi(silent: boolean = false): Promise { + try { + const response = await axios.get("https://api.cline.bot/v1/mcp/marketplace", { + headers: { + "Content-Type": "application/json", + }, + }) + + if (!response.data) { + throw new Error("Invalid response from MCP marketplace API") + } + + const catalog: McpMarketplaceCatalog = { + items: (response.data || []).map((item: any) => ({ + ...item, + githubStars: item.githubStars ?? 0, + downloadCount: item.downloadCount ?? 0, + tags: item.tags ?? [], + })), + } + + // Store in global state + await this.updateGlobalState("mcpMarketplaceCatalog", catalog) + return catalog + } catch (error) { + console.error("Failed to fetch MCP marketplace:", error) + if (!silent) { + const errorMessage = error instanceof Error ? error.message : "Failed to fetch MCP marketplace" + await this.postMessageToWebview({ + type: "mcpMarketplaceCatalog", + error: errorMessage, + }) + vscode.window.showErrorMessage(errorMessage) + } + return undefined + } + } + + async prefetchMcpMarketplace() { + try { + await this.fetchMcpMarketplaceFromApi(true) + } catch (error) { + console.error("Failed to prefetch MCP marketplace:", error) + } + } + + async silentlyRefreshMcpMarketplace() { + try { + const catalog = await this.fetchMcpMarketplaceFromApi(true) + if (catalog) { + await this.postMessageToWebview({ + type: "mcpMarketplaceCatalog", + mcpMarketplaceCatalog: catalog, + }) + } + } catch (error) { + console.error("Failed to silently refresh MCP marketplace:", error) + } + } + private async fetchMcpMarketplace(forceRefresh: boolean = false) { try { // Check if we have cached data @@ -390,41 +450,12 @@ export class ClineProvider implements vscode.WebviewViewProvider { return } - try { - const response = await axios.get("https://api.cline.bot/v1/mcp/marketplace", { - headers: { - "Content-Type": "application/json", - }, - }) - - if (!response.data) { - throw new Error("Invalid response from MCP marketplace API") - } - - const catalog: McpMarketplaceCatalog = { - items: (response.data || []).map((item: any) => ({ - ...item, - githubStars: item.githubStars ?? 0, - downloadCount: item.downloadCount ?? 0, - tags: item.tags ?? [], - })), - } - - // Store in global state - await this.updateGlobalState("mcpMarketplaceCatalog", catalog) - + const catalog = await this.fetchMcpMarketplaceFromApi(false) + if (catalog) { await this.postMessageToWebview({ type: "mcpMarketplaceCatalog", mcpMarketplaceCatalog: catalog, }) - } catch (error) { - console.error("Failed to fetch MCP marketplace:", error) - const errorMessage = error instanceof Error ? error.message : "Failed to fetch MCP marketplace" - await this.postMessageToWebview({ - type: "mcpMarketplaceCatalog", - error: errorMessage, - }) - vscode.window.showErrorMessage(errorMessage) } } catch (error) { console.error("Failed to handle cached MCP marketplace:", error) @@ -540,19 +571,23 @@ export class ClineProvider implements vscode.WebviewViewProvider { // 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. // 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 // (see normalizeApiConfiguration > openrouter) - this.refreshOpenRouterModels().then(async (openRouterModels) => { - if (openRouterModels) { - // 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) - const { apiConfiguration } = await this.getState() - if (apiConfiguration.openRouterModelId) { - await this.updateGlobalState( - "openRouterModelInfo", - openRouterModels[apiConfiguration.openRouterModelId], - ) - await this.postStateToWebview() + // Prefetch marketplace and OpenRouter models + Promise.all([ + this.prefetchMcpMarketplace(), + this.refreshOpenRouterModels().then(async (openRouterModels) => { + if (openRouterModels) { + // 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) + const { apiConfiguration } = await this.getState() + if (apiConfiguration.openRouterModelId) { + await this.updateGlobalState( + "openRouterModelInfo", + openRouterModels[apiConfiguration.openRouterModelId], + ) + await this.postStateToWebview() + } } - } - }) + }), + ]).catch(console.error) break case "newTask": // Code that should run in response to the hello message command @@ -929,6 +964,10 @@ export class ClineProvider implements vscode.WebviewViewProvider { } break } + case "silentlyRefreshMcpMarketplace": { + await this.silentlyRefreshMcpMarketplace() + break + } case "openMcpMarketplaceServerDetails": { if (message.mcpId) { const response = await fetch(`https://api.cline.bot/v1/mcp/marketplace/item?mcpId=${message.mcpId}`) diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 5390ff0bdb..2d82474382 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -46,6 +46,7 @@ export interface WebviewMessage { | "fetchMcpMarketplace" | "downloadMcp" | "openMcpMarketplaceServerDetails" + | "silentlyRefreshMcpMarketplace" // | "relaunchChromeDebugMode" text?: string disabled?: boolean diff --git a/webview-ui/src/components/mcp/McpView.tsx b/webview-ui/src/components/mcp/McpView.tsx index f05f4d0546..3c0eef4811 100644 --- a/webview-ui/src/components/mcp/McpView.tsx +++ b/webview-ui/src/components/mcp/McpView.tsx @@ -23,6 +23,13 @@ const McpView = ({ onDone }: McpViewProps) => { const { mcpServers: servers } = useExtensionState() const [activeTab, setActiveTab] = useState("marketplace") + const handleTabChange = (tab: string) => { + setActiveTab(tab) + if (tab === "marketplace") { + vscode.postMessage({ type: "silentlyRefreshMcpMarketplace" }) + } + } + // const [servers, setServers] = useState([ // // Add some mock servers for testing // { @@ -115,10 +122,10 @@ const McpView = ({ onDone }: McpViewProps) => { padding: "0 20px 0 20px", borderBottom: "1px solid var(--vscode-panel-border)", }}> - setActiveTab("marketplace")}> + handleTabChange("marketplace")}> Marketplace - setActiveTab("installed")}> + handleTabChange("installed")}> Installed
From 31443f2a86f17e5cb281f179e26dbc3b09fceb72 Mon Sep 17 00:00:00 2001 From: pashpashpash Date: Mon, 17 Feb 2025 16:36:18 -0800 Subject: [PATCH 054/100] removed top border from submit card --- webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx | 1 + webview-ui/src/components/mcp/marketplace/McpSubmitCard.tsx | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx index d07cdb4e1a..711530eb58 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx @@ -295,6 +295,7 @@ const McpMarketplaceView = () => { height: "100%", padding: "20px", color: "var(--vscode-descriptionForeground)", + borderBottom: "1px solid var(--vscode-list-inactiveSelectionBackground)", }}> {searchQuery || selectedCategory ? "No matching MCP servers found" diff --git a/webview-ui/src/components/mcp/marketplace/McpSubmitCard.tsx b/webview-ui/src/components/mcp/marketplace/McpSubmitCard.tsx index 3373a96783..717fdf4688 100644 --- a/webview-ui/src/components/mcp/marketplace/McpSubmitCard.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpSubmitCard.tsx @@ -10,7 +10,6 @@ const McpSubmitCard = () => { alignItems: "center", gap: "24px", padding: "32px 20px", - borderTop: "1px solid var(--vscode-list-inactiveSelectionBackground)", marginTop: "16px", }}> {/* Logo */} From 15b269c8f2e37684045fc737e203ace621394323 Mon Sep 17 00:00:00 2001 From: pashpashpash Date: Mon, 17 Feb 2025 16:39:40 -0800 Subject: [PATCH 055/100] not using vscodelink just a tag --- .../mcp/marketplace/McpMarketplaceCard.tsx | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx index 12555244f2..b35cef80b4 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx @@ -40,15 +40,6 @@ const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps) .mcp-card:hover { background-color: var(--vscode-list-hoverBackground); } - vscode-link::part(control) { - text-decoration: none !important; - border: none !important; - } - vscode-link:hover::part(control) { - color: var(--link-active-foreground); - text-decoration: none !important; - border: none !important; - } `}
- { e.currentTarget.style.opacity = "0.8" + e.currentTarget.style.color = "var(--link-active-foreground)" }} onMouseLeave={(e) => { e.currentTarget.style.opacity = "0.5" + e.currentTarget.style.color = "var(--vscode-foreground)" }}>
@@ -172,7 +164,7 @@ const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps) {item.author}
-
+
Date: Mon, 17 Feb 2025 17:27:31 -0800 Subject: [PATCH 056/100] cleaned up unused vars --- webview-ui/src/components/mcp/McpView.tsx | 9 +----- .../mcp/marketplace/McpMarketplaceCard.tsx | 3 +- .../mcp/marketplace/McpMarketplaceView.tsx | 29 ------------------- .../mcp/marketplace/McpSubmitCard.tsx | 27 ----------------- 4 files changed, 2 insertions(+), 66 deletions(-) diff --git a/webview-ui/src/components/mcp/McpView.tsx b/webview-ui/src/components/mcp/McpView.tsx index 3c0eef4811..4e7d2b0da9 100644 --- a/webview-ui/src/components/mcp/McpView.tsx +++ b/webview-ui/src/components/mcp/McpView.tsx @@ -1,11 +1,4 @@ -import { - VSCodeButton, - VSCodeLink, - VSCodePanels, - VSCodePanelTab, - VSCodePanelView, - VSCodeDivider, -} from "@vscode/webview-ui-toolkit/react" +import { VSCodeButton, VSCodeLink, VSCodePanels, VSCodePanelTab, VSCodePanelView } from "@vscode/webview-ui-toolkit/react" import { useState } from "react" import { vscode } from "../../utils/vscode" import { useExtensionState } from "../../context/ExtensionStateContext" diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx index b35cef80b4..18840f2b70 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx @@ -1,5 +1,4 @@ -import { VSCodeLink } from "@vscode/webview-ui-toolkit/react" -import { CSSProperties, useCallback, useEffect, useState, useRef } from "react" +import { useCallback, useState, useRef } from "react" import styled from "styled-components" import { McpMarketplaceItem, McpServer } from "../../../../../src/shared/mcp" import { vscode } from "../../../utils/vscode" diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx index 711530eb58..29a2502135 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx @@ -13,35 +13,6 @@ import { useExtensionState } from "../../../context/ExtensionStateContext" import { vscode } from "../../../utils/vscode" import McpMarketplaceCard from "./McpMarketplaceCard" import McpSubmitCard from "./McpSubmitCard" - -const controlHeight = "28px" - -const searchInputStyles = { - width: "100%", - height: controlHeight, - padding: "0 8px 0 32px", // Removed vertical padding since we're using fixed height - background: "var(--vscode-input-background)", - border: "1px solid var(--vscode-input-border)", - color: "var(--vscode-input-foreground)", - borderRadius: "2px", - outline: "none", - transition: "border-color 0.1s ease-in-out, opacity 0.1s ease-in-out", - cursor: "text", // Show text cursor for input -} as const - -const selectStyles = { - height: controlHeight, - padding: "0 12px", // Removed vertical padding since we're using fixed height - background: "var(--vscode-dropdown-background)", - border: "1px solid var(--vscode-dropdown-border)", - color: "var(--vscode-dropdown-foreground)", - borderRadius: "2px", - outline: "none", - transition: "border-color 0.1s ease-in-out, opacity 0.1s ease-in-out", - minWidth: "140px", // Ensure consistent width for dropdowns - cursor: "pointer", // Show pointer cursor on hover -} as const - const McpMarketplaceView = () => { const { mcpServers } = useExtensionState() const [items, setItems] = useState([]) diff --git a/webview-ui/src/components/mcp/marketplace/McpSubmitCard.tsx b/webview-ui/src/components/mcp/marketplace/McpSubmitCard.tsx index 717fdf4688..b00eedc5de 100644 --- a/webview-ui/src/components/mcp/marketplace/McpSubmitCard.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpSubmitCard.tsx @@ -1,6 +1,3 @@ -import { VSCodeLink } from "@vscode/webview-ui-toolkit/react" -import styled from "styled-components" - const McpSubmitCard = () => { return (
{ ) } -const StyledGitHubButton = styled.div` - font-size: 13px; - font-weight: 500; - padding: 8px; - border-radius: 2px; - border: 1px solid var(--vscode-button-border, transparent); - cursor: pointer; - background: var(--vscode-button-background); - color: var(--vscode-button-foreground); - display: flex; - align-items: center; - justify-content: center; - width: 100%; - - &:hover { - background: var(--vscode-button-hoverBackground); - } - - &:active { - background: var(--vscode-button-background); - opacity: 0.7; - } -` - export default McpSubmitCard From 5f57415785607da768f4c51111269801bc9da530 Mon Sep 17 00:00:00 2001 From: pashpashpash Date: Mon, 17 Feb 2025 19:08:29 -0800 Subject: [PATCH 057/100] removed promise all --- src/core/webview/ClineProvider.ts | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 525f41792e..461b62e1a2 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -573,22 +573,22 @@ export class ClineProvider implements vscode.WebviewViewProvider { // 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 // (see normalizeApiConfiguration > openrouter) // Prefetch marketplace and OpenRouter models - Promise.all([ - this.prefetchMcpMarketplace(), - this.refreshOpenRouterModels().then(async (openRouterModels) => { - if (openRouterModels) { - // 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) - const { apiConfiguration } = await this.getState() - if (apiConfiguration.openRouterModelId) { - await this.updateGlobalState( - "openRouterModelInfo", - openRouterModels[apiConfiguration.openRouterModelId], - ) - await this.postStateToWebview() - } + + this.prefetchMcpMarketplace() + this.refreshOpenRouterModels().then(async (openRouterModels) => { + if (openRouterModels) { + // 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) + const { apiConfiguration } = await this.getState() + if (apiConfiguration.openRouterModelId) { + await this.updateGlobalState( + "openRouterModelInfo", + openRouterModels[apiConfiguration.openRouterModelId], + ) + await this.postStateToWebview() } - }), - ]).catch(console.error) + } + }) + break case "newTask": // Code that should run in response to the hello message command From 8927879d939c8ed3c6410f2e7993ebfc5f53f7e1 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Mon, 17 Feb 2025 19:31:13 -0800 Subject: [PATCH 058/100] Refactor --- src/core/webview/ClineProvider.ts | 338 +++++++++++++++--------------- 1 file changed, 170 insertions(+), 168 deletions(-) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 461b62e1a2..6fc4d051ce 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -379,174 +379,6 @@ export class ClineProvider implements vscode.WebviewViewProvider { * * @param webview A reference to the extension webview */ - private async fetchMcpMarketplaceFromApi(silent: boolean = false): Promise { - try { - const response = await axios.get("https://api.cline.bot/v1/mcp/marketplace", { - headers: { - "Content-Type": "application/json", - }, - }) - - if (!response.data) { - throw new Error("Invalid response from MCP marketplace API") - } - - const catalog: McpMarketplaceCatalog = { - items: (response.data || []).map((item: any) => ({ - ...item, - githubStars: item.githubStars ?? 0, - downloadCount: item.downloadCount ?? 0, - tags: item.tags ?? [], - })), - } - - // Store in global state - await this.updateGlobalState("mcpMarketplaceCatalog", catalog) - return catalog - } catch (error) { - console.error("Failed to fetch MCP marketplace:", error) - if (!silent) { - const errorMessage = error instanceof Error ? error.message : "Failed to fetch MCP marketplace" - await this.postMessageToWebview({ - type: "mcpMarketplaceCatalog", - error: errorMessage, - }) - vscode.window.showErrorMessage(errorMessage) - } - return undefined - } - } - - async prefetchMcpMarketplace() { - try { - await this.fetchMcpMarketplaceFromApi(true) - } catch (error) { - console.error("Failed to prefetch MCP marketplace:", error) - } - } - - async silentlyRefreshMcpMarketplace() { - try { - const catalog = await this.fetchMcpMarketplaceFromApi(true) - if (catalog) { - await this.postMessageToWebview({ - type: "mcpMarketplaceCatalog", - mcpMarketplaceCatalog: catalog, - }) - } - } catch (error) { - console.error("Failed to silently refresh MCP marketplace:", error) - } - } - - private async fetchMcpMarketplace(forceRefresh: boolean = false) { - try { - // Check if we have cached data - const cachedCatalog = (await this.getGlobalState("mcpMarketplaceCatalog")) as McpMarketplaceCatalog | undefined - if (!forceRefresh && cachedCatalog?.items) { - await this.postMessageToWebview({ - type: "mcpMarketplaceCatalog", - mcpMarketplaceCatalog: cachedCatalog, - }) - return - } - - const catalog = await this.fetchMcpMarketplaceFromApi(false) - if (catalog) { - await this.postMessageToWebview({ - type: "mcpMarketplaceCatalog", - mcpMarketplaceCatalog: catalog, - }) - } - } catch (error) { - console.error("Failed to handle cached MCP marketplace:", error) - const errorMessage = error instanceof Error ? error.message : "Failed to handle cached MCP marketplace" - await this.postMessageToWebview({ - type: "mcpMarketplaceCatalog", - error: errorMessage, - }) - vscode.window.showErrorMessage(errorMessage) - } - } - - private async downloadMcp(mcpId: string) { - try { - // First check if we already have this MCP server installed - const servers = this.mcpHub?.getServers() || [] - const isInstalled = servers.some((server: McpServer) => server.name === mcpId) - - if (isInstalled) { - throw new Error("This MCP server is already installed") - } - - // Fetch server details from marketplace - const response = await axios.post( - "https://api.cline.bot/v1/mcp/download", - { mcpId }, - { - headers: { "Content-Type": "application/json" }, - timeout: 10000, - }, - ) - - if (!response.data) { - throw new Error("Invalid response from MCP marketplace API") - } - - console.log("[downloadMcp] Response from download API", { response }) - - const mcpDetails = response.data - - // Validate required fields - if (!mcpDetails.githubUrl) { - throw new Error("Missing GitHub URL in MCP download response") - } - if (!mcpDetails.readmeContent) { - throw new Error("Missing README content in MCP download response") - } - - // Send details to webview - await this.postMessageToWebview({ - type: "mcpDownloadDetails", - mcpDownloadDetails: mcpDetails, - }) - - // Create task with context from README - const task = `Set up the MCP server from ${mcpDetails.githubUrl}. Use "${mcpDetails.mcpId}" as the server name in cline_mcp_settings.json. Here's some additional context from the README:\n\n${mcpDetails.readmeContent}\n${mcpDetails.llmsInstallationContent}` - - // Initialize task and show chat view - await this.initClineWithTask(task) - await this.postMessageToWebview({ - type: "action", - action: "chatButtonClicked", - }) - } catch (error) { - console.error("Failed to download MCP:", error) - let errorMessage = "Failed to download MCP" - - if (axios.isAxiosError(error)) { - if (error.code === "ECONNABORTED") { - errorMessage = "Request timed out. Please try again." - } else if (error.response?.status === 404) { - errorMessage = "MCP server not found in marketplace." - } else if (error.response?.status === 500) { - errorMessage = "Internal server error. Please try again later." - } else if (!error.response && error.request) { - errorMessage = "Network error. Please check your internet connection." - } - } else if (error instanceof Error) { - errorMessage = error.message - } - - // Show error in both notification and marketplace UI - vscode.window.showErrorMessage(errorMessage) - await this.postMessageToWebview({ - type: "mcpDownloadDetails", - error: errorMessage, - }) - } - } - private setWebviewMessageListener(webview: vscode.Webview) { webview.onDidReceiveMessage( async (message: WebviewMessage) => { @@ -1242,6 +1074,176 @@ export class ClineProvider implements vscode.WebviewViewProvider { } } + // MCP Marketplace + + private async fetchMcpMarketplaceFromApi(silent: boolean = false): Promise { + try { + const response = await axios.get("https://api.cline.bot/v1/mcp/marketplace", { + headers: { + "Content-Type": "application/json", + }, + }) + + if (!response.data) { + throw new Error("Invalid response from MCP marketplace API") + } + + const catalog: McpMarketplaceCatalog = { + items: (response.data || []).map((item: any) => ({ + ...item, + githubStars: item.githubStars ?? 0, + downloadCount: item.downloadCount ?? 0, + tags: item.tags ?? [], + })), + } + + // Store in global state + await this.updateGlobalState("mcpMarketplaceCatalog", catalog) + return catalog + } catch (error) { + console.error("Failed to fetch MCP marketplace:", error) + if (!silent) { + const errorMessage = error instanceof Error ? error.message : "Failed to fetch MCP marketplace" + await this.postMessageToWebview({ + type: "mcpMarketplaceCatalog", + error: errorMessage, + }) + vscode.window.showErrorMessage(errorMessage) + } + return undefined + } + } + + async prefetchMcpMarketplace() { + try { + await this.fetchMcpMarketplaceFromApi(true) + } catch (error) { + console.error("Failed to prefetch MCP marketplace:", error) + } + } + + async silentlyRefreshMcpMarketplace() { + try { + const catalog = await this.fetchMcpMarketplaceFromApi(true) + if (catalog) { + await this.postMessageToWebview({ + type: "mcpMarketplaceCatalog", + mcpMarketplaceCatalog: catalog, + }) + } + } catch (error) { + console.error("Failed to silently refresh MCP marketplace:", error) + } + } + + private async fetchMcpMarketplace(forceRefresh: boolean = false) { + try { + // Check if we have cached data + const cachedCatalog = (await this.getGlobalState("mcpMarketplaceCatalog")) as McpMarketplaceCatalog | undefined + if (!forceRefresh && cachedCatalog?.items) { + await this.postMessageToWebview({ + type: "mcpMarketplaceCatalog", + mcpMarketplaceCatalog: cachedCatalog, + }) + return + } + + const catalog = await this.fetchMcpMarketplaceFromApi(false) + if (catalog) { + await this.postMessageToWebview({ + type: "mcpMarketplaceCatalog", + mcpMarketplaceCatalog: catalog, + }) + } + } catch (error) { + console.error("Failed to handle cached MCP marketplace:", error) + const errorMessage = error instanceof Error ? error.message : "Failed to handle cached MCP marketplace" + await this.postMessageToWebview({ + type: "mcpMarketplaceCatalog", + error: errorMessage, + }) + vscode.window.showErrorMessage(errorMessage) + } + } + + private async downloadMcp(mcpId: string) { + try { + // First check if we already have this MCP server installed + const servers = this.mcpHub?.getServers() || [] + const isInstalled = servers.some((server: McpServer) => server.name === mcpId) + + if (isInstalled) { + throw new Error("This MCP server is already installed") + } + + // Fetch server details from marketplace + const response = await axios.post( + "https://api.cline.bot/v1/mcp/download", + { mcpId }, + { + headers: { "Content-Type": "application/json" }, + timeout: 10000, + }, + ) + + if (!response.data) { + throw new Error("Invalid response from MCP marketplace API") + } + + console.log("[downloadMcp] Response from download API", { response }) + + const mcpDetails = response.data + + // Validate required fields + if (!mcpDetails.githubUrl) { + throw new Error("Missing GitHub URL in MCP download response") + } + if (!mcpDetails.readmeContent) { + throw new Error("Missing README content in MCP download response") + } + + // Send details to webview + await this.postMessageToWebview({ + type: "mcpDownloadDetails", + mcpDownloadDetails: mcpDetails, + }) + + // Create task with context from README + const task = `Set up the MCP server from ${mcpDetails.githubUrl}. Use "${mcpDetails.mcpId}" as the server name in cline_mcp_settings.json. Here's some additional context from the README:\n\n${mcpDetails.readmeContent}\n${mcpDetails.llmsInstallationContent}` + + // Initialize task and show chat view + await this.initClineWithTask(task) + await this.postMessageToWebview({ + type: "action", + action: "chatButtonClicked", + }) + } catch (error) { + console.error("Failed to download MCP:", error) + let errorMessage = "Failed to download MCP" + + if (axios.isAxiosError(error)) { + if (error.code === "ECONNABORTED") { + errorMessage = "Request timed out. Please try again." + } else if (error.response?.status === 404) { + errorMessage = "MCP server not found in marketplace." + } else if (error.response?.status === 500) { + errorMessage = "Internal server error. Please try again later." + } else if (!error.response && error.request) { + errorMessage = "Network error. Please check your internet connection." + } + } else if (error instanceof Error) { + errorMessage = error.message + } + + // Show error in both notification and marketplace UI + vscode.window.showErrorMessage(errorMessage) + await this.postMessageToWebview({ + type: "mcpDownloadDetails", + error: errorMessage, + }) + } + } + // OpenAi async getOpenAiModels(baseUrl?: string, apiKey?: string) { From 24d3bfbccb95ff3779928e8f8c15f6d9986fd22a Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Mon, 17 Feb 2025 19:50:35 -0800 Subject: [PATCH 059/100] Adjust styling --- .../components/mcp/marketplace/McpMarketplaceCard.tsx | 9 +++++---- .../components/mcp/marketplace/McpMarketplaceView.tsx | 7 +++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx index 18840f2b70..45383e68d2 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx @@ -56,14 +56,14 @@ const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps) }) }} style={{ - padding: "16px 20px", + padding: "14px 16px", display: "flex", flexDirection: "column", - gap: 16, + gap: 12, cursor: isLoading ? "wait" : "pointer", }}> {/* Main container with logo and content */} -
+
{/* Logo */} {item.logoUrl && ( {/* Description and tags */} -
+

{item.description}

{ color: "var(--vscode-descriptionForeground)", textTransform: "uppercase", fontWeight: 500, + flexShrink: 0, }}> Filter:
setSelectedCategory((e.target as HTMLSelectElement).value || null)}> All Categories From 691cf91a4ddf2a3baa6a00efece4f33ee4d609e0 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Mon, 17 Feb 2025 20:05:16 -0800 Subject: [PATCH 060/100] Fixes --- src/core/webview/ClineProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 6fc4d051ce..94204867d4 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1209,7 +1209,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { }) // Create task with context from README - const task = `Set up the MCP server from ${mcpDetails.githubUrl}. Use "${mcpDetails.mcpId}" as the server name in cline_mcp_settings.json. Here's some additional context from the README:\n\n${mcpDetails.readmeContent}\n${mcpDetails.llmsInstallationContent}` + const task = `Set up the MCP server from ${mcpDetails.githubUrl}. Use "${mcpDetails.mcpId}" as the server name in cline_mcp_settings.json. Here is the project's README to help you get started:\n\n${mcpDetails.readmeContent}\n${mcpDetails.llmsInstallationContent}` // Initialize task and show chat view await this.initClineWithTask(task) From b7519a3669da9c3444d7aee8e64f8375dc85c9ea Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Mon, 17 Feb 2025 20:13:57 -0800 Subject: [PATCH 061/100] Create stupid-mayflies-melt.md --- .changeset/stupid-mayflies-melt.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/stupid-mayflies-melt.md diff --git a/.changeset/stupid-mayflies-melt.md b/.changeset/stupid-mayflies-melt.md new file mode 100644 index 0000000000..cd2a31cbc8 --- /dev/null +++ b/.changeset/stupid-mayflies-melt.md @@ -0,0 +1,5 @@ +--- +"claude-dev": patch +--- + +Add MCP Marketplace From 9c840fc89c876d3937ee4374fb85995ba71160ff Mon Sep 17 00:00:00 2001 From: pashpashpash Date: Mon, 17 Feb 2025 20:27:11 -0800 Subject: [PATCH 062/100] Update webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> --- .../src/components/mcp/marketplace/McpMarketplaceView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx index 2271e1d17d..2204709415 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx @@ -48,7 +48,7 @@ const McpMarketplaceView = () => { case "name": return a.name.localeCompare(b.name) case "newest": - return b.githubStars - a.githubStars // FIXME: b.createdAt - a.createdAt // Assuming there's a createdAt field + return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime() default: return 0 } From e587115d62ba27d84b8b8b89a2fe7d71601c0045 Mon Sep 17 00:00:00 2001 From: pashpashpash Date: Mon, 17 Feb 2025 20:33:25 -0800 Subject: [PATCH 063/100] added silent fetching to when mcp view is clicked --- webview-ui/src/components/mcp/McpView.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/webview-ui/src/components/mcp/McpView.tsx b/webview-ui/src/components/mcp/McpView.tsx index 4e7d2b0da9..e1da3dff71 100644 --- a/webview-ui/src/components/mcp/McpView.tsx +++ b/webview-ui/src/components/mcp/McpView.tsx @@ -1,5 +1,5 @@ import { VSCodeButton, VSCodeLink, VSCodePanels, VSCodePanelTab, VSCodePanelView } from "@vscode/webview-ui-toolkit/react" -import { useState } from "react" +import { useState, useEffect } from "react" import { vscode } from "../../utils/vscode" import { useExtensionState } from "../../context/ExtensionStateContext" import { McpServer } from "../../../../src/shared/mcp" @@ -18,11 +18,12 @@ const McpView = ({ onDone }: McpViewProps) => { const handleTabChange = (tab: string) => { setActiveTab(tab) - if (tab === "marketplace") { - vscode.postMessage({ type: "silentlyRefreshMcpMarketplace" }) - } } + useEffect(() => { + vscode.postMessage({ type: "silentlyRefreshMcpMarketplace" }) + }, []) + // const [servers, setServers] = useState([ // // Add some mock servers for testing // { From 95acb1e95c16b04febd94d4ffaf06b0ff504880f Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Mon, 17 Feb 2025 20:34:29 -0800 Subject: [PATCH 064/100] Fix overflowing mcp server name --- webview-ui/src/components/mcp/McpView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webview-ui/src/components/mcp/McpView.tsx b/webview-ui/src/components/mcp/McpView.tsx index e1da3dff71..511ac2de3c 100644 --- a/webview-ui/src/components/mcp/McpView.tsx +++ b/webview-ui/src/components/mcp/McpView.tsx @@ -280,7 +280,7 @@ const ServerRow = ({ server }: { server: McpServer }) => { {!server.error && ( )} - {server.name} + {server.name}
e.stopPropagation()}>
Date: Mon, 17 Feb 2025 22:01:26 -0800 Subject: [PATCH 065/100] Add warning for community made servers; open GitHub on click --- src/core/webview/ClineProvider.ts | 58 ++++++++--------- src/shared/WebviewMessage.ts | 1 - .../mcp/marketplace/McpMarketplaceCard.tsx | 64 +++++++++++-------- .../mcp/marketplace/McpMarketplaceView.tsx | 1 - .../mcp/marketplace/McpSubmitCard.tsx | 32 ++++------ 5 files changed, 81 insertions(+), 75 deletions(-) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 94204867d4..b19295cdd6 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -801,41 +801,41 @@ export class ClineProvider implements vscode.WebviewViewProvider { await this.silentlyRefreshMcpMarketplace() break } - case "openMcpMarketplaceServerDetails": { - if (message.mcpId) { - const response = await fetch(`https://api.cline.bot/v1/mcp/marketplace/item?mcpId=${message.mcpId}`) - const details: McpDownloadResponse = await response.json() + // case "openMcpMarketplaceServerDetails": { + // if (message.text) { + // const response = await fetch(`https://api.cline.bot/v1/mcp/marketplace/item?mcpId=${message.mcpId}`) + // const details: McpDownloadResponse = await response.json() - if (details.readmeContent) { - // Disable markdown preview markers - const config = vscode.workspace.getConfiguration("markdown") - await config.update("preview.markEditorSelection", false, true) + // if (details.readmeContent) { + // // Disable markdown preview markers + // const config = vscode.workspace.getConfiguration("markdown") + // await config.update("preview.markEditorSelection", false, true) - // Create URI with base64 encoded markdown content - const uri = vscode.Uri.parse( - `${DIFF_VIEW_URI_SCHEME}:${details.name} README?${Buffer.from(details.readmeContent).toString("base64")}`, - ) + // // Create URI with base64 encoded markdown content + // const uri = vscode.Uri.parse( + // `${DIFF_VIEW_URI_SCHEME}:${details.name} README?${Buffer.from(details.readmeContent).toString("base64")}`, + // ) - // close existing - const tabs = vscode.window.tabGroups.all - .flatMap((tg) => tg.tabs) - .filter((tab) => tab.label && tab.label.includes("README") && tab.label.includes("Preview")) - for (const tab of tabs) { - await vscode.window.tabGroups.close(tab) - } + // // close existing + // const tabs = vscode.window.tabGroups.all + // .flatMap((tg) => tg.tabs) + // .filter((tab) => tab.label && tab.label.includes("README") && tab.label.includes("Preview")) + // for (const tab of tabs) { + // await vscode.window.tabGroups.close(tab) + // } - // Show only the preview - await vscode.commands.executeCommand("markdown.showPreview", uri, { - sideBySide: true, - preserveFocus: true, - }) - } - } + // // Show only the preview + // await vscode.commands.executeCommand("markdown.showPreview", uri, { + // sideBySide: true, + // preserveFocus: true, + // }) + // } + // } - this.postMessageToWebview({ type: "relinquishControl" }) + // this.postMessageToWebview({ type: "relinquishControl" }) - break - } + // break + // } case "toggleMcpServer": { try { await this.mcpHub?.toggleServerDisabled(message.serverName!, message.disabled!) diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 5c6ea5a3d5..b721d4f3de 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -45,7 +45,6 @@ export interface WebviewMessage { | "subscribeEmail" | "fetchMcpMarketplace" | "downloadMcp" - | "openMcpMarketplaceServerDetails" | "silentlyRefreshMcpMarketplace" | "searchCommits" // | "relaunchChromeDebugMode" diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx index 45383e68d2..5e5d03b779 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx @@ -1,4 +1,4 @@ -import { useCallback, useState, useRef } from "react" +import { useCallback, useState, useRef, useMemo } from "react" import styled from "styled-components" import { McpMarketplaceItem, McpServer } from "../../../../../src/shared/mcp" import { vscode } from "../../../utils/vscode" @@ -29,6 +29,15 @@ const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps) useEvent("message", handleMessage) + const githubAuthorUrl = useMemo(() => { + const url = new URL(item.githubUrl) + const pathParts = url.pathname.split("/") + if (pathParts.length >= 2) { + return `${url.origin}/${pathParts[1]}` + } + return item.githubUrl + }, [item.githubUrl]) + return ( <> -
{ - if (githubLinkRef.current?.contains(e.target as Node)) { - return - } - - console.log("Card clicked:", item.mcpId) - setIsLoading(true) - vscode.postMessage({ - type: "openMcpMarketplaceServerDetails", - mcpId: item.mcpId, - }) - }} style={{ padding: "14px 16px", display: "flex", flexDirection: "column", gap: 12, cursor: isLoading ? "wait" : "pointer", + textDecoration: "none", + color: "inherit", }}> {/* Main container with logo and content */}
@@ -104,7 +104,8 @@ const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps)

{ - e.stopPropagation() // Prevent card click when clicking install + e.preventDefault() // Prevent card click when clicking install + e.stopPropagation() // Stop event from bubbling up to parent link if (!isInstalled && !isDownloading) { setIsDownloading(true) vscode.postMessage({ @@ -125,31 +126,31 @@ const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps) style={{ display: "flex", alignItems: "center", - gap: "12px", + gap: "8px", fontSize: "12px", color: "var(--vscode-descriptionForeground)", flexWrap: "wrap", minWidth: 0, - rowGap: 0, // Add this to remove vertical gap + rowGap: 0, }}> { - e.currentTarget.style.opacity = "0.8" + e.currentTarget.style.opacity = "1" e.currentTarget.style.color = "var(--link-active-foreground)" }} onMouseLeave={(e) => { - e.currentTarget.style.opacity = "0.5" + e.currentTarget.style.opacity = "0.7" e.currentTarget.style.color = "var(--vscode-foreground)" }}>
@@ -190,15 +191,28 @@ const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps) {item.requiresApiKey && ( )} - {item.isRecommended && ( - - )}
{/* Description and tags */}
+ {!item.isRecommended && ( +
+ + Community Made (use at your own risk) +
+ )} +

{item.description}

-
+ ) } diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx index 2204709415..bece71baf6 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx @@ -269,7 +269,6 @@ const McpMarketplaceView = () => { height: "100%", padding: "20px", color: "var(--vscode-descriptionForeground)", - borderBottom: "1px solid var(--vscode-list-inactiveSelectionBackground)", }}> {searchQuery || selectedCategory ? "No matching MCP servers found" diff --git a/webview-ui/src/components/mcp/marketplace/McpSubmitCard.tsx b/webview-ui/src/components/mcp/marketplace/McpSubmitCard.tsx index b00eedc5de..5ce28933ea 100644 --- a/webview-ui/src/components/mcp/marketplace/McpSubmitCard.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpSubmitCard.tsx @@ -5,20 +5,14 @@ const McpSubmitCard = () => { display: "flex", flexDirection: "column", alignItems: "center", - gap: "24px", - padding: "32px 20px", - marginTop: "16px", + gap: "12px", + padding: "15px", + margin: "20px", + backgroundColor: "var(--vscode-textBlockQuote-background)", + borderRadius: "6px", }}> - {/* Logo */} - Cline bot logo + {/* Icon */} + {/* Content */}
{ display: "flex", flexDirection: "column", alignItems: "center", - gap: "12px", + gap: "4px", textAlign: "center", maxWidth: "480px", }}>

- Is something missing? + Submit MCP Server

- Submit your own MCP servers to the marketplace by{" "} - submitting an issue on the official MCP Marketplace - repo on GitHub. + Help others discover great MCP servers by submitting an issue to{" "} + github.com/cline/mcp-marketplace

From ec3bd5690350a4e48675b4f88b33eee601060ad5 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Mon, 17 Feb 2025 22:15:43 -0800 Subject: [PATCH 066/100] FIx outline --- .../src/components/mcp/marketplace/McpMarketplaceCard.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx index 5e5d03b779..5c5279ced6 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx @@ -44,10 +44,14 @@ const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps) {` .mcp-card { cursor: pointer; + outline: none !important; } .mcp-card:hover { background-color: var(--vscode-list-hoverBackground); } + .mcp-card:focus { + outline: none !important; + } `} Date: Tue, 18 Feb 2025 10:49:37 -0800 Subject: [PATCH 067/100] Refactor --- src/api/providers/openrouter.ts | 9 +++---- src/api/transform/r1-format.ts | 48 +++++++++++++++------------------ src/core/Cline.ts | 1 - 3 files changed, 24 insertions(+), 34 deletions(-) diff --git a/src/api/providers/openrouter.ts b/src/api/providers/openrouter.ts index 8e72d6554c..5f95639fdf 100644 --- a/src/api/providers/openrouter.ts +++ b/src/api/providers/openrouter.ts @@ -103,14 +103,11 @@ export class OpenRouterHandler implements ApiHandler { let temperature = 0 let topP: number | undefined = undefined - // Handle models based on deepseek-r1 if (this.getModel().id.startsWith("deepseek/deepseek-r1") || this.getModel().id === "perplexity/sonar-reasoning") { - // Recommended temperature for DeepSeek reasoning models - temperature = 0.6 - // DeepSeek highly recommends using user instead of system role - openAiMessages = convertToR1Format([{ role: "user", content: systemPrompt }, ...messages]) - // Some provider support topP and 0.95 is value that Deepseek used in their benchmarks + // Recommended values from DeepSeek + temperature = 0.7 topP = 0.95 + openAiMessages = convertToR1Format([{ role: "user", content: systemPrompt }, ...messages]) } // 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. diff --git a/src/api/transform/r1-format.ts b/src/api/transform/r1-format.ts index 51a4b94dbc..a080841bfe 100644 --- a/src/api/transform/r1-format.ts +++ b/src/api/transform/r1-format.ts @@ -1,30 +1,24 @@ import { Anthropic } from "@anthropic-ai/sdk" import OpenAI from "openai" -type ContentPartText = OpenAI.Chat.ChatCompletionContentPartText -type ContentPartImage = OpenAI.Chat.ChatCompletionContentPartImage -type UserMessage = OpenAI.Chat.ChatCompletionUserMessageParam -type AssistantMessage = OpenAI.Chat.ChatCompletionAssistantMessageParam -type Message = OpenAI.Chat.ChatCompletionMessageParam -type AnthropicMessage = Anthropic.Messages.MessageParam - /** - * Converts Anthropic messages to OpenAI format while merging consecutive messages with the same role. + * Converts Anthropic messages to OpenAI format and merges consecutive messages with the same role. * This is required for DeepSeek Reasoner which does not support successive messages with the same role. + * DeepSeek highly recommends using 'user' role instead of 'system' role for optimal performance. * * @param messages Array of Anthropic messages - * @returns Array of OpenAI messages where consecutive messages with the same role are combined + * @returns Array of OpenAI messages where consecutive messages with the same role are merged together */ -export function convertToR1Format(messages: AnthropicMessage[]): Message[] { - return messages.reduce((merged, message) => { +export function convertToR1Format(messages: Anthropic.Messages.MessageParam[]): OpenAI.Chat.ChatCompletionMessageParam[] { + return messages.reduce((merged, message) => { const lastMessage = merged[merged.length - 1] - let messageContent: string | (ContentPartText | ContentPartImage)[] = "" + let messageContent: string | (OpenAI.Chat.ChatCompletionContentPartText | OpenAI.Chat.ChatCompletionContentPartImage)[] = + "" let hasImages = false - // Convert content to appropriate format if (Array.isArray(message.content)) { const textParts: string[] = [] - const imageParts: ContentPartImage[] = [] + const imageParts: OpenAI.Chat.ChatCompletionContentPartImage[] = [] message.content.forEach((part) => { if (part.type === "text") { @@ -40,7 +34,7 @@ export function convertToR1Format(messages: AnthropicMessage[]): Message[] { }) if (hasImages) { - const parts: (ContentPartText | ContentPartImage)[] = [] + const parts: (OpenAI.Chat.ChatCompletionContentPartText | OpenAI.Chat.ChatCompletionContentPartImage)[] = [] if (textParts.length > 0) { parts.push({ type: "text", text: textParts.join("\n") }) } @@ -53,13 +47,11 @@ export function convertToR1Format(messages: AnthropicMessage[]): Message[] { messageContent = message.content } - // If last message has same role, merge the content + // If the last message has the same role, merge the content if (lastMessage?.role === message.role) { if (typeof lastMessage.content === "string" && typeof messageContent === "string") { lastMessage.content += `\n${messageContent}` - } - // If either has image content, convert both to array format - else { + } else { const lastContent = Array.isArray(lastMessage.content) ? lastMessage.content : [{ type: "text" as const, text: lastMessage.content || "" }] @@ -69,30 +61,32 @@ export function convertToR1Format(messages: AnthropicMessage[]): Message[] { : [{ type: "text" as const, text: messageContent }] if (message.role === "assistant") { - const mergedContent = [...lastContent, ...newContent] as AssistantMessage["content"] + const mergedContent = [ + ...lastContent, + ...newContent, + ] as OpenAI.Chat.ChatCompletionAssistantMessageParam["content"] lastMessage.content = mergedContent } else { - const mergedContent = [...lastContent, ...newContent] as UserMessage["content"] + const mergedContent = [...lastContent, ...newContent] as OpenAI.Chat.ChatCompletionUserMessageParam["content"] lastMessage.content = mergedContent } } } else { - // Add as new message with the correct type based on role + // Adds new message with the correct type based on role if (message.role === "assistant") { - const newMessage: AssistantMessage = { + const newMessage: OpenAI.Chat.ChatCompletionAssistantMessageParam = { role: "assistant", - content: messageContent as AssistantMessage["content"], + content: messageContent as OpenAI.Chat.ChatCompletionAssistantMessageParam["content"], } merged.push(newMessage) } else { - const newMessage: UserMessage = { + const newMessage: OpenAI.Chat.ChatCompletionUserMessageParam = { role: "user", - content: messageContent as UserMessage["content"], + content: messageContent as OpenAI.Chat.ChatCompletionUserMessageParam["content"], } merged.push(newMessage) } } - return merged }, []) } diff --git a/src/core/Cline.ts b/src/core/Cline.ts index acecbd4fe6..9209eeacb5 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -3137,7 +3137,6 @@ export class Cline { try { for await (const chunk of stream) { if (!chunk) { - // Sometimes chunk is undefined, no idea that can cause it, but this workaround seems to fix it continue } switch (chunk.type) { From 220603e530642e3228185f516c1df8e2f2f820d8 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 18 Feb 2025 10:59:43 -0800 Subject: [PATCH 068/100] Update changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1eaebc759..c60aa595d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,11 @@ ## [3.4.0] -- Send current textfield contents as additional feedback when toggling from Plan to Act Mode, or when hitting 'Approve' button +- Use more visual checkpoints indicators after editing files & running commands +- Create a checkpoint at the beginning of each task to easily revert to the initial state - Add 'Terminal' context mention to reference the active terminal's contents - Add 'Git Commits' context mention to reference current working changes or specific commits (thanks @mrubens!) +- Send current textfield contents as additional feedback when toggling from Plan to Act Mode, or when hitting 'Approve' button - Add advanced configuration options for OpenAI Compatible (context window, max output, pricing, etc.) - Add Alibaba Qwen 2.5 coder models, VL models, and DeepSeek-R1/V3 support - Improve support for AWS Bedrock Profiles From cb02277b452b8202a623362e901bc0e84441511b Mon Sep 17 00:00:00 2001 From: Evan <58194240+celestial-vault@users.noreply.github.com> Date: Tue, 18 Feb 2025 12:25:03 -0800 Subject: [PATCH 069/100] More Mermaids (#1833) * wip * install dependencies * rendering mermaid graphs * fix render failure on streaming * fix bouncy screen by debouncing mermaid parsing * add loading state; clean up styling * replace tag symbols when rendering code * better mermaid theme for visibility * added changeset * remove rehype-mermaid * update webview package.lock * clean up * fix package-lock * remove regular markdown background styling for mermaid blocks --- .changeset/cyan-bags-work.md | 5 + webview-ui/package-lock.json | 1110 ++++++++++++++++- webview-ui/package.json | 1 + .../src/components/common/MarkdownBlock.tsx | 25 +- .../src/components/common/MermaidBlock.tsx | 95 ++ webview-ui/src/utils/useDebounceEffect.ts | 42 + 6 files changed, 1275 insertions(+), 3 deletions(-) create mode 100644 .changeset/cyan-bags-work.md create mode 100644 webview-ui/src/components/common/MermaidBlock.tsx create mode 100644 webview-ui/src/utils/useDebounceEffect.ts diff --git a/.changeset/cyan-bags-work.md b/.changeset/cyan-bags-work.md new file mode 100644 index 0000000000..21e4a0d3e0 --- /dev/null +++ b/.changeset/cyan-bags-work.md @@ -0,0 +1,5 @@ +--- +"claude-dev": minor +--- + +Add support for rendering mermaid graphs in the chat. diff --git a/webview-ui/package-lock.json b/webview-ui/package-lock.json index e1015aca29..1fa57354ba 100644 --- a/webview-ui/package-lock.json +++ b/webview-ui/package-lock.json @@ -14,6 +14,7 @@ "fast-deep-equal": "^3.1.3", "fuse.js": "^7.0.0", "fzf": "^0.5.2", + "mermaid": "^11.4.1", "pretty-bytes": "^6.1.1", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -73,6 +74,26 @@ "node": ">=6.0.0" } }, + "node_modules/@antfu/install-pkg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.0.0.tgz", + "integrity": "sha512-xvX6P/lo1B3ej0OsaErAjqgFYzYVcJpamjLAFLYh9vRJngBrMoUG7aVnrGTeqM7yxbyTD5p3F2+0/QUEh8Vzhw==", + "dependencies": { + "package-manager-detector": "^0.2.8", + "tinyexec": "^0.3.2" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@antfu/utils": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-8.1.1.tgz", + "integrity": "sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/@asamuzakjp/css-color": { "version": "2.8.3", "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-2.8.3.tgz", @@ -2131,6 +2152,45 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "license": "MIT" }, + "node_modules/@braintree/sanitize-url": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.1.tgz", + "integrity": "sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==" + }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz", + "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==", + "dependencies": { + "@chevrotain/gast": "11.0.3", + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/gast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz", + "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==", + "dependencies": { + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/regexp-to-ast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", + "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==" + }, + "node_modules/@chevrotain/types": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz", + "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==" + }, + "node_modules/@chevrotain/utils": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz", + "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==" + }, "node_modules/@csstools/color-helpers": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.1.tgz", @@ -3097,6 +3157,37 @@ "deprecated": "Use @eslint/object-schema instead", "license": "BSD-3-Clause" }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==" + }, + "node_modules/@iconify/utils": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-2.3.0.tgz", + "integrity": "sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==", + "dependencies": { + "@antfu/install-pkg": "^1.0.0", + "@antfu/utils": "^8.1.0", + "@iconify/types": "^2.0.0", + "debug": "^4.4.0", + "globals": "^15.14.0", + "kolorist": "^1.8.0", + "local-pkg": "^1.0.0", + "mlly": "^1.7.4" + } + }, + "node_modules/@iconify/utils/node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -3662,6 +3753,14 @@ "unist-util-is": "^3.0.0" } }, + "node_modules/@mermaid-js/parser": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.3.0.tgz", + "integrity": "sha512-HsvL6zgE5sUPGgkIDlmAWR1HTNHz2Iy11BAWPTa4Jjabkpguy4Ze2gzfLrg6pdRuBvFwgUYyxiaNqZwrEEXepA==", + "dependencies": { + "langium": "3.0.0" + } + }, "node_modules/@microsoft/fast-element": { "version": "1.14.0", "resolved": "https://registry.npmjs.org/@microsoft/fast-element/-/fast-element-1.14.0.tgz", @@ -4658,6 +4757,228 @@ "@types/node": "*" } }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz", + "integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, "node_modules/@types/eslint": { "version": "8.56.12", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz", @@ -4720,6 +5041,11 @@ "@types/send": "*" } }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==" + }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -6880,6 +7206,30 @@ "integrity": "sha512-+67P1GkJRaxQD6PKK0Et9DhwQB+vGg3PM5+aavopCpZT1lj9jeqfvpgTLAWErNj8qApkkmXlu/Ug74kmhagkXg==", "license": "MIT" }, + "node_modules/chevrotain": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", + "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", + "dependencies": { + "@chevrotain/cst-dts-gen": "11.0.3", + "@chevrotain/gast": "11.0.3", + "@chevrotain/regexp-to-ast": "11.0.3", + "@chevrotain/types": "11.0.3", + "@chevrotain/utils": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/chevrotain-allstar": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", + "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", + "dependencies": { + "lodash-es": "^4.17.21" + }, + "peerDependencies": { + "chevrotain": "^11.0.0" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -7197,6 +7547,11 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "license": "MIT" }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==" + }, "node_modules/confusing-browser-globals": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", @@ -7304,6 +7659,14 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "license": "MIT" }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "dependencies": { + "layout-base": "^1.0.0" + } + }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -7711,6 +8074,471 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, + "node_modules/cytoscape": { + "version": "3.31.0", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.31.0.tgz", + "integrity": "sha512-zDGn1K/tfZwEnoGOcHc0H4XazqAAXAuDpcYw9mUnUjATjqljyCNGJv8uEvbvxGaGHaVshxMecyl6oc6uKzRfbw==", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "dependencies": { + "cose-base": "^2.2.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "dependencies": { + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==" + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.11.tgz", + "integrity": "sha512-tvlJLyQf834SylNKax8Wkzco/1ias1OPw8DcUMDE7oUIoSEW25riQVuiu/0OWEFqT0cxHT3Pa9/D82Jr47IONw==", + "dependencies": { + "d3": "^7.9.0", + "lodash-es": "^4.17.21" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -7782,6 +8610,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" + }, "node_modules/debounce": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/debounce/-/debounce-2.2.0.tgz", @@ -7903,6 +8736,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -8136,6 +8977,14 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.4.tgz", + "integrity": "sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/domutils": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", @@ -10098,6 +10947,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/hachure-fill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", + "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==" + }, "node_modules/handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", @@ -10721,6 +11575,14 @@ "node": ">= 0.4" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, "node_modules/ipaddr.js": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", @@ -12810,6 +13672,21 @@ "node": ">=4.0" } }, + "node_modules/katex": { + "version": "0.16.21", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.21.tgz", + "integrity": "sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -12819,6 +13696,11 @@ "json-buffer": "3.0.1" } }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -12846,6 +13728,26 @@ "node": ">= 8" } }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==" + }, + "node_modules/langium": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/langium/-/langium-3.0.0.tgz", + "integrity": "sha512-+Ez9EoiByeoTu/2BXmEaZ06iPNXM6thWJp02KfBO/raSMyCJ4jw7AkWWa+zBCTm0+Tw1Fj9FOxdqSskyN5nAwg==", + "dependencies": { + "chevrotain": "~11.0.3", + "chevrotain-allstar": "~0.3.0", + "vscode-languageserver": "~9.0.1", + "vscode-languageserver-textdocument": "~1.0.11", + "vscode-uri": "~3.0.8" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/language-subtag-registry": { "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", @@ -12874,6 +13776,11 @@ "shell-quote": "^1.8.1" } }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==" + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -12934,6 +13841,21 @@ "node": ">=8.9.0" } }, + "node_modules/local-pkg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.0.0.tgz", + "integrity": "sha512-bbgPw/wmroJsil/GgL4qjDzs5YLTBMQ99weRsok1XCDccQeehbHA/I1oRvk2NPtr7KGZgT/Y5tPRnAtMqeG2Kg==", + "dependencies": { + "mlly": "^1.7.3", + "pkg-types": "^1.3.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -12955,6 +13877,11 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -13089,6 +14016,17 @@ "tmpl": "1.0.5" } }, + "node_modules/marked": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-13.0.3.tgz", + "integrity": "sha512-rqRix3/TWzE9rIoFGIn8JmsVfhiuC8VIQ8IdX5TfzmeBucdY05/0UlzKaw0eVtpcN/OdVFpBk7CjKGo9iHJ/zA==", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -13285,6 +14223,45 @@ "node": ">= 8" } }, + "node_modules/mermaid": { + "version": "11.4.1", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.4.1.tgz", + "integrity": "sha512-Mb01JT/x6CKDWaxigwfZYuYmDZ6xtrNwNlidKZwkSrDaY9n90tdrJTV5Umk+wP1fZscGptmKFXHsXMDEVZ+Q6A==", + "dependencies": { + "@braintree/sanitize-url": "^7.0.1", + "@iconify/utils": "^2.1.32", + "@mermaid-js/parser": "^0.3.0", + "@types/d3": "^7.4.3", + "cytoscape": "^3.29.2", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", + "d3": "^7.9.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.11", + "dayjs": "^1.11.10", + "dompurify": "^3.2.1", + "katex": "^0.16.9", + "khroma": "^2.1.0", + "lodash-es": "^4.17.21", + "marked": "^13.0.2", + "roughjs": "^4.6.6", + "stylis": "^4.3.1", + "ts-dedent": "^2.2.0", + "uuid": "^9.0.1" + } + }, + "node_modules/mermaid/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -13447,6 +14424,22 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mlly": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", + "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", + "dependencies": { + "acorn": "^8.14.0", + "pathe": "^2.0.1", + "pkg-types": "^1.3.0", + "ufo": "^1.5.4" + } + }, + "node_modules/mlly/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -13937,6 +14930,11 @@ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "license": "BlueOak-1.0.0" }, + "node_modules/package-manager-detector": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.9.tgz", + "integrity": "sha512-+vYvA/Y31l8Zk8dwxHhL3JfTuHPm6tlxM2A3GeQyl7ovYnSp1+mzAxClxaOr0qO1TtPxbQxetI7v5XqKLJZk7Q==" + }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -14040,6 +15038,11 @@ "tslib": "^2.0.3" } }, + "node_modules/path-data-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -14233,6 +15236,21 @@ "node": ">=8" } }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/pkg-types/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==" + }, "node_modules/pkg-up": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", @@ -14306,6 +15324,20 @@ "node": ">=4" } }, + "node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==" + }, + "node_modules/points-on-path": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -16591,6 +17623,11 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" + }, "node_modules/rollup": { "version": "2.79.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", @@ -16645,6 +17682,17 @@ "randombytes": "^2.1.0" } }, + "node_modules/roughjs": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", + "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", + "dependencies": { + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + }, "node_modules/rrweb-cssom": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", @@ -16684,6 +17732,11 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, "node_modules/safe-array-concat": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", @@ -18540,7 +19593,6 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, "license": "MIT" }, "node_modules/tinypool": { @@ -18668,6 +19720,14 @@ "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", "license": "MIT" }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "engines": { + "node": ">=6.10" + } + }, "node_modules/ts-easing": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/ts-easing/-/ts-easing-0.2.0.tgz", @@ -18882,6 +19942,11 @@ "node": ">=14.17" } }, + "node_modules/ufo": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==" + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -19638,6 +20703,49 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" + }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", diff --git a/webview-ui/package.json b/webview-ui/package.json index 8f15b44187..fd3743ad01 100644 --- a/webview-ui/package.json +++ b/webview-ui/package.json @@ -9,6 +9,7 @@ "fast-deep-equal": "^3.1.3", "fuse.js": "^7.0.0", "fzf": "^0.5.2", + "mermaid": "^11.4.1", "pretty-bytes": "^6.1.1", "react": "^18.3.1", "react-dom": "^18.3.1", diff --git a/webview-ui/src/components/common/MarkdownBlock.tsx b/webview-ui/src/components/common/MarkdownBlock.tsx index 91e129ceb2..89b7966915 100644 --- a/webview-ui/src/components/common/MarkdownBlock.tsx +++ b/webview-ui/src/components/common/MarkdownBlock.tsx @@ -1,10 +1,11 @@ -import { memo, useEffect } from "react" +import React, { memo, useEffect } from "react" import { useRemark } from "react-remark" import rehypeHighlight, { Options } from "rehype-highlight" import styled from "styled-components" import { visit } from "unist-util-visit" import { useExtensionState } from "../../context/ExtensionStateContext" import { CODE_BLOCK_BG_COLOR } from "./CodeBlock" +import MermaidBlock from "./MermaidBlock" interface MarkdownBlockProps { markdown?: string @@ -220,7 +221,27 @@ const MarkdownBlock = memo(({ markdown }: MarkdownBlockProps) => { ], rehypeReactOptions: { components: { - pre: ({ node, ...preProps }: any) => , + pre: ({ node, children, ...preProps }: any) => { + if (Array.isArray(children) && children.length === 1 && React.isValidElement(children[0])) { + const child = children[0] as React.ReactElement<{ className?: string }> + if (child.props?.className?.includes("language-mermaid")) { + return child + } + } + return ( + + {children} + + ) + }, + code: (props: any) => { + const className = props.className || "" + if (className.includes("language-mermaid")) { + const codeText = String(props.children || "") + return + } + return + }, }, }, }) diff --git a/webview-ui/src/components/common/MermaidBlock.tsx b/webview-ui/src/components/common/MermaidBlock.tsx new file mode 100644 index 0000000000..5c7919359e --- /dev/null +++ b/webview-ui/src/components/common/MermaidBlock.tsx @@ -0,0 +1,95 @@ +import { useEffect, useRef, useState } from "react" +import mermaid from "mermaid" +import { useDebounceEffect } from "../../utils/useDebounceEffect" +import styled from "styled-components" + +mermaid.initialize({ + startOnLoad: false, + securityLevel: "loose", + theme: "dark", + themeVariables: { + background: "#1e1e1e", + textColor: "#ffffff", // make text much brighter + mainBkg: "#2d2d2d", + lineColor: "#cccccc", // light enough for contrast + fontSize: "16px", + primaryColor: "#3c3c3c", // node fill color, etc. + }, +}) + +interface MermaidBlockProps { + code: string +} + +export default function MermaidBlock({ code }: MermaidBlockProps) { + const containerRef = useRef(null) + const [isLoading, setIsLoading] = useState(false) + + // 1) Whenever `code` changes, mark that we need to re-render a new chart + useEffect(() => { + setIsLoading(true) + }, [code]) + + // 2) Debounce the actual parse/render + useDebounceEffect( + () => { + if (containerRef.current) { + containerRef.current.innerHTML = "" + } + mermaid + .parse(code, { suppressErrors: true }) + .then((isValid) => { + if (!isValid) { + throw new Error("Invalid or incomplete Mermaid code") + } + const id = `mermaid-${Math.random().toString(36).substring(2)}` + return mermaid.render(id, code) + }) + .then(({ svg }) => { + if (containerRef.current) { + containerRef.current.innerHTML = svg + } + }) + .catch((err) => { + console.warn("Mermaid parse/render failed:", err) + containerRef.current!.innerHTML = code.replace(//g, ">") + }) + .finally(() => { + setIsLoading(false) + }) + }, + 500, // Delay 500ms + [code], // Dependencies for scheduling + ) + + return ( + + {isLoading && Creating mermaid chart...} + + {/* The container for the final or raw code. */} + + + ) +} + +const MermaidBlockContainer = styled.div` + position: relative; + margin: 8px 0; +` + +const LoadingMessage = styled.div` + padding: 8px 0; + color: var(--vscode-descriptionForeground); + font-style: italic; + font-size: 0.9em; +` + +interface SvgContainerProps { + $isLoading: boolean +} + +const SvgContainer = styled.div` + opacity: ${(props) => (props.$isLoading ? 0.3 : 1)}; + min-height: 20px; + transition: opacity 0.2s ease; +` diff --git a/webview-ui/src/utils/useDebounceEffect.ts b/webview-ui/src/utils/useDebounceEffect.ts new file mode 100644 index 0000000000..b1374ff68d --- /dev/null +++ b/webview-ui/src/utils/useDebounceEffect.ts @@ -0,0 +1,42 @@ +import { useEffect, useRef } from "react" + +type VoidFn = () => void + +/** + * Runs `effectRef.current()` after `delay` ms whenever any of the `deps` change, + * but cancels/re-schedules if they change again before the delay. + */ +export function useDebounceEffect(effect: VoidFn, delay: number, deps: any[]) { + const callbackRef = useRef(effect) + const timeoutRef = useRef(null) + + // Keep callbackRef current + useEffect(() => { + callbackRef.current = effect + }, [effect]) + + useEffect(() => { + // Clear any queued call + if (timeoutRef.current) { + clearTimeout(timeoutRef.current) + } + + // Schedule a new call + timeoutRef.current = setTimeout(() => { + // always call the *latest* version of effect + callbackRef.current() + }, delay) + + // Cleanup on unmount or next effect + return () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current) + } + } + + // We want to re‐schedule if any item in `deps` changed, + // or if `delay` changed. + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [delay, ...deps]) +} From a20a2444b3a6ba8028e88c60102a603b4ac6af37 Mon Sep 17 00:00:00 2001 From: Kenji Hikmatullah <43457338+kenjihikmatullah@users.noreply.github.com> Date: Wed, 19 Feb 2025 03:33:21 +0700 Subject: [PATCH 070/100] Improve Mode Tooltip (#1839) * feat: Improve Mode Tooltip * Update webview-ui/src/components/chat/ChatTextArea.tsx Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * fix: typo --------- Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> --- .changeset/chilly-pandas-retire.md | 5 +++++ .../src/components/chat/ChatTextArea.tsx | 19 ++++++++++++++++--- webview-ui/src/components/common/Tooltip.tsx | 10 +++------- 3 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 .changeset/chilly-pandas-retire.md diff --git a/.changeset/chilly-pandas-retire.md b/.changeset/chilly-pandas-retire.md new file mode 100644 index 0000000000..a3e0905971 --- /dev/null +++ b/.changeset/chilly-pandas-retire.md @@ -0,0 +1,5 @@ +--- +"claude-dev": minor +--- + +tooltip for each mode can be shown no matter what the current mode is diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index 2e642f9a46..c111aebe71 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -22,6 +22,7 @@ import Tooltip from "../common/Tooltip" import ApiOptions, { normalizeApiConfiguration } from "../settings/ApiOptions" import { MAX_IMAGES_PER_MESSAGE } from "./ChatView" import ContextMenu from "./ContextMenu" +import { ChatSettings } from "../../../../src/shared/ChatSettings" interface ChatTextAreaProps { inputValue: string @@ -236,6 +237,7 @@ const ChatTextArea = forwardRef( const buttonRef = useRef(null) const [arrowPosition, setArrowPosition] = useState(0) const [menuPosition, setMenuPosition] = useState(0) + const [shownTooltipMode, setShownTooltipMode] = useState(null) const [, metaKeyChar] = useMetaKeyDetection(platform) @@ -1130,12 +1132,23 @@ const ChatTextArea = forwardRef( - Plan - Act + setShownTooltipMode("plan")} + onMouseLeave={() => setShownTooltipMode(null)}> + Plan + + setShownTooltipMode("act")} + onMouseLeave={() => setShownTooltipMode(null)}> + Act + diff --git a/webview-ui/src/components/common/Tooltip.tsx b/webview-ui/src/components/common/Tooltip.tsx index de3ee05908..595711fdff 100644 --- a/webview-ui/src/components/common/Tooltip.tsx +++ b/webview-ui/src/components/common/Tooltip.tsx @@ -9,6 +9,7 @@ import { } from "../../utils/vscStyles" interface TooltipProps { + visible: boolean hintText: string tipText: string children: React.ReactNode @@ -38,14 +39,9 @@ const Hint = styled.div` margin-top: 2px; ` -const Tooltip: React.FC = ({ tipText, hintText, children }) => { - const [visible, setVisible] = useState(false) - - const showTooltip = () => setVisible(true) - const hideTooltip = () => setVisible(false) - +const Tooltip: React.FC = ({ visible, tipText, hintText, children }) => { return ( -
+
{children} {visible && ( From 19e5edbc25d074a21d038ca798b4903d30764dca Mon Sep 17 00:00:00 2001 From: nickbaumann98 <163209607+nickbaumann98@users.noreply.github.com> Date: Tue, 18 Feb 2025 14:35:40 -0600 Subject: [PATCH 071/100] Added raw cline-memory-bank custom instructions file to be used for "Add Memory Bank" functionality (#1832) * Update README.md * Create neat-apricots-search.md * added cline-memory-bank markdown --------- Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> --- .changeset/neat-apricots-search.md | 5 + .../raw-instructions/cline-memory-bank.md | 153 ++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 .changeset/neat-apricots-search.md create mode 100644 docs/prompting/custom instructions library/raw-instructions/cline-memory-bank.md diff --git a/.changeset/neat-apricots-search.md b/.changeset/neat-apricots-search.md new file mode 100644 index 0000000000..46ce78fc0f --- /dev/null +++ b/.changeset/neat-apricots-search.md @@ -0,0 +1,5 @@ +--- +"claude-dev": patch +--- + +Update README.md to include Getting Started diff --git a/docs/prompting/custom instructions library/raw-instructions/cline-memory-bank.md b/docs/prompting/custom instructions library/raw-instructions/cline-memory-bank.md new file mode 100644 index 0000000000..dd4cf84e65 --- /dev/null +++ b/docs/prompting/custom instructions library/raw-instructions/cline-memory-bank.md @@ -0,0 +1,153 @@ +# Cline's Memory Bank + +I am Cline, an expert software engineer with a unique characteristic: my memory resets completely between sessions. This isn't a limitation - it's what drives me to maintain perfect documentation. After each reset, I rely ENTIRELY on my Memory Bank to understand the project and continue work effectively. I MUST read ALL memory bank files at the start of EVERY task - this is not optional. + +## Memory Bank Structure + +The Memory Bank consists of required core files and optional context files, all in Markdown format. Files build upon each other in a clear hierarchy: + +```mermaid +flowchart TD + PB[projectbrief.md] --> PC[productContext.md] + PB --> SP[systemPatterns.md] + PB --> TC[techContext.md] + + PC --> AC[activeContext.md] + SP --> AC + TC --> AC + + AC --> P[progress.md] +``` + +### Core Files (Required) +1. `projectbrief.md` + - Foundation document that shapes all other files + - Created at project start if it doesn't exist + - Defines core requirements and goals + - Source of truth for project scope + +2. `productContext.md` + - Why this project exists + - Problems it solves + - How it should work + - User experience goals + +3. `activeContext.md` + - Current work focus + - Recent changes + - Next steps + - Active decisions and considerations + +4. `systemPatterns.md` + - System architecture + - Key technical decisions + - Design patterns in use + - Component relationships + +5. `techContext.md` + - Technologies used + - Development setup + - Technical constraints + - Dependencies + +6. `progress.md` + - What works + - What's left to build + - Current status + - Known issues + +### Additional Context +Create additional files/folders within memory-bank/ when they help organize: +- Complex feature documentation +- Integration specifications +- API documentation +- Testing strategies +- Deployment procedures + +## Core Workflows + +### Plan Mode +```mermaid +flowchart TD + Start[Start] --> ReadFiles[Read Memory Bank] + ReadFiles --> CheckFiles{Files Complete?} + + CheckFiles -->|No| Plan[Create Plan] + Plan --> Document[Document in Chat] + + CheckFiles -->|Yes| Verify[Verify Context] + Verify --> Strategy[Develop Strategy] + Strategy --> Present[Present Approach] +``` + +### Act Mode +```mermaid +flowchart TD + Start[Start] --> Context[Check Memory Bank] + Context --> Update[Update Documentation] + Update --> Rules[Update .clinerules if needed] + Rules --> Execute[Execute Task] + Execute --> Document[Document Changes] +``` + +## Documentation Updates + +Memory Bank updates occur when: +1. Discovering new project patterns +2. After implementing significant changes +3. When user requests with **update memory bank** (MUST review ALL files) +4. When context needs clarification + +```mermaid +flowchart TD + Start[Update Process] + + subgraph Process + P1[Review ALL Files] + P2[Document Current State] + P3[Clarify Next Steps] + P4[Update .clinerules] + + P1 --> P2 --> P3 --> P4 + end + + Start --> Process +``` + +Note: When triggered by **update memory bank**, I MUST review every memory bank file, even if some don't require updates. Focus particularly on activeContext.md and progress.md as they track current state. + +## Project Intelligence (.clinerules) + +The .clinerules file is my learning journal for each project. It captures important patterns, preferences, and project intelligence that help me work more effectively. As I work with you and the project, I'll discover and document key insights that aren't obvious from the code alone. + +```mermaid +flowchart TD + Start{Discover New Pattern} + + subgraph Learn [Learning Process] + D1[Identify Pattern] + D2[Validate with User] + D3[Document in .clinerules] + end + + subgraph Apply [Usage] + A1[Read .clinerules] + A2[Apply Learned Patterns] + A3[Improve Future Work] + end + + Start --> Learn + Learn --> Apply +``` + +### What to Capture +- Critical implementation paths +- User preferences and workflow +- Project-specific patterns +- Known challenges +- Evolution of project decisions +- Tool usage patterns + +The format is flexible - focus on capturing valuable insights that help me work more effectively with you and the project. Think of .clinerules as a living document that grows smarter as we work together. + +REMEMBER: After every memory reset, I begin completely fresh. The Memory Bank is my only link to previous work. It must be maintained with precision and clarity, as my effectiveness depends entirely on its accuracy. \ No newline at end of file From 9289d73a81cb4514acc84f0365232a07ffac8a87 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 18 Feb 2025 14:00:33 -0800 Subject: [PATCH 072/100] Prepare for release --- src/core/prompts/system.ts | 19 ++++++++++++++++++- webview-ui/src/components/chat/ChatRow.tsx | 4 ++-- webview-ui/src/components/mcp/McpView.tsx | 13 ++++++++++++- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index a9a13f1e90..65e6e7b8eb 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -331,7 +331,24 @@ ${ weather-server weather://san-francisco/current -` + + +## Example 6: Another example of using an MCP tool (where the server name is a unique identifier such as a URL) + + +github.com/modelcontextprotocol/servers/tree/main/src/github +create_issue + +{ + "owner": "octocat", + "repo": "hello-world", + "title": "Found a bug", + "body": "I'm having a problem with this.", + "labels": ["bug", "help wanted"], + "assignees": ["octocat"] +} + +` : "" } diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 889ba0efb7..c5e13d39ec 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -201,9 +201,9 @@ export const ChatRowContent = ({ message, isExpanded, onToggleExpand, lastModifi marginBottom: "-1.5px", }}> ), - + Cline wants to {mcpServerUse.type === "use_mcp_tool" ? "use a tool" : "access a resource"} on the{" "} - {mcpServerUse.serverName} MCP server: + {mcpServerUse.serverName} MCP server: , ] case "completion_result": diff --git a/webview-ui/src/components/mcp/McpView.tsx b/webview-ui/src/components/mcp/McpView.tsx index 511ac2de3c..6cc31f2956 100644 --- a/webview-ui/src/components/mcp/McpView.tsx +++ b/webview-ui/src/components/mcp/McpView.tsx @@ -280,7 +280,18 @@ const ServerRow = ({ server }: { server: McpServer }) => { {!server.error && ( )} - {server.name} + + {server.name} +
e.stopPropagation()}>
Date: Tue, 18 Feb 2025 14:17:46 -0800 Subject: [PATCH 073/100] Mermaid system prompt (#1849) * wip * install dependencies * rendering mermaid graphs * fix render failure on streaming * fix bouncy screen by debouncing mermaid parsing * add loading state; clean up styling * replace tag symbols when rendering code * better mermaid theme for visibility * added changeset * remove rehype-mermaid * update webview package.lock * clean up * fix package-lock * remove regular markdown background styling for mermaid blocks * updated system prompt * Update system.ts * reopen pr * revert random change * random change * reopen pr --------- Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Co-authored-by: Evan Fannin --- src/core/prompts/system.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index 65e6e7b8eb..40cc212ced 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -882,9 +882,10 @@ In each user message, the environment_details will specify the current mode. The ## What is PLAN MODE? - While you are usually in ACT MODE, the user may switch to PLAN MODE in order to have a back and forth with you to plan how to best accomplish the task. -- When starting in PLAN MODE, depending on the user's request, you may need to do some information gathering e.g. using read_file or search_files to get more context about the task. You may also ask the user clarifying questions to get a better understanding of the task. -- Once you've gained more context about the user's request, you should architect a detailed plan for how you will accomplish the task. +- When starting in PLAN MODE, depending on the user's request, you may need to do some information gathering e.g. using read_file or search_files to get more context about the task. You may also ask the user clarifying questions to get a better understanding of the task. You may return mermaid diagrams to visually display your understanding. +- Once you've gained more context about the user's request, you should architect a detailed plan for how you will accomplish the task. Returning mermaid diagrams may be helpful here as well. - Then you might ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and plan the best way to accomplish it. +- If at any point a mermaid diagram would make your plan clearer to help the user quickly see the structure, you are encouraged to include a Mermaid code block in the response. - Finally once it seems like you've reached a good plan, ask the user to switch you back to ACT MODE to implement the solution. ==== From d6be431cb8cc5c362ecae8d6ca7d227bcb0cc789 Mon Sep 17 00:00:00 2001 From: Evan <58194240+celestial-vault@users.noreply.github.com> Date: Tue, 18 Feb 2025 14:24:14 -0800 Subject: [PATCH 074/100] reopen img pr (#1850) --- .../src/components/common/MermaidBlock.tsx | 91 ++++++++++++++++++- 1 file changed, 86 insertions(+), 5 deletions(-) diff --git a/webview-ui/src/components/common/MermaidBlock.tsx b/webview-ui/src/components/common/MermaidBlock.tsx index 5c7919359e..7c0a55ec72 100644 --- a/webview-ui/src/components/common/MermaidBlock.tsx +++ b/webview-ui/src/components/common/MermaidBlock.tsx @@ -2,18 +2,27 @@ import { useEffect, useRef, useState } from "react" import mermaid from "mermaid" import { useDebounceEffect } from "../../utils/useDebounceEffect" import styled from "styled-components" +import { vscode } from "../../utils/vscode" + +const MERMAID_THEME = { + background: "#1e1e1e", + textColor: "#ffffff", + mainBkg: "#2d2d2d", + lineColor: "#cccccc", + primaryColor: "#3c3c3c", +} mermaid.initialize({ startOnLoad: false, securityLevel: "loose", theme: "dark", themeVariables: { - background: "#1e1e1e", - textColor: "#ffffff", // make text much brighter - mainBkg: "#2d2d2d", - lineColor: "#cccccc", // light enough for contrast + background: MERMAID_THEME.background, + textColor: MERMAID_THEME.textColor, + mainBkg: MERMAID_THEME.mainBkg, + lineColor: MERMAID_THEME.lineColor, fontSize: "16px", - primaryColor: "#3c3c3c", // node fill color, etc. + primaryColor: MERMAID_THEME.primaryColor, }, }) @@ -62,6 +71,26 @@ export default function MermaidBlock({ code }: MermaidBlockProps) { [code], // Dependencies for scheduling ) + /** + * Called when user clicks the rendered diagram. + * Converts the to a PNG and sends it to the extension. + */ + const handleClick = async () => { + if (!containerRef.current) return + const svgEl = containerRef.current.querySelector("svg") + if (!svgEl) return + + try { + const pngDataUrl = await svgToPng(svgEl) + vscode.postMessage({ + type: "openImage", + text: pngDataUrl, + }) + } catch (err) { + console.error("Error converting SVG to PNG:", err) + } + } + return ( {isLoading && Creating mermaid chart...} @@ -72,6 +101,58 @@ export default function MermaidBlock({ code }: MermaidBlockProps) { ) } +async function svgToPng(svgEl: SVGElement): Promise { + console.log("svgToPng function called") + // Clone the SVG to avoid modifying the original + const svgClone = svgEl.cloneNode(true) as SVGElement + + // Get the original viewBox + const viewBox = svgClone.getAttribute("viewBox")?.split(" ").map(Number) || [] + const originalWidth = viewBox[2] || svgClone.clientWidth + const originalHeight = viewBox[3] || svgClone.clientHeight + + // Calculate the scale factor to fit editor width while maintaining aspect ratio + + // Unless we can find a way to get the actual editor window dimensions through the VS Code API (which might be possible but would require changes to the extension side), + // the fixed 1200px width seems like a reliable approach. + const editorWidth = 1200 + + const scale = editorWidth / originalWidth + const scaledHeight = originalHeight * scale + + // Update SVG dimensions + svgClone.setAttribute("width", `${editorWidth}`) + svgClone.setAttribute("height", `${scaledHeight}`) + + const serializer = new XMLSerializer() + const svgString = serializer.serializeToString(svgClone) + const svgDataUrl = "data:image/svg+xml;base64," + btoa(decodeURIComponent(encodeURIComponent(svgString))) + + return new Promise((resolve, reject) => { + const img = new Image() + img.onload = () => { + const canvas = document.createElement("canvas") + canvas.width = editorWidth + canvas.height = scaledHeight + + const ctx = canvas.getContext("2d") + if (!ctx) return reject("Canvas context not available") + + // Fill background with Mermaid's dark theme background color + ctx.fillStyle = MERMAID_THEME.background + ctx.fillRect(0, 0, canvas.width, canvas.height) + + ctx.imageSmoothingEnabled = true + ctx.imageSmoothingQuality = "high" + + ctx.drawImage(img, 0, 0, editorWidth, scaledHeight) + resolve(canvas.toDataURL("image/png", 1.0)) + } + img.onerror = reject + img.src = svgDataUrl + }) +} + const MermaidBlockContainer = styled.div` position: relative; margin: 8px 0; From d7788c2fd62794d4889be9bfb8727dead6e4105f Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 18 Feb 2025 14:48:37 -0800 Subject: [PATCH 075/100] Fix mermaid diagram --- .../src/components/common/MermaidBlock.tsx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/webview-ui/src/components/common/MermaidBlock.tsx b/webview-ui/src/components/common/MermaidBlock.tsx index 7c0a55ec72..eeae2b272c 100644 --- a/webview-ui/src/components/common/MermaidBlock.tsx +++ b/webview-ui/src/components/common/MermaidBlock.tsx @@ -16,14 +16,14 @@ mermaid.initialize({ startOnLoad: false, securityLevel: "loose", theme: "dark", - themeVariables: { - background: MERMAID_THEME.background, - textColor: MERMAID_THEME.textColor, - mainBkg: MERMAID_THEME.mainBkg, - lineColor: MERMAID_THEME.lineColor, - fontSize: "16px", - primaryColor: MERMAID_THEME.primaryColor, - }, + // themeVariables: { + // background: MERMAID_THEME.background, + // textColor: MERMAID_THEME.textColor, + // mainBkg: MERMAID_THEME.mainBkg, + // lineColor: MERMAID_THEME.lineColor, + // fontSize: "16px", + // primaryColor: MERMAID_THEME.primaryColor, + // }, }) interface MermaidBlockProps { @@ -93,10 +93,10 @@ export default function MermaidBlock({ code }: MermaidBlockProps) { return ( - {isLoading && Creating mermaid chart...} + {isLoading && Generating mermaid diagram...} {/* The container for the final or raw code. */} - + ) } From a01471f469aeac720d9ff2f2704de9faf4afad83 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 18 Feb 2025 15:37:42 -0800 Subject: [PATCH 076/100] Show MCP server display name instead of ID --- src/core/webview/ClineProvider.ts | 18 +++++++++--------- webview-ui/src/components/chat/ChatRow.tsx | 9 ++++++--- webview-ui/src/components/mcp/McpView.tsx | 5 ++++- .../src/context/ExtensionStateContext.tsx | 12 ++++++++++-- 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index b19295cdd6..5ccdea2a95 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -406,7 +406,15 @@ export class ClineProvider implements vscode.WebviewViewProvider { // (see normalizeApiConfiguration > openrouter) // Prefetch marketplace and OpenRouter models - this.prefetchMcpMarketplace() + this.getGlobalState("mcpMarketplaceCatalog").then((mcpMarketplaceCatalog) => { + if (mcpMarketplaceCatalog) { + this.postMessageToWebview({ + type: "mcpMarketplaceCatalog", + mcpMarketplaceCatalog: mcpMarketplaceCatalog as McpMarketplaceCatalog, + }) + } + }) + this.silentlyRefreshMcpMarketplace() this.refreshOpenRouterModels().then(async (openRouterModels) => { if (openRouterModels) { // 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) @@ -1114,14 +1122,6 @@ export class ClineProvider implements vscode.WebviewViewProvider { } } - async prefetchMcpMarketplace() { - try { - await this.fetchMcpMarketplaceFromApi(true) - } catch (error) { - console.error("Failed to prefetch MCP marketplace:", error) - } - } - async silentlyRefreshMcpMarketplace() { try { const catalog = await this.fetchMcpMarketplaceFromApi(true) diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index c5e13d39ec..f77019fc3f 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -13,7 +13,7 @@ import { } from "../../../../src/shared/ExtensionMessage" import { COMMAND_OUTPUT_STRING, COMMAND_REQ_APP_STRING } from "../../../../src/shared/combineCommandSequences" import { useExtensionState } from "../../context/ExtensionStateContext" -import { findMatchingResourceOrTemplate } from "../../utils/mcp" +import { findMatchingResourceOrTemplate, getMcpServerDisplayName } from "../../utils/mcp" import { vscode } from "../../utils/vscode" import { CheckpointControls, CheckpointOverlay } from "../common/CheckpointControls" import CodeAccordian, { cleanPathPrefix } from "../common/CodeAccordian" @@ -100,7 +100,7 @@ const ChatRow = memo( export default ChatRow export const ChatRowContent = ({ message, isExpanded, onToggleExpand, lastModifiedMessage, isLast }: ChatRowContentProps) => { - const { mcpServers } = useExtensionState() + const { mcpServers, mcpMarketplaceCatalog } = useExtensionState() const [seeNewChangesDisabled, setSeeNewChangesDisabled] = useState(false) @@ -203,7 +203,10 @@ export const ChatRowContent = ({ message, isExpanded, onToggleExpand, lastModifi ), Cline wants to {mcpServerUse.type === "use_mcp_tool" ? "use a tool" : "access a resource"} on the{" "} - {mcpServerUse.serverName} MCP server: + + {getMcpServerDisplayName(mcpServerUse.serverName, mcpMarketplaceCatalog)} + {" "} + MCP server: , ] case "completion_result": diff --git a/webview-ui/src/components/mcp/McpView.tsx b/webview-ui/src/components/mcp/McpView.tsx index 6cc31f2956..7f5d3b0809 100644 --- a/webview-ui/src/components/mcp/McpView.tsx +++ b/webview-ui/src/components/mcp/McpView.tsx @@ -7,6 +7,7 @@ import McpToolRow from "./McpToolRow" import McpResourceRow from "./McpResourceRow" import McpMarketplaceView from "./marketplace/McpMarketplaceView" import styled from "styled-components" +import { getMcpServerDisplayName } from "../../utils/mcp" type McpViewProps = { onDone: () => void @@ -238,6 +239,8 @@ const TabButton = ({ children, isActive, onClick }: { children: React.ReactNode; // Server Row Component const ServerRow = ({ server }: { server: McpServer }) => { + const { mcpMarketplaceCatalog } = useExtensionState() + const [isExpanded, setIsExpanded] = useState(false) const getStatusColor = () => { @@ -290,7 +293,7 @@ const ServerRow = ({ server }: { server: McpServer }) => { alignItems: "center", marginRight: "4px", }}> - {server.name} + {getMcpServerDisplayName(server.name, mcpMarketplaceCatalog)}
e.stopPropagation()}>
openAiModels: string[] mcpServers: McpServer[] + mcpMarketplaceCatalog: McpMarketplaceCatalog filePaths: string[] setApiConfiguration: (config: ApiConfiguration) => void setCustomInstructions: (value?: string) => void @@ -49,7 +50,7 @@ export const ExtensionStateContextProvider: React.FC<{ const [openAiModels, setOpenAiModels] = useState([]) const [mcpServers, setMcpServers] = useState([]) - + const [mcpMarketplaceCatalog, setMcpMarketplaceCatalog] = useState({ items: [] }) const handleMessage = useCallback((event: MessageEvent) => { const message: ExtensionMessage = event.data switch (message.type) { @@ -121,6 +122,12 @@ export const ExtensionStateContextProvider: React.FC<{ setMcpServers(message.mcpServers ?? []) break } + case "mcpMarketplaceCatalog": { + if (message.mcpMarketplaceCatalog) { + setMcpMarketplaceCatalog(message.mcpMarketplaceCatalog) + } + break + } } }, []) @@ -138,6 +145,7 @@ export const ExtensionStateContextProvider: React.FC<{ openRouterModels, openAiModels, mcpServers, + mcpMarketplaceCatalog, filePaths, setApiConfiguration: (value) => setState((prevState) => ({ From 000b4af458f174a1c423438e5a0213b2438fcca7 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 18 Feb 2025 15:49:57 -0800 Subject: [PATCH 077/100] Add getMcpServerDisplayName --- webview-ui/src/utils/mcp.ts | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/webview-ui/src/utils/mcp.ts b/webview-ui/src/utils/mcp.ts index e7a17b5bcd..98e38f43b9 100644 --- a/webview-ui/src/utils/mcp.ts +++ b/webview-ui/src/utils/mcp.ts @@ -1,4 +1,4 @@ -import { McpResource, McpResourceTemplate } from "../../../src/shared/mcp" +import { McpMarketplaceCatalog, McpResource, McpResourceTemplate } from "../../../src/shared/mcp" /** * Matches a URI against an array of URI templates and returns the matching template @@ -40,3 +40,25 @@ export function findMatchingResourceOrTemplate( // If no exact match, try to find a matching template return findMatchingTemplate(uri, templates) } + +/** + * Attempts to convert an MCP server name to its display name using the marketplace catalog + * @param serverName The server name/ID to look up + * @param mcpMarketplaceCatalog The marketplace catalog containing server metadata + * @returns The display name if found in catalog, otherwise returns the original server name + */ +export function getMcpServerDisplayName(serverName: string, mcpMarketplaceCatalog: McpMarketplaceCatalog): string { + // Find matching item in marketplace catalog + const catalogItem = mcpMarketplaceCatalog.items.find((item) => item.mcpId === serverName) + // Log if no matching catalog item found + if (!catalogItem) { + console.warn(`No marketplace catalog item found for MCP server: ${serverName}`) + } else { + console.log(`Found marketplace catalog item for MCP server: ${serverName}`, catalogItem) + } + + console.log(mcpMarketplaceCatalog) + + // Return display name if found, otherwise return original server name + return catalogItem?.name || serverName +} From ccdbe3a4a9d7144323f92f69b068b20137e59f46 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 18 Feb 2025 17:10:59 -0800 Subject: [PATCH 078/100] Prepare for release --- .changeset/chilly-pandas-retire.md | 5 --- .changeset/cyan-bags-work.md | 5 --- .changeset/fair-snails-confess.md | 10 ------ .changeset/neat-apricots-search.md | 5 --- .changeset/stupid-mayflies-melt.md | 5 --- CHANGELOG.md | 2 ++ src/core/webview/ClineProvider.ts | 6 +++- src/shared/WebviewMessage.ts | 1 + .../src/components/chat/Announcement.tsx | 31 ++++++++++--------- 9 files changed, 25 insertions(+), 45 deletions(-) delete mode 100644 .changeset/chilly-pandas-retire.md delete mode 100644 .changeset/cyan-bags-work.md delete mode 100644 .changeset/fair-snails-confess.md delete mode 100644 .changeset/neat-apricots-search.md delete mode 100644 .changeset/stupid-mayflies-melt.md diff --git a/.changeset/chilly-pandas-retire.md b/.changeset/chilly-pandas-retire.md deleted file mode 100644 index a3e0905971..0000000000 --- a/.changeset/chilly-pandas-retire.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"claude-dev": minor ---- - -tooltip for each mode can be shown no matter what the current mode is diff --git a/.changeset/cyan-bags-work.md b/.changeset/cyan-bags-work.md deleted file mode 100644 index 21e4a0d3e0..0000000000 --- a/.changeset/cyan-bags-work.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"claude-dev": minor ---- - -Add support for rendering mermaid graphs in the chat. diff --git a/.changeset/fair-snails-confess.md b/.changeset/fair-snails-confess.md deleted file mode 100644 index f54c839141..0000000000 --- a/.changeset/fair-snails-confess.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"claude-dev": patch ---- - -Improve Requesty provider integration - -- Adding Cline headers to API requests, to enable targeted optimizations -- Read o3 reasoning effort from Cline config, not model name -- Show token information in task header -- Get total cost from response when available diff --git a/.changeset/neat-apricots-search.md b/.changeset/neat-apricots-search.md deleted file mode 100644 index 46ce78fc0f..0000000000 --- a/.changeset/neat-apricots-search.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"claude-dev": patch ---- - -Update README.md to include Getting Started diff --git a/.changeset/stupid-mayflies-melt.md b/.changeset/stupid-mayflies-melt.md deleted file mode 100644 index cd2a31cbc8..0000000000 --- a/.changeset/stupid-mayflies-melt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"claude-dev": patch ---- - -Add MCP Marketplace diff --git a/CHANGELOG.md b/CHANGELOG.md index c60aa595d4..fd32320fd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## [3.4.0] +- Introducing MCP Marketplace! You can now discover and install the best MCP servers right from within the extension, with new servers added regularly +- Add mermaid diagram support in Plan mode! You can now see visual representations of mermaid code blocks in chat, and click on them to see an expanded view - Use more visual checkpoints indicators after editing files & running commands - Create a checkpoint at the beginning of each task to easily revert to the initial state - Add 'Terminal' context mention to reference the active terminal's contents diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 5ccdea2a95..0f2b0cdf7f 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -111,7 +111,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { workspaceTracker?: WorkspaceTracker mcpHub?: McpHub private authManager: FirebaseAuthManager - private latestAnnouncementId = "jan-20-2025" // update to some unique identifier when we add a new announcement + private latestAnnouncementId = "feb-18-2025" // update to some unique identifier when we add a new announcement constructor( readonly context: vscode.ExtensionContext, @@ -788,6 +788,10 @@ export class ClineProvider implements vscode.WebviewViewProvider { await this.handleSignOut() break } + case "showMcpView": { + await this.postMessageToWebview({ type: "action", action: "mcpButtonClicked" }) + break + } case "openMcpSettings": { const mcpSettingsFilePath = await this.mcpHub?.getMcpSettingsFilePath() if (mcpSettingsFilePath) { diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index b721d4f3de..123b345759 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -47,6 +47,7 @@ export interface WebviewMessage { | "downloadMcp" | "silentlyRefreshMcpMarketplace" | "searchCommits" + | "showMcpView" // | "relaunchChromeDebugMode" text?: string disabled?: boolean diff --git a/webview-ui/src/components/chat/Announcement.tsx b/webview-ui/src/components/chat/Announcement.tsx index da528089a4..7db36825b3 100644 --- a/webview-ui/src/components/chat/Announcement.tsx +++ b/webview-ui/src/components/chat/Announcement.tsx @@ -1,6 +1,7 @@ import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react" import { memo } from "react" import { getAsVar, VSC_DESCRIPTION_FOREGROUND, VSC_INACTIVE_SELECTION_BACKGROUND } from "../../utils/vscStyles" +import { vscode } from "../../utils/vscode" interface AnnouncementProps { version: string @@ -30,27 +31,29 @@ const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => {
  • - Plan/Act mode toggle: Plan mode turns Cline into an architect that gathers information, asks clarifying - questions, and designs a solution. Switch back to Act mode to let him execute the plan!{" "} - - See a demo here. + Introducing MCP Marketplace: Discover and install the best MCP servers right from the extension, with + new servers added regularly! Get started by going to the{" "} + + { + vscode.postMessage({ type: "showMcpView" }) + }}> + MCP Servers tab + .
  • - Quick API/model switching with a new popup menu under the chat field + Mermaid diagrams in Plan mode! Cline can now visualize his plans using flowcharts, sequences, + entity-relationships, and more. When he explains his approach using mermaid, you'll see a diagram right in + chat that you can click to expand.
  • - VS Code LM API lets you use models from other extensions like GitHub Copilot + Use @terminal to reference terminal contents, and @git to reference working changes + and commits!
  • - MCP server improvements: On/off toggle to disable servers when not in use, and Auto-approve option for - individual tools -
  • -
  • - In case you missed it, Cline now supports Checkpoints!{" "} - - See it in action here. - + New visual indicator for checkpoints after edits & commands, and automatic checkpoint at the start of each + task.
{/*
    From 3d64ab298dabe982b74420d19974d9c0752d61e6 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 18 Feb 2025 17:33:48 -0800 Subject: [PATCH 079/100] Update zod --- package-lock.json | 17 +++++++++++++---- package.json | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index d89b7dc8dd..c18892d575 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,7 +48,7 @@ "tree-sitter-wasms": "^0.1.11", "turndown": "^7.2.0", "web-tree-sitter": "^0.22.6", - "zod": "^3.23.8" + "zod": "^3.24.2" }, "devDependencies": { "@changesets/cli": "^2.27.12", @@ -7070,6 +7070,15 @@ "devtools-protocol": "*" } }, + "node_modules/chromium-bidi/node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -13810,9 +13819,9 @@ } }, "node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "version": "3.24.2", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz", + "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index 5decc223d0..1e0888ef74 100644 --- a/package.json +++ b/package.json @@ -271,6 +271,6 @@ "tree-sitter-wasms": "^0.1.11", "turndown": "^7.2.0", "web-tree-sitter": "^0.22.6", - "zod": "^3.23.8" + "zod": "^3.24.2" } } From 97abdc500bd128c61e8ff3e289d548e29fe6d160 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 18 Feb 2025 18:03:08 -0800 Subject: [PATCH 080/100] Show pointer on mermaids --- webview-ui/src/components/common/MermaidBlock.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/webview-ui/src/components/common/MermaidBlock.tsx b/webview-ui/src/components/common/MermaidBlock.tsx index eeae2b272c..926725ef17 100644 --- a/webview-ui/src/components/common/MermaidBlock.tsx +++ b/webview-ui/src/components/common/MermaidBlock.tsx @@ -173,4 +173,5 @@ const SvgContainer = styled.div` opacity: ${(props) => (props.$isLoading ? 0.3 : 1)}; min-height: 20px; transition: opacity 0.2s ease; + cursor: pointer; ` From f9025426b8e46fa1f79c110570b6d942755cf33b Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 18 Feb 2025 18:24:08 -0800 Subject: [PATCH 081/100] Toggle to act mode and enable MCP prompt if user one-click installs MCP server --- src/core/webview/ClineProvider.ts | 215 ++++++++++-------- src/shared/WebviewMessage.ts | 2 +- src/test/webview/chat-native.test.ts | 4 +- .../src/components/chat/ChatTextArea.tsx | 2 +- 4 files changed, 121 insertions(+), 102 deletions(-) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 0f2b0cdf7f..c665419218 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -31,6 +31,7 @@ import { BrowserSettings, DEFAULT_BROWSER_SETTINGS } from "../../shared/BrowserS import { ChatSettings, DEFAULT_CHAT_SETTINGS } from "../../shared/ChatSettings" import { DIFF_VIEW_URI_SCHEME } from "../../integrations/editor/DiffViewProvider" import { searchCommits } from "../../utils/git" +import { ChatContent } from "../../shared/ChatContent" /* https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts @@ -550,105 +551,9 @@ export class ClineProvider implements vscode.WebviewViewProvider { await this.postStateToWebview() } break - case "chatSettings": + case "togglePlanActMode": if (message.chatSettings) { - const didSwitchToActMode = message.chatSettings.mode === "act" - - // Get previous model info that we will revert to after saving current mode api info - const { - apiConfiguration, - previousModeApiProvider: newApiProvider, - previousModeModelId: newModelId, - previousModeModelInfo: newModelInfo, - } = await this.getState() - - // Save the last model used in this mode - await this.updateGlobalState("previousModeApiProvider", apiConfiguration.apiProvider) - switch (apiConfiguration.apiProvider) { - case "anthropic": - case "bedrock": - case "vertex": - case "gemini": - await this.updateGlobalState("previousModeModelId", apiConfiguration.apiModelId) - break - case "openrouter": - await this.updateGlobalState("previousModeModelId", apiConfiguration.openRouterModelId) - await this.updateGlobalState("previousModeModelInfo", apiConfiguration.openRouterModelInfo) - break - case "vscode-lm": - await this.updateGlobalState("previousModeModelId", apiConfiguration.vsCodeLmModelSelector) - break - case "openai": - await this.updateGlobalState("previousModeModelId", apiConfiguration.openAiModelId) - await this.updateGlobalState("previousModeModelInfo", apiConfiguration.openAiModelInfo) - break - case "ollama": - await this.updateGlobalState("previousModeModelId", apiConfiguration.ollamaModelId) - break - case "lmstudio": - await this.updateGlobalState("previousModeModelId", apiConfiguration.lmStudioModelId) - break - case "litellm": - await this.updateGlobalState("previousModeModelId", apiConfiguration.liteLlmModelId) - break - } - - // Restore the model used in previous mode - if (newApiProvider && newModelId) { - await this.updateGlobalState("apiProvider", newApiProvider) - switch (newApiProvider) { - case "anthropic": - case "bedrock": - case "vertex": - case "gemini": - await this.updateGlobalState("apiModelId", newModelId) - break - case "openrouter": - await this.updateGlobalState("openRouterModelId", newModelId) - await this.updateGlobalState("openRouterModelInfo", newModelInfo) - break - case "vscode-lm": - await this.updateGlobalState("vsCodeLmModelSelector", newModelId) - break - case "openai": - await this.updateGlobalState("openAiModelId", newModelId) - await this.updateGlobalState("openAiModelInfo", newModelInfo) - break - case "ollama": - await this.updateGlobalState("ollamaModelId", newModelId) - break - case "lmstudio": - await this.updateGlobalState("lmStudioModelId", newModelId) - break - case "litellm": - await this.updateGlobalState("liteLlmModelId", newModelId) - break - } - - if (this.cline) { - const { apiConfiguration: updatedApiConfiguration } = await this.getState() - this.cline.api = buildApiHandler(updatedApiConfiguration) - } - } - - await this.updateGlobalState("chatSettings", message.chatSettings) - await this.postStateToWebview() - // console.log("chatSettings", message.chatSettings) - if (this.cline) { - this.cline.updateChatSettings(message.chatSettings) - if (this.cline.isAwaitingPlanResponse && didSwitchToActMode) { - this.cline.didRespondToPlanAskBySwitchingMode = true - // this is necessary for the webview to update accordingly, but Cline instance will not send text back as feedback message - await this.postMessageToWebview({ - type: "invoke", - invoke: "sendMessage", - text: message.chatContent?.message || "PLAN_MODE_TOGGLE_RESPONSE", - images: message.chatContent?.images, - }) - } else { - this.cancelTask() - } - } + await this.togglePlanActModeWithChatSettings(message.chatSettings, message.chatContent) } break // case "relaunchChromeDebugMode": @@ -805,6 +710,20 @@ export class ClineProvider implements vscode.WebviewViewProvider { } case "downloadMcp": { if (message.mcpId) { + // 1. Toggle to act mode if we are in plan mode + const { chatSettings } = await this.getStateToPostToWebview() + if (chatSettings.mode === "plan") { + await this.togglePlanActModeWithChatSettings({ mode: "act" }) + } + + // 2. Enable MCP settings if disabled + // Enable MCP mode if disabled + const mcpConfig = vscode.workspace.getConfiguration("cline.mcp") + if (mcpConfig.get("mode") !== "full") { + await mcpConfig.update("mode", "full", true) + } + + // 3. download MCP await this.downloadMcp(message.mcpId) } break @@ -904,6 +823,106 @@ export class ClineProvider implements vscode.WebviewViewProvider { ) } + async togglePlanActModeWithChatSettings(chatSettings: ChatSettings, chatContent?: ChatContent) { + const didSwitchToActMode = chatSettings.mode === "act" + + // Get previous model info that we will revert to after saving current mode api info + const { + apiConfiguration, + previousModeApiProvider: newApiProvider, + previousModeModelId: newModelId, + previousModeModelInfo: newModelInfo, + } = await this.getState() + + // Save the last model used in this mode + await this.updateGlobalState("previousModeApiProvider", apiConfiguration.apiProvider) + switch (apiConfiguration.apiProvider) { + case "anthropic": + case "bedrock": + case "vertex": + case "gemini": + await this.updateGlobalState("previousModeModelId", apiConfiguration.apiModelId) + break + case "openrouter": + await this.updateGlobalState("previousModeModelId", apiConfiguration.openRouterModelId) + await this.updateGlobalState("previousModeModelInfo", apiConfiguration.openRouterModelInfo) + break + case "vscode-lm": + await this.updateGlobalState("previousModeModelId", apiConfiguration.vsCodeLmModelSelector) + break + case "openai": + await this.updateGlobalState("previousModeModelId", apiConfiguration.openAiModelId) + await this.updateGlobalState("previousModeModelInfo", apiConfiguration.openAiModelInfo) + break + case "ollama": + await this.updateGlobalState("previousModeModelId", apiConfiguration.ollamaModelId) + break + case "lmstudio": + await this.updateGlobalState("previousModeModelId", apiConfiguration.lmStudioModelId) + break + case "litellm": + await this.updateGlobalState("previousModeModelId", apiConfiguration.liteLlmModelId) + break + } + + // Restore the model used in previous mode + if (newApiProvider && newModelId) { + await this.updateGlobalState("apiProvider", newApiProvider) + switch (newApiProvider) { + case "anthropic": + case "bedrock": + case "vertex": + case "gemini": + await this.updateGlobalState("apiModelId", newModelId) + break + case "openrouter": + await this.updateGlobalState("openRouterModelId", newModelId) + await this.updateGlobalState("openRouterModelInfo", newModelInfo) + break + case "vscode-lm": + await this.updateGlobalState("vsCodeLmModelSelector", newModelId) + break + case "openai": + await this.updateGlobalState("openAiModelId", newModelId) + await this.updateGlobalState("openAiModelInfo", newModelInfo) + break + case "ollama": + await this.updateGlobalState("ollamaModelId", newModelId) + break + case "lmstudio": + await this.updateGlobalState("lmStudioModelId", newModelId) + break + case "litellm": + await this.updateGlobalState("liteLlmModelId", newModelId) + break + } + + if (this.cline) { + const { apiConfiguration: updatedApiConfiguration } = await this.getState() + this.cline.api = buildApiHandler(updatedApiConfiguration) + } + } + + await this.updateGlobalState("chatSettings", chatSettings) + await this.postStateToWebview() + // console.log("chatSettings", message.chatSettings) + if (this.cline) { + this.cline.updateChatSettings(chatSettings) + if (this.cline.isAwaitingPlanResponse && didSwitchToActMode) { + this.cline.didRespondToPlanAskBySwitchingMode = true + // this is necessary for the webview to update accordingly, but Cline instance will not send text back as feedback message + await this.postMessageToWebview({ + type: "invoke", + invoke: "sendMessage", + text: chatContent?.message || "PLAN_MODE_TOGGLE_RESPONSE", + images: chatContent?.images, + }) + } else { + this.cancelTask() + } + } + } + async subscribeEmail(email?: string) { if (!email) { return diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 123b345759..bb2a155783 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -31,7 +31,7 @@ export interface WebviewMessage { | "restartMcpServer" | "autoApprovalSettings" | "browserSettings" - | "chatSettings" + | "togglePlanActMode" | "checkpointDiff" | "checkpointRestore" | "taskCompletionViewChanges" diff --git a/src/test/webview/chat-native.test.ts b/src/test/webview/chat-native.test.ts index d3fe630a94..8e35b1528a 100644 --- a/src/test/webview/chat-native.test.ts +++ b/src/test/webview/chat-native.test.ts @@ -84,7 +84,7 @@ describe("Chat Integration Tests", () => { // Set up state change listener const stateChangePromise = new Promise((resolve) => { panel.webview.onDidReceiveMessage((message) => { - if (message.type === "chatSettings") { + if (message.type === "togglePlanActMode") { resolve(message) } }) @@ -102,7 +102,7 @@ describe("Chat Integration Tests", () => { // Set up state change listener const stateChangePromise = new Promise((resolve) => { panel.webview.onDidReceiveMessage((message) => { - if (message.type === "chatSettings") { + if (message.type === "togglePlanActMode") { resolve(message) } }) diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index c111aebe71..c45f88e256 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -655,7 +655,7 @@ const ChatTextArea = forwardRef( setTimeout(() => { const newMode = chatSettings.mode === "plan" ? "act" : "plan" vscode.postMessage({ - type: "chatSettings", + type: "togglePlanActMode", chatSettings: { mode: newMode, }, From a6e109718e0f5d11b90b95f41412872a03dcf60b Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 18 Feb 2025 18:30:36 -0800 Subject: [PATCH 082/100] Remove logs --- webview-ui/src/utils/mcp.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/webview-ui/src/utils/mcp.ts b/webview-ui/src/utils/mcp.ts index 98e38f43b9..4c823598b6 100644 --- a/webview-ui/src/utils/mcp.ts +++ b/webview-ui/src/utils/mcp.ts @@ -50,14 +50,6 @@ export function findMatchingResourceOrTemplate( export function getMcpServerDisplayName(serverName: string, mcpMarketplaceCatalog: McpMarketplaceCatalog): string { // Find matching item in marketplace catalog const catalogItem = mcpMarketplaceCatalog.items.find((item) => item.mcpId === serverName) - // Log if no matching catalog item found - if (!catalogItem) { - console.warn(`No marketplace catalog item found for MCP server: ${serverName}`) - } else { - console.log(`Found marketplace catalog item for MCP server: ${serverName}`, catalogItem) - } - - console.log(mcpMarketplaceCatalog) // Return display name if found, otherwise return original server name return catalogItem?.name || serverName From 04f8c294c23bb1f56023462326ced33494bd70b7 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 18 Feb 2025 18:50:24 -0800 Subject: [PATCH 083/100] Fix mermaid width --- webview-ui/src/components/common/MermaidBlock.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webview-ui/src/components/common/MermaidBlock.tsx b/webview-ui/src/components/common/MermaidBlock.tsx index 926725ef17..35c725b267 100644 --- a/webview-ui/src/components/common/MermaidBlock.tsx +++ b/webview-ui/src/components/common/MermaidBlock.tsx @@ -114,8 +114,8 @@ async function svgToPng(svgEl: SVGElement): Promise { // Calculate the scale factor to fit editor width while maintaining aspect ratio // Unless we can find a way to get the actual editor window dimensions through the VS Code API (which might be possible but would require changes to the extension side), - // the fixed 1200px width seems like a reliable approach. - const editorWidth = 1200 + // the fixed width seems like a reliable approach. + const editorWidth = 3_600 const scale = editorWidth / originalWidth const scaledHeight = originalHeight * scale From ac4ca51ced4ad15d9515959831ee846c86a9bccb Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 18 Feb 2025 18:54:30 -0800 Subject: [PATCH 084/100] Center mermaid diagram --- webview-ui/src/components/common/MermaidBlock.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/webview-ui/src/components/common/MermaidBlock.tsx b/webview-ui/src/components/common/MermaidBlock.tsx index 35c725b267..ea9d37a4f7 100644 --- a/webview-ui/src/components/common/MermaidBlock.tsx +++ b/webview-ui/src/components/common/MermaidBlock.tsx @@ -174,4 +174,6 @@ const SvgContainer = styled.div` min-height: 20px; transition: opacity 0.2s ease; cursor: pointer; + display: flex; + justify-content: center; ` From f80552f92d9f86a2bcca6ff0e553914e908fea62 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 18 Feb 2025 19:12:35 -0800 Subject: [PATCH 085/100] Adjust MCP install prompt to demonstrate --- src/core/webview/ClineProvider.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index c665419218..fa6f8850d7 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1232,7 +1232,10 @@ export class ClineProvider implements vscode.WebviewViewProvider { }) // Create task with context from README - const task = `Set up the MCP server from ${mcpDetails.githubUrl}. Use "${mcpDetails.mcpId}" as the server name in cline_mcp_settings.json. Here is the project's README to help you get started:\n\n${mcpDetails.readmeContent}\n${mcpDetails.llmsInstallationContent}` + const task = `Set up the MCP server from ${mcpDetails.githubUrl}. +Use "${mcpDetails.mcpId}" as the server name in cline_mcp_settings.json. +Once installed, demonstrate the server's capabilities by using one of its tools. +Here is the project's README to help you get started:\n\n${mcpDetails.readmeContent}\n${mcpDetails.llmsInstallationContent}` // Initialize task and show chat view await this.initClineWithTask(task) From 65ac7ec3f71e7a0a2c48c70df89898ea46b793a9 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 18 Feb 2025 20:04:17 -0800 Subject: [PATCH 086/100] Fix MCP empty list state --- webview-ui/src/components/mcp/McpView.tsx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/webview-ui/src/components/mcp/McpView.tsx b/webview-ui/src/components/mcp/McpView.tsx index 7f5d3b0809..7dcae6a980 100644 --- a/webview-ui/src/components/mcp/McpView.tsx +++ b/webview-ui/src/components/mcp/McpView.tsx @@ -171,14 +171,11 @@ const McpView = ({ onDone }: McpViewProps) => { flexDirection: "column", alignItems: "center", gap: "12px", - marginTop: "20px", + marginTop: 20, + marginBottom: 20, color: "var(--vscode-descriptionForeground)", }}> -
    No MCP servers installed yet
    - setActiveTab("marketplace")}> - - Browse Marketplace - + No MCP servers installed
)} From f938c4053904e06f1bb66479a930227075605845 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 18 Feb 2025 20:54:10 -0800 Subject: [PATCH 087/100] Fix MCP prompt to handle dist/index.js convention --- src/core/prompts/system.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index 40cc212ced..55ae8d62a6 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -767,7 +767,7 @@ IMPORTANT: Regardless of what else you see in the MCP settings file, you must de (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.) -6. 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. +6. 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. (Note: sometimes connecting to an MCP server may fail, in which case it might help to explore the server's directory to troubleshoot e.g., it may be outputting the compiled script to dist/ instead of build/) 7. 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?" From ceeb33fdc5fd8f70c9672adb3d3df0c9aa5f509e Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 18 Feb 2025 21:38:43 -0800 Subject: [PATCH 088/100] Fix race condition where MCP servers were sent to webview before it was initialized --- src/core/webview/ClineProvider.ts | 4 ++++ src/services/mcp/McpHub.ts | 4 ++++ src/shared/WebviewMessage.ts | 1 + webview-ui/src/components/mcp/McpView.tsx | 1 + 4 files changed, 10 insertions(+) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index fa6f8850d7..a72f368123 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -791,6 +791,10 @@ export class ClineProvider implements vscode.WebviewViewProvider { } break } + case "fetchLatestMcpServersFromHub": { + this.mcpHub?.sendLatestMcpServers() + break + } case "searchCommits": { const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0) if (cwd) { diff --git a/src/services/mcp/McpHub.ts b/src/services/mcp/McpHub.ts index 9c6fee3a56..9e4a6b9cfb 100644 --- a/src/services/mcp/McpHub.ts +++ b/src/services/mcp/McpHub.ts @@ -472,6 +472,10 @@ export class McpHub { }) } + async sendLatestMcpServers() { + await this.notifyWebviewOfServerChanges() + } + // Using server // Public methods for server management diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index bb2a155783..87f6785019 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -48,6 +48,7 @@ export interface WebviewMessage { | "silentlyRefreshMcpMarketplace" | "searchCommits" | "showMcpView" + | "fetchLatestMcpServersFromHub" // | "relaunchChromeDebugMode" text?: string disabled?: boolean diff --git a/webview-ui/src/components/mcp/McpView.tsx b/webview-ui/src/components/mcp/McpView.tsx index 7dcae6a980..e154e87c59 100644 --- a/webview-ui/src/components/mcp/McpView.tsx +++ b/webview-ui/src/components/mcp/McpView.tsx @@ -23,6 +23,7 @@ const McpView = ({ onDone }: McpViewProps) => { useEffect(() => { vscode.postMessage({ type: "silentlyRefreshMcpMarketplace" }) + vscode.postMessage({ type: "fetchLatestMcpServersFromHub" }) }, []) // const [servers, setServers] = useState([ From efaa0d057ec5d97ebe80630bd11353484b1b3a0d Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 18 Feb 2025 21:40:38 -0800 Subject: [PATCH 089/100] Add instruction about handling MCP servers with unexpected build path --- src/core/prompts/system.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index 55ae8d62a6..1f8fcce010 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -767,7 +767,7 @@ IMPORTANT: Regardless of what else you see in the MCP settings file, you must de (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.) -6. 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. (Note: sometimes connecting to an MCP server may fail, in which case it might help to explore the server's directory to troubleshoot e.g., it may be outputting the compiled script to dist/ instead of build/) +6. 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. (Note: If you encounter a 'not connected' error when testing a newly installed mcp server, a common cause is an incorrect build path in your MCP settings configuration. Since compiled JavaScript files are commonly output to either 'dist/' or 'build/' directories, double-check that the build path in your MCP settings matches where your files are actually being compiled. E.g. If you assumed 'build' as the folder, check tsconfig.json to see if it's using 'dist' instead.) 7. 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?" From c846bc4455d93b2016f247db7a7495aeb384c5d3 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 18 Feb 2025 22:14:36 -0800 Subject: [PATCH 090/100] Adjust mermaid diagram colors --- src/core/prompts/system.ts | 2 +- .../src/components/common/MermaidBlock.tsx | 74 +++++++++++++++---- 2 files changed, 62 insertions(+), 14 deletions(-) diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index 1f8fcce010..8d6d13766d 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -885,7 +885,7 @@ In each user message, the environment_details will specify the current mode. The - When starting in PLAN MODE, depending on the user's request, you may need to do some information gathering e.g. using read_file or search_files to get more context about the task. You may also ask the user clarifying questions to get a better understanding of the task. You may return mermaid diagrams to visually display your understanding. - Once you've gained more context about the user's request, you should architect a detailed plan for how you will accomplish the task. Returning mermaid diagrams may be helpful here as well. - Then you might ask the user if they are pleased with this plan, or if they would like to make any changes. Think of this as a brainstorming session where you can discuss the task and plan the best way to accomplish it. -- If at any point a mermaid diagram would make your plan clearer to help the user quickly see the structure, you are encouraged to include a Mermaid code block in the response. +- If at any point a mermaid diagram would make your plan clearer to help the user quickly see the structure, you are encouraged to include a Mermaid code block in the response. (Note: if you use colors in your mermaid diagrams, be sure to use high contrast colors so the text is readable.) - Finally once it seems like you've reached a good plan, ask the user to switch you back to ACT MODE to implement the solution. ==== diff --git a/webview-ui/src/components/common/MermaidBlock.tsx b/webview-ui/src/components/common/MermaidBlock.tsx index ea9d37a4f7..619188179e 100644 --- a/webview-ui/src/components/common/MermaidBlock.tsx +++ b/webview-ui/src/components/common/MermaidBlock.tsx @@ -5,25 +5,73 @@ import styled from "styled-components" import { vscode } from "../../utils/vscode" const MERMAID_THEME = { - background: "#1e1e1e", - textColor: "#ffffff", - mainBkg: "#2d2d2d", - lineColor: "#cccccc", - primaryColor: "#3c3c3c", + background: "#1e1e1e", // VS Code dark theme background + textColor: "#ffffff", // Main text color + mainBkg: "#2d2d2d", // Background for nodes + nodeBorder: "#888888", // Border color for nodes + lineColor: "#cccccc", // Lines connecting nodes + primaryColor: "#3c3c3c", // Primary color for highlights + primaryTextColor: "#ffffff", // Text in primary colored elements + primaryBorderColor: "#888888", + secondaryColor: "#2d2d2d", // Secondary color for alternate elements + tertiaryColor: "#454545", // Third color for special elements + + // Class diagram specific + classText: "#ffffff", + + // State diagram specific + labelColor: "#ffffff", + + // Sequence diagram specific + actorLineColor: "#cccccc", + actorBkg: "#2d2d2d", + actorBorder: "#888888", + actorTextColor: "#ffffff", + + // Flow diagram specific + fillType0: "#2d2d2d", + fillType1: "#3c3c3c", + fillType2: "#454545", } mermaid.initialize({ startOnLoad: false, securityLevel: "loose", theme: "dark", - // themeVariables: { - // background: MERMAID_THEME.background, - // textColor: MERMAID_THEME.textColor, - // mainBkg: MERMAID_THEME.mainBkg, - // lineColor: MERMAID_THEME.lineColor, - // fontSize: "16px", - // primaryColor: MERMAID_THEME.primaryColor, - // }, + themeVariables: { + ...MERMAID_THEME, + fontSize: "16px", + fontFamily: "var(--vscode-font-family, 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif)", + + // Additional styling + noteTextColor: "#ffffff", + noteBkgColor: "#454545", + noteBorderColor: "#888888", + + // Improve contrast for special elements + critBorderColor: "#ff9580", + critBkgColor: "#803d36", + + // Task diagram specific + taskTextColor: "#ffffff", + taskTextOutsideColor: "#ffffff", + taskTextLightColor: "#ffffff", + + // Numbers/sections + sectionBkgColor: "#2d2d2d", + sectionBkgColor2: "#3c3c3c", + + // Alt sections in sequence diagrams + altBackground: "#2d2d2d", + + // Links + linkColor: "#6cb6ff", + + // Borders and lines + compositeBackground: "#2d2d2d", + compositeBorder: "#888888", + titleColor: "#ffffff", + }, }) interface MermaidBlockProps { From d6f51b4525f8c90be66aeb6109144a90c5c5b261 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 18 Feb 2025 22:17:01 -0800 Subject: [PATCH 091/100] Fix tests --- src/test/webview/chat-native.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/webview/chat-native.test.ts b/src/test/webview/chat-native.test.ts index 8e35b1528a..bb73703b6b 100644 --- a/src/test/webview/chat-native.test.ts +++ b/src/test/webview/chat-native.test.ts @@ -29,7 +29,7 @@ describe("Chat Integration Tests", () => { break; case 'toggleMode': vscode.postMessage({ - type: 'chatSettings', + type: 'togglePlanActMode', chatSettings: { mode: 'act' }, chatContent: { message: "message test", From 97fa8cffb9e846f1a3b95732edbf57e258f6117e Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Wed, 19 Feb 2025 01:57:07 -0800 Subject: [PATCH 092/100] Remove unnecessary recommended flag --- .../src/components/mcp/marketplace/McpMarketplaceCard.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx index 5c5279ced6..e9e446998c 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx @@ -201,7 +201,7 @@ const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps) {/* Description and tags */}
- {!item.isRecommended && ( + {/* {!item.isRecommended && (
Community Made (use at your own risk)
- )} + )} */}

{item.description}

Date: Wed, 19 Feb 2025 09:20:44 -0800 Subject: [PATCH 093/100] Prepare for release --- src/core/webview/ClineProvider.ts | 2 +- webview-ui/src/components/chat/Announcement.tsx | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index a72f368123..72a93df073 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -112,7 +112,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { workspaceTracker?: WorkspaceTracker mcpHub?: McpHub private authManager: FirebaseAuthManager - private latestAnnouncementId = "feb-18-2025" // update to some unique identifier when we add a new announcement + private latestAnnouncementId = "feb-19-2025" // update to some unique identifier when we add a new announcement constructor( readonly context: vscode.ExtensionContext, diff --git a/webview-ui/src/components/chat/Announcement.tsx b/webview-ui/src/components/chat/Announcement.tsx index 7db36825b3..a7b9ec9a46 100644 --- a/webview-ui/src/components/chat/Announcement.tsx +++ b/webview-ui/src/components/chat/Announcement.tsx @@ -56,6 +56,9 @@ const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => { task. + + See a demo of the changes here! + {/*
  • OpenRouter now supports prompt caching! They also have much higher rate limits than other providers, @@ -112,9 +115,12 @@ const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => { }} />

    - Join our{" "} + Join us on{" "} + + X, + {" "} - discord + discord, {" "} or{" "} From 0a14f0bad53f9472cd82c6275c1bf130351eb953 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Wed, 19 Feb 2025 10:05:52 -0800 Subject: [PATCH 094/100] Update version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1e0888ef74..e4395a3ce5 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "claude-dev", "displayName": "Cline", "description": "Autonomous coding agent right in your IDE, capable of creating/editing files, running commands, using the browser, and more with your permission every step of the way.", - "version": "3.4.0", + "version": "3.4.1", "icon": "assets/icons/icon.png", "galleryBanner": { "color": "#617A91", From 2491b469e7d3328d10d919e67f238c66e8ff703e Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Wed, 19 Feb 2025 15:05:33 -0800 Subject: [PATCH 095/100] Remove downloadCount temporarily --- package-lock.json | 4 ++-- package.json | 2 +- .../components/mcp/marketplace/McpMarketplaceCard.tsx | 4 ++-- .../components/mcp/marketplace/McpMarketplaceView.tsx | 10 +++++----- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index c18892d575..35cca727ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "claude-dev", - "version": "3.4.0", + "version": "3.4.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "claude-dev", - "version": "3.4.0", + "version": "3.4.1", "license": "Apache-2.0", "dependencies": { "@anthropic-ai/bedrock-sdk": "^0.10.2", diff --git a/package.json b/package.json index e4395a3ce5..ce704ae698 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "claude-dev", "displayName": "Cline", "description": "Autonomous coding agent right in your IDE, capable of creating/editing files, running commands, using the browser, and more with your permission every step of the way.", - "version": "3.4.1", + "version": "3.4.2", "icon": "assets/icons/icon.png", "galleryBanner": { "color": "#617A91", diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx index e9e446998c..c908070fae 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceCard.tsx @@ -181,7 +181,7 @@ const McpMarketplaceCard = ({ item, installedServers }: McpMarketplaceCardProps) {item.githubStars?.toLocaleString() ?? 0}

-
{item.downloadCount?.toLocaleString() ?? 0} -
+
*/} {item.requiresApiKey && ( )} diff --git a/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx b/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx index bece71baf6..6c1798d2f7 100644 --- a/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx +++ b/webview-ui/src/components/mcp/marketplace/McpMarketplaceView.tsx @@ -21,7 +21,7 @@ const McpMarketplaceView = () => { const [isRefreshing, setIsRefreshing] = useState(false) const [searchQuery, setSearchQuery] = useState("") const [selectedCategory, setSelectedCategory] = useState(null) - const [sortBy, setSortBy] = useState<"downloadCount" | "stars" | "name" | "newest">("downloadCount") + const [sortBy, setSortBy] = useState<"newest" | "stars" | "name">("newest") const categories = useMemo(() => { const uniqueCategories = new Set(items.map((item) => item.category)) @@ -41,8 +41,8 @@ const McpMarketplaceView = () => { }) .sort((a, b) => { switch (sortBy) { - case "downloadCount": - return b.downloadCount - a.downloadCount + // case "downloadCount": + // return b.downloadCount - a.downloadCount case "stars": return b.githubStars - a.githubStars case "name": @@ -232,9 +232,9 @@ const McpMarketplaceView = () => { }} value={sortBy} onChange={(e) => setSortBy((e.target as HTMLInputElement).value as typeof sortBy)}> - Most Installs - Most Stars + {/* Most Installs */} Newest + GitHub Stars Name
From a925e672ed30c562e2ba203c67b1398cd5ce576a Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Wed, 19 Feb 2025 15:30:02 -0800 Subject: [PATCH 096/100] Update MCP servers icon --- package.json | 2 +- webview-ui/src/components/chat/Announcement.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ce704ae698..72db4e14e9 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ { "command": "cline.mcpButtonClicked", "title": "MCP Servers", - "icon": "$(server)" + "icon": "$(extensions)" }, { "command": "cline.historyButtonClicked", diff --git a/webview-ui/src/components/chat/Announcement.tsx b/webview-ui/src/components/chat/Announcement.tsx index a7b9ec9a46..95c737076a 100644 --- a/webview-ui/src/components/chat/Announcement.tsx +++ b/webview-ui/src/components/chat/Announcement.tsx @@ -33,7 +33,7 @@ const Announcement = ({ version, hideAnnouncement }: AnnouncementProps) => {
  • Introducing MCP Marketplace: Discover and install the best MCP servers right from the extension, with new servers added regularly! Get started by going to the{" "} - + { vscode.postMessage({ type: "showMcpView" }) From 26be9469c43f78d162cf3a3ec5500d68dabce23b Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Wed, 19 Feb 2025 18:02:20 -0800 Subject: [PATCH 097/100] Add delete button to MCP server --- src/core/webview/ClineProvider.ts | 6 ++++ src/services/mcp/McpHub.ts | 27 +++++++++++++++++ src/shared/WebviewMessage.ts | 1 + .../src/components/common/DangerButton.tsx | 30 +++++++++++++++++++ webview-ui/src/components/mcp/McpView.tsx | 21 +++++++++++++ 5 files changed, 85 insertions(+) create mode 100644 webview-ui/src/components/common/DangerButton.tsx diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 72a93df073..1eb078247f 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -791,6 +791,12 @@ export class ClineProvider implements vscode.WebviewViewProvider { } break } + case "deleteMcpServer": { + if (message.serverName) { + this.mcpHub?.deleteServer(message.serverName) + } + break + } case "fetchLatestMcpServersFromHub": { this.mcpHub?.sendLatestMcpServers() break diff --git a/src/services/mcp/McpHub.ts b/src/services/mcp/McpHub.ts index 9e4a6b9cfb..556d6d8c47 100644 --- a/src/services/mcp/McpHub.ts +++ b/src/services/mcp/McpHub.ts @@ -636,6 +636,33 @@ export class McpHub { } } + public async deleteServer(serverName: string) { + try { + const settingsPath = await this.getMcpSettingsFilePath() + const content = await fs.readFile(settingsPath, "utf-8") + const config = JSON.parse(content) + if (!config.mcpServers || typeof config.mcpServers !== "object") { + config.mcpServers = {} + } + if (config.mcpServers[serverName]) { + delete config.mcpServers[serverName] + const updatedConfig = { + mcpServers: config.mcpServers, + } + await fs.writeFile(settingsPath, JSON.stringify(updatedConfig, null, 2)) + await this.updateServerConnections(config.mcpServers) + vscode.window.showInformationMessage(`Deleted ${serverName} MCP server`) + } else { + vscode.window.showWarningMessage(`${serverName} not found in MCP configuration`) + } + } catch (error) { + vscode.window.showErrorMessage( + `Failed to delete MCP server: ${error instanceof Error ? error.message : String(error)}`, + ) + throw error + } + } + async dispose(): Promise { this.removeAllFileWatchers() for (const connection of this.connections) { diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 87f6785019..e42cb5860b 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -29,6 +29,7 @@ export interface WebviewMessage { | "refreshOpenAiModels" | "openMcpSettings" | "restartMcpServer" + | "deleteMcpServer" | "autoApprovalSettings" | "browserSettings" | "togglePlanActMode" diff --git a/webview-ui/src/components/common/DangerButton.tsx b/webview-ui/src/components/common/DangerButton.tsx new file mode 100644 index 0000000000..572105db01 --- /dev/null +++ b/webview-ui/src/components/common/DangerButton.tsx @@ -0,0 +1,30 @@ +import { VSCodeButton } from "@vscode/webview-ui-toolkit/react" +import styled from "styled-components" + +const StyledButton = styled(VSCodeButton)` + --danger-button-bg: #c42b2b; + --danger-button-hover: #a82424; + --danger-button-active: #8f1f1f; + + background-color: var(--danger-button-bg) !important; + border-color: var(--danger-button-bg) !important; + color: #ffffff !important; + + &:hover { + background-color: var(--danger-button-hover) !important; + border-color: var(--danger-button-hover) !important; + } + + &:active { + background-color: var(--danger-button-active) !important; + border-color: var(--danger-button-active) !important; + } +` + +interface DangerButtonProps extends React.ComponentProps {} + +const DangerButton: React.FC = (props) => { + return +} + +export default DangerButton diff --git a/webview-ui/src/components/mcp/McpView.tsx b/webview-ui/src/components/mcp/McpView.tsx index e154e87c59..0b121a94bd 100644 --- a/webview-ui/src/components/mcp/McpView.tsx +++ b/webview-ui/src/components/mcp/McpView.tsx @@ -8,6 +8,7 @@ import McpResourceRow from "./McpResourceRow" import McpMarketplaceView from "./marketplace/McpMarketplaceView" import styled from "styled-components" import { getMcpServerDisplayName } from "../../utils/mcp" +import DangerButton from "../common/DangerButton" type McpViewProps = { onDone: () => void @@ -240,6 +241,7 @@ const ServerRow = ({ server }: { server: McpServer }) => { const { mcpMarketplaceCatalog } = useExtensionState() const [isExpanded, setIsExpanded] = useState(false) + const [isDeleting, setIsDeleting] = useState(false) const getStatusColor = () => { switch (server.status) { @@ -265,6 +267,14 @@ const ServerRow = ({ server }: { server: McpServer }) => { }) } + const handleDelete = () => { + setIsDeleting(true) + vscode.postMessage({ + type: "deleteMcpServer", + serverName: server.name, + }) + } + return (
    { }}> {server.status === "connecting" ? "Restarting..." : "Restart Server"} + + + {isDeleting ? "Deleting..." : "Delete Server"} +
    ) )} From ff7e5c88668121a44c6dc6fdbbb8ad24fb59780d Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Wed, 19 Feb 2025 18:03:11 -0800 Subject: [PATCH 098/100] Update package --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 72db4e14e9..dcffdf0d6c 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "claude-dev", "displayName": "Cline", "description": "Autonomous coding agent right in your IDE, capable of creating/editing files, running commands, using the browser, and more with your permission every step of the way.", - "version": "3.4.2", + "version": "3.4.3", "icon": "assets/icons/icon.png", "galleryBanner": { "color": "#617A91", From ab8376fee495c7bf33392af197a245bd66ad4bce Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Wed, 19 Feb 2025 18:57:16 -0800 Subject: [PATCH 099/100] Add 'Restore Task' option back to checkpoints --- src/core/Cline.ts | 18 ++++++++++-------- .../src/components/common/CheckmarkControl.tsx | 4 ++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 9209eeacb5..b1c6baa90c 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -353,15 +353,17 @@ export class Cline { break } - // Set isCheckpointCheckedOut flag on the message - // Find all checkpoint messages before this one - const checkpointMessages = this.clineMessages.filter((m) => m.say === "checkpoint_created") - const currentMessageIndex = checkpointMessages.findIndex((m) => m.ts === messageTs) + if (restoreType !== "task") { + // Set isCheckpointCheckedOut flag on the message + // Find all checkpoint messages before this one + const checkpointMessages = this.clineMessages.filter((m) => m.say === "checkpoint_created") + const currentMessageIndex = checkpointMessages.findIndex((m) => m.ts === messageTs) - // Set isCheckpointCheckedOut to false for all checkpoint messages - checkpointMessages.forEach((m, i) => { - m.isCheckpointCheckedOut = i === currentMessageIndex - }) + // Set isCheckpointCheckedOut to false for all checkpoint messages + checkpointMessages.forEach((m, i) => { + m.isCheckpointCheckedOut = i === currentMessageIndex + }) + } await this.saveClineMessages() diff --git a/webview-ui/src/components/common/CheckmarkControl.tsx b/webview-ui/src/components/common/CheckmarkControl.tsx index b57a6a0bf6..9641e77b7d 100644 --- a/webview-ui/src/components/common/CheckmarkControl.tsx +++ b/webview-ui/src/components/common/CheckmarkControl.tsx @@ -180,7 +180,7 @@ export const CheckmarkControl = ({ messageTs, isCheckpointCheckedOut }: Checkmar what will be reverted)

    - {/* +

    Deletes messages after this point (does not affect workspace files)

    -
    */} +
    Date: Wed, 19 Feb 2025 18:58:03 -0800 Subject: [PATCH 100/100] Update version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dcffdf0d6c..ef4989622a 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "claude-dev", "displayName": "Cline", "description": "Autonomous coding agent right in your IDE, capable of creating/editing files, running commands, using the browser, and more with your permission every step of the way.", - "version": "3.4.3", + "version": "3.4.4", "icon": "assets/icons/icon.png", "galleryBanner": { "color": "#617A91",