mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
Get auto-approval working by checking state before sending asks
This commit is contained in:
parent
d480015b16
commit
93af89dbf9
10 changed files with 379 additions and 303 deletions
|
|
@ -66,7 +66,6 @@ export class Cline {
|
|||
private browserSession: BrowserSession
|
||||
private didEditFile: boolean = false
|
||||
customInstructions?: string
|
||||
alwaysAllowReadOnly: boolean
|
||||
autoApprovalSettings: AutoApprovalSettings
|
||||
apiConversationHistory: Anthropic.MessageParam[] = []
|
||||
clineMessages: ClineMessage[] = []
|
||||
|
|
@ -97,7 +96,6 @@ export class Cline {
|
|||
apiConfiguration: ApiConfiguration,
|
||||
autoApprovalSettings: AutoApprovalSettings,
|
||||
customInstructions?: string,
|
||||
alwaysAllowReadOnly?: boolean,
|
||||
task?: string,
|
||||
images?: string[],
|
||||
historyItem?: HistoryItem,
|
||||
|
|
@ -109,7 +107,6 @@ export class Cline {
|
|||
this.browserSession = new BrowserSession(provider.context)
|
||||
this.diffViewProvider = new DiffViewProvider(cwd)
|
||||
this.customInstructions = customInstructions
|
||||
this.alwaysAllowReadOnly = alwaysAllowReadOnly ?? false
|
||||
this.autoApprovalSettings = autoApprovalSettings
|
||||
if (historyItem) {
|
||||
this.taskId = historyItem.id
|
||||
|
|
@ -754,6 +751,29 @@ export class Cline {
|
|||
}
|
||||
}
|
||||
|
||||
shouldAutoApproveTool(toolName: ToolUseName): boolean {
|
||||
if (this.autoApprovalSettings.enabled) {
|
||||
switch (toolName) {
|
||||
case "read_file":
|
||||
case "list_files":
|
||||
case "list_code_definition_names":
|
||||
case "search_files":
|
||||
return this.autoApprovalSettings.actions.readFiles
|
||||
case "write_to_file":
|
||||
case "replace_in_file":
|
||||
return this.autoApprovalSettings.actions.editFiles
|
||||
case "execute_command":
|
||||
return this.autoApprovalSettings.actions.executeCommands
|
||||
case "browser_action":
|
||||
return this.autoApprovalSettings.actions.useBrowser
|
||||
case "access_mcp_resource":
|
||||
case "use_mcp_tool":
|
||||
return this.autoApprovalSettings.actions.useMcp
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
async *attemptApiRequest(previousApiReqIndex: number): ApiStream {
|
||||
// Wait for MCP servers to be connected before generating system prompt
|
||||
await pWaitFor(() => this.providerRef.deref()?.mcpHub?.isConnecting !== true, { timeout: 10_000 }).catch(() => {
|
||||
|
|
@ -1110,7 +1130,11 @@ export class Cline {
|
|||
if (block.partial) {
|
||||
// update gui message
|
||||
const partialMessage = JSON.stringify(sharedMessageProps)
|
||||
await this.ask("tool", partialMessage, block.partial).catch(() => {})
|
||||
if (this.shouldAutoApproveTool(block.name)) {
|
||||
await this.say("tool", partialMessage, undefined, block.partial)
|
||||
} else {
|
||||
await this.ask("tool", partialMessage, block.partial).catch(() => {})
|
||||
}
|
||||
// update editor
|
||||
if (!this.diffViewProvider.isEditing) {
|
||||
// open the editor and prepare to stream content in
|
||||
|
|
@ -1165,11 +1189,17 @@ export class Cline {
|
|||
// )
|
||||
// : undefined,
|
||||
} satisfies ClineSayTool)
|
||||
const didApprove = await askApproval("tool", completeMessage)
|
||||
if (!didApprove) {
|
||||
await this.diffViewProvider.revertChanges()
|
||||
break
|
||||
|
||||
if (this.shouldAutoApproveTool(block.name)) {
|
||||
await this.say("tool", completeMessage, undefined, false)
|
||||
} else {
|
||||
const didApprove = await askApproval("tool", completeMessage)
|
||||
if (!didApprove) {
|
||||
await this.diffViewProvider.revertChanges()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const { newProblemsMessage, userEdits, finalContent } =
|
||||
await this.diffViewProvider.saveChanges()
|
||||
this.didEditFile = true // used to determine if we should wait for busy terminal to update before sending api request
|
||||
|
|
@ -1224,7 +1254,7 @@ export class Cline {
|
|||
...sharedMessageProps,
|
||||
content: undefined,
|
||||
} satisfies ClineSayTool)
|
||||
if (this.alwaysAllowReadOnly) {
|
||||
if (this.shouldAutoApproveTool(block.name)) {
|
||||
await this.say("tool", partialMessage, undefined, block.partial)
|
||||
} else {
|
||||
await this.ask("tool", partialMessage, block.partial).catch(() => {})
|
||||
|
|
@ -1242,7 +1272,7 @@ export class Cline {
|
|||
...sharedMessageProps,
|
||||
content: absolutePath,
|
||||
} satisfies ClineSayTool)
|
||||
if (this.alwaysAllowReadOnly) {
|
||||
if (this.shouldAutoApproveTool(block.name)) {
|
||||
await this.say("tool", completeMessage, undefined, false) // need to be sending partialValue bool, since undefined has its own purpose in that the message is treated neither as a partial or completion of a partial, but as a single complete message
|
||||
} else {
|
||||
const didApprove = await askApproval("tool", completeMessage)
|
||||
|
|
@ -1274,7 +1304,7 @@ export class Cline {
|
|||
...sharedMessageProps,
|
||||
content: "",
|
||||
} satisfies ClineSayTool)
|
||||
if (this.alwaysAllowReadOnly) {
|
||||
if (this.shouldAutoApproveTool(block.name)) {
|
||||
await this.say("tool", partialMessage, undefined, block.partial)
|
||||
} else {
|
||||
await this.ask("tool", partialMessage, block.partial).catch(() => {})
|
||||
|
|
@ -1294,7 +1324,7 @@ export class Cline {
|
|||
...sharedMessageProps,
|
||||
content: result,
|
||||
} satisfies ClineSayTool)
|
||||
if (this.alwaysAllowReadOnly) {
|
||||
if (this.shouldAutoApproveTool(block.name)) {
|
||||
await this.say("tool", completeMessage, undefined, false)
|
||||
} else {
|
||||
const didApprove = await askApproval("tool", completeMessage)
|
||||
|
|
@ -1322,7 +1352,7 @@ export class Cline {
|
|||
...sharedMessageProps,
|
||||
content: "",
|
||||
} satisfies ClineSayTool)
|
||||
if (this.alwaysAllowReadOnly) {
|
||||
if (this.shouldAutoApproveTool(block.name)) {
|
||||
await this.say("tool", partialMessage, undefined, block.partial)
|
||||
} else {
|
||||
await this.ask("tool", partialMessage, block.partial).catch(() => {})
|
||||
|
|
@ -1343,7 +1373,7 @@ export class Cline {
|
|||
...sharedMessageProps,
|
||||
content: result,
|
||||
} satisfies ClineSayTool)
|
||||
if (this.alwaysAllowReadOnly) {
|
||||
if (this.shouldAutoApproveTool(block.name)) {
|
||||
await this.say("tool", completeMessage, undefined, false)
|
||||
} else {
|
||||
const didApprove = await askApproval("tool", completeMessage)
|
||||
|
|
@ -1375,7 +1405,7 @@ export class Cline {
|
|||
...sharedMessageProps,
|
||||
content: "",
|
||||
} satisfies ClineSayTool)
|
||||
if (this.alwaysAllowReadOnly) {
|
||||
if (this.shouldAutoApproveTool(block.name)) {
|
||||
await this.say("tool", partialMessage, undefined, block.partial)
|
||||
} else {
|
||||
await this.ask("tool", partialMessage, block.partial).catch(() => {})
|
||||
|
|
@ -1399,7 +1429,7 @@ export class Cline {
|
|||
...sharedMessageProps,
|
||||
content: results,
|
||||
} satisfies ClineSayTool)
|
||||
if (this.alwaysAllowReadOnly) {
|
||||
if (this.shouldAutoApproveTool(block.name)) {
|
||||
await this.say("tool", completeMessage, undefined, false)
|
||||
} else {
|
||||
const didApprove = await askApproval("tool", completeMessage)
|
||||
|
|
@ -1434,11 +1464,20 @@ export class Cline {
|
|||
try {
|
||||
if (block.partial) {
|
||||
if (action === "launch") {
|
||||
await this.ask(
|
||||
"browser_action_launch",
|
||||
removeClosingTag("url", url),
|
||||
block.partial,
|
||||
).catch(() => {})
|
||||
if (this.shouldAutoApproveTool(block.name)) {
|
||||
await this.say(
|
||||
"browser_action_launch",
|
||||
removeClosingTag("url", url),
|
||||
undefined,
|
||||
block.partial,
|
||||
)
|
||||
} else {
|
||||
await this.ask(
|
||||
"browser_action_launch",
|
||||
removeClosingTag("url", url),
|
||||
block.partial,
|
||||
).catch(() => {})
|
||||
}
|
||||
} else {
|
||||
await this.say(
|
||||
"browser_action",
|
||||
|
|
@ -1464,9 +1503,14 @@ export class Cline {
|
|||
break
|
||||
}
|
||||
this.consecutiveMistakeCount = 0
|
||||
const didApprove = await askApproval("browser_action_launch", url)
|
||||
if (!didApprove) {
|
||||
break
|
||||
|
||||
if (this.shouldAutoApproveTool(block.name)) {
|
||||
await this.say("browser_action_launch", url, undefined, false)
|
||||
} else {
|
||||
const didApprove = await askApproval("browser_action_launch", url)
|
||||
if (!didApprove) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: it's okay that we call this message since the partial inspect_site is finished streaming. The only scenario we have to avoid is sending messages WHILE a partial message exists at the end of the messages array. For example the api_req_finished message would interfere with the partial message, so we needed to remove that.
|
||||
|
|
@ -1568,9 +1612,20 @@ export class Cline {
|
|||
|
||||
try {
|
||||
if (block.partial) {
|
||||
await this.ask("command", removeClosingTag("command", command), block.partial).catch(
|
||||
() => {},
|
||||
)
|
||||
if (this.shouldAutoApproveTool(block.name)) {
|
||||
await this.say(
|
||||
"command",
|
||||
removeClosingTag("command", command),
|
||||
undefined,
|
||||
block.partial,
|
||||
).catch(() => {})
|
||||
} else {
|
||||
await this.ask(
|
||||
"command",
|
||||
removeClosingTag("command", command),
|
||||
block.partial,
|
||||
).catch(() => {})
|
||||
}
|
||||
break
|
||||
} else {
|
||||
if (!command) {
|
||||
|
|
@ -1591,13 +1646,20 @@ export class Cline {
|
|||
break
|
||||
}
|
||||
this.consecutiveMistakeCount = 0
|
||||
const didApprove = await askApproval(
|
||||
"command",
|
||||
command + `${requiresApproval ? COMMAND_REQ_APP_STRING : ""}`, // ugly hack until we refactor combineCommandSequences
|
||||
)
|
||||
if (!didApprove) {
|
||||
break
|
||||
|
||||
if (!requiresApproval && this.shouldAutoApproveTool(block.name)) {
|
||||
await this.say("command", command, undefined, false)
|
||||
} else {
|
||||
const didApprove = await askApproval(
|
||||
"command",
|
||||
command +
|
||||
`${this.shouldAutoApproveTool(block.name) && requiresApproval ? COMMAND_REQ_APP_STRING : ""}`, // ugly hack until we refactor combineCommandSequences
|
||||
)
|
||||
if (!didApprove) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const [userRejected, result] = await this.executeCommandTool(command)
|
||||
if (userRejected) {
|
||||
this.didRejectTool = true
|
||||
|
|
@ -1622,7 +1684,13 @@ export class Cline {
|
|||
toolName: removeClosingTag("tool_name", tool_name),
|
||||
arguments: removeClosingTag("arguments", mcp_arguments),
|
||||
} satisfies ClineAskUseMcpServer)
|
||||
await this.ask("use_mcp_server", partialMessage, block.partial).catch(() => {})
|
||||
|
||||
if (this.shouldAutoApproveTool(block.name)) {
|
||||
await this.say("use_mcp_server", partialMessage, undefined, block.partial)
|
||||
} else {
|
||||
await this.ask("use_mcp_server", partialMessage, block.partial).catch(() => {})
|
||||
}
|
||||
|
||||
break
|
||||
} else {
|
||||
if (!server_name) {
|
||||
|
|
@ -1670,10 +1738,16 @@ export class Cline {
|
|||
toolName: tool_name,
|
||||
arguments: mcp_arguments,
|
||||
} satisfies ClineAskUseMcpServer)
|
||||
const didApprove = await askApproval("use_mcp_server", completeMessage)
|
||||
if (!didApprove) {
|
||||
break
|
||||
|
||||
if (this.shouldAutoApproveTool(block.name)) {
|
||||
await this.say("use_mcp_server", completeMessage, undefined, false)
|
||||
} else {
|
||||
const didApprove = await askApproval("use_mcp_server", completeMessage)
|
||||
if (!didApprove) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// now execute the tool
|
||||
await this.say("mcp_server_request_started") // same as browser_action_result
|
||||
const toolResult = await this.providerRef
|
||||
|
|
@ -1715,7 +1789,13 @@ export class Cline {
|
|||
serverName: removeClosingTag("server_name", server_name),
|
||||
uri: removeClosingTag("uri", uri),
|
||||
} satisfies ClineAskUseMcpServer)
|
||||
await this.ask("use_mcp_server", partialMessage, block.partial).catch(() => {})
|
||||
|
||||
if (this.shouldAutoApproveTool(block.name)) {
|
||||
await this.say("use_mcp_server", partialMessage, undefined, block.partial)
|
||||
} else {
|
||||
await this.ask("use_mcp_server", partialMessage, block.partial).catch(() => {})
|
||||
}
|
||||
|
||||
break
|
||||
} else {
|
||||
if (!server_name) {
|
||||
|
|
@ -1738,10 +1818,16 @@ export class Cline {
|
|||
serverName: server_name,
|
||||
uri,
|
||||
} satisfies ClineAskUseMcpServer)
|
||||
const didApprove = await askApproval("use_mcp_server", completeMessage)
|
||||
if (!didApprove) {
|
||||
break
|
||||
|
||||
if (this.shouldAutoApproveTool(block.name)) {
|
||||
await this.say("use_mcp_server", completeMessage, undefined, false)
|
||||
} else {
|
||||
const didApprove = await askApproval("use_mcp_server", completeMessage)
|
||||
if (!didApprove) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// now execute the tool
|
||||
await this.say("mcp_server_request_started")
|
||||
const resourceResult = await this.providerRef
|
||||
|
|
@ -1824,6 +1910,7 @@ export class Cline {
|
|||
// remove the previous partial attempt_completion ask, replace with say, post state to webview, then stream command
|
||||
|
||||
// const secondLastMessage = this.clineMessages.at(-2)
|
||||
// NOTE: we do not want to auto approve a command run as part of the attempt_completion tool
|
||||
if (lastMessage && lastMessage.ask === "command") {
|
||||
// update command
|
||||
await this.ask(
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@ type GlobalStateKey =
|
|||
| "vertexRegion"
|
||||
| "lastShownAnnouncementId"
|
||||
| "customInstructions"
|
||||
| "alwaysAllowReadOnly"
|
||||
| "taskHistory"
|
||||
| "openAiBaseUrl"
|
||||
| "openAiModelId"
|
||||
|
|
@ -200,29 +199,18 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
|
||||
async initClineWithTask(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 { apiConfiguration, customInstructions, alwaysAllowReadOnly, autoApprovalSettings } =
|
||||
await this.getState()
|
||||
this.cline = new Cline(
|
||||
this,
|
||||
apiConfiguration,
|
||||
autoApprovalSettings,
|
||||
customInstructions,
|
||||
alwaysAllowReadOnly,
|
||||
task,
|
||||
images,
|
||||
)
|
||||
const { apiConfiguration, customInstructions, autoApprovalSettings } = await this.getState()
|
||||
this.cline = new Cline(this, apiConfiguration, autoApprovalSettings, customInstructions, task, images)
|
||||
}
|
||||
|
||||
async initClineWithHistoryItem(historyItem: HistoryItem) {
|
||||
await this.clearTask()
|
||||
const { apiConfiguration, customInstructions, alwaysAllowReadOnly, autoApprovalSettings } =
|
||||
await this.getState()
|
||||
const { apiConfiguration, customInstructions, autoApprovalSettings } = await this.getState()
|
||||
this.cline = new Cline(
|
||||
this,
|
||||
apiConfiguration,
|
||||
autoApprovalSettings,
|
||||
customInstructions,
|
||||
alwaysAllowReadOnly,
|
||||
undefined,
|
||||
undefined,
|
||||
historyItem,
|
||||
|
|
@ -426,13 +414,6 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
case "customInstructions":
|
||||
await this.updateCustomInstructions(message.text)
|
||||
break
|
||||
case "alwaysAllowReadOnly":
|
||||
await this.updateGlobalState("alwaysAllowReadOnly", message.bool ?? undefined)
|
||||
if (this.cline) {
|
||||
this.cline.alwaysAllowReadOnly = message.bool ?? false
|
||||
}
|
||||
await this.postStateToWebview()
|
||||
break
|
||||
case "autoApprovalSettings":
|
||||
if (message.autoApprovalSettings) {
|
||||
await this.updateGlobalState("autoApprovalSettings", message.autoApprovalSettings)
|
||||
|
|
@ -846,19 +827,12 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
}
|
||||
|
||||
async getStateToPostToWebview() {
|
||||
const {
|
||||
apiConfiguration,
|
||||
lastShownAnnouncementId,
|
||||
customInstructions,
|
||||
alwaysAllowReadOnly,
|
||||
taskHistory,
|
||||
autoApprovalSettings,
|
||||
} = await this.getState()
|
||||
const { apiConfiguration, lastShownAnnouncementId, customInstructions, taskHistory, autoApprovalSettings } =
|
||||
await this.getState()
|
||||
return {
|
||||
version: this.context.extension?.packageJSON?.version ?? "",
|
||||
apiConfiguration,
|
||||
customInstructions,
|
||||
alwaysAllowReadOnly,
|
||||
uriScheme: vscode.env.uriScheme,
|
||||
clineMessages: this.cline?.clineMessages || [],
|
||||
taskHistory: (taskHistory || []).filter((item) => item.ts && item.task).sort((a, b) => b.ts - a.ts),
|
||||
|
|
@ -946,7 +920,6 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
openRouterModelInfo,
|
||||
lastShownAnnouncementId,
|
||||
customInstructions,
|
||||
alwaysAllowReadOnly,
|
||||
taskHistory,
|
||||
autoApprovalSettings,
|
||||
] = await Promise.all([
|
||||
|
|
@ -976,7 +949,6 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
this.getGlobalState("openRouterModelInfo") as Promise<ModelInfo | 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>,
|
||||
this.getGlobalState("autoApprovalSettings") as Promise<AutoApprovalSettings | undefined>,
|
||||
])
|
||||
|
|
@ -1024,7 +996,6 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||
},
|
||||
lastShownAnnouncementId,
|
||||
customInstructions,
|
||||
alwaysAllowReadOnly: alwaysAllowReadOnly ?? false,
|
||||
taskHistory,
|
||||
autoApprovalSettings: autoApprovalSettings || DEFAULT_AUTO_APPROVAL_SETTINGS, // default value can be 0 or empty string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ export interface ExtensionState {
|
|||
version: string
|
||||
apiConfiguration?: ApiConfiguration
|
||||
customInstructions?: string
|
||||
alwaysAllowReadOnly?: boolean
|
||||
uriScheme?: string
|
||||
clineMessages: ClineMessage[]
|
||||
taskHistory: HistoryItem[]
|
||||
|
|
@ -82,13 +81,16 @@ export type ClineSay =
|
|||
| "user_feedback"
|
||||
| "user_feedback_diff"
|
||||
| "api_req_retried"
|
||||
| "command"
|
||||
| "command_output"
|
||||
| "tool"
|
||||
| "shell_integration_warning"
|
||||
| "browser_action_launch"
|
||||
| "browser_action"
|
||||
| "browser_action_result"
|
||||
| "mcp_server_request_started"
|
||||
| "mcp_server_response"
|
||||
| "use_mcp_server"
|
||||
|
||||
export interface ClineSayTool {
|
||||
tool:
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ export interface WebviewMessage {
|
|||
type:
|
||||
| "apiConfiguration"
|
||||
| "customInstructions"
|
||||
| "alwaysAllowReadOnly"
|
||||
| "webviewDidLaunch"
|
||||
| "newTask"
|
||||
| "askResponse"
|
||||
|
|
|
|||
|
|
@ -25,13 +25,13 @@ export function combineCommandSequences(messages: ClineMessage[]): ClineMessage[
|
|||
|
||||
// First pass: combine commands with their outputs
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
if (messages[i].type === "ask" && messages[i].ask === "command") {
|
||||
if (messages[i].type === "ask" && (messages[i].ask === "command" || messages[i].say === "command")) {
|
||||
let combinedText = messages[i].text || ""
|
||||
let didAddOutput = false
|
||||
let j = i + 1
|
||||
|
||||
while (j < messages.length) {
|
||||
if (messages[j].type === "ask" && messages[j].ask === "command") {
|
||||
if (messages[j].type === "ask" && (messages[j].ask === "command" || messages[j].say === "command")) {
|
||||
// Stop if we encounter the next command
|
||||
break
|
||||
}
|
||||
|
|
@ -63,7 +63,7 @@ export function combineCommandSequences(messages: ClineMessage[]): ClineMessage[
|
|||
return messages
|
||||
.filter((msg) => !(msg.ask === "command_output" || msg.say === "command_output"))
|
||||
.map((msg) => {
|
||||
if (msg.type === "ask" && msg.ask === "command") {
|
||||
if (msg.type === "ask" && (msg.ask === "command" || msg.say === "command")) {
|
||||
const combinedCommand = combinedCommands.find((cmd) => cmd.ts === msg.ts)
|
||||
return combinedCommand || msg
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
|
|||
let nextActionMessages: ClineMessage[] = []
|
||||
|
||||
messages.forEach((message) => {
|
||||
if (message.ask === "browser_action_launch") {
|
||||
if (message.ask === "browser_action_launch" || message.say === "browser_action_launch") {
|
||||
// Start first page
|
||||
currentStateMessages = [message]
|
||||
} else if (message.say === "browser_action_result") {
|
||||
|
|
@ -137,10 +137,19 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
|
|||
|
||||
// Get initial URL from launch message
|
||||
const initialUrl = useMemo(() => {
|
||||
const launchMessage = messages.find((m) => m.ask === "browser_action_launch")
|
||||
const launchMessage = messages.find(
|
||||
(m) => m.ask === "browser_action_launch" || m.say === "browser_action_launch",
|
||||
)
|
||||
return launchMessage?.text || ""
|
||||
}, [messages])
|
||||
|
||||
const isAutoApproved = useMemo(() => {
|
||||
const launchMessage = messages.find(
|
||||
(m) => m.ask === "browser_action_launch" || m.say === "browser_action_launch",
|
||||
)
|
||||
return launchMessage?.say === "browser_action_launch"
|
||||
}, [messages])
|
||||
|
||||
// Find the latest available URL and screenshot
|
||||
const latestState = useMemo(() => {
|
||||
for (let i = pages.length - 1; i >= 0; i--) {
|
||||
|
|
@ -232,7 +241,7 @@ const BrowserSessionRow = memo((props: BrowserSessionRowProps) => {
|
|||
style={{ color: "var(--vscode-foreground)", marginBottom: "-1.5px" }}></span>
|
||||
)}
|
||||
<span style={{ fontWeight: "bold" }}>
|
||||
<>Cline wants to use the browser:</>
|
||||
<>{isAutoApproved ? "Cline is using the browser:" : "Cline wants to use the browser:"}</>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
|
|
@ -418,6 +427,25 @@ const BrowserSessionRowContent = ({
|
|||
marginBottom: "10px",
|
||||
}
|
||||
|
||||
if (message.ask === "browser_action_launch" || message.say === "browser_action_launch") {
|
||||
return (
|
||||
<>
|
||||
<div style={headerStyle}>
|
||||
<span style={{ fontWeight: "bold" }}>Browser Session Started</span>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
borderRadius: 3,
|
||||
border: "1px solid var(--vscode-editorGroup-border)",
|
||||
overflow: "hidden",
|
||||
backgroundColor: CODE_BLOCK_BG_COLOR,
|
||||
}}>
|
||||
<CodeBlock source={`${"```"}shell\n${message.text}\n${"```"}`} forceWrap={true} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
switch (message.type) {
|
||||
case "say":
|
||||
switch (message.say) {
|
||||
|
|
@ -456,24 +484,6 @@ const BrowserSessionRowContent = ({
|
|||
|
||||
case "ask":
|
||||
switch (message.ask) {
|
||||
case "browser_action_launch":
|
||||
return (
|
||||
<>
|
||||
<div style={headerStyle}>
|
||||
<span style={{ fontWeight: "bold" }}>Browser Session Started</span>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
borderRadius: 3,
|
||||
border: "1px solid var(--vscode-editorGroup-border)",
|
||||
overflow: "hidden",
|
||||
backgroundColor: CODE_BLOCK_BG_COLOR,
|
||||
}}>
|
||||
<CodeBlock source={`${"```"}shell\n${message.text}\n${"```"}`} forceWrap={true} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,7 +90,9 @@ export const ChatRowContent = ({
|
|||
? lastModifiedMessage?.text
|
||||
: undefined
|
||||
const isCommandExecuting =
|
||||
isLast && lastModifiedMessage?.ask === "command" && lastModifiedMessage?.text?.includes(COMMAND_OUTPUT_STRING)
|
||||
isLast &&
|
||||
(lastModifiedMessage?.ask === "command" || lastModifiedMessage?.say === "command") &&
|
||||
lastModifiedMessage?.text?.includes(COMMAND_OUTPUT_STRING)
|
||||
|
||||
const isMcpServerResponding = isLast && lastModifiedMessage?.say === "mcp_server_request_started"
|
||||
|
||||
|
|
@ -127,7 +129,9 @@ export const ChatRowContent = ({
|
|||
style={{ color: normalColor, marginBottom: "-1.5px" }}></span>
|
||||
),
|
||||
<span style={{ color: normalColor, fontWeight: "bold" }}>
|
||||
Cline wants to execute this command:
|
||||
{message.type === "ask"
|
||||
? "Cline wants to execute this command:"
|
||||
: "Cline executed this command:"}
|
||||
</span>,
|
||||
]
|
||||
case "use_mcp_server":
|
||||
|
|
@ -141,8 +145,18 @@ export const ChatRowContent = ({
|
|||
style={{ color: normalColor, marginBottom: "-1.5px" }}></span>
|
||||
),
|
||||
<span style={{ color: normalColor, fontWeight: "bold" }}>
|
||||
Cline wants to {mcpServerUse.type === "use_mcp_tool" ? "use a tool" : "access a resource"} on
|
||||
the <code>{mcpServerUse.serverName}</code> MCP server:
|
||||
{message.type === "ask" ? (
|
||||
<>
|
||||
Cline wants to{" "}
|
||||
{mcpServerUse.type === "use_mcp_tool" ? "use a tool" : "access a resource"} on the{" "}
|
||||
<code>{mcpServerUse.serverName}</code> MCP server:
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
Cline {mcpServerUse.type === "use_mcp_tool" ? "used a tool" : "accessed a resource"} on
|
||||
the <code>{mcpServerUse.serverName}</code> MCP server:
|
||||
</>
|
||||
)}
|
||||
</span>,
|
||||
]
|
||||
case "completion_result":
|
||||
|
|
@ -217,6 +231,7 @@ export const ChatRowContent = ({
|
|||
apiReqCancelReason,
|
||||
isMcpServerResponding,
|
||||
message.text,
|
||||
message.type,
|
||||
])
|
||||
|
||||
const headerStyle: React.CSSProperties = {
|
||||
|
|
@ -253,7 +268,9 @@ export const ChatRowContent = ({
|
|||
<>
|
||||
<div style={headerStyle}>
|
||||
{toolIcon("edit")}
|
||||
<span style={{ fontWeight: "bold" }}>Cline wants to edit this file:</span>
|
||||
<span style={{ fontWeight: "bold" }}>
|
||||
{message.type === "ask" ? "Cline wants to edit this file:" : "Cline edited this file:"}
|
||||
</span>
|
||||
</div>
|
||||
<CodeAccordian
|
||||
// isLoading={message.partial}
|
||||
|
|
@ -269,7 +286,11 @@ export const ChatRowContent = ({
|
|||
<>
|
||||
<div style={headerStyle}>
|
||||
{toolIcon("new-file")}
|
||||
<span style={{ fontWeight: "bold" }}>Cline wants to create a new file:</span>
|
||||
<span style={{ fontWeight: "bold" }}>
|
||||
{message.type === "ask"
|
||||
? "Cline wants to create a new file:"
|
||||
: "Cline created a new file:"}
|
||||
</span>
|
||||
</div>
|
||||
<CodeAccordian
|
||||
isLoading={message.partial}
|
||||
|
|
@ -453,6 +474,169 @@ export const ChatRowContent = ({
|
|||
}
|
||||
}
|
||||
|
||||
if (message.ask === "command" || message.say === "command") {
|
||||
const splitMessage = (text: string) => {
|
||||
const outputIndex = text.indexOf(COMMAND_OUTPUT_STRING)
|
||||
if (outputIndex === -1) {
|
||||
return { command: text, output: "" }
|
||||
}
|
||||
return {
|
||||
command: text.slice(0, outputIndex).trim(),
|
||||
output: text
|
||||
.slice(outputIndex + COMMAND_OUTPUT_STRING.length)
|
||||
.trim()
|
||||
.split("")
|
||||
.map((char) => {
|
||||
switch (char) {
|
||||
case "\t":
|
||||
return "→ "
|
||||
case "\b":
|
||||
return "⌫"
|
||||
case "\f":
|
||||
return "⏏"
|
||||
case "\v":
|
||||
return "⇳"
|
||||
default:
|
||||
return char
|
||||
}
|
||||
})
|
||||
.join(""),
|
||||
}
|
||||
}
|
||||
|
||||
const { command: rawCommand, output } = splitMessage(message.text || "")
|
||||
|
||||
const requestsApproval = rawCommand.endsWith(COMMAND_REQ_APP_STRING)
|
||||
const command = requestsApproval ? rawCommand.slice(0, -COMMAND_REQ_APP_STRING.length) : rawCommand
|
||||
|
||||
return (
|
||||
<>
|
||||
<div style={headerStyle}>
|
||||
{icon}
|
||||
{title}
|
||||
</div>
|
||||
{/* <Terminal
|
||||
rawOutput={command + (output ? "\n" + output : "")}
|
||||
shouldAllowInput={!!isCommandExecuting && output.length > 0}
|
||||
/> */}
|
||||
<div
|
||||
style={{
|
||||
borderRadius: 3,
|
||||
border: "1px solid var(--vscode-editorGroup-border)",
|
||||
overflow: "hidden",
|
||||
backgroundColor: CODE_BLOCK_BG_COLOR,
|
||||
}}>
|
||||
<CodeBlock source={`${"```"}shell\n${command}\n${"```"}`} forceWrap={true} />
|
||||
{output.length > 0 && (
|
||||
<div style={{ width: "100%" }}>
|
||||
<div
|
||||
onClick={onToggleExpand}
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "4px",
|
||||
width: "100%",
|
||||
justifyContent: "flex-start",
|
||||
cursor: "pointer",
|
||||
padding: `2px 8px ${isExpanded ? 0 : 8}px 8px`,
|
||||
}}>
|
||||
<span className={`codicon codicon-chevron-${isExpanded ? "down" : "right"}`}></span>
|
||||
<span style={{ fontSize: "0.8em" }}>Command Output</span>
|
||||
</div>
|
||||
{isExpanded && <CodeBlock source={`${"```"}shell\n${output}\n${"```"}`} />}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{requestsApproval && (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 10,
|
||||
padding: 8,
|
||||
fontSize: "12px",
|
||||
color: "var(--vscode-errorForeground)",
|
||||
}}>
|
||||
<i className="codicon codicon-warning"></i>
|
||||
<span>The model has determined this command requires explicit approval</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
if (message.ask === "use_mcp_server" || message.say === "use_mcp_server") {
|
||||
const useMcpServer = JSON.parse(message.text || "{}") as ClineAskUseMcpServer
|
||||
const server = mcpServers.find((server) => server.name === useMcpServer.serverName)
|
||||
return (
|
||||
<>
|
||||
<div style={headerStyle}>
|
||||
{icon}
|
||||
{title}
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
background: "var(--vscode-textCodeBlock-background)",
|
||||
borderRadius: "3px",
|
||||
padding: "8px 10px",
|
||||
marginTop: "8px",
|
||||
}}>
|
||||
{useMcpServer.type === "access_mcp_resource" && (
|
||||
<McpResourceRow
|
||||
item={{
|
||||
// Use the matched resource/template details, with fallbacks
|
||||
...(findMatchingResourceOrTemplate(
|
||||
useMcpServer.uri || "",
|
||||
server?.resources,
|
||||
server?.resourceTemplates,
|
||||
) || {
|
||||
name: "",
|
||||
mimeType: "",
|
||||
description: "",
|
||||
}),
|
||||
// Always use the actual URI from the request
|
||||
uri: useMcpServer.uri || "",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{useMcpServer.type === "use_mcp_tool" && (
|
||||
<>
|
||||
<McpToolRow
|
||||
tool={{
|
||||
name: useMcpServer.toolName || "",
|
||||
description:
|
||||
server?.tools?.find((tool) => tool.name === useMcpServer.toolName)
|
||||
?.description || "",
|
||||
}}
|
||||
/>
|
||||
{useMcpServer.arguments && useMcpServer.arguments !== "{}" && (
|
||||
<div style={{ marginTop: "8px" }}>
|
||||
<div
|
||||
style={{
|
||||
marginBottom: "4px",
|
||||
opacity: 0.8,
|
||||
fontSize: "12px",
|
||||
textTransform: "uppercase",
|
||||
}}>
|
||||
Arguments
|
||||
</div>
|
||||
<CodeAccordian
|
||||
code={useMcpServer.arguments}
|
||||
language="json"
|
||||
isExpanded={true}
|
||||
onToggleExpand={onToggleExpand}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
switch (message.type) {
|
||||
case "say":
|
||||
switch (message.say) {
|
||||
|
|
@ -702,166 +886,7 @@ export const ChatRowContent = ({
|
|||
<p style={{ ...pStyle, color: "var(--vscode-errorForeground)" }}>{message.text}</p>
|
||||
</>
|
||||
)
|
||||
case "command":
|
||||
const splitMessage = (text: string) => {
|
||||
const outputIndex = text.indexOf(COMMAND_OUTPUT_STRING)
|
||||
if (outputIndex === -1) {
|
||||
return { command: text, output: "" }
|
||||
}
|
||||
return {
|
||||
command: text.slice(0, outputIndex).trim(),
|
||||
output: text
|
||||
.slice(outputIndex + COMMAND_OUTPUT_STRING.length)
|
||||
.trim()
|
||||
.split("")
|
||||
.map((char) => {
|
||||
switch (char) {
|
||||
case "\t":
|
||||
return "→ "
|
||||
case "\b":
|
||||
return "⌫"
|
||||
case "\f":
|
||||
return "⏏"
|
||||
case "\v":
|
||||
return "⇳"
|
||||
default:
|
||||
return char
|
||||
}
|
||||
})
|
||||
.join(""),
|
||||
}
|
||||
}
|
||||
|
||||
const { command: rawCommand, output } = splitMessage(message.text || "")
|
||||
|
||||
const requestsApproval = rawCommand.endsWith(COMMAND_REQ_APP_STRING)
|
||||
const command = requestsApproval ? rawCommand.slice(0, -COMMAND_REQ_APP_STRING.length) : rawCommand
|
||||
|
||||
return (
|
||||
<>
|
||||
<div style={headerStyle}>
|
||||
{icon}
|
||||
{title}
|
||||
</div>
|
||||
{/* <Terminal
|
||||
rawOutput={command + (output ? "\n" + output : "")}
|
||||
shouldAllowInput={!!isCommandExecuting && output.length > 0}
|
||||
/> */}
|
||||
<div
|
||||
style={{
|
||||
borderRadius: 3,
|
||||
border: "1px solid var(--vscode-editorGroup-border)",
|
||||
overflow: "hidden",
|
||||
backgroundColor: CODE_BLOCK_BG_COLOR,
|
||||
}}>
|
||||
<CodeBlock source={`${"```"}shell\n${command}\n${"```"}`} forceWrap={true} />
|
||||
{output.length > 0 && (
|
||||
<div style={{ width: "100%" }}>
|
||||
<div
|
||||
onClick={onToggleExpand}
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "4px",
|
||||
width: "100%",
|
||||
justifyContent: "flex-start",
|
||||
cursor: "pointer",
|
||||
padding: `2px 8px ${isExpanded ? 0 : 8}px 8px`,
|
||||
}}>
|
||||
<span
|
||||
className={`codicon codicon-chevron-${isExpanded ? "down" : "right"}`}></span>
|
||||
<span style={{ fontSize: "0.8em" }}>Command Output</span>
|
||||
</div>
|
||||
{isExpanded && <CodeBlock source={`${"```"}shell\n${output}\n${"```"}`} />}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{requestsApproval && (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 10,
|
||||
padding: 8,
|
||||
fontSize: "12px",
|
||||
color: "var(--vscode-errorForeground)",
|
||||
}}>
|
||||
<i className="codicon codicon-warning"></i>
|
||||
<span>The model has determined this command requires explicit approval</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
case "use_mcp_server":
|
||||
const useMcpServer = JSON.parse(message.text || "{}") as ClineAskUseMcpServer
|
||||
const server = mcpServers.find((server) => server.name === useMcpServer.serverName)
|
||||
return (
|
||||
<>
|
||||
<div style={headerStyle}>
|
||||
{icon}
|
||||
{title}
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
background: "var(--vscode-textCodeBlock-background)",
|
||||
borderRadius: "3px",
|
||||
padding: "8px 10px",
|
||||
marginTop: "8px",
|
||||
}}>
|
||||
{useMcpServer.type === "access_mcp_resource" && (
|
||||
<McpResourceRow
|
||||
item={{
|
||||
// Use the matched resource/template details, with fallbacks
|
||||
...(findMatchingResourceOrTemplate(
|
||||
useMcpServer.uri || "",
|
||||
server?.resources,
|
||||
server?.resourceTemplates,
|
||||
) || {
|
||||
name: "",
|
||||
mimeType: "",
|
||||
description: "",
|
||||
}),
|
||||
// Always use the actual URI from the request
|
||||
uri: useMcpServer.uri || "",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{useMcpServer.type === "use_mcp_tool" && (
|
||||
<>
|
||||
<McpToolRow
|
||||
tool={{
|
||||
name: useMcpServer.toolName || "",
|
||||
description:
|
||||
server?.tools?.find((tool) => tool.name === useMcpServer.toolName)
|
||||
?.description || "",
|
||||
}}
|
||||
/>
|
||||
{useMcpServer.arguments && useMcpServer.arguments !== "{}" && (
|
||||
<div style={{ marginTop: "8px" }}>
|
||||
<div
|
||||
style={{
|
||||
marginBottom: "4px",
|
||||
opacity: 0.8,
|
||||
fontSize: "12px",
|
||||
textTransform: "uppercase",
|
||||
}}>
|
||||
Arguments
|
||||
</div>
|
||||
<CodeAccordian
|
||||
code={useMcpServer.arguments}
|
||||
language="json"
|
||||
isExpanded={true}
|
||||
onToggleExpand={onToggleExpand}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
case "completion_result":
|
||||
if (message.text) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -52,8 +52,8 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
|||
// we need to hold on to the ask because useEffect > lastMessage will always let us know when an ask comes in and handle it, but by the time handleMessage is called, the last message might not be the ask anymore (it could be a say that followed)
|
||||
const [clineAsk, setClineAsk] = useState<ClineAsk | undefined>(undefined)
|
||||
const [enableButtons, setEnableButtons] = useState<boolean>(false)
|
||||
const [primaryButtonText, setPrimaryButtonText] = useState<string | undefined>(undefined)
|
||||
const [secondaryButtonText, setSecondaryButtonText] = useState<string | undefined>(undefined)
|
||||
const [primaryButtonText, setPrimaryButtonText] = useState<string | undefined>("Approve")
|
||||
const [secondaryButtonText, setSecondaryButtonText] = useState<string | undefined>("Reject")
|
||||
const [didClickCancel, setDidClickCancel] = useState(false)
|
||||
const virtuosoRef = useRef<VirtuosoHandle>(null)
|
||||
const [expandedRows, setExpandedRows] = useState<Record<number, boolean>>({})
|
||||
|
|
@ -186,6 +186,9 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
|||
case "text":
|
||||
case "browser_action":
|
||||
case "browser_action_result":
|
||||
case "browser_action_launch":
|
||||
case "command":
|
||||
case "use_mcp_server":
|
||||
case "command_output":
|
||||
case "mcp_server_request_started":
|
||||
case "mcp_server_response":
|
||||
|
|
@ -210,8 +213,8 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
|||
setTextAreaDisabled(false)
|
||||
setClineAsk(undefined)
|
||||
setEnableButtons(false)
|
||||
setPrimaryButtonText(undefined)
|
||||
setSecondaryButtonText(undefined)
|
||||
setPrimaryButtonText("Approve")
|
||||
setSecondaryButtonText("Reject")
|
||||
}
|
||||
}, [messages.length])
|
||||
|
||||
|
|
@ -462,7 +465,13 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
|||
return ["browser_action_launch"].includes(message.ask!)
|
||||
}
|
||||
if (message.type === "say") {
|
||||
return ["api_req_started", "text", "browser_action", "browser_action_result"].includes(message.say!)
|
||||
return [
|
||||
"browser_action_launch",
|
||||
"api_req_started",
|
||||
"text",
|
||||
"browser_action",
|
||||
"browser_action_result",
|
||||
].includes(message.say!)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
@ -481,7 +490,7 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie
|
|||
}
|
||||
|
||||
visibleMessages.forEach((message) => {
|
||||
if (message.ask === "browser_action_launch") {
|
||||
if (message.ask === "browser_action_launch" || message.say === "browser_action_launch") {
|
||||
// complete existing browser session if any
|
||||
endBrowserSession()
|
||||
// start new
|
||||
|
|
@ -881,7 +890,7 @@ const ScrollToBottomButton = styled.div`
|
|||
justify-content: center;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
height: 25px;
|
||||
height: 24px;
|
||||
|
||||
&:hover {
|
||||
background-color: color-mix(in srgb, var(--vscode-toolbar-hoverBackground) 90%, transparent);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { VSCodeButton, VSCodeCheckbox, VSCodeLink, VSCodeTextArea } from "@vscode/webview-ui-toolkit/react"
|
||||
import { VSCodeButton, VSCodeLink, VSCodeTextArea } from "@vscode/webview-ui-toolkit/react"
|
||||
import { memo, useEffect, useState } from "react"
|
||||
import { useExtensionState } from "../../context/ExtensionStateContext"
|
||||
import { validateApiConfiguration, validateModelId } from "../../utils/validate"
|
||||
|
|
@ -12,15 +12,8 @@ type SettingsViewProps = {
|
|||
}
|
||||
|
||||
const SettingsView = ({ onDone }: SettingsViewProps) => {
|
||||
const {
|
||||
apiConfiguration,
|
||||
version,
|
||||
customInstructions,
|
||||
setCustomInstructions,
|
||||
alwaysAllowReadOnly,
|
||||
setAlwaysAllowReadOnly,
|
||||
openRouterModels,
|
||||
} = useExtensionState()
|
||||
const { apiConfiguration, version, customInstructions, setCustomInstructions, openRouterModels } =
|
||||
useExtensionState()
|
||||
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
|
||||
const [modelIdErrorMessage, setModelIdErrorMessage] = useState<string | undefined>(undefined)
|
||||
const handleSubmit = () => {
|
||||
|
|
@ -32,7 +25,6 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|||
if (!apiValidationResult && !modelIdValidationResult) {
|
||||
vscode.postMessage({ type: "apiConfiguration", apiConfiguration })
|
||||
vscode.postMessage({ type: "customInstructions", text: customInstructions })
|
||||
vscode.postMessage({ type: "alwaysAllowReadOnly", bool: alwaysAllowReadOnly })
|
||||
onDone()
|
||||
}
|
||||
}
|
||||
|
|
@ -113,23 +105,6 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: 5 }}>
|
||||
<VSCodeCheckbox
|
||||
checked={alwaysAllowReadOnly}
|
||||
onChange={(e: any) => setAlwaysAllowReadOnly(e.target.checked)}>
|
||||
<span style={{ fontWeight: "500" }}>Always approve read-only operations</span>
|
||||
</VSCodeCheckbox>
|
||||
<p
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
marginTop: "5px",
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
}}>
|
||||
When enabled, Cline will automatically view directory contents and read files without requiring
|
||||
you to click the Approve button.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{IS_DEV && (
|
||||
<>
|
||||
<div style={{ marginTop: "10px", marginBottom: "4px" }}>Debug</div>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ interface ExtensionStateContextType extends ExtensionState {
|
|||
filePaths: string[]
|
||||
setApiConfiguration: (config: ApiConfiguration) => void
|
||||
setCustomInstructions: (value?: string) => void
|
||||
setAlwaysAllowReadOnly: (value: boolean) => void
|
||||
setShowAnnouncement: (value: boolean) => void
|
||||
}
|
||||
|
||||
|
|
@ -123,7 +122,6 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
|||
filePaths,
|
||||
setApiConfiguration: (value) => setState((prevState) => ({ ...prevState, apiConfiguration: value })),
|
||||
setCustomInstructions: (value) => setState((prevState) => ({ ...prevState, customInstructions: value })),
|
||||
setAlwaysAllowReadOnly: (value) => setState((prevState) => ({ ...prevState, alwaysAllowReadOnly: value })),
|
||||
setShowAnnouncement: (value) => setState((prevState) => ({ ...prevState, shouldShowAnnouncement: value })),
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue