diff --git a/.changeset/eight-sheep-remember.md b/.changeset/eight-sheep-remember.md new file mode 100644 index 000000000..33d51c8be --- /dev/null +++ b/.changeset/eight-sheep-remember.md @@ -0,0 +1,5 @@ +--- +"claude-dev": minor +--- + +Migrate refreshClineRules to protobus diff --git a/proto/file.proto b/proto/file.proto index 5ce50d87f..a6866942a 100644 --- a/proto/file.proto +++ b/proto/file.proto @@ -46,6 +46,18 @@ service FileService { // Toggle a Windsurf rule (enable or disable) rpc toggleWindsurfRule(ToggleWindsurfRuleRequest) returns (ClineRulesToggles); + + // Refreshes all rule toggles (Cline, External, and Workflows) + rpc refreshRules(EmptyRequest) returns (RefreshedRules); +} + +// Response for refreshRules operation +message RefreshedRules { + ClineRulesToggles global_cline_rules_toggles = 1; + ClineRulesToggles local_cline_rules_toggles = 2; + ClineRulesToggles local_cursor_rules_toggles = 3; + ClineRulesToggles local_windsurf_rules_toggles = 4; + ClineRulesToggles workflow_toggles = 5; } // Request to toggle a Windsurf rule diff --git a/src/core/controller/file/methods.ts b/src/core/controller/file/methods.ts index 6740e2426..55dc01aae 100644 --- a/src/core/controller/file/methods.ts +++ b/src/core/controller/file/methods.ts @@ -10,6 +10,7 @@ import { getRelativePaths } from "./getRelativePaths" import { openFile } from "./openFile" import { openImage } from "./openImage" import { openMention } from "./openMention" +import { refreshRules } from "./refreshRules" import { searchCommits } from "./searchCommits" import { searchFiles } from "./searchFiles" import { selectImages } from "./selectImages" @@ -27,6 +28,7 @@ export function registerAllMethods(): void { registerMethod("openFile", openFile) registerMethod("openImage", openImage) registerMethod("openMention", openMention) + registerMethod("refreshRules", refreshRules) registerMethod("searchCommits", searchCommits) registerMethod("searchFiles", searchFiles) registerMethod("selectImages", selectImages) diff --git a/src/core/controller/file/refreshRules.ts b/src/core/controller/file/refreshRules.ts new file mode 100644 index 000000000..1b897b01b --- /dev/null +++ b/src/core/controller/file/refreshRules.ts @@ -0,0 +1,32 @@ +import { EmptyRequest } from "@shared/proto/common" +import { RefreshedRules } from "@shared/proto/file" +import type { Controller } from "../index" +import { refreshClineRulesToggles } from "@core/context/instructions/user-instructions/cline-rules" +import { refreshExternalRulesToggles } from "@core/context/instructions/user-instructions/external-rules" +import { refreshWorkflowToggles } from "@core/context/instructions/user-instructions/workflows" +import { cwd } from "@core/task" + +/** + * Refreshes all rule toggles (Cline, External, and Workflows) + * @param controller The controller instance + * @param _request The empty request + * @returns RefreshedRules containing updated toggles for all rule types + */ +export async function refreshRules(controller: Controller, _request: EmptyRequest): Promise { + try { + const { globalToggles, localToggles } = await refreshClineRulesToggles(controller.context, cwd) + const { cursorLocalToggles, windsurfLocalToggles } = await refreshExternalRulesToggles(controller.context, cwd) + const workflowToggles = await refreshWorkflowToggles(controller.context, cwd) + + return { + globalClineRulesToggles: { toggles: globalToggles }, + localClineRulesToggles: { toggles: localToggles }, + localCursorRulesToggles: { toggles: cursorLocalToggles }, + localWindsurfRulesToggles: { toggles: windsurfLocalToggles }, + workflowToggles: { toggles: workflowToggles }, + } + } catch (error) { + console.error("Failed to refresh rules:", error) + throw error + } +} diff --git a/src/core/controller/index.ts b/src/core/controller/index.ts index 5eb36ff27..8cb3db184 100644 --- a/src/core/controller/index.ts +++ b/src/core/controller/index.ts @@ -321,12 +321,6 @@ export class Controller { await updateGlobalState(this.context, "lastShownAnnouncementId", this.latestAnnouncementId) await this.postStateToWebview() break - case "refreshClineRules": - await refreshClineRulesToggles(this.context, cwd) - await refreshExternalRulesToggles(this.context, cwd) - await refreshWorkflowToggles(this.context, cwd) - await this.postStateToWebview() - break case "openInBrowser": if (message.url) { vscode.env.openExternal(vscode.Uri.parse(message.url)) diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 6ae6c882c..5faa639c9 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -17,7 +17,6 @@ export interface WebviewMessage { | "didShowAnnouncement" | "openInBrowser" | "showChatView" - | "refreshClineRules" | "openMcpSettings" | "autoApprovalSettings" | "togglePlanActMode" diff --git a/src/shared/proto/file.ts b/src/shared/proto/file.ts index 43c4b153d..1f5377cd8 100644 --- a/src/shared/proto/file.ts +++ b/src/shared/proto/file.ts @@ -10,6 +10,15 @@ import { Empty, EmptyRequest, Metadata, StringArray, StringRequest } from "./com export const protobufPackage = "cline" +/** Response for refreshRules operation */ +export interface RefreshedRules { + globalClineRulesToggles?: ClineRulesToggles | undefined + localClineRulesToggles?: ClineRulesToggles | undefined + localCursorRulesToggles?: ClineRulesToggles | undefined + localWindsurfRulesToggles?: ClineRulesToggles | undefined + workflowToggles?: ClineRulesToggles | undefined +} + /** Request to toggle a Windsurf rule */ export interface ToggleWindsurfRuleRequest { metadata?: Metadata | undefined @@ -132,6 +141,159 @@ export interface ToggleCursorRuleRequest { enabled: boolean } +function createBaseRefreshedRules(): RefreshedRules { + return { + globalClineRulesToggles: undefined, + localClineRulesToggles: undefined, + localCursorRulesToggles: undefined, + localWindsurfRulesToggles: undefined, + workflowToggles: undefined, + } +} + +export const RefreshedRules: MessageFns = { + encode(message: RefreshedRules, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + if (message.globalClineRulesToggles !== undefined) { + ClineRulesToggles.encode(message.globalClineRulesToggles, writer.uint32(10).fork()).join() + } + if (message.localClineRulesToggles !== undefined) { + ClineRulesToggles.encode(message.localClineRulesToggles, writer.uint32(18).fork()).join() + } + if (message.localCursorRulesToggles !== undefined) { + ClineRulesToggles.encode(message.localCursorRulesToggles, writer.uint32(26).fork()).join() + } + if (message.localWindsurfRulesToggles !== undefined) { + ClineRulesToggles.encode(message.localWindsurfRulesToggles, writer.uint32(34).fork()).join() + } + if (message.workflowToggles !== undefined) { + ClineRulesToggles.encode(message.workflowToggles, writer.uint32(42).fork()).join() + } + return writer + }, + + decode(input: BinaryReader | Uint8Array, length?: number): RefreshedRules { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input) + let end = length === undefined ? reader.len : reader.pos + length + const message = createBaseRefreshedRules() + while (reader.pos < end) { + const tag = reader.uint32() + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break + } + + message.globalClineRulesToggles = ClineRulesToggles.decode(reader, reader.uint32()) + continue + } + case 2: { + if (tag !== 18) { + break + } + + message.localClineRulesToggles = ClineRulesToggles.decode(reader, reader.uint32()) + continue + } + case 3: { + if (tag !== 26) { + break + } + + message.localCursorRulesToggles = ClineRulesToggles.decode(reader, reader.uint32()) + continue + } + case 4: { + if (tag !== 34) { + break + } + + message.localWindsurfRulesToggles = ClineRulesToggles.decode(reader, reader.uint32()) + continue + } + case 5: { + if (tag !== 42) { + break + } + + message.workflowToggles = ClineRulesToggles.decode(reader, reader.uint32()) + continue + } + } + if ((tag & 7) === 4 || tag === 0) { + break + } + reader.skip(tag & 7) + } + return message + }, + + fromJSON(object: any): RefreshedRules { + return { + globalClineRulesToggles: isSet(object.globalClineRulesToggles) + ? ClineRulesToggles.fromJSON(object.globalClineRulesToggles) + : undefined, + localClineRulesToggles: isSet(object.localClineRulesToggles) + ? ClineRulesToggles.fromJSON(object.localClineRulesToggles) + : undefined, + localCursorRulesToggles: isSet(object.localCursorRulesToggles) + ? ClineRulesToggles.fromJSON(object.localCursorRulesToggles) + : undefined, + localWindsurfRulesToggles: isSet(object.localWindsurfRulesToggles) + ? ClineRulesToggles.fromJSON(object.localWindsurfRulesToggles) + : undefined, + workflowToggles: isSet(object.workflowToggles) ? ClineRulesToggles.fromJSON(object.workflowToggles) : undefined, + } + }, + + toJSON(message: RefreshedRules): unknown { + const obj: any = {} + if (message.globalClineRulesToggles !== undefined) { + obj.globalClineRulesToggles = ClineRulesToggles.toJSON(message.globalClineRulesToggles) + } + if (message.localClineRulesToggles !== undefined) { + obj.localClineRulesToggles = ClineRulesToggles.toJSON(message.localClineRulesToggles) + } + if (message.localCursorRulesToggles !== undefined) { + obj.localCursorRulesToggles = ClineRulesToggles.toJSON(message.localCursorRulesToggles) + } + if (message.localWindsurfRulesToggles !== undefined) { + obj.localWindsurfRulesToggles = ClineRulesToggles.toJSON(message.localWindsurfRulesToggles) + } + if (message.workflowToggles !== undefined) { + obj.workflowToggles = ClineRulesToggles.toJSON(message.workflowToggles) + } + return obj + }, + + create, I>>(base?: I): RefreshedRules { + return RefreshedRules.fromPartial(base ?? ({} as any)) + }, + fromPartial, I>>(object: I): RefreshedRules { + const message = createBaseRefreshedRules() + message.globalClineRulesToggles = + object.globalClineRulesToggles !== undefined && object.globalClineRulesToggles !== null + ? ClineRulesToggles.fromPartial(object.globalClineRulesToggles) + : undefined + message.localClineRulesToggles = + object.localClineRulesToggles !== undefined && object.localClineRulesToggles !== null + ? ClineRulesToggles.fromPartial(object.localClineRulesToggles) + : undefined + message.localCursorRulesToggles = + object.localCursorRulesToggles !== undefined && object.localCursorRulesToggles !== null + ? ClineRulesToggles.fromPartial(object.localCursorRulesToggles) + : undefined + message.localWindsurfRulesToggles = + object.localWindsurfRulesToggles !== undefined && object.localWindsurfRulesToggles !== null + ? ClineRulesToggles.fromPartial(object.localWindsurfRulesToggles) + : undefined + message.workflowToggles = + object.workflowToggles !== undefined && object.workflowToggles !== null + ? ClineRulesToggles.fromPartial(object.workflowToggles) + : undefined + return message + }, +} + function createBaseToggleWindsurfRuleRequest(): ToggleWindsurfRuleRequest { return { metadata: undefined, rulePath: "", enabled: false } } @@ -1604,6 +1766,15 @@ export const FileServiceDefinition = { responseStream: false, options: {}, }, + /** Refreshes all rule toggles (Cline, External, and Workflows) */ + refreshRules: { + name: "refreshRules", + requestType: EmptyRequest, + requestStream: false, + responseType: RefreshedRules, + responseStream: false, + options: {}, + }, }, } as const diff --git a/src/standalone/server-setup.ts b/src/standalone/server-setup.ts index 7be6af228..960eb0a43 100644 --- a/src/standalone/server-setup.ts +++ b/src/standalone/server-setup.ts @@ -34,6 +34,7 @@ import { searchFiles } from "../core/controller/file/searchFiles" import { toggleClineRule } from "../core/controller/file/toggleClineRule" import { toggleCursorRule } from "../core/controller/file/toggleCursorRule" import { toggleWindsurfRule } from "../core/controller/file/toggleWindsurfRule" +import { refreshRules } from "../core/controller/file/refreshRules" // Mcp Service import { toggleMcpServer } from "../core/controller/mcp/toggleMcpServer" @@ -130,6 +131,7 @@ export function addServices( toggleClineRule: wrapper(toggleClineRule, controller), toggleCursorRule: wrapper(toggleCursorRule, controller), toggleWindsurfRule: wrapper(toggleWindsurfRule, controller), + refreshRules: wrapper(refreshRules, controller), }) // Mcp Service diff --git a/webview-ui/src/components/cline-rules/ClineRulesToggleModal.tsx b/webview-ui/src/components/cline-rules/ClineRulesToggleModal.tsx index b547ab2ca..41da5c85e 100644 --- a/webview-ui/src/components/cline-rules/ClineRulesToggleModal.tsx +++ b/webview-ui/src/components/cline-rules/ClineRulesToggleModal.tsx @@ -8,7 +8,8 @@ import { VSCodeButton } from "@vscode/webview-ui-toolkit/react" import RulesToggleList from "./RulesToggleList" import Tooltip from "@/components/common/Tooltip" import styled from "styled-components" -import { ClineRulesToggles, ToggleWindsurfRuleRequest } from "@shared/proto/file" +import { ClineRulesToggles, RefreshedRules, ToggleWindsurfRuleRequest } from "@shared/proto/file" +import { EmptyRequest } from "@shared/proto/common" const ClineRulesToggleModal: React.FC = () => { const { @@ -21,6 +22,7 @@ const ClineRulesToggleModal: React.FC = () => { setLocalClineRulesToggles, setLocalCursorRulesToggles, setLocalWindsurfRulesToggles, + setWorkflowToggles, } = useExtensionState() const [isVisible, setIsVisible] = useState(false) const buttonRef = useRef(null) @@ -32,7 +34,28 @@ const ClineRulesToggleModal: React.FC = () => { useEffect(() => { if (isVisible) { - vscode.postMessage({ type: "refreshClineRules" }) + FileServiceClient.refreshRules({} as EmptyRequest) + .then((response: RefreshedRules) => { + // Update state with the response data using all available setters + if (response.globalClineRulesToggles?.toggles) { + setGlobalClineRulesToggles(response.globalClineRulesToggles.toggles) + } + if (response.localClineRulesToggles?.toggles) { + setLocalClineRulesToggles(response.localClineRulesToggles.toggles) + } + if (response.localCursorRulesToggles?.toggles) { + setLocalCursorRulesToggles(response.localCursorRulesToggles.toggles) + } + if (response.localWindsurfRulesToggles?.toggles) { + setLocalWindsurfRulesToggles(response.localWindsurfRulesToggles.toggles) + } + if (response.workflowToggles?.toggles) { + setWorkflowToggles(response.workflowToggles.toggles) + } + }) + .catch((error) => { + console.error("Failed to refresh rules:", error) + }) } }, [isVisible]) diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index 5d54f751a..6091077e1 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -55,6 +55,7 @@ interface ExtensionStateContextType extends ExtensionState { setLocalClineRulesToggles: (toggles: Record) => void setLocalCursorRulesToggles: (toggles: Record) => void setLocalWindsurfRulesToggles: (toggles: Record) => void + setWorkflowToggles: (toggles: Record) => void setMcpMarketplaceCatalog: (value: McpMarketplaceCatalog) => void // Navigation state setters @@ -540,6 +541,11 @@ export const ExtensionStateContextProvider: React.FC<{ ...prevState, localWindsurfRulesToggles: toggles, })), + setWorkflowToggles: (toggles) => + setState((prevState) => ({ + ...prevState, + workflowToggles: toggles, + })), setMcpTab, }