Add 'Always allow read-only' option

This commit is contained in:
Saoud Rizwan
2024-08-25 01:46:14 -04:00
parent 469898f5c2
commit 12840c40ed
6 changed files with 70 additions and 8 deletions

View File

@@ -233,6 +233,7 @@ export class ClaudeDev {
private api: ApiHandler private api: ApiHandler
private maxRequestsPerTask: number private maxRequestsPerTask: number
private customInstructions?: string private customInstructions?: string
private alwaysAllowReadOnly: boolean
private requestCount = 0 private requestCount = 0
apiConversationHistory: Anthropic.MessageParam[] = [] apiConversationHistory: Anthropic.MessageParam[] = []
claudeMessages: ClaudeMessage[] = [] claudeMessages: ClaudeMessage[] = []
@@ -249,6 +250,7 @@ export class ClaudeDev {
apiConfiguration: ApiConfiguration, apiConfiguration: ApiConfiguration,
maxRequestsPerTask?: number, maxRequestsPerTask?: number,
customInstructions?: string, customInstructions?: string,
alwaysAllowReadOnly?: boolean,
task?: string, task?: string,
images?: string[], images?: string[],
historyItem?: HistoryItem historyItem?: HistoryItem
@@ -257,6 +259,7 @@ export class ClaudeDev {
this.api = buildApiHandler(apiConfiguration) this.api = buildApiHandler(apiConfiguration)
this.maxRequestsPerTask = maxRequestsPerTask ?? DEFAULT_MAX_REQUESTS_PER_TASK this.maxRequestsPerTask = maxRequestsPerTask ?? DEFAULT_MAX_REQUESTS_PER_TASK
this.customInstructions = customInstructions this.customInstructions = customInstructions
this.alwaysAllowReadOnly = alwaysAllowReadOnly ?? false
if (historyItem) { if (historyItem) {
this.taskId = historyItem.id this.taskId = historyItem.id
@@ -281,6 +284,10 @@ export class ClaudeDev {
this.customInstructions = customInstructions this.customInstructions = customInstructions
} }
updateAlwaysAllowReadOnly(alwaysAllowReadOnly: boolean | undefined) {
this.alwaysAllowReadOnly = alwaysAllowReadOnly ?? false
}
async handleWebviewAskResponse(askResponse: ClaudeAskResponse, text?: string, images?: string[]) { async handleWebviewAskResponse(askResponse: ClaudeAskResponse, text?: string, images?: string[]) {
this.askResponse = askResponse this.askResponse = askResponse
this.askResponseText = text this.askResponseText = text

View File

@@ -26,6 +26,7 @@ type GlobalStateKey =
| "maxRequestsPerTask" | "maxRequestsPerTask"
| "lastShownAnnouncementId" | "lastShownAnnouncementId"
| "customInstructions" | "customInstructions"
| "alwaysAllowReadOnly"
| "taskHistory" | "taskHistory"
export class ClaudeDevProvider implements vscode.WebviewViewProvider { export class ClaudeDevProvider implements vscode.WebviewViewProvider {
@@ -144,18 +145,27 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
async initClaudeDevWithTask(task?: string, images?: string[]) { 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 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() const { maxRequestsPerTask, apiConfiguration, customInstructions, alwaysAllowReadOnly } = 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()
this.claudeDev = new ClaudeDev( this.claudeDev = new ClaudeDev(
this, this,
apiConfiguration, apiConfiguration,
maxRequestsPerTask, maxRequestsPerTask,
customInstructions, 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,
undefined, undefined,
historyItem historyItem
@@ -312,6 +322,11 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
this.claudeDev?.updateCustomInstructions(message.text || undefined) this.claudeDev?.updateCustomInstructions(message.text || undefined)
await this.postStateToWebview() await this.postStateToWebview()
break break
case "alwaysAllowReadOnly":
await this.updateGlobalState("alwaysAllowReadOnly", message.bool || undefined)
this.claudeDev?.updateAlwaysAllowReadOnly(message.bool || undefined)
await this.postStateToWebview()
break
case "askResponse": case "askResponse":
this.claudeDev?.handleWebviewAskResponse(message.askResponse!, message.text, message.images) this.claudeDev?.handleWebviewAskResponse(message.askResponse!, message.text, message.images)
break break
@@ -481,6 +496,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
maxRequestsPerTask, maxRequestsPerTask,
lastShownAnnouncementId, lastShownAnnouncementId,
customInstructions, customInstructions,
alwaysAllowReadOnly,
taskHistory, taskHistory,
koduCredits, koduCredits,
} = await this.getState() } = await this.getState()
@@ -491,6 +507,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
apiConfiguration, apiConfiguration,
maxRequestsPerTask, maxRequestsPerTask,
customInstructions, customInstructions,
alwaysAllowReadOnly,
themeName: vscode.workspace.getConfiguration("workbench").get<string>("colorTheme"), themeName: vscode.workspace.getConfiguration("workbench").get<string>("colorTheme"),
claudeMessages: this.claudeDev?.claudeMessages || [], claudeMessages: this.claudeDev?.claudeMessages || [],
taskHistory: (taskHistory || []).filter((item) => item.ts && item.task).sort((a, b) => b.ts - a.ts), 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, maxRequestsPerTask,
lastShownAnnouncementId, lastShownAnnouncementId,
customInstructions, customInstructions,
alwaysAllowReadOnly,
taskHistory, taskHistory,
] = await Promise.all([ ] = await Promise.all([
this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>, 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("maxRequestsPerTask") as Promise<number | undefined>,
this.getGlobalState("lastShownAnnouncementId") as Promise<string | undefined>, this.getGlobalState("lastShownAnnouncementId") as Promise<string | undefined>,
this.getGlobalState("customInstructions") 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>, this.getGlobalState("taskHistory") as Promise<HistoryItem[] | undefined>,
]) ])
@@ -648,6 +667,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider {
maxRequestsPerTask, maxRequestsPerTask,
lastShownAnnouncementId, lastShownAnnouncementId,
customInstructions, customInstructions,
alwaysAllowReadOnly,
taskHistory, taskHistory,
koduCredits, koduCredits,
} }

View File

@@ -23,6 +23,7 @@ export interface ExtensionState {
apiConfiguration?: ApiConfiguration apiConfiguration?: ApiConfiguration
maxRequestsPerTask?: number maxRequestsPerTask?: number
customInstructions?: string customInstructions?: string
alwaysAllowReadOnly?: boolean
themeName?: string themeName?: string
claudeMessages: ClaudeMessage[] claudeMessages: ClaudeMessage[]
taskHistory: HistoryItem[] taskHistory: HistoryItem[]

View File

@@ -5,6 +5,7 @@ export interface WebviewMessage {
| "apiConfiguration" | "apiConfiguration"
| "maxRequestsPerTask" | "maxRequestsPerTask"
| "customInstructions" | "customInstructions"
| "alwaysAllowReadOnly"
| "webviewDidLaunch" | "webviewDidLaunch"
| "newTask" | "newTask"
| "askResponse" | "askResponse"
@@ -23,6 +24,7 @@ export interface WebviewMessage {
askResponse?: ClaudeAskResponse askResponse?: ClaudeAskResponse
apiConfiguration?: ApiConfiguration apiConfiguration?: ApiConfiguration
images?: string[] images?: string[]
bool?: boolean
} }
export type ClaudeAskResponse = "yesButtonTapped" | "noButtonTapped" | "messageResponse" export type ClaudeAskResponse = "yesButtonTapped" | "noButtonTapped" | "messageResponse"

View File

@@ -26,6 +26,7 @@ const App: React.FC = () => {
const [apiConfiguration, setApiConfiguration] = useState<ApiConfiguration | undefined>(undefined) const [apiConfiguration, setApiConfiguration] = useState<ApiConfiguration | undefined>(undefined)
const [maxRequestsPerTask, setMaxRequestsPerTask] = useState<string>("") const [maxRequestsPerTask, setMaxRequestsPerTask] = useState<string>("")
const [customInstructions, setCustomInstructions] = useState<string>("") const [customInstructions, setCustomInstructions] = useState<string>("")
const [alwaysAllowReadOnly, setAlwaysAllowReadOnly] = useState<boolean>(false)
const [vscodeThemeName, setVscodeThemeName] = useState<string | undefined>(undefined) const [vscodeThemeName, setVscodeThemeName] = useState<string | undefined>(undefined)
const [claudeMessages, setClaudeMessages] = useState<ClaudeMessage[]>([]) const [claudeMessages, setClaudeMessages] = useState<ClaudeMessage[]>([])
const [taskHistory, setTaskHistory] = useState<HistoryItem[]>([]) const [taskHistory, setTaskHistory] = useState<HistoryItem[]>([])
@@ -52,6 +53,7 @@ const App: React.FC = () => {
message.state!.maxRequestsPerTask !== undefined ? message.state!.maxRequestsPerTask.toString() : "" message.state!.maxRequestsPerTask !== undefined ? message.state!.maxRequestsPerTask.toString() : ""
) )
setCustomInstructions(message.state!.customInstructions || "") setCustomInstructions(message.state!.customInstructions || "")
setAlwaysAllowReadOnly(message.state!.alwaysAllowReadOnly || false)
setVscodeThemeName(message.state!.themeName) setVscodeThemeName(message.state!.themeName)
setClaudeMessages(message.state!.claudeMessages) setClaudeMessages(message.state!.claudeMessages)
setTaskHistory(message.state!.taskHistory) setTaskHistory(message.state!.taskHistory)
@@ -112,6 +114,8 @@ const App: React.FC = () => {
setMaxRequestsPerTask={setMaxRequestsPerTask} setMaxRequestsPerTask={setMaxRequestsPerTask}
customInstructions={customInstructions} customInstructions={customInstructions}
setCustomInstructions={setCustomInstructions} setCustomInstructions={setCustomInstructions}
alwaysAllowReadOnly={alwaysAllowReadOnly}
setAlwaysAllowReadOnly={setAlwaysAllowReadOnly}
onDone={() => setShowSettings(false)} onDone={() => setShowSettings(false)}
/> />
)} )}

View File

@@ -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 React, { useEffect, useState } from "react"
import { ApiConfiguration } from "../../../src/shared/api" import { ApiConfiguration } from "../../../src/shared/api"
import { validateApiConfiguration, validateMaxRequestsPerTask } from "../utils/validate" import { validateApiConfiguration, validateMaxRequestsPerTask } from "../utils/validate"
@@ -15,6 +21,8 @@ type SettingsViewProps = {
customInstructions: string customInstructions: string
setCustomInstructions: React.Dispatch<React.SetStateAction<string>> setCustomInstructions: React.Dispatch<React.SetStateAction<string>>
onDone: () => void onDone: () => void
alwaysAllowReadOnly: boolean
setAlwaysAllowReadOnly: React.Dispatch<React.SetStateAction<boolean>>
} }
const SettingsView = ({ const SettingsView = ({
@@ -27,6 +35,8 @@ const SettingsView = ({
customInstructions, customInstructions,
setCustomInstructions, setCustomInstructions,
onDone, onDone,
alwaysAllowReadOnly,
setAlwaysAllowReadOnly,
}: SettingsViewProps) => { }: SettingsViewProps) => {
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined) const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
const [maxRequestsErrorMessage, setMaxRequestsErrorMessage] = 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: "apiConfiguration", apiConfiguration })
vscode.postMessage({ type: "maxRequestsPerTask", text: maxRequestsPerTask }) vscode.postMessage({ type: "maxRequestsPerTask", text: maxRequestsPerTask })
vscode.postMessage({ type: "customInstructions", text: customInstructions }) vscode.postMessage({ type: "customInstructions", text: customInstructions })
vscode.postMessage({ type: "alwaysAllowReadOnly", bool: alwaysAllowReadOnly })
onDone() onDone()
} }
} }
@@ -125,6 +136,23 @@ const SettingsView = ({
/> />
</div> </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 }}> <div style={{ marginBottom: 5 }}>
<VSCodeTextArea <VSCodeTextArea
value={customInstructions} value={customInstructions}