Skip to content

Commit 8e8ef7e

Browse files
committed
feat: add search_and_replace tool for batch text replacements
Adds a new native tool that allows multiple search/replace operations in a single file edit, reducing round-trips for common editing patterns. - Add search_and_replace to tool types and tool groups - Implement SearchAndReplaceTool with streaming support - Add native tool schema and parser support - Support partial matching and error recovery for individual operations
1 parent fb4f235 commit 8e8ef7e

File tree

7 files changed

+372
-1
lines changed

7 files changed

+372
-1
lines changed

packages/types/src/tool.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export const toolNames = [
2020
"write_to_file",
2121
"apply_diff",
2222
"insert_content",
23+
"search_and_replace",
2324
"search_files",
2425
"list_files",
2526
"list_code_definition_names",

src/core/assistant-message/NativeToolCallParser.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ export class NativeToolCallParser {
119119
}
120120
break
121121

122+
case "search_and_replace":
123+
if (args.path !== undefined && args.operations !== undefined && Array.isArray(args.operations)) {
124+
nativeArgs = {
125+
path: args.path,
126+
operations: args.operations,
127+
} as NativeArgsFor<TName>
128+
}
129+
break
130+
122131
case "ask_followup_question":
123132
if (args.question !== undefined && args.follow_up !== undefined) {
124133
nativeArgs = {

src/core/assistant-message/presentAssistantMessage.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { shouldUseSingleFileRead, TOOL_PROTOCOL } from "@roo-code/types"
1717
import { writeToFileTool } from "../tools/WriteToFileTool"
1818
import { applyDiffTool } from "../tools/MultiApplyDiffTool"
1919
import { insertContentTool } from "../tools/InsertContentTool"
20+
import { searchAndReplaceTool } from "../tools/SearchAndReplaceTool"
2021
import { listCodeDefinitionNamesTool } from "../tools/ListCodeDefinitionNamesTool"
2122
import { searchFilesTool } from "../tools/SearchFilesTool"
2223
import { browserActionTool } from "../tools/BrowserActionTool"
@@ -215,6 +216,8 @@ export async function presentAssistantMessage(cline: Task) {
215216
}]`
216217
case "insert_content":
217218
return `[${block.name} for '${block.params.path}']`
219+
case "search_and_replace":
220+
return `[${block.name} for '${block.params.path}']`
218221
case "list_files":
219222
return `[${block.name} for '${block.params.path}']`
220223
case "list_code_definition_names":
@@ -624,6 +627,16 @@ export async function presentAssistantMessage(cline: Task) {
624627
toolProtocol,
625628
})
626629
break
630+
case "search_and_replace":
631+
await checkpointSaveAndMark(cline)
632+
await searchAndReplaceTool.handle(cline, block as ToolUse<"search_and_replace">, {
633+
askApproval,
634+
handleError,
635+
pushToolResult,
636+
removeClosingTag,
637+
toolProtocol,
638+
})
639+
break
627640
case "read_file":
628641
// Check if this model should use the simplified single-file read tool
629642
if (shouldUseSingleFileRead(cachedModelId)) {

src/core/prompts/tools/native-tools/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import listFiles from "./list_files"
1212
import newTask from "./new_task"
1313
import { createReadFileTool } from "./read_file"
1414
import runSlashCommand from "./run_slash_command"
15+
import searchAndReplace from "./search_and_replace"
1516
import searchFiles from "./search_files"
1617
import switchMode from "./switch_mode"
1718
import updateTodoList from "./update_todo_list"
@@ -43,6 +44,7 @@ export function getNativeTools(partialReadsEnabled: boolean = true): OpenAI.Chat
4344
newTask,
4445
createReadFileTool(partialReadsEnabled),
4546
runSlashCommand,
47+
searchAndReplace,
4648
searchFiles,
4749
switchMode,
4850
updateTodoList,
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import type OpenAI from "openai"
2+
3+
const SEARCH_AND_REPLACE_DESCRIPTION = `Perform one or more text replacements in a file. This tool finds and replaces exact text matches. Use this for straightforward replacements where you know the exact text to find and replace. You can perform multiple replacements in a single call by providing an array of operations. For complex multi-line changes or when you need line number precision, use apply_diff instead.`
4+
5+
const search_and_replace = {
6+
type: "function",
7+
function: {
8+
name: "search_and_replace",
9+
description: SEARCH_AND_REPLACE_DESCRIPTION,
10+
parameters: {
11+
type: "object",
12+
properties: {
13+
path: {
14+
type: "string",
15+
description: "The path of the file to modify, relative to the current workspace directory.",
16+
},
17+
operations: {
18+
type: "array",
19+
description: "Array of search and replace operations to perform on the file.",
20+
items: {
21+
type: "object",
22+
properties: {
23+
search: {
24+
type: "string",
25+
description:
26+
"The exact text to find in the file. Must match exactly, including whitespace.",
27+
},
28+
replace: {
29+
type: "string",
30+
description: "The text to replace the search text with.",
31+
},
32+
},
33+
required: ["search", "replace"],
34+
},
35+
minItems: 1,
36+
},
37+
},
38+
required: ["path", "operations"],
39+
additionalProperties: false,
40+
},
41+
},
42+
} satisfies OpenAI.Chat.ChatCompletionTool
43+
44+
export default search_and_replace

0 commit comments

Comments
 (0)