Compare commits

..

8 Commits

Author SHA1 Message Date
Elephant Lumps 62463284d9 changeset 2025-05-20 19:11:05 -07:00
Elephant Lumps 21f13f56c3 use navigation directly for openSettings 2025-05-20 19:09:59 -07:00
Ara b737911cdc Fix: Temporary revert in protobus changes in toggle plan and act mode (#3683)
* Fix: Temporary revert protobus changes for Toggle plan and act mode

* Fix: Temporary revert protobus changes for Toggle plan and act mode

---------

Co-authored-by: Cline Evaluation <cline@example.com>
2025-05-20 18:14:50 -07:00
Evan 1de02e9ab2 Migrate silentlyRefreshMcpMarketplace protobus (#3628)
* migrate silentlyRefreshMcpMarketplace

* changeset

---------

Co-authored-by: Elephant Lumps <celestial_vault@Elephants-MacBook-Pro.local>
2025-05-20 17:47:06 -07:00
canvrno 08d4240e70 [PROTOBUS] Move openMention to protobus (#3607)
* openMention protobus migration

* Cleanup and comments

* rebase
2025-05-20 17:40:16 -07:00
omercelik fe13ce8d6f feat: added gemini flash 05-20 (#3680)
* feat: added gemini flash 05-20

* feat: added gemini flash 2.5 05-20 to gemini models also

* Create funny-adults-tease.md
2025-05-20 17:24:00 -07:00
Evan 7fe7605a85 Migrate updateTerminalConnectionTimeout protobus (#3669)
* migrate setActiveQuote

* changeset

* migrate updateTerminalConnectionTimeout

* changeset

---------

Co-authored-by: Elephant Lumps <celestial_vault@Elephants-MacBook-Pro.local>
2025-05-20 17:10:33 -07:00
Evan f0e352489f Migrate toggleWindsurfRule protobus (#3677)
* migrate toggleWindsurfRules

* changeset

---------

Co-authored-by: Elephant Lumps <celestial_vault@Elephants-MacBook-Pro.local>
2025-05-20 16:55:53 -07:00
30 changed files with 725 additions and 53 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": minor
---
Migrate openSettings message to use state navigation handlers
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
feat: added gemini flash 05-20
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": minor
---
Migrate updateTerminalConnectionTimeout to protobus
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Fixing bug in toggle plan and act
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
openMention protobus migration
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": minor
---
Migrate silentlyRefreshMcpMarketplace to protobus
+3
View File
@@ -16,6 +16,9 @@ service FileService {
// Opens an image in the system viewer
rpc openImage(StringRequest) returns (Empty);
// Opens a mention (file, path, git commit, problem, terminal, or URL)
rpc openMention(StringRequest) returns (Empty);
// Deletes a rule file from either global or workspace rules directory
rpc deleteRuleFile(RuleFileRequest) returns (RuleFile);
+26
View File
@@ -14,6 +14,7 @@ service McpService {
rpc restartMcpServer(StringRequest) returns (McpServers);
rpc deleteMcpServer(StringRequest) returns (McpServers);
rpc toggleToolAutoApprove(ToggleToolAutoApproveRequest) returns (McpServers);
rpc refreshMcpMarketplace(EmptyRequest) returns (McpMarketplaceCatalog);
}
message ToggleMcpServerRequest {
@@ -85,3 +86,28 @@ message McpServer {
message McpServers {
repeated McpServer mcp_servers = 1;
}
message McpMarketplaceItem {
string mcp_id = 1;
string github_url = 2;
string name = 3;
string author = 4;
string description = 5;
string codicon_icon = 6;
string logo_url = 7;
string category = 8;
repeated string tags = 9;
bool requires_api_key = 10;
optional string readme_content = 11;
optional string llms_installation_content = 12;
bool is_recommended = 13;
int32 github_stars = 14;
int32 download_count = 15;
string created_at = 16;
string updated_at = 17;
string last_github_sync = 18;
}
message McpMarketplaceCatalog {
repeated McpMarketplaceItem items = 1;
}
+1
View File
@@ -9,6 +9,7 @@ service StateService {
rpc toggleFavoriteModel(StringRequest) returns (Empty);
rpc resetState(EmptyRequest) returns (Empty);
rpc togglePlanActMode(TogglePlanActModeRequest) returns (Empty);
rpc updateTerminalConnectionTimeout(Int64Request) returns (Int64);
}
message State {
+2
View File
@@ -9,6 +9,7 @@ import { deleteRuleFile } from "./deleteRuleFile"
import { getRelativePaths } from "./getRelativePaths"
import { openFile } from "./openFile"
import { openImage } from "./openImage"
import { openMention } from "./openMention"
import { searchCommits } from "./searchCommits"
import { searchFiles } from "./searchFiles"
import { selectImages } from "./selectImages"
@@ -25,6 +26,7 @@ export function registerAllMethods(): void {
registerMethod("getRelativePaths", getRelativePaths)
registerMethod("openFile", openFile)
registerMethod("openImage", openImage)
registerMethod("openMention", openMention)
registerMethod("searchCommits", searchCommits)
registerMethod("searchFiles", searchFiles)
registerMethod("selectImages", selectImages)
+14
View File
@@ -0,0 +1,14 @@
import { Controller } from ".."
import { Empty, StringRequest } from "../../../shared/proto/common"
import { openMention as coreOpenMention } from "../../mentions"
/**
* Opens a mention (file path, problem, terminal, or URL)
* @param controller The controller instance
* @param request The string request containing the mention text
* @returns Empty response
*/
export async function openMention(controller: Controller, request: StringRequest): Promise<Empty> {
coreOpenMention(request.value)
return Empty.create()
}
+53 -30
View File
@@ -32,7 +32,6 @@ import { fileExistsAtPath } from "@utils/fs"
import { getWorkingState } from "@utils/git"
import { extractCommitMessage } from "@integrations/git/commit-message-generator"
import { getTotalTasksSize } from "@utils/storage"
import { openMention } from "../mentions"
import { ensureMcpServersDirectoryExists, ensureSettingsDirectoryExists, GlobalFileNames } from "../storage/disk"
import {
getAllExtensionState,
@@ -306,6 +305,11 @@ export class Controller {
}
}
break
case "togglePlanActMode":
if (message.chatSettings) {
await this.togglePlanActModeWithChatSettings(message.chatSettings, message.chatContent)
}
break
case "optionsResponse":
await this.postMessageToWebview({
type: "invoke",
@@ -328,9 +332,6 @@ export class Controller {
vscode.env.openExternal(vscode.Uri.parse(message.url))
}
break
case "openMention":
openMention(message.text)
break
case "showAccountViewClicked": {
await this.postMessageToWebview({ type: "action", action: "accountButtonClicked" })
break
@@ -350,10 +351,6 @@ export class Controller {
await this.fetchMcpMarketplace(message.bool)
break
}
case "silentlyRefreshMcpMarketplace": {
await this.silentlyRefreshMcpMarketplace()
break
}
// case "openMcpMarketplaceServerDetails": {
// if (message.text) {
// const response = await fetch(`https://api.cline.bot/v1/mcp/marketplace/item?mcpId=${message.mcpId}`)
@@ -426,13 +423,6 @@ export class Controller {
break
}
// telemetry
case "openSettings": {
await this.postMessageToWebview({
type: "action",
action: "settingsButtonClicked",
})
break
}
case "telemetrySetting": {
if (message.telemetrySetting) {
await this.updateTelemetrySetting(message.telemetrySetting)
@@ -516,21 +506,6 @@ export class Controller {
break
}
case "updateTerminalConnectionTimeout": {
if (message.shellIntegrationTimeout !== undefined) {
const timeout = message.shellIntegrationTimeout
if (typeof timeout === "number" && !isNaN(timeout) && timeout > 0) {
await updateGlobalState(this.context, "shellIntegrationTimeout", timeout)
await this.postStateToWebview()
} else {
console.warn(
`Invalid shell integration timeout value received: ${timeout}. ` + `Expected a positive number.`,
)
}
}
break
}
// Add more switch case statements here as more webview message commands
// are created within the webview context (i.e. inside media/main.js)
}
@@ -846,6 +821,40 @@ export class Controller {
}
}
private async fetchMcpMarketplaceFromApiRPC(silent: boolean = false): Promise<McpMarketplaceCatalog | undefined> {
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 updateGlobalState(this.context, "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"
throw new Error(errorMessage)
}
return undefined
}
}
async silentlyRefreshMcpMarketplace() {
try {
const catalog = await this.fetchMcpMarketplaceFromApi(true)
@@ -860,6 +869,20 @@ export class Controller {
}
}
/**
* RPC variant that silently refreshes the MCP marketplace catalog and returns the result
* Unlike silentlyRefreshMcpMarketplace, this doesn't post a message to the webview
* @returns MCP marketplace catalog or undefined if refresh failed
*/
async silentlyRefreshMcpMarketplaceRPC() {
try {
return await this.fetchMcpMarketplaceFromApiRPC(true)
} catch (error) {
console.error("Failed to silently refresh MCP marketplace (RPC):", error)
return undefined
}
}
private async fetchMcpMarketplace(forceRefresh: boolean = false) {
try {
// Check if we have cached data
+2
View File
@@ -6,6 +6,7 @@ import { registerMethod } from "./index"
import { addRemoteMcpServer } from "./addRemoteMcpServer"
import { deleteMcpServer } from "./deleteMcpServer"
import { downloadMcp } from "./downloadMcp"
import { refreshMcpMarketplace } from "./refreshMcpMarketplace"
import { restartMcpServer } from "./restartMcpServer"
import { toggleMcpServer } from "./toggleMcpServer"
import { toggleToolAutoApprove } from "./toggleToolAutoApprove"
@@ -17,6 +18,7 @@ export function registerAllMethods(): void {
registerMethod("addRemoteMcpServer", addRemoteMcpServer)
registerMethod("deleteMcpServer", deleteMcpServer)
registerMethod("downloadMcp", downloadMcp)
registerMethod("refreshMcpMarketplace", refreshMcpMarketplace)
registerMethod("restartMcpServer", restartMcpServer)
registerMethod("toggleMcpServer", toggleMcpServer)
registerMethod("toggleToolAutoApprove", toggleToolAutoApprove)
@@ -0,0 +1,27 @@
import type { EmptyRequest } from "../../../shared/proto/common"
import type { McpMarketplaceCatalog } from "../../../shared/proto/mcp"
import type { Controller } from "../index"
/**
* RPC handler that silently refreshes the MCP marketplace catalog
* @param controller Controller instance
* @param _request Empty request
* @returns MCP marketplace catalog
*/
export async function refreshMcpMarketplace(controller: Controller, _request: EmptyRequest): Promise<McpMarketplaceCatalog> {
try {
// Call the RPC variant which returns the result directly
const catalog = await controller.silentlyRefreshMcpMarketplaceRPC()
if (catalog) {
// Types are structurally identical, use direct type assertion
return catalog as McpMarketplaceCatalog
}
// Return empty catalog if nothing was fetched
return { items: [] }
} catch (error) {
console.error("Failed to refresh MCP marketplace:", error)
return { items: [] }
}
}
+2
View File
@@ -8,6 +8,7 @@ import { resetState } from "./resetState"
import { subscribeToState } from "./subscribeToState"
import { toggleFavoriteModel } from "./toggleFavoriteModel"
import { togglePlanActMode } from "./togglePlanActMode"
import { updateTerminalConnectionTimeout } from "./updateTerminalConnectionTimeout"
// Streaming methods for this service
export const streamingMethods = ["subscribeToState"]
@@ -20,4 +21,5 @@ export function registerAllMethods(): void {
registerMethod("subscribeToState", subscribeToState, { isStreaming: true })
registerMethod("toggleFavoriteModel", toggleFavoriteModel)
registerMethod("togglePlanActMode", togglePlanActMode)
registerMethod("updateTerminalConnectionTimeout", updateTerminalConnectionTimeout)
}
@@ -0,0 +1,27 @@
import { Controller } from ".."
import { Int64, Int64Request } from "../../../shared/proto/common"
import { updateGlobalState } from "../../storage/state"
/**
* Updates the terminal connection timeout setting
* @param controller The controller instance
* @param request The request containing the timeout value in milliseconds
* @returns The updated timeout value
*/
export async function updateTerminalConnectionTimeout(controller: Controller, request: Int64Request): Promise<Int64> {
try {
const timeout = request.value
if (typeof timeout === "number" && !isNaN(timeout) && timeout > 0) {
// Update the global state directly
await updateGlobalState(controller.context, "shellIntegrationTimeout", timeout)
return { value: timeout }
} else {
console.warn(`Invalid shell integration timeout value received: ${timeout}. Expected a positive number.`)
throw new Error("Invalid timeout value. Expected a positive number.")
}
} catch (error) {
console.error(`Failed to update terminal connection timeout: ${error}`)
throw error
}
}
+1 -4
View File
@@ -16,22 +16,20 @@ export interface WebviewMessage {
| "reportBug"
| "didShowAnnouncement"
| "openInBrowser"
| "openMention"
| "showChatView"
| "refreshClineRules"
| "openMcpSettings"
| "autoApprovalSettings"
| "togglePlanActMode"
| "openExtensionSettings"
| "requestVsCodeLmModels"
| "showAccountViewClicked"
| "authStateChanged"
| "authCallback"
| "fetchMcpMarketplace"
| "silentlyRefreshMcpMarketplace"
| "searchCommits"
| "fetchLatestMcpServersFromHub"
| "telemetrySetting"
| "openSettings"
| "invoke"
| "updateSettings"
| "clearAllTaskHistory"
@@ -42,7 +40,6 @@ export interface WebviewMessage {
| "grpc_request"
| "grpc_request_cancel"
| "toggleWorkflow"
| "updateTerminalConnectionTimeout"
text?: string
disabled?: boolean
+25
View File
@@ -483,6 +483,19 @@ export const vertexModels = {
outputPrice: 3.5,
},
},
"gemini-2.5-flash-preview-05-20": {
maxTokens: 65536,
contextWindow: 1_048_576,
supportsImages: true,
supportsPromptCache: false,
supportsGlobalEndpoint: true,
inputPrice: 0.15,
outputPrice: 0.6,
thinkingConfig: {
maxBudget: 24576,
outputPrice: 3.5,
},
},
"gemini-2.0-flash-thinking-exp-01-21": {
maxTokens: 65_536,
contextWindow: 1_048_576,
@@ -601,6 +614,18 @@ export const geminiModels = {
},
],
},
"gemini-2.5-flash-preview-05-20": {
maxTokens: 65536,
contextWindow: 1_048_576,
supportsImages: true,
supportsPromptCache: false,
inputPrice: 0.15,
outputPrice: 0.6,
thinkingConfig: {
maxBudget: 24576,
outputPrice: 3.5,
},
},
"gemini-2.5-flash-preview-04-17": {
maxTokens: 65536,
contextWindow: 1_048_576,
+9
View File
@@ -1514,6 +1514,15 @@ export const FileServiceDefinition = {
responseStream: false,
options: {},
},
/** Opens a mention (file, path, git commit, problem, terminal, or URL) */
openMention: {
name: "openMention",
requestType: StringRequest,
requestStream: false,
responseType: Empty,
responseStream: false,
options: {},
},
/** Deletes a rule file from either global or workspace rules directory */
deleteRuleFile: {
name: "deleteRuleFile",
+447 -1
View File
@@ -6,7 +6,7 @@
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"
import { Empty, Metadata, StringRequest } from "./common"
import { Empty, EmptyRequest, Metadata, StringRequest } from "./common"
export const protobufPackage = "cline"
@@ -115,6 +115,31 @@ export interface McpServers {
mcpServers: McpServer[]
}
export interface McpMarketplaceItem {
mcpId: string
githubUrl: string
name: string
author: string
description: string
codiconIcon: string
logoUrl: string
category: string
tags: string[]
requiresApiKey: boolean
readmeContent?: string | undefined
llmsInstallationContent?: string | undefined
isRecommended: boolean
githubStars: number
downloadCount: number
createdAt: string
updatedAt: string
lastGithubSync: string
}
export interface McpMarketplaceCatalog {
items: McpMarketplaceItem[]
}
function createBaseToggleMcpServerRequest(): ToggleMcpServerRequest {
return { metadata: undefined, serverName: "", disabled: false }
}
@@ -1091,6 +1116,419 @@ export const McpServers: MessageFns<McpServers> = {
},
}
function createBaseMcpMarketplaceItem(): McpMarketplaceItem {
return {
mcpId: "",
githubUrl: "",
name: "",
author: "",
description: "",
codiconIcon: "",
logoUrl: "",
category: "",
tags: [],
requiresApiKey: false,
readmeContent: undefined,
llmsInstallationContent: undefined,
isRecommended: false,
githubStars: 0,
downloadCount: 0,
createdAt: "",
updatedAt: "",
lastGithubSync: "",
}
}
export const McpMarketplaceItem: MessageFns<McpMarketplaceItem> = {
encode(message: McpMarketplaceItem, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.mcpId !== "") {
writer.uint32(10).string(message.mcpId)
}
if (message.githubUrl !== "") {
writer.uint32(18).string(message.githubUrl)
}
if (message.name !== "") {
writer.uint32(26).string(message.name)
}
if (message.author !== "") {
writer.uint32(34).string(message.author)
}
if (message.description !== "") {
writer.uint32(42).string(message.description)
}
if (message.codiconIcon !== "") {
writer.uint32(50).string(message.codiconIcon)
}
if (message.logoUrl !== "") {
writer.uint32(58).string(message.logoUrl)
}
if (message.category !== "") {
writer.uint32(66).string(message.category)
}
for (const v of message.tags) {
writer.uint32(74).string(v!)
}
if (message.requiresApiKey !== false) {
writer.uint32(80).bool(message.requiresApiKey)
}
if (message.readmeContent !== undefined) {
writer.uint32(90).string(message.readmeContent)
}
if (message.llmsInstallationContent !== undefined) {
writer.uint32(98).string(message.llmsInstallationContent)
}
if (message.isRecommended !== false) {
writer.uint32(104).bool(message.isRecommended)
}
if (message.githubStars !== 0) {
writer.uint32(112).int32(message.githubStars)
}
if (message.downloadCount !== 0) {
writer.uint32(120).int32(message.downloadCount)
}
if (message.createdAt !== "") {
writer.uint32(130).string(message.createdAt)
}
if (message.updatedAt !== "") {
writer.uint32(138).string(message.updatedAt)
}
if (message.lastGithubSync !== "") {
writer.uint32(146).string(message.lastGithubSync)
}
return writer
},
decode(input: BinaryReader | Uint8Array, length?: number): McpMarketplaceItem {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input)
let end = length === undefined ? reader.len : reader.pos + length
const message = createBaseMcpMarketplaceItem()
while (reader.pos < end) {
const tag = reader.uint32()
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break
}
message.mcpId = reader.string()
continue
}
case 2: {
if (tag !== 18) {
break
}
message.githubUrl = reader.string()
continue
}
case 3: {
if (tag !== 26) {
break
}
message.name = reader.string()
continue
}
case 4: {
if (tag !== 34) {
break
}
message.author = reader.string()
continue
}
case 5: {
if (tag !== 42) {
break
}
message.description = reader.string()
continue
}
case 6: {
if (tag !== 50) {
break
}
message.codiconIcon = reader.string()
continue
}
case 7: {
if (tag !== 58) {
break
}
message.logoUrl = reader.string()
continue
}
case 8: {
if (tag !== 66) {
break
}
message.category = reader.string()
continue
}
case 9: {
if (tag !== 74) {
break
}
message.tags.push(reader.string())
continue
}
case 10: {
if (tag !== 80) {
break
}
message.requiresApiKey = reader.bool()
continue
}
case 11: {
if (tag !== 90) {
break
}
message.readmeContent = reader.string()
continue
}
case 12: {
if (tag !== 98) {
break
}
message.llmsInstallationContent = reader.string()
continue
}
case 13: {
if (tag !== 104) {
break
}
message.isRecommended = reader.bool()
continue
}
case 14: {
if (tag !== 112) {
break
}
message.githubStars = reader.int32()
continue
}
case 15: {
if (tag !== 120) {
break
}
message.downloadCount = reader.int32()
continue
}
case 16: {
if (tag !== 130) {
break
}
message.createdAt = reader.string()
continue
}
case 17: {
if (tag !== 138) {
break
}
message.updatedAt = reader.string()
continue
}
case 18: {
if (tag !== 146) {
break
}
message.lastGithubSync = reader.string()
continue
}
}
if ((tag & 7) === 4 || tag === 0) {
break
}
reader.skip(tag & 7)
}
return message
},
fromJSON(object: any): McpMarketplaceItem {
return {
mcpId: isSet(object.mcpId) ? globalThis.String(object.mcpId) : "",
githubUrl: isSet(object.githubUrl) ? globalThis.String(object.githubUrl) : "",
name: isSet(object.name) ? globalThis.String(object.name) : "",
author: isSet(object.author) ? globalThis.String(object.author) : "",
description: isSet(object.description) ? globalThis.String(object.description) : "",
codiconIcon: isSet(object.codiconIcon) ? globalThis.String(object.codiconIcon) : "",
logoUrl: isSet(object.logoUrl) ? globalThis.String(object.logoUrl) : "",
category: isSet(object.category) ? globalThis.String(object.category) : "",
tags: globalThis.Array.isArray(object?.tags) ? object.tags.map((e: any) => globalThis.String(e)) : [],
requiresApiKey: isSet(object.requiresApiKey) ? globalThis.Boolean(object.requiresApiKey) : false,
readmeContent: isSet(object.readmeContent) ? globalThis.String(object.readmeContent) : undefined,
llmsInstallationContent: isSet(object.llmsInstallationContent)
? globalThis.String(object.llmsInstallationContent)
: undefined,
isRecommended: isSet(object.isRecommended) ? globalThis.Boolean(object.isRecommended) : false,
githubStars: isSet(object.githubStars) ? globalThis.Number(object.githubStars) : 0,
downloadCount: isSet(object.downloadCount) ? globalThis.Number(object.downloadCount) : 0,
createdAt: isSet(object.createdAt) ? globalThis.String(object.createdAt) : "",
updatedAt: isSet(object.updatedAt) ? globalThis.String(object.updatedAt) : "",
lastGithubSync: isSet(object.lastGithubSync) ? globalThis.String(object.lastGithubSync) : "",
}
},
toJSON(message: McpMarketplaceItem): unknown {
const obj: any = {}
if (message.mcpId !== "") {
obj.mcpId = message.mcpId
}
if (message.githubUrl !== "") {
obj.githubUrl = message.githubUrl
}
if (message.name !== "") {
obj.name = message.name
}
if (message.author !== "") {
obj.author = message.author
}
if (message.description !== "") {
obj.description = message.description
}
if (message.codiconIcon !== "") {
obj.codiconIcon = message.codiconIcon
}
if (message.logoUrl !== "") {
obj.logoUrl = message.logoUrl
}
if (message.category !== "") {
obj.category = message.category
}
if (message.tags?.length) {
obj.tags = message.tags
}
if (message.requiresApiKey !== false) {
obj.requiresApiKey = message.requiresApiKey
}
if (message.readmeContent !== undefined) {
obj.readmeContent = message.readmeContent
}
if (message.llmsInstallationContent !== undefined) {
obj.llmsInstallationContent = message.llmsInstallationContent
}
if (message.isRecommended !== false) {
obj.isRecommended = message.isRecommended
}
if (message.githubStars !== 0) {
obj.githubStars = Math.round(message.githubStars)
}
if (message.downloadCount !== 0) {
obj.downloadCount = Math.round(message.downloadCount)
}
if (message.createdAt !== "") {
obj.createdAt = message.createdAt
}
if (message.updatedAt !== "") {
obj.updatedAt = message.updatedAt
}
if (message.lastGithubSync !== "") {
obj.lastGithubSync = message.lastGithubSync
}
return obj
},
create<I extends Exact<DeepPartial<McpMarketplaceItem>, I>>(base?: I): McpMarketplaceItem {
return McpMarketplaceItem.fromPartial(base ?? ({} as any))
},
fromPartial<I extends Exact<DeepPartial<McpMarketplaceItem>, I>>(object: I): McpMarketplaceItem {
const message = createBaseMcpMarketplaceItem()
message.mcpId = object.mcpId ?? ""
message.githubUrl = object.githubUrl ?? ""
message.name = object.name ?? ""
message.author = object.author ?? ""
message.description = object.description ?? ""
message.codiconIcon = object.codiconIcon ?? ""
message.logoUrl = object.logoUrl ?? ""
message.category = object.category ?? ""
message.tags = object.tags?.map((e) => e) || []
message.requiresApiKey = object.requiresApiKey ?? false
message.readmeContent = object.readmeContent ?? undefined
message.llmsInstallationContent = object.llmsInstallationContent ?? undefined
message.isRecommended = object.isRecommended ?? false
message.githubStars = object.githubStars ?? 0
message.downloadCount = object.downloadCount ?? 0
message.createdAt = object.createdAt ?? ""
message.updatedAt = object.updatedAt ?? ""
message.lastGithubSync = object.lastGithubSync ?? ""
return message
},
}
function createBaseMcpMarketplaceCatalog(): McpMarketplaceCatalog {
return { items: [] }
}
export const McpMarketplaceCatalog: MessageFns<McpMarketplaceCatalog> = {
encode(message: McpMarketplaceCatalog, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
for (const v of message.items) {
McpMarketplaceItem.encode(v!, writer.uint32(10).fork()).join()
}
return writer
},
decode(input: BinaryReader | Uint8Array, length?: number): McpMarketplaceCatalog {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input)
let end = length === undefined ? reader.len : reader.pos + length
const message = createBaseMcpMarketplaceCatalog()
while (reader.pos < end) {
const tag = reader.uint32()
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break
}
message.items.push(McpMarketplaceItem.decode(reader, reader.uint32()))
continue
}
}
if ((tag & 7) === 4 || tag === 0) {
break
}
reader.skip(tag & 7)
}
return message
},
fromJSON(object: any): McpMarketplaceCatalog {
return {
items: globalThis.Array.isArray(object?.items) ? object.items.map((e: any) => McpMarketplaceItem.fromJSON(e)) : [],
}
},
toJSON(message: McpMarketplaceCatalog): unknown {
const obj: any = {}
if (message.items?.length) {
obj.items = message.items.map((e) => McpMarketplaceItem.toJSON(e))
}
return obj
},
create<I extends Exact<DeepPartial<McpMarketplaceCatalog>, I>>(base?: I): McpMarketplaceCatalog {
return McpMarketplaceCatalog.fromPartial(base ?? ({} as any))
},
fromPartial<I extends Exact<DeepPartial<McpMarketplaceCatalog>, I>>(object: I): McpMarketplaceCatalog {
const message = createBaseMcpMarketplaceCatalog()
message.items = object.items?.map((e) => McpMarketplaceItem.fromPartial(e)) || []
return message
},
}
export type McpServiceDefinition = typeof McpServiceDefinition
export const McpServiceDefinition = {
name: "McpService",
@@ -1152,6 +1590,14 @@ export const McpServiceDefinition = {
responseStream: false,
options: {},
},
refreshMcpMarketplace: {
name: "refreshMcpMarketplace",
requestType: EmptyRequest,
requestStream: false,
responseType: McpMarketplaceCatalog,
responseStream: false,
options: {},
},
},
} as const
+9 -1
View File
@@ -6,7 +6,7 @@
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"
import { Empty, EmptyRequest, Metadata, StringRequest } from "./common"
import { Empty, EmptyRequest, Int64, Int64Request, Metadata, StringRequest } from "./common"
export const protobufPackage = "cline"
@@ -436,6 +436,14 @@ export const StateServiceDefinition = {
responseStream: false,
options: {},
},
updateTerminalConnectionTimeout: {
name: "updateTerminalConnectionTimeout",
requestType: Int64Request,
requestStream: false,
responseType: Int64,
responseStream: false,
options: {},
},
},
} as const
+6
View File
@@ -24,6 +24,7 @@ import { checkpointRestore } from "../core/controller/checkpoints/checkpointRest
import { copyToClipboard } from "../core/controller/file/copyToClipboard"
import { openFile } from "../core/controller/file/openFile"
import { openImage } from "../core/controller/file/openImage"
import { openMention } from "../core/controller/file/openMention"
import { deleteRuleFile } from "../core/controller/file/deleteRuleFile"
import { createRuleFile } from "../core/controller/file/createRuleFile"
import { searchCommits } from "../core/controller/file/searchCommits"
@@ -42,6 +43,7 @@ import { downloadMcp } from "../core/controller/mcp/downloadMcp"
import { restartMcpServer } from "../core/controller/mcp/restartMcpServer"
import { deleteMcpServer } from "../core/controller/mcp/deleteMcpServer"
import { toggleToolAutoApprove } from "../core/controller/mcp/toggleToolAutoApprove"
import { refreshMcpMarketplace } from "../core/controller/mcp/refreshMcpMarketplace"
// Models Service
import { getOllamaModels } from "../core/controller/models/getOllamaModels"
@@ -61,6 +63,7 @@ import { subscribeToState } from "../core/controller/state/subscribeToState"
import { toggleFavoriteModel } from "../core/controller/state/toggleFavoriteModel"
import { resetState } from "../core/controller/state/resetState"
import { togglePlanActMode } from "../core/controller/state/togglePlanActMode"
import { updateTerminalConnectionTimeout } from "../core/controller/state/updateTerminalConnectionTimeout"
// Task Service
import { cancelTask } from "../core/controller/task/cancelTask"
@@ -117,6 +120,7 @@ export function addServices(
copyToClipboard: wrapper(copyToClipboard, controller),
openFile: wrapper(openFile, controller),
openImage: wrapper(openImage, controller),
openMention: wrapper(openMention, controller),
deleteRuleFile: wrapper(deleteRuleFile, controller),
createRuleFile: wrapper(createRuleFile, controller),
searchCommits: wrapper(searchCommits, controller),
@@ -137,6 +141,7 @@ export function addServices(
restartMcpServer: wrapper(restartMcpServer, controller),
deleteMcpServer: wrapper(deleteMcpServer, controller),
toggleToolAutoApprove: wrapper(toggleToolAutoApprove, controller),
refreshMcpMarketplace: wrapper(refreshMcpMarketplace, controller),
})
// Models Service
@@ -162,6 +167,7 @@ export function addServices(
toggleFavoriteModel: wrapper(toggleFavoriteModel, controller),
resetState: wrapper(resetState, controller),
togglePlanActMode: wrapper(togglePlanActMode, controller),
updateTerminalConnectionTimeout: wrapper(updateTerminalConnectionTimeout, controller),
})
// Task Service
@@ -13,7 +13,7 @@ interface ConnectionInfo {
}
export const BrowserSettingsMenu = () => {
const { browserSettings } = useExtensionState()
const { browserSettings, navigateToSettings } = useExtensionState()
const containerRef = useRef<HTMLDivElement>(null)
const [showInfoPopover, setShowInfoPopover] = useState(false)
const [connectionInfo, setConnectionInfo] = useState<ConnectionInfo>({
@@ -65,10 +65,8 @@ export const BrowserSettingsMenu = () => {
}, [showInfoPopover])
const openBrowserSettings = () => {
// First open the settings panel
vscode.postMessage({
type: "openSettings",
})
// First open the settings panel using direct navigation
navigateToSettings()
// After a short delay, send a message to scroll to browser settings
setTimeout(async () => {
@@ -966,7 +966,8 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
}
setTimeout(() => {
const newMode = chatSettings.mode === "plan" ? "act" : "plan"
StateServiceClient.togglePlanActMode({
vscode.postMessage({
type: "togglePlanActMode",
chatSettings: {
mode: newMode,
},
@@ -11,7 +11,7 @@ import Thumbnails from "@/components/common/Thumbnails"
import { normalizeApiConfiguration } from "@/components/settings/ApiOptions"
import { validateSlashCommand } from "@/utils/slash-commands"
import TaskTimeline from "./TaskTimeline"
import { TaskServiceClient } from "@/services/grpc-client"
import { TaskServiceClient, FileServiceClient } from "@/services/grpc-client"
import HeroTooltip from "@/components/common/HeroTooltip"
interface TaskHeaderProps {
@@ -597,7 +597,7 @@ export const highlightMentions = (text: string, withShadow = true) => {
key={index}
className={withShadow ? "mention-context-highlight-with-shadow" : "mention-context-highlight"}
style={{ cursor: "pointer" }}
onClick={() => vscode.postMessage({ type: "openMention", text: part })}>
onClick={() => FileServiceClient.openMention({ value: part })}>
@{part}
</span>
)
@@ -1,6 +1,7 @@
import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react"
import { memo, useState } from "react"
import styled from "styled-components"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { vscode } from "@/utils/vscode"
import { TelemetrySetting } from "@shared/TelemetrySetting"
@@ -45,9 +46,11 @@ const ButtonContainer = styled.div`
`
const TelemetryBanner = () => {
const { navigateToSettings } = useExtensionState()
const handleOpenSettings = () => {
handleClose()
vscode.postMessage({ type: "openSettings" })
navigateToSettings()
}
const handleClose = () => {
@@ -3,6 +3,7 @@ import { useEffect, useState } from "react"
import styled from "styled-components"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { vscode } from "@/utils/vscode"
import { McpServiceClient } from "@/services/grpc-client"
import AddRemoteServerForm from "./tabs/add-server/AddRemoteServerForm"
import McpMarketplaceView from "./tabs/marketplace/McpMarketplaceView"
import InstalledServersView from "./tabs/installed/InstalledServersView"
@@ -28,9 +29,20 @@ const McpConfigurationView = ({ onDone, initialTab }: McpViewProps) => {
}
}, [mcpMarketplaceEnabled, activeTab])
// Get setter for MCP marketplace catalog from context
const { setMcpMarketplaceCatalog } = useExtensionState()
useEffect(() => {
if (mcpMarketplaceEnabled) {
vscode.postMessage({ type: "silentlyRefreshMcpMarketplace" })
McpServiceClient.refreshMcpMarketplace({})
.then((response) => {
// Types are structurally identical, use response directly
setMcpMarketplaceCatalog(response)
})
.catch((error) => {
console.error("Error refreshing MCP marketplace:", error)
})
vscode.postMessage({ type: "fetchLatestMcpServersFromHub" })
}
}, [mcpMarketplaceEnabled])
@@ -115,7 +115,8 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
switch (message.type) {
case "didUpdateSettings":
if (pendingTabChange) {
StateServiceClient.togglePlanActMode({
vscode.postMessage({
type: "togglePlanActMode",
chatSettings: {
mode: pendingTabChange,
},
@@ -1,7 +1,8 @@
import React, { useState } from "react"
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { vscode } from "@/utils/vscode"
import { StateServiceClient } from "@/services/grpc-client"
import { Int64, Int64Request } from "@shared/proto/common"
export const TerminalSettingsSection: React.FC = () => {
const { shellIntegrationTimeout, setShellIntegrationTimeout } = useExtensionState()
@@ -26,11 +27,17 @@ export const TerminalSettingsSection: React.FC = () => {
// Update local state
setShellIntegrationTimeout(timeout)
// Send to extension
vscode.postMessage({
type: "updateTerminalConnectionTimeout",
shellIntegrationTimeout: timeout,
})
// Send to extension using gRPC
StateServiceClient.updateTerminalConnectionTimeout({
value: timeout,
} as Int64Request)
.then((response: Int64) => {
setShellIntegrationTimeout(response.value)
setInputValue((response.value / 1000).toString())
})
.catch((error) => {
console.error("Failed to update terminal connection timeout:", error)
})
}
const handleInputBlur = () => {
@@ -55,6 +55,7 @@ interface ExtensionStateContextType extends ExtensionState {
setLocalClineRulesToggles: (toggles: Record<string, boolean>) => void
setLocalCursorRulesToggles: (toggles: Record<string, boolean>) => void
setLocalWindsurfRulesToggles: (toggles: Record<string, boolean>) => void
setMcpMarketplaceCatalog: (value: McpMarketplaceCatalog) => void
// Navigation state setters
setShowMcp: (value: boolean) => void
@@ -500,6 +501,7 @@ export const ExtensionStateContextProvider: React.FC<{
shellIntegrationTimeout: value,
})),
setMcpServers: (mcpServers: McpServer[]) => setMcpServers(mcpServers),
setMcpMarketplaceCatalog: (catalog: McpMarketplaceCatalog) => setMcpMarketplaceCatalog(catalog),
setShowMcp,
closeMcpView,
setChatSettings: (value) => {