mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 20:31:37 -05:00
Send tools as says when alwaysAllowReadOnly
This commit is contained in:
116
src/ClaudeDev.ts
116
src/ClaudeDev.ts
@@ -877,17 +877,25 @@ export class ClaudeDev {
|
|||||||
try {
|
try {
|
||||||
const absolutePath = path.resolve(cwd, relPath)
|
const absolutePath = path.resolve(cwd, relPath)
|
||||||
const content = await fs.readFile(absolutePath, "utf-8")
|
const content = await fs.readFile(absolutePath, "utf-8")
|
||||||
const { response, text, images } = await this.ask(
|
|
||||||
"tool",
|
const message = JSON.stringify({
|
||||||
JSON.stringify({ tool: "readFile", path: this.getReadablePath(relPath), content } as ClaudeSayTool)
|
tool: "readFile",
|
||||||
)
|
path: this.getReadablePath(relPath),
|
||||||
if (response !== "yesButtonTapped") {
|
content,
|
||||||
if (response === "messageResponse") {
|
} as ClaudeSayTool)
|
||||||
await this.say("user_feedback", text, images)
|
if (this.alwaysAllowReadOnly) {
|
||||||
return this.formatIntoToolResponse(this.formatGenericToolFeedback(text), images)
|
await this.say("tool", message)
|
||||||
|
} else {
|
||||||
|
const { response, text, images } = await this.ask("tool", message)
|
||||||
|
if (response !== "yesButtonTapped") {
|
||||||
|
if (response === "messageResponse") {
|
||||||
|
await this.say("user_feedback", text, images)
|
||||||
|
return this.formatIntoToolResponse(this.formatGenericToolFeedback(text), images)
|
||||||
|
}
|
||||||
|
return "The user denied this operation."
|
||||||
}
|
}
|
||||||
return "The user denied this operation."
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return content
|
return content
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorString = `Error reading file: ${JSON.stringify(serializeError(error))}`
|
const errorString = `Error reading file: ${JSON.stringify(serializeError(error))}`
|
||||||
@@ -911,21 +919,25 @@ export class ClaudeDev {
|
|||||||
const absolutePath = path.resolve(cwd, relDirPath)
|
const absolutePath = path.resolve(cwd, relDirPath)
|
||||||
const files = await listFiles(absolutePath, false)
|
const files = await listFiles(absolutePath, false)
|
||||||
const result = this.formatFilesList(absolutePath, files)
|
const result = this.formatFilesList(absolutePath, files)
|
||||||
const { response, text, images } = await this.ask(
|
|
||||||
"tool",
|
const message = JSON.stringify({
|
||||||
JSON.stringify({
|
tool: "listFilesTopLevel",
|
||||||
tool: "listFilesTopLevel",
|
path: this.getReadablePath(relDirPath),
|
||||||
path: this.getReadablePath(relDirPath),
|
content: result,
|
||||||
content: result,
|
} as ClaudeSayTool)
|
||||||
} as ClaudeSayTool)
|
if (this.alwaysAllowReadOnly) {
|
||||||
)
|
await this.say("tool", message)
|
||||||
if (response !== "yesButtonTapped") {
|
} else {
|
||||||
if (response === "messageResponse") {
|
const { response, text, images } = await this.ask("tool", message)
|
||||||
await this.say("user_feedback", text, images)
|
if (response !== "yesButtonTapped") {
|
||||||
return this.formatIntoToolResponse(this.formatGenericToolFeedback(text), images)
|
if (response === "messageResponse") {
|
||||||
|
await this.say("user_feedback", text, images)
|
||||||
|
return this.formatIntoToolResponse(this.formatGenericToolFeedback(text), images)
|
||||||
|
}
|
||||||
|
return "The user denied this operation."
|
||||||
}
|
}
|
||||||
return "The user denied this operation."
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorString = `Error listing files and directories: ${JSON.stringify(serializeError(error))}`
|
const errorString = `Error listing files and directories: ${JSON.stringify(serializeError(error))}`
|
||||||
@@ -951,21 +963,25 @@ export class ClaudeDev {
|
|||||||
const absolutePath = path.resolve(cwd, relDirPath)
|
const absolutePath = path.resolve(cwd, relDirPath)
|
||||||
const files = await listFiles(absolutePath, true)
|
const files = await listFiles(absolutePath, true)
|
||||||
const result = this.formatFilesList(absolutePath, files)
|
const result = this.formatFilesList(absolutePath, files)
|
||||||
const { response, text, images } = await this.ask(
|
|
||||||
"tool",
|
const message = JSON.stringify({
|
||||||
JSON.stringify({
|
tool: "listFilesRecursive",
|
||||||
tool: "listFilesRecursive",
|
path: this.getReadablePath(relDirPath),
|
||||||
path: this.getReadablePath(relDirPath),
|
content: result,
|
||||||
content: result,
|
} as ClaudeSayTool)
|
||||||
} as ClaudeSayTool)
|
if (this.alwaysAllowReadOnly) {
|
||||||
)
|
await this.say("tool", message)
|
||||||
if (response !== "yesButtonTapped") {
|
} else {
|
||||||
if (response === "messageResponse") {
|
const { response, text, images } = await this.ask("tool", message)
|
||||||
await this.say("user_feedback", text, images)
|
if (response !== "yesButtonTapped") {
|
||||||
return this.formatIntoToolResponse(this.formatGenericToolFeedback(text), images)
|
if (response === "messageResponse") {
|
||||||
|
await this.say("user_feedback", text, images)
|
||||||
|
return this.formatIntoToolResponse(this.formatGenericToolFeedback(text), images)
|
||||||
|
}
|
||||||
|
return "The user denied this operation."
|
||||||
}
|
}
|
||||||
return "The user denied this operation."
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorString = `Error listing files recursively: ${JSON.stringify(serializeError(error))}`
|
const errorString = `Error listing files recursively: ${JSON.stringify(serializeError(error))}`
|
||||||
@@ -1037,21 +1053,25 @@ export class ClaudeDev {
|
|||||||
try {
|
try {
|
||||||
const absolutePath = path.resolve(cwd, relDirPath)
|
const absolutePath = path.resolve(cwd, relDirPath)
|
||||||
const result = await parseSourceCodeForDefinitionsTopLevel(absolutePath)
|
const result = await parseSourceCodeForDefinitionsTopLevel(absolutePath)
|
||||||
const { response, text, images } = await this.ask(
|
|
||||||
"tool",
|
const message = JSON.stringify({
|
||||||
JSON.stringify({
|
tool: "viewSourceCodeDefinitionsTopLevel",
|
||||||
tool: "viewSourceCodeDefinitionsTopLevel",
|
path: this.getReadablePath(relDirPath),
|
||||||
path: this.getReadablePath(relDirPath),
|
content: result,
|
||||||
content: result,
|
} as ClaudeSayTool)
|
||||||
} as ClaudeSayTool)
|
if (this.alwaysAllowReadOnly) {
|
||||||
)
|
await this.say("tool", message)
|
||||||
if (response !== "yesButtonTapped") {
|
} else {
|
||||||
if (response === "messageResponse") {
|
const { response, text, images } = await this.ask("tool", message)
|
||||||
await this.say("user_feedback", text, images)
|
if (response !== "yesButtonTapped") {
|
||||||
return this.formatIntoToolResponse(this.formatGenericToolFeedback(text), images)
|
if (response === "messageResponse") {
|
||||||
|
await this.say("user_feedback", text, images)
|
||||||
|
return this.formatIntoToolResponse(this.formatGenericToolFeedback(text), images)
|
||||||
|
}
|
||||||
|
return "The user denied this operation."
|
||||||
}
|
}
|
||||||
return "The user denied this operation."
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorString = `Error parsing source code definitions: ${JSON.stringify(serializeError(error))}`
|
const errorString = `Error parsing source code definitions: ${JSON.stringify(serializeError(error))}`
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ export type ClaudeSay =
|
|||||||
| "user_feedback"
|
| "user_feedback"
|
||||||
| "api_req_retried"
|
| "api_req_retried"
|
||||||
| "command_output"
|
| "command_output"
|
||||||
|
| "tool"
|
||||||
|
|
||||||
export interface ClaudeSayTool {
|
export interface ClaudeSayTool {
|
||||||
tool:
|
tool:
|
||||||
|
|||||||
@@ -325,6 +325,8 @@ const ChatRow: React.FC<ChatRowProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
case "tool":
|
||||||
|
return renderTool(message, headerStyle)
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -341,123 +343,7 @@ const ChatRow: React.FC<ChatRowProps> = ({
|
|||||||
case "ask":
|
case "ask":
|
||||||
switch (message.ask) {
|
switch (message.ask) {
|
||||||
case "tool":
|
case "tool":
|
||||||
const tool = JSON.parse(message.text || "{}") as ClaudeSayTool
|
return renderTool(message, headerStyle)
|
||||||
const toolIcon = (name: string) => (
|
|
||||||
<span
|
|
||||||
className={`codicon codicon-${name}`}
|
|
||||||
style={{ color: "var(--vscode-foreground)", marginBottom: "-1.5px" }}></span>
|
|
||||||
)
|
|
||||||
|
|
||||||
switch (tool.tool) {
|
|
||||||
case "editedExistingFile":
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div style={headerStyle}>
|
|
||||||
{toolIcon("edit")}
|
|
||||||
<span style={{ fontWeight: "bold" }}>Claude wants to edit this file:</span>
|
|
||||||
</div>
|
|
||||||
<CodeBlock
|
|
||||||
diff={tool.diff!}
|
|
||||||
path={tool.path!}
|
|
||||||
syntaxHighlighterStyle={syntaxHighlighterStyle}
|
|
||||||
isExpanded={isExpanded}
|
|
||||||
onToggleExpand={onToggleExpand}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
case "newFileCreated":
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div style={headerStyle}>
|
|
||||||
{toolIcon("new-file")}
|
|
||||||
<span style={{ fontWeight: "bold" }}>
|
|
||||||
Claude wants to create a new file:
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<CodeBlock
|
|
||||||
code={tool.content!}
|
|
||||||
path={tool.path!}
|
|
||||||
syntaxHighlighterStyle={syntaxHighlighterStyle}
|
|
||||||
isExpanded={isExpanded}
|
|
||||||
onToggleExpand={onToggleExpand}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
case "readFile":
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div style={headerStyle}>
|
|
||||||
{toolIcon("file-code")}
|
|
||||||
<span style={{ fontWeight: "bold" }}>Claude wants to read this file:</span>
|
|
||||||
</div>
|
|
||||||
<CodeBlock
|
|
||||||
code={tool.content!}
|
|
||||||
path={tool.path!}
|
|
||||||
syntaxHighlighterStyle={syntaxHighlighterStyle}
|
|
||||||
isExpanded={isExpanded}
|
|
||||||
onToggleExpand={onToggleExpand}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
case "listFilesTopLevel":
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div style={headerStyle}>
|
|
||||||
{toolIcon("folder-opened")}
|
|
||||||
<span style={{ fontWeight: "bold" }}>
|
|
||||||
Claude wants to view the top level files in this directory:
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<CodeBlock
|
|
||||||
code={tool.content!}
|
|
||||||
path={tool.path!}
|
|
||||||
language="shell-session"
|
|
||||||
syntaxHighlighterStyle={syntaxHighlighterStyle}
|
|
||||||
isExpanded={isExpanded}
|
|
||||||
onToggleExpand={onToggleExpand}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
case "listFilesRecursive":
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div style={headerStyle}>
|
|
||||||
{toolIcon("folder-opened")}
|
|
||||||
<span style={{ fontWeight: "bold" }}>
|
|
||||||
Claude wants to recursively view all files in this directory:
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<CodeBlock
|
|
||||||
code={tool.content!}
|
|
||||||
path={tool.path!}
|
|
||||||
language="shell-session"
|
|
||||||
syntaxHighlighterStyle={syntaxHighlighterStyle}
|
|
||||||
isExpanded={isExpanded}
|
|
||||||
onToggleExpand={onToggleExpand}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
case "viewSourceCodeDefinitionsTopLevel":
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div style={headerStyle}>
|
|
||||||
{toolIcon("file-code")}
|
|
||||||
<span style={{ fontWeight: "bold" }}>
|
|
||||||
Claude wants to view source code definitions in files at the top level
|
|
||||||
of this directory:
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<CodeBlock
|
|
||||||
code={tool.content!}
|
|
||||||
path={tool.path!}
|
|
||||||
syntaxHighlighterStyle={syntaxHighlighterStyle}
|
|
||||||
isExpanded={isExpanded}
|
|
||||||
onToggleExpand={onToggleExpand}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case "request_limit_reached":
|
case "request_limit_reached":
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -547,6 +433,125 @@ const ChatRow: React.FC<ChatRowProps> = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const renderTool = (message: ClaudeMessage, headerStyle: React.CSSProperties) => {
|
||||||
|
const tool = JSON.parse(message.text || "{}") as ClaudeSayTool
|
||||||
|
const toolIcon = (name: string) => (
|
||||||
|
<span
|
||||||
|
className={`codicon codicon-${name}`}
|
||||||
|
style={{ color: "var(--vscode-foreground)", marginBottom: "-1.5px" }}></span>
|
||||||
|
)
|
||||||
|
|
||||||
|
switch (tool.tool) {
|
||||||
|
case "editedExistingFile":
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div style={headerStyle}>
|
||||||
|
{toolIcon("edit")}
|
||||||
|
<span style={{ fontWeight: "bold" }}>Claude wants to edit this file:</span>
|
||||||
|
</div>
|
||||||
|
<CodeBlock
|
||||||
|
diff={tool.diff!}
|
||||||
|
path={tool.path!}
|
||||||
|
syntaxHighlighterStyle={syntaxHighlighterStyle}
|
||||||
|
isExpanded={isExpanded}
|
||||||
|
onToggleExpand={onToggleExpand}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
case "newFileCreated":
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div style={headerStyle}>
|
||||||
|
{toolIcon("new-file")}
|
||||||
|
<span style={{ fontWeight: "bold" }}>Claude wants to create a new file:</span>
|
||||||
|
</div>
|
||||||
|
<CodeBlock
|
||||||
|
code={tool.content!}
|
||||||
|
path={tool.path!}
|
||||||
|
syntaxHighlighterStyle={syntaxHighlighterStyle}
|
||||||
|
isExpanded={isExpanded}
|
||||||
|
onToggleExpand={onToggleExpand}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
case "readFile":
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div style={headerStyle}>
|
||||||
|
{toolIcon("file-code")}
|
||||||
|
<span style={{ fontWeight: "bold" }}>Claude wants to read this file:</span>
|
||||||
|
</div>
|
||||||
|
<CodeBlock
|
||||||
|
code={tool.content!}
|
||||||
|
path={tool.path!}
|
||||||
|
syntaxHighlighterStyle={syntaxHighlighterStyle}
|
||||||
|
isExpanded={isExpanded}
|
||||||
|
onToggleExpand={onToggleExpand}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
case "listFilesTopLevel":
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div style={headerStyle}>
|
||||||
|
{toolIcon("folder-opened")}
|
||||||
|
<span style={{ fontWeight: "bold" }}>
|
||||||
|
Claude wants to view the top level files in this directory:
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<CodeBlock
|
||||||
|
code={tool.content!}
|
||||||
|
path={tool.path!}
|
||||||
|
language="shell-session"
|
||||||
|
syntaxHighlighterStyle={syntaxHighlighterStyle}
|
||||||
|
isExpanded={isExpanded}
|
||||||
|
onToggleExpand={onToggleExpand}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
case "listFilesRecursive":
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div style={headerStyle}>
|
||||||
|
{toolIcon("folder-opened")}
|
||||||
|
<span style={{ fontWeight: "bold" }}>
|
||||||
|
Claude wants to recursively view all files in this directory:
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<CodeBlock
|
||||||
|
code={tool.content!}
|
||||||
|
path={tool.path!}
|
||||||
|
language="shell-session"
|
||||||
|
syntaxHighlighterStyle={syntaxHighlighterStyle}
|
||||||
|
isExpanded={isExpanded}
|
||||||
|
onToggleExpand={onToggleExpand}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
case "viewSourceCodeDefinitionsTopLevel":
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div style={headerStyle}>
|
||||||
|
{toolIcon("file-code")}
|
||||||
|
<span style={{ fontWeight: "bold" }}>
|
||||||
|
Claude wants to view source code definitions in files at the top level of this
|
||||||
|
directory:
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<CodeBlock
|
||||||
|
code={tool.content!}
|
||||||
|
path={tool.path!}
|
||||||
|
syntaxHighlighterStyle={syntaxHighlighterStyle}
|
||||||
|
isExpanded={isExpanded}
|
||||||
|
onToggleExpand={onToggleExpand}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: we cannot return null as virtuoso does not support it, so we must use a separate visibleMessages array to filter out messages that should not be rendered
|
// NOTE: we cannot return null as virtuoso does not support it, so we must use a separate visibleMessages array to filter out messages that should not be rendered
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -165,10 +165,6 @@ const ChatView = ({
|
|||||||
case "say":
|
case "say":
|
||||||
// don't want to reset since there could be a "say" after an "ask" while ask is waiting for response
|
// don't want to reset since there could be a "say" after an "ask" while ask is waiting for response
|
||||||
switch (lastMessage.say) {
|
switch (lastMessage.say) {
|
||||||
case "task":
|
|
||||||
break
|
|
||||||
case "error":
|
|
||||||
break
|
|
||||||
case "api_req_started":
|
case "api_req_started":
|
||||||
if (messages.at(-2)?.ask === "command_output") {
|
if (messages.at(-2)?.ask === "command_output") {
|
||||||
// if the last ask is a command_output, and we receive an api_req_started, then that means the command has finished and we don't need input from the user anymore (in every other case, the user has to interact with input field or buttons to continue, which does the following automatically)
|
// if the last ask is a command_output, and we receive an api_req_started, then that means the command has finished and we don't need input from the user anymore (in every other case, the user has to interact with input field or buttons to continue, which does the following automatically)
|
||||||
@@ -179,13 +175,13 @@ const ChatView = ({
|
|||||||
setEnableButtons(false)
|
setEnableButtons(false)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
case "task":
|
||||||
|
case "error":
|
||||||
case "api_req_finished":
|
case "api_req_finished":
|
||||||
break
|
|
||||||
case "text":
|
case "text":
|
||||||
break
|
|
||||||
case "command_output":
|
case "command_output":
|
||||||
break
|
|
||||||
case "completion_result":
|
case "completion_result":
|
||||||
|
case "tool":
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|||||||
Reference in New Issue
Block a user