How to Implement File Editing Using the str_replace Tool in Codebuff
The str_replace tool in Codebuff applies exact-match string replacements to files and returns a unified diff, enabling programmatic file editing through JSON payloads or the TypeScript SDK.
To implement file editing using the str_replace tool in Codebuff, you need to understand its architecture across the monorepo. The tool is defined in the common package, handled by the agent-runtime, and consumed by the CLI or custom agents. It performs exact-match replacements with optional indentation handling and generates unified diffs for verification.
Understanding the str_replace Tool Architecture
Core Components and File Locations
The implementation spans several packages in the Codebuff repository:
-
common/src/tools/params/tool/str-replace.ts– Defines the Zod schema for inputs (path,replacements) and outputs (file,message,unifiedDiff). This schema validates that each replacement containsold,new, and optionalallowMultipleboolean. -
packages/agent-runtime/src/tools/handlers/tool/str-replace.ts– Contains thehandleStrReplacefunction that orchestrates file reading, delegates to the core processor, and streams results back to the client. -
packages/agent-runtime/src/process-str-replace.ts– Houses theprocessStrReplacefunction, which performs the actual text replacement, normalizes line endings (\r\nto\n), handles indentation viatryToDoStringReplacementWithExtraIndentation, and generates diffs usingdiff.createPatch. -
cli/src/utils/implementor-helpers.ts– ProvidesconstructDiffFromReplacementsfor rendering human-readable diffs in CLI timelines. -
common/src/tools/list.ts– Registersstr_replaceandpropose_str_replaceso agents can discover and invoke them.
Data Flow Overview
When you implement file editing using the str_replace tool in Codebuff, the data flows through these stages:
-
Invocation – An agent or script creates a tool call with
cb_tool_name: "str_replace"and an input payload matching the Zod schema. -
Handling – The runtime routes the call to
handleStrReplace, which fetches the current file content (or pending edits) and callsprocessStrReplace. -
Processing –
processStrReplacenormalizes line endings, iterates over each replacement entry, and applies exact-match logic. IfallowMultipleisfalseand multiple matches exist, it returns an error. If indentation differs, it attempts fuzzy matching viatryToDoStringReplacementWithExtraIndentation. -
Diff Generation – The processor creates a unified diff using
createPatch, stripping headers to return only the hunks. -
Response – The handler streams the result back, including the new file content, success message, and
unifiedDiffstring.
Implementing str_replace in Your Code
Basic JSON Payload Structure
To invoke the tool directly, construct a JSON block following the schema defined in common/src/tools/params/tool/str-replace.ts:
{
"cb_tool_name": "str_replace",
"input": {
"path": "src/utils/logger.ts",
"replacements": [
{
"old": "export const logger = createLogger();",
"new": "export const logger = createLogger({ level: 'debug' });",
"allowMultiple": false
}
]
}
}
The allowMultiple parameter controls whether to replace all occurrences (true) or enforce a single match (false). When false, the tool returns an error if the old string appears more than once, preventing accidental mass replacements.
TypeScript SDK Implementation
For programmatic access within the Codebuff ecosystem, use the SDK's invokeTool function:
import { invokeTool } from '@codebuff/sdk';
const toolCall = {
cb_tool_name: 'str_replace' as const,
input: {
path: 'src/util/logger.ts',
replacements: [
{
old: 'export const logger = createLogger();',
new: 'export const logger = createLogger({ level: "debug" });',
allowMultiple: false,
},
],
},
};
const result = await invokeTool(toolCall);
console.log(result.unifiedDiff);
This approach routes through the runtime's handleStrReplace handler, ensuring proper file locking, diff generation, and error handling.
Previewing Changes with propose_str_replace
Before committing changes, use propose_str_replace to generate a diff without writing to disk:
{
"cb_tool_name": "propose_str_replace",
"input": {
"path": "src/api.ts",
"replacements": [
{
"old": "export function fetchData() {",
"new": "export async function fetchData() {",
"allowMultiple": false
}
]
}
}
The response includes unifiedDiff but leaves the filesystem untouched. After review, execute the identical payload with str_replace to apply the edit.
Handling Edge Cases and Validation
The processStrReplace function in packages/agent-runtime/src/process-str-replace.ts includes robust handling for common editing pitfalls:
-
Line ending normalization – Converts Windows
\r\nto Unix\ninternally, then restores the original line ending style in the output (lines 40-42). -
Indentation tolerance – When exact matches fail due to whitespace differences,
tryToDoStringReplacementWithExtraIndentationattempts to align the replacement by adding or removing leading spaces (lines 52-60). -
Multiple occurrence protection – Unless
allowMultipleis explicitlytrue, the tool errors if the search string appears more than once, preventing unintended global replacements (lines 41-46). -
No-op detection – If replacements result in identical content, the tool returns a "No change to the file" error rather than writing redundant data (lines 84-98).
Rendering Diffs for User Interface
To display edits in a CLI timeline or custom interface, use the helper from cli/src/utils/implementor-helpers.ts:
import { constructDiffFromReplacements } from '../../cli/src/utils/implementor-helpers';
const diff = constructDiffFromReplacements([
{ old: 'const x = 1;', new: 'const x = 2;' },
{ old: 'console.log("debug");', new: '' },
]);
console.log(diff);
This generates a Git-style diff view showing removed lines with - and added lines with +, suitable for terminal output or web rendering.
Summary
- The
str_replacetool in Codebuff provides atomic, exact-match file editing with built-in diff generation and validation. - Implementation requires constructing a JSON payload with
pathandreplacementsarray, or using the TypeScript SDK'sinvokeToolfunction. - The architecture spans
common/src/tools/params/tool/str-replace.tsfor schemas,packages/agent-runtime/src/process-str-replace.tsfor core logic, andcli/src/utils/implementor-helpers.tsfor UI rendering. - Use
propose_str_replacefor dry-run previews, and leverage edge-case handling like indentation tolerance and multiple-occurrence protection to ensure safe edits.
Frequently Asked Questions
What is the difference between str_replace and propose_str_replace in Codebuff?
The str_replace tool writes changes directly to the filesystem and returns a unified diff, while propose_str_replace performs the same validation and diff generation without modifying the file. Use propose_str_replace when you need user approval or want to preview changes in a multi-step workflow before committing them.
How does str_replace handle whitespace and indentation differences?
According to the source code in packages/agent-runtime/src/process-str-replace.ts, the tool first attempts an exact match including whitespace. If that fails, it calls tryToDoStringReplacementWithExtraIndentation to adjust for leading spaces or tabs, allowing the replacement to succeed even when the indentation level differs between the search string and the actual file content.
Can str_replace perform multiple replacements in a single file?
Yes, the replacements array in the input payload accepts multiple objects, each with old, new, and optional allowMultiple properties. However, for each individual replacement, allowMultiple controls whether that specific string can be replaced multiple times within the file. Setting it to false enforces a single match and returns an error if duplicates are found, preventing accidental mass replacements.
What happens if the replacement does not change the file content?
The processStrReplace function explicitly checks for no-op edits. If the new content is identical to the original after processing all replacements, it returns an error with the message "No change to the file" rather than writing redundant data to disk. This prevents empty commits and unnecessary file system operations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →