diff --git a/.changeset/eight-ears-flash.md b/.changeset/eight-ears-flash.md new file mode 100644 index 0000000000..efea297d67 --- /dev/null +++ b/.changeset/eight-ears-flash.md @@ -0,0 +1,5 @@ +--- +"claude-dev": minor +--- + +Add a delete button to the Cline Rules modal to delete rule files diff --git a/src/core/context/instructions/user-instructions/cline-rules.ts b/src/core/context/instructions/user-instructions/cline-rules.ts index 9e258157cd..3f19c54b7a 100644 --- a/src/core/context/instructions/user-instructions/cline-rules.ts +++ b/src/core/context/instructions/user-instructions/cline-rules.ts @@ -173,3 +173,49 @@ export async function refreshClineRulesToggles( localToggles: updatedLocalToggles, } } + +export async function deleteRuleFile( + context: vscode.ExtensionContext, + rulePath: string, + isGlobal: boolean, +): Promise<{ success: boolean; message: string }> { + try { + // Check if file exists + const fileExists = await fileExistsAtPath(rulePath) + if (!fileExists) { + return { + success: false, + message: `Rule file does not exist: ${rulePath}`, + } + } + + // Delete the file from disk + await fs.unlink(rulePath) + + // Get the filename for messages + const fileName = path.basename(rulePath) + + // Update the appropriate toggles + if (isGlobal) { + const toggles = ((await getGlobalState(context, "globalClineRulesToggles")) as ClineRulesToggles) || {} + delete toggles[rulePath] + await updateGlobalState(context, "globalClineRulesToggles", toggles) + } else { + const toggles = ((await getWorkspaceState(context, "localClineRulesToggles")) as ClineRulesToggles) || {} + delete toggles[rulePath] + await updateWorkspaceState(context, "localClineRulesToggles", toggles) + } + + return { + success: true, + message: `Rule file "${fileName}" deleted successfully`, + } + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + console.error(`Error deleting rule file: ${errorMessage}`, error) + return { + success: false, + message: `Failed to delete rule file.`, + } + } +} diff --git a/src/core/controller/index.ts b/src/core/controller/index.ts index 84226d20b0..93d2eccc5f 100644 --- a/src/core/controller/index.ts +++ b/src/core/controller/index.ts @@ -49,7 +49,7 @@ import { } from "../storage/state" import { Task, cwd } from "../task" import { ClineRulesToggles } from "../../shared/cline-rules" -import { refreshClineRulesToggles } from "../context/instructions/user-instructions/cline-rules" +import { deleteRuleFile, refreshClineRulesToggles } from "../context/instructions/user-instructions/cline-rules" /* https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts @@ -634,6 +634,24 @@ export class Controller { } break } + case "deleteClineRule": { + const { isGlobal, rulePath } = message + if (rulePath && typeof isGlobal === "boolean") { + const result = await deleteRuleFile(this.context, rulePath, isGlobal) + if (result.success) { + await refreshClineRulesToggles(this.context, cwd) + await this.postStateToWebview() + } else { + console.error("Failed to delete rule file:", result.message) + } + } else { + console.error("deleteClineRule: Missing or invalid parameters", { + rulePath, + isGlobal: typeof isGlobal === "boolean" ? isGlobal : `Invalid: ${typeof isGlobal}`, + }) + } + break + } case "requestTotalTasksSize": { this.refreshTotalTasksSize() break diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index 6fae550431..8c14847a89 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -82,6 +82,7 @@ export interface WebviewMessage { | "toggleFavoriteModel" | "grpc_request" | "toggleClineRule" + | "deleteClineRule" // | "relaunchChromeDebugMode" text?: string diff --git a/webview-ui/src/components/cline-rules/ClineRulesToggleModal.tsx b/webview-ui/src/components/cline-rules/ClineRulesToggleModal.tsx index ea3c66a80e..fb07f6c737 100644 --- a/webview-ui/src/components/cline-rules/ClineRulesToggleModal.tsx +++ b/webview-ui/src/components/cline-rules/ClineRulesToggleModal.tsx @@ -111,6 +111,7 @@ const ClineRulesToggleModal: React.FC = () => { toggleRule(true, rulePath, enabled)} + isGlobal={true} listGap="small" /> @@ -121,6 +122,7 @@ const ClineRulesToggleModal: React.FC = () => { toggleRule(false, rulePath, enabled)} + isGlobal={false} listGap="small" /> diff --git a/webview-ui/src/components/cline-rules/RuleRow.tsx b/webview-ui/src/components/cline-rules/RuleRow.tsx index 0db8b73042..3f43fd2d86 100644 --- a/webview-ui/src/components/cline-rules/RuleRow.tsx +++ b/webview-ui/src/components/cline-rules/RuleRow.tsx @@ -4,8 +4,9 @@ import { VSCodeButton } from "@vscode/webview-ui-toolkit/react" const RuleRow: React.FC<{ rulePath: string enabled: boolean + isGlobal: boolean toggleRule: (rulePath: string, enabled: boolean) => void -}> = ({ rulePath, enabled, toggleRule }) => { +}> = ({ rulePath, enabled, isGlobal, toggleRule }) => { // Get the filename from the path for display const displayName = rulePath.split("/").pop() || rulePath @@ -16,6 +17,14 @@ const RuleRow: React.FC<{ }) } + const handleDeleteClick = () => { + vscode.postMessage({ + type: "deleteClineRule", + rulePath: rulePath, + isGlobal: isGlobal, + }) + } + return (
+ + +
diff --git a/webview-ui/src/components/cline-rules/RulesToggleList.tsx b/webview-ui/src/components/cline-rules/RulesToggleList.tsx index 5e244bed73..930ceb2458 100644 --- a/webview-ui/src/components/cline-rules/RulesToggleList.tsx +++ b/webview-ui/src/components/cline-rules/RulesToggleList.tsx @@ -3,10 +3,12 @@ import RuleRow from "./RuleRow" const RulesToggleList = ({ rules, toggleRule, + isGlobal, listGap = "medium", }: { rules: [string, boolean][] toggleRule: (rulePath: string, enabled: boolean) => void + isGlobal: boolean listGap?: "small" | "medium" | "large" }) => { const gapClasses = { @@ -20,7 +22,7 @@ const RulesToggleList = ({ return rules.length > 0 ? (
{rules.map(([rulePath, enabled]) => ( - + ))}
) : (