diff --git a/src/ClaudeDev.ts b/src/ClaudeDev.ts index 8a8f609..56ac26b 100644 --- a/src/ClaudeDev.ts +++ b/src/ClaudeDev.ts @@ -233,6 +233,7 @@ export class ClaudeDev { private api: ApiHandler private maxRequestsPerTask: number private customInstructions?: string + private alwaysAllowReadOnly: boolean private requestCount = 0 apiConversationHistory: Anthropic.MessageParam[] = [] claudeMessages: ClaudeMessage[] = [] @@ -249,6 +250,7 @@ export class ClaudeDev { apiConfiguration: ApiConfiguration, maxRequestsPerTask?: number, customInstructions?: string, + alwaysAllowReadOnly?: boolean, task?: string, images?: string[], historyItem?: HistoryItem @@ -257,6 +259,7 @@ export class ClaudeDev { this.api = buildApiHandler(apiConfiguration) this.maxRequestsPerTask = maxRequestsPerTask ?? DEFAULT_MAX_REQUESTS_PER_TASK this.customInstructions = customInstructions + this.alwaysAllowReadOnly = alwaysAllowReadOnly ?? false if (historyItem) { this.taskId = historyItem.id @@ -281,6 +284,10 @@ export class ClaudeDev { this.customInstructions = customInstructions } + updateAlwaysAllowReadOnly(alwaysAllowReadOnly: boolean | undefined) { + this.alwaysAllowReadOnly = alwaysAllowReadOnly ?? false + } + async handleWebviewAskResponse(askResponse: ClaudeAskResponse, text?: string, images?: string[]) { this.askResponse = askResponse this.askResponseText = text diff --git a/src/providers/ClaudeDevProvider.ts b/src/providers/ClaudeDevProvider.ts index e238534..cdf3c12 100644 --- a/src/providers/ClaudeDevProvider.ts +++ b/src/providers/ClaudeDevProvider.ts @@ -26,6 +26,7 @@ type GlobalStateKey = | "maxRequestsPerTask" | "lastShownAnnouncementId" | "customInstructions" + | "alwaysAllowReadOnly" | "taskHistory" export class ClaudeDevProvider implements vscode.WebviewViewProvider { @@ -144,18 +145,27 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { async initClaudeDevWithTask(task?: string, images?: string[]) { await this.clearTask() // ensures that an exising task doesn't exist before starting a new one, although this shouldn't be possible since user must clear task before starting a new one - const { maxRequestsPerTask, apiConfiguration, customInstructions } = await this.getState() - this.claudeDev = new ClaudeDev(this, apiConfiguration, maxRequestsPerTask, customInstructions, task, images) - } - - async initClaudeDevWithHistoryItem(historyItem: HistoryItem) { - await this.clearTask() - const { maxRequestsPerTask, apiConfiguration, customInstructions } = await this.getState() + const { maxRequestsPerTask, apiConfiguration, customInstructions, alwaysAllowReadOnly } = await this.getState() this.claudeDev = new ClaudeDev( this, apiConfiguration, maxRequestsPerTask, customInstructions, + alwaysAllowReadOnly, + task, + images + ) + } + + async initClaudeDevWithHistoryItem(historyItem: HistoryItem) { + await this.clearTask() + const { maxRequestsPerTask, apiConfiguration, customInstructions, alwaysAllowReadOnly } = await this.getState() + this.claudeDev = new ClaudeDev( + this, + apiConfiguration, + maxRequestsPerTask, + customInstructions, + alwaysAllowReadOnly, undefined, undefined, historyItem @@ -312,6 +322,11 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { this.claudeDev?.updateCustomInstructions(message.text || undefined) await this.postStateToWebview() break + case "alwaysAllowReadOnly": + await this.updateGlobalState("alwaysAllowReadOnly", message.bool || undefined) + this.claudeDev?.updateAlwaysAllowReadOnly(message.bool || undefined) + await this.postStateToWebview() + break case "askResponse": this.claudeDev?.handleWebviewAskResponse(message.askResponse!, message.text, message.images) break @@ -481,6 +496,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { maxRequestsPerTask, lastShownAnnouncementId, customInstructions, + alwaysAllowReadOnly, taskHistory, koduCredits, } = await this.getState() @@ -491,6 +507,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { apiConfiguration, maxRequestsPerTask, customInstructions, + alwaysAllowReadOnly, themeName: vscode.workspace.getConfiguration("workbench").get("colorTheme"), claudeMessages: this.claudeDev?.claudeMessages || [], taskHistory: (taskHistory || []).filter((item) => item.ts && item.task).sort((a, b) => b.ts - a.ts), @@ -601,6 +618,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { maxRequestsPerTask, lastShownAnnouncementId, customInstructions, + alwaysAllowReadOnly, taskHistory, ] = await Promise.all([ this.getGlobalState("apiProvider") as Promise, @@ -616,6 +634,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { this.getGlobalState("maxRequestsPerTask") as Promise, this.getGlobalState("lastShownAnnouncementId") as Promise, this.getGlobalState("customInstructions") as Promise, + this.getGlobalState("alwaysAllowReadOnly") as Promise, this.getGlobalState("taskHistory") as Promise, ]) @@ -648,6 +667,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { maxRequestsPerTask, lastShownAnnouncementId, customInstructions, + alwaysAllowReadOnly, taskHistory, koduCredits, } diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 1203d62..7ae96d4 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -23,6 +23,7 @@ export interface ExtensionState { apiConfiguration?: ApiConfiguration maxRequestsPerTask?: number customInstructions?: string + alwaysAllowReadOnly?: boolean themeName?: string claudeMessages: ClaudeMessage[] taskHistory: HistoryItem[] diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index f858817..1fd5c78 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -5,6 +5,7 @@ export interface WebviewMessage { | "apiConfiguration" | "maxRequestsPerTask" | "customInstructions" + | "alwaysAllowReadOnly" | "webviewDidLaunch" | "newTask" | "askResponse" @@ -23,6 +24,7 @@ export interface WebviewMessage { askResponse?: ClaudeAskResponse apiConfiguration?: ApiConfiguration images?: string[] + bool?: boolean } export type ClaudeAskResponse = "yesButtonTapped" | "noButtonTapped" | "messageResponse" diff --git a/webview-ui/src/App.tsx b/webview-ui/src/App.tsx index 3b7b53f..655421c 100644 --- a/webview-ui/src/App.tsx +++ b/webview-ui/src/App.tsx @@ -26,6 +26,7 @@ const App: React.FC = () => { const [apiConfiguration, setApiConfiguration] = useState(undefined) const [maxRequestsPerTask, setMaxRequestsPerTask] = useState("") const [customInstructions, setCustomInstructions] = useState("") + const [alwaysAllowReadOnly, setAlwaysAllowReadOnly] = useState(false) const [vscodeThemeName, setVscodeThemeName] = useState(undefined) const [claudeMessages, setClaudeMessages] = useState([]) const [taskHistory, setTaskHistory] = useState([]) @@ -52,6 +53,7 @@ const App: React.FC = () => { message.state!.maxRequestsPerTask !== undefined ? message.state!.maxRequestsPerTask.toString() : "" ) setCustomInstructions(message.state!.customInstructions || "") + setAlwaysAllowReadOnly(message.state!.alwaysAllowReadOnly || false) setVscodeThemeName(message.state!.themeName) setClaudeMessages(message.state!.claudeMessages) setTaskHistory(message.state!.taskHistory) @@ -112,6 +114,8 @@ const App: React.FC = () => { setMaxRequestsPerTask={setMaxRequestsPerTask} customInstructions={customInstructions} setCustomInstructions={setCustomInstructions} + alwaysAllowReadOnly={alwaysAllowReadOnly} + setAlwaysAllowReadOnly={setAlwaysAllowReadOnly} onDone={() => setShowSettings(false)} /> )} diff --git a/webview-ui/src/components/SettingsView.tsx b/webview-ui/src/components/SettingsView.tsx index 755dbde..e9d28f0 100644 --- a/webview-ui/src/components/SettingsView.tsx +++ b/webview-ui/src/components/SettingsView.tsx @@ -1,4 +1,10 @@ -import { VSCodeButton, VSCodeLink, VSCodeTextArea, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" +import { + VSCodeButton, + VSCodeLink, + VSCodeTextArea, + VSCodeTextField, + VSCodeCheckbox, +} from "@vscode/webview-ui-toolkit/react" import React, { useEffect, useState } from "react" import { ApiConfiguration } from "../../../src/shared/api" import { validateApiConfiguration, validateMaxRequestsPerTask } from "../utils/validate" @@ -15,6 +21,8 @@ type SettingsViewProps = { customInstructions: string setCustomInstructions: React.Dispatch> onDone: () => void + alwaysAllowReadOnly: boolean + setAlwaysAllowReadOnly: React.Dispatch> } const SettingsView = ({ @@ -27,6 +35,8 @@ const SettingsView = ({ customInstructions, setCustomInstructions, onDone, + alwaysAllowReadOnly, + setAlwaysAllowReadOnly, }: SettingsViewProps) => { const [apiErrorMessage, setApiErrorMessage] = useState(undefined) const [maxRequestsErrorMessage, setMaxRequestsErrorMessage] = useState(undefined) @@ -42,6 +52,7 @@ const SettingsView = ({ vscode.postMessage({ type: "apiConfiguration", apiConfiguration }) vscode.postMessage({ type: "maxRequestsPerTask", text: maxRequestsPerTask }) vscode.postMessage({ type: "customInstructions", text: customInstructions }) + vscode.postMessage({ type: "alwaysAllowReadOnly", bool: alwaysAllowReadOnly }) onDone() } } @@ -125,6 +136,23 @@ const SettingsView = ({ /> +
+ setAlwaysAllowReadOnly(e.target.checked)}> + Always allow read-only operations + +

+ When enabled, Claude will automatically read files and view directories without requiring you to + click the Allow button. +

+
+