Răsfoiți Sursa

Add 'Always allow read-only' option

Saoud Rizwan 1 an în urmă
părinte
comite
12840c40ed

+ 7 - 0
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

+ 23 - 3
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)
+		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 } = await this.getState()
+		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<string>("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<ApiProvider | undefined>,
@@ -616,6 +634,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
 			this.getGlobalState("maxRequestsPerTask") as Promise<number | undefined>,
 			this.getGlobalState("lastShownAnnouncementId") as Promise<string | undefined>,
 			this.getGlobalState("customInstructions") as Promise<string | undefined>,
+			this.getGlobalState("alwaysAllowReadOnly") as Promise<boolean | undefined>,
 			this.getGlobalState("taskHistory") as Promise<HistoryItem[] | undefined>,
 		])
 
@@ -648,6 +667,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
 			maxRequestsPerTask,
 			lastShownAnnouncementId,
 			customInstructions,
+			alwaysAllowReadOnly,
 			taskHistory,
 			koduCredits,
 		}

+ 1 - 0
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[]

+ 2 - 0
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"

+ 4 - 0
webview-ui/src/App.tsx

@@ -26,6 +26,7 @@ const App: React.FC = () => {
 	const [apiConfiguration, setApiConfiguration] = useState<ApiConfiguration | undefined>(undefined)
 	const [maxRequestsPerTask, setMaxRequestsPerTask] = useState<string>("")
 	const [customInstructions, setCustomInstructions] = useState<string>("")
+	const [alwaysAllowReadOnly, setAlwaysAllowReadOnly] = useState<boolean>(false)
 	const [vscodeThemeName, setVscodeThemeName] = useState<string | undefined>(undefined)
 	const [claudeMessages, setClaudeMessages] = useState<ClaudeMessage[]>([])
 	const [taskHistory, setTaskHistory] = useState<HistoryItem[]>([])
@@ -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)}
 						/>
 					)}

+ 29 - 1
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<React.SetStateAction<string>>
 	onDone: () => void
+	alwaysAllowReadOnly: boolean
+	setAlwaysAllowReadOnly: React.Dispatch<React.SetStateAction<boolean>>
 }
 
 const SettingsView = ({
@@ -27,6 +35,8 @@ const SettingsView = ({
 	customInstructions,
 	setCustomInstructions,
 	onDone,
+	alwaysAllowReadOnly,
+	setAlwaysAllowReadOnly,
 }: SettingsViewProps) => {
 	const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
 	const [maxRequestsErrorMessage, setMaxRequestsErrorMessage] = useState<string | undefined>(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 = ({
 					/>
 				</div>
 
+				<div style={{ marginBottom: 5 }}>
+					<VSCodeCheckbox
+						checked={alwaysAllowReadOnly}
+						onChange={(e: any) => setAlwaysAllowReadOnly(e.target.checked)}>
+						<span style={{ fontWeight: "500" }}>Always allow read-only operations</span>
+					</VSCodeCheckbox>
+					<p
+						style={{
+							fontSize: "12px",
+							marginTop: "5px",
+							color: "var(--vscode-descriptionForeground)",
+						}}>
+						When enabled, Claude will automatically read files and view directories without requiring you to
+						click the Allow button.
+					</p>
+				</div>
+
 				<div style={{ marginBottom: 5 }}>
 					<VSCodeTextArea
 						value={customInstructions}