Let the AI write, edit, and manage files for you—with powerful safety controls and surgical precision.
Consoul's file editing tools let AI models create, modify, and delete files in your project. Instead of copying code from a chat window and manually pasting it into your editor, the AI can make changes directly—and you can review them first.
Quick example:
$ consoul ask "Add error handling to calculate_total in src/utils.py"The AI will find the function, add try/catch blocks, preserve your formatting, and make the change—all without you opening an editor.
Related Tools:
- Code Search - Find code before editing
- Image Analysis - Analyze screenshots and UI
- Overview
- Available Tools
- Configuration
- Security & Permissions
- Progressive Matching
- Common Workflows
- Troubleshooting
File editing tools enable AI models to create, modify, and delete files in your project with precision and safety controls. These tools provide:
- Line-based editing: Modify specific lines or ranges with surgical precision
- Search/replace: Find and replace content with progressive matching
- Safety controls: Path validation, extension filtering, size limits
- Preview-before-edit: Dry-run mode shows diff previews without modifications
- Atomic operations: Temp file + rename prevents corruption
- Optimistic locking: Detects concurrent edits via expected_hash
Instead of manual editing:
# Manual approach
$ vim src/utils.py # Find line 42
$ # Make change manually
$ # Hope you didn't introduce syntax errorsAI-powered editing:
console.chat("Add error handling to the calculate_total function in src/utils.py")
# AI finds function, adds try/catch, preserves formattingBenefits:
- ✅ AI understands context and intent
- ✅ Preserves formatting (tabs/spaces, line endings)
- ✅ Generates diff previews for review
- ✅ Validates changes before applying
- ✅ Prevents path traversal and dangerous operations
Most file editing tools are classified as Risk Level: CAUTION, with the exception of delete_file which is Risk Level: DANGEROUS due to its destructive nature. All file operations require user approval by default.
Security layers:
- Path validation: Blocks
..traversal and absolute dangerous paths - Extension filtering: Whitelist of allowed file extensions
- Size limits: Prevents runaway edits (max bytes, max edits)
- Approval workflow: User confirms before modifications
- Audit logging: All operations logged with timestamps
Enable in configuration:
profiles:
default:
tools:
enabled: true
permission_policy: balanced # Prompts for CAUTION-level toolsBasic usage:
from consoul import Consoul
console = Consoul(tools=True)
# Let AI edit files
console.chat("Fix the typo in line 12 of README.md")
console.chat("Refactor the login function to use async/await")
console.chat("Add type hints to all functions in src/models.py")Edit specific lines or line ranges with exact precision.
Capabilities:
- Edit single lines or ranges (e.g., "5-10", "42")
- Replace multiple non-overlapping ranges in one operation
- Delete lines by providing empty content
- Insert lines by expanding ranges
- Optimistic locking via
expected_hash - Dry-run mode for previews
Risk Level: CAUTION (modifies files, requires approval)
Input Schema:
class EditFileLinesInput(BaseModel):
file_path: str # File to edit
line_edits: dict[str, str] # {"3-5": "new content", "10": "line 10"}
expected_hash: str | None # SHA256 for optimistic locking
dry_run: bool # Preview without modifying (default: False)Example:
from consoul.ai.tools.implementations import edit_file_lines
# Edit single line
result = edit_file_lines.invoke({
"file_path": "src/main.py",
"line_edits": {"42": " logger.error('Something went wrong')"}
})
# Edit multiple ranges
result = edit_file_lines.invoke({
"file_path": "src/config.py",
"line_edits": {
"1-3": "# Updated header\n# Version 2.0\n# Author: Team",
"25": "DEBUG = False"
}
})
# Preview changes (dry-run)
result = edit_file_lines.invoke({
"file_path": "src/utils.py",
"line_edits": {"10-15": "# TODO: Refactor this section"},
"dry_run": True
})
# Result contains diff preview, file unchangedResponse Format:
{
"status": "success",
"bytes_written": 1024,
"checksum": "abc123...",
"changed_lines": ["3-5", "10"],
"preview": "--- a/src/main.py\n+++ b/src/main.py\n@@ -42,1 +42,1 @@\n- print('Error')\n+ logger.error('Something went wrong')\n"
}Common Use Cases:
- Fix specific lines identified by linters
- Update version numbers or constants
- Add logging statements
- Fix syntax errors at known line numbers
- Batch update configuration values
Search for content and replace with progressive matching (strict/whitespace/fuzzy).
Capabilities:
- Strict matching: Exact text match (default)
- Whitespace tolerance: Ignores leading/trailing whitespace and indentation
- Fuzzy matching: Similarity-based matching for typos
- Tab preservation: Maintains original indentation style
- CRLF preservation: Keeps Windows line endings
- Ambiguity detection: Warns if search text appears multiple times
- Similarity suggestions: "Did you mean...?" for failed searches
Risk Level: CAUTION (modifies files, requires approval)
Input Schema:
class EditFileSearchReplaceInput(BaseModel):
file_path: str
edits: list[dict] # [{"search": "old", "replace": "new"}]
tolerance: str # "strict", "whitespace", or "fuzzy"
expected_hash: str | None
dry_run: boolExample (Strict):
from consoul.ai.tools.implementations import edit_file_search_replace
# Exact match
result = edit_file_search_replace.invoke({
"file_path": "src/models.py",
"edits": [
{"search": "class User:", "replace": "class User(BaseModel):"}
],
"tolerance": "strict"
})Example (Whitespace Tolerance):
# Ignores indentation differences - useful when search block has different indent
result = edit_file_search_replace.invoke({
"file_path": "src/app.py",
"edits": [
{
"search": "def login():\n validate()\n return True",
"replace": "async def login():\n await validate()\n return True"
}
],
"tolerance": "whitespace"
})
# Matches even if actual file has different indentation
# Preserves original tabs/spaces in replacementExample (Fuzzy Matching):
# Handles typos and minor differences
result = edit_file_search_replace.invoke({
"file_path": "src/utils.py",
"edits": [
{
"search": "def calculate_totle():", # Typo in search
"replace": "def calculate_total():"
}
],
"tolerance": "fuzzy"
})
# Matches "def calculate_total():" with 90%+ similarity
# Warning: "Fuzzy matched with 94% similarity"Response Format:
{
"status": "success",
"bytes_written": 2048,
"checksum": "def456...",
"preview": "--- a/src/models.py\n+++ b/src/models.py\n...",
"warnings": ["Fuzzy matched 'calculate_totle' → 'calculate_total' (94% similarity)"]
}Common Use Cases:
- Refactor function signatures
- Rename classes/variables
- Update import statements
- Fix typos with fuzzy matching
- Multi-line block replacements
Create new files or overwrite existing ones with safety controls.
Capabilities:
- Create files with automatic parent directory creation
- Overwrite protection (disabled by default)
- Extension validation
- Content size limits
- Dry-run preview shows "new file" diff
- Returns SHA256 checksum
Risk Level: CAUTION (creates files, requires approval)
Input Schema:
class CreateFileInput(BaseModel):
file_path: str
content: str
overwrite: bool # Allow overwriting existing files (default: False)
dry_run: boolExample:
from consoul.ai.tools.implementations import create_file
# Create new file
result = create_file.invoke({
"file_path": "src/new_feature.py",
"content": '''"""New feature module."""
def new_function():
"""Placeholder for new functionality."""
pass
'''
})
# Create with nested directories
result = create_file.invoke({
"file_path": "docs/guides/advanced/custom-tools.md",
"content": "# Custom Tools Guide\n\n..."
})
# Creates docs/guides/advanced/ directories automatically
# Preview file creation (dry-run)
result = create_file.invoke({
"file_path": "config/production.yaml",
"content": "env: production\ndebug: false\n",
"dry_run": True
})
# Shows diff: all lines marked as additions, file not createdOverwrite Control:
# Attempt to overwrite without permission (default)
result = create_file.invoke({
"file_path": "README.md", # File exists
"content": "New content",
"overwrite": False # Default
})
# Error: "File already exists and overwrite=False"
# Explicit overwrite (requires allow_overwrite in config)
result = create_file.invoke({
"file_path": "README.md",
"content": "Updated content",
"overwrite": True # Requires FileEditToolConfig.allow_overwrite=True
})Response Format:
{
"status": "success",
"bytes_written": 512,
"checksum": "789abc...",
"preview": "--- /dev/null\n+++ b/src/new_feature.py\n@@ -0,0 +1,5 @@\n+\"\"\"New feature module.\"\"\"\n+\n+def new_function():\n+..."
}Common Use Cases:
- Generate boilerplate code files
- Create configuration files
- Initialize project structure
- Generate documentation
- Create test files
Safely delete files with validation and approval.
Capabilities:
- Delete individual files (not directories)
- Path validation (blocks dangerous paths)
- Extension filtering
- Dry-run preview shows deletion diff
- Returns absolute path and timestamp
Risk Level: DANGEROUS (destructive operation, always requires approval)
Input Schema:
class DeleteFileInput(BaseModel):
file_path: str
dry_run: boolExample:
from consoul.ai.tools.implementations import delete_file
# Delete file
result = delete_file.invoke({
"file_path": "old_module.py"
})
# Preview deletion (dry-run)
result = delete_file.invoke({
"file_path": "temp/cache.json",
"dry_run": True
})
# Shows diff: all lines marked as deletions, file not deletedResponse Format:
{
"status": "deleted",
"path": "/absolute/path/to/old_module.py",
"timestamp": "2025-11-15T10:30:45.123456Z"
}Security:
# Blocked: Directory deletion
delete_file.invoke({"file_path": "src/"})
# Error: "Not a file: src/ is a directory"
# Blocked: Path traversal
delete_file.invoke({"file_path": "../../../etc/passwd"})
# Error: "Path traversal detected"
# Blocked: Extension filtering (if configured)
delete_file.invoke({"file_path": "important.db"})
# Error: "Extension .db not allowed"Common Use Cases:
- Remove deprecated files
- Clean up temporary files
- Delete generated files before regeneration
- Remove old backups
- Cleanup after refactoring
Append content to existing files or create new ones.
Capabilities:
- Append to end of existing files
- Smart newline handling (adds separator if needed)
- Create file if missing (opt-in via
create_if_missing) - CRLF/LF preservation
- Size limit validation (total file size after append)
- Dry-run preview shows appended content
Risk Level: CAUTION (modifies files, requires approval)
Input Schema:
class AppendToFileInput(BaseModel):
file_path: str
content: str
create_if_missing: bool # Create file if doesn't exist (default: True)
dry_run: boolExample:
from consoul.ai.tools.implementations import append_to_file
# Append to existing file
result = append_to_file.invoke({
"file_path": "CHANGELOG.md",
"content": "\n## v2.0.0\n\n- Added file editing tools\n- Improved security"
})
# Append with newline separator (automatic)
result = append_to_file.invoke({
"file_path": "logs/app.log",
"content": "[2025-11-15] Application started"
})
# If file doesn't end with \n, adds \n before appending
# Create new file (create_if_missing=True)
result = append_to_file.invoke({
"file_path": "notes.txt",
"content": "First note\n",
"create_if_missing": True
})
# Prevent creation (create_if_missing=False)
result = append_to_file.invoke({
"file_path": "missing.txt",
"content": "Content",
"create_if_missing": False
})
# Error: "File not found and create_if_missing=False"Newline Handling:
# File ends without newline: "line1\nline2"
append_to_file.invoke({"file_path": "test.txt", "content": "line3"})
# Result: "line1\nline2\nline3" (added separator)
# File ends with newline: "line1\nline2\n"
append_to_file.invoke({"file_path": "test.txt", "content": "line3"})
# Result: "line1\nline2\nline3" (no double newline)Response Format:
{
"status": "success",
"bytes_written": 1536,
"checksum": "bcd789...",
"preview": "--- a/CHANGELOG.md\n+++ b/CHANGELOG.md\n@@ -10,0 +10,4 @@\n+\n+## v2.0.0\n+..."
}Common Use Cases:
- Add changelog entries
- Append log entries
- Add test cases
- Extend configuration files
- Add documentation sections
Configure file editing tools via FileEditToolConfig in your profile:
profiles:
default:
tools:
enabled: true
file_edit:
# Extension filtering
allowed_extensions:
- ".py"
- ".js"
- ".ts"
- ".md"
- ".txt"
- ".json"
- ".yaml"
- ".yml"
# "" allows extensionless files (Dockerfile, Makefile)
# Path blocking
blocked_paths:
# System defaults
- "/etc/shadow"
- "/etc/passwd"
- "/proc"
- "/dev"
- "/sys"
# CRITICAL: Add secret paths (not in defaults!)
- "~/.ssh"
- "~/.aws"
- "~/.gnupg"
# Size limits
max_payload_bytes: 1048576 # 1MB max content size
max_edits: 50 # Max line edits per operation
# Overwrite control
allow_overwrite: false # Must be true to overwrite existing files
# Encoding
default_encoding: "utf-8" # File encoding for read/write| Option | Type | Default | Description |
|---|---|---|---|
allowed_extensions |
list[str] | [] (all) |
Allowed file extensions (empty = all allowed) |
blocked_paths |
list[str] | ["/etc", "/sys", ...] |
Paths that cannot be edited |
max_payload_bytes |
int | 1048576 (1MB) |
Maximum content size in bytes |
max_edits |
int | 50 |
Maximum number of line edits per operation |
allow_overwrite |
bool | false |
Allow create_file to overwrite existing files |
default_encoding |
str | "utf-8" |
Default file encoding |
Allow all file types:
file_edit:
allowed_extensions: [] # Empty list = all extensions allowedRestrict to specific types:
file_edit:
allowed_extensions:
- ".py" # Python
- ".js" # JavaScript
- ".md" # Markdown
- ".txt" # Text
- "" # Extensionless (Dockerfile, Makefile)Case-insensitive matching:
.pymatches.PY,.Py,.pY- Extension filtering is case-insensitive
Default blocked paths:
DEFAULT_BLOCKED_PATHS = [
"/etc/shadow", # Shadow password file
"/etc/passwd", # User account information
"/proc", # Process information
"/dev", # Device files
"/sys", # Kernel interface
]~/.ssh, ~/.aws, or ~/.gnupg. You must explicitly add these to your configuration for production use.
Recommended production configuration:
file_edit:
blocked_paths:
# System defaults (already included)
- "/etc/shadow"
- "/etc/passwd"
- "/proc"
- "/dev"
- "/sys"
# CRITICAL ADDITIONS for production
- "~/.ssh" # SSH keys
- "~/.aws" # AWS credentials
- "~/.gnupg" # GPG keys
- "~/.config/gcloud" # Google Cloud credentials
# Optional project-specific blocks
- "/var/www/production" # Production web root
- "~/.config/secrets" # Local secrets
- "${PROJECT_ROOT}/vendor" # Third-party codePrevent large edits:
file_edit:
max_payload_bytes: 524288 # 512KB limit
max_edits: 25 # Max 25 line ranges per editValidation:
edit_file_lines: Total size of allline_editsvaluesedit_file_search_replace: Total size of allreplacevaluescreate_file: Size ofcontentparameterappend_to_file: Size of existing file + newcontent
UTF-8 (default):
file_edit:
default_encoding: "utf-8" # Unicode supportLatin-1 (fallback):
file_edit:
default_encoding: "latin-1" # ASCII + extended charactersNote: Files are always read with UTF-8 first, falling back to Latin-1 if decoding fails.
Risk Levels:
- Most file editing tools: CAUTION (edit_file_lines, edit_file_search_replace, create_file, append_to_file)
delete_file: DANGEROUS (destructive operation)
Approval Requirements:
- CAUTION tools: Require approval with
balancedorparanoid, auto-approved withtrusting - DANGEROUS tools: Always require approval (even with
trusting), only auto-approved withunrestricted
Protections:
- Path traversal detection: Blocks
..in paths - Absolute path validation: Checks against blocked paths
- Symlink resolution: Resolves to real path before validation
- Directory rejection: File operations reject directories
Example:
# BLOCKED: Path traversal
edit_file_lines.invoke({
"file_path": "../../../etc/passwd",
"line_edits": {"1": "hacked"}
})
# Error: "Path traversal detected: .. not allowed"
# BLOCKED: Blocked path
create_file.invoke({
"file_path": "/etc/malicious.conf",
"content": "bad content"
})
# Error: "Path is blocked: /etc"
# ALLOWED: Safe relative path
edit_file_lines.invoke({
"file_path": "src/utils.py",
"line_edits": {"42": "fixed"}
})Enforcement:
- Checked AFTER path traversal validation
- Case-insensitive comparison
- Extensionless files require explicit
""in allowed list
Example:
# Configuration
file_edit:
allowed_extensions:
- ".py"
- ".md"# ALLOWED
edit_file_lines.invoke({
"file_path": "src/main.py", # .py allowed
"line_edits": {"1": "# Updated"}
})
# BLOCKED
edit_file_lines.invoke({
"file_path": "config.yaml", # .yaml not in allowed list
"line_edits": {"1": "env: prod"}
})
# Error: "Extension .yaml not allowed"
# BLOCKED: Extensionless without explicit permission
edit_file_lines.invoke({
"file_path": "Dockerfile",
"line_edits": {"1": "FROM python:3.12"}
})
# Error: "Extensionless files not allowed (add '' to allowed_extensions)"With TUI (interactive):
- AI requests file edit operation
- Approval modal shows:
- Tool name and file path
- Diff preview (if dry-run supported)
- Risk level: CAUTION
- User approves or denies
- If approved, operation executes
- Result shown in chat interface
With SDK (programmatic):
from consoul.ai.tools.approval import ApprovalProvider, ToolApprovalRequest, ToolApprovalResponse
class CustomApprovalProvider(ApprovalProvider):
async def request_approval(self, request: ToolApprovalRequest) -> ToolApprovalResponse:
# Custom approval logic
if request.preview:
print("Diff preview:\n" + request.preview)
# Auto-approve Python files
if request.arguments["file_path"].endswith(".py"):
return ToolApprovalResponse(approved=True)
# Deny other files
return ToolApprovalResponse(
approved=False,
reason="Only .py files auto-approved"
)All file operations logged:
{"timestamp": "2025-11-15T10:30:45Z", "event_type": "request", "tool_name": "edit_file_lines", "arguments": {"file_path": "src/main.py", "line_edits": {...}}}
{"timestamp": "2025-11-15T10:30:46Z", "event_type": "approval", "tool_name": "edit_file_lines", "decision": true}
{"timestamp": "2025-11-15T10:30:47Z", "event_type": "result", "tool_name": "edit_file_lines", "result": {"status": "success", "bytes_written": 1024, ...}}Query audit log:
# View all file edits
jq 'select(.tool_name | startswith("edit_") or startswith("create_") or startswith("delete_") or startswith("append_"))' \
~/.consoul/tool_audit.jsonl
# Find denied operations
jq 'select(.event_type=="denial" and (.tool_name | contains("file")))' \
~/.consoul/tool_audit.jsonlProgressive matching allows edit_file_search_replace to handle variations in whitespace, formatting, and even typos. Three tolerance levels are available:
| Tolerance | Use Case | Matches | Preserves |
|---|---|---|---|
| strict | Exact replacement | Character-for-character | N/A (exact match) |
| whitespace | Refactoring | Ignores indentation | Original tabs/spaces, CRLF |
| fuzzy | Fixing typos | Similarity ≥80% | Original content structure |
Behavior:
- Exact character-for-character match
- Whitespace must match exactly
- Line endings must match exactly
- Fastest matching mode
Use when:
- You know the exact content
- Replacing constants or literals
- Working with machine-generated code
Example:
edit_file_search_replace.invoke({
"file_path": "config.py",
"edits": [
{"search": "DEBUG = True", "replace": "DEBUG = False"}
],
"tolerance": "strict"
})
# Only matches if exactly "DEBUG = True" (no extra spaces)Behavior:
- Ignores leading/trailing whitespace on each line
- Ignores differences in indentation
- Preserves original tab/space style
- Preserves original line endings (CRLF/LF)
Use when:
- Search block has different indentation than file
- Refactoring code with varying indent levels
- Copying code from documentation
Example:
# File contains (with 4-space indent):
"""
def login(user):
validate(user)
return True
"""
# Search with no indent
edit_file_search_replace.invoke({
"file_path": "auth.py",
"edits": [
{
"search": "def login(user):\n validate(user)\n return True",
"replace": "async def login(user):\n await validate(user)\n return True"
}
],
"tolerance": "whitespace"
})
# Matches despite indentation difference
# Preserves original 4-space indent in replacementTab Preservation:
# File uses tabs: "\tdef foo():\n\t\tpass"
# Search uses spaces: "def foo():\n pass"
edit_file_search_replace.invoke({
"edits": [{"search": "def foo():\n pass", "replace": "def bar():\n return"}],
"tolerance": "whitespace"
})
# Matches and preserves tabs: "\tdef bar():\n\t\treturn"CRLF Preservation:
# File uses CRLF: "line1\r\nline2\r\n"
# Search uses LF: "line1\nline2"
edit_file_search_replace.invoke({
"edits": [{"search": "line1\nline2", "replace": "LINE1\nLINE2"}],
"tolerance": "whitespace"
})
# Matches and preserves CRLF: "LINE1\r\nLINE2\r\n"Behavior:
- Matches based on similarity score (≥80% default)
- Handles typos in search text
- Shows confidence score in warning
- Suggests similar blocks if no match
Use when:
- You have a typo in the search text
- Code has minor formatting differences
- You want "did you mean?" suggestions
Example:
# File contains: "def calculate_total(items):"
# Search has typo
edit_file_search_replace.invoke({
"file_path": "utils.py",
"edits": [
{
"search": "def calculate_totle(items):", # Typo: "totle"
"replace": "def calculate_sum(items):"
}
],
"tolerance": "fuzzy"
})
# Success with warning: "Fuzzy matched with 94% similarity"Similarity Suggestions:
# Search text not found
edit_file_search_replace.invoke({
"edits": [{"search": "def proces_order():", "replace": "..."}],
"tolerance": "fuzzy"
})
# Error: "Search text 'def proces_order():' not found"
# Did you mean:
# - def process_order(): (91% similar)
# - def process_payment(): (85% similar)Objective: Rename function and update signature
from consoul import Consoul
console = Consoul(tools=True)
# Step 1: Find function definition and usages
console.chat("Find all usages of calculate_total() in the project")
# AI uses code_search + find_references
# Step 2: Rename function with whitespace tolerance
console.chat("""
Rename calculate_total() to calculate_sum() in src/utils.py.
The function signature should also accept a 'tax_rate' parameter.
""")
# AI uses edit_file_search_replace with tolerance="whitespace"
# Step 3: Update all call sites
console.chat("Update all calls to calculate_sum() to include tax_rate=0.08")
# AI edits each file with line-based editsObjective: Update config values across multiple files
console.chat("""
Update all YAML files in config/ to set:
- environment: production
- debug: false
- log_level: INFO
""")
# AI uses glob pattern to find files, edit_file_search_replace for eachObjective: Add docstrings to undocumented functions
console.chat("""
Find all functions in src/ without docstrings and add comprehensive
docstrings following Google style guide.
""")
# AI uses code_search to find functions, edit_file_lines to add docstringsObjective: Add try/catch to risky operations
console.chat("""
Review all file I/O operations in src/ and add proper error handling
with try/except blocks. Log errors using the logger module.
""")
# AI uses grep_search to find file operations, edit_file_search_replace to wrap in try/catchObjective: Add type hints to existing Python code
console.chat("""
Add type hints to all function signatures in src/models.py.
Use typing module for complex types (List, Dict, Optional).
""")
# AI uses edit_file_lines to add type hints while preserving formattingProblem: edit_file_search_replace can't find the search text
Causes & Solutions:
-
Whitespace differences:
# Change tolerance from strict to whitespace edit_file_search_replace.invoke({ "edits": [...], "tolerance": "whitespace" # Instead of "strict" })
-
Typo in search text:
# Use fuzzy matching edit_file_search_replace.invoke({ "edits": [...], "tolerance": "fuzzy" }) # Check "Did you mean?" suggestions in error message
-
Content changed since read:
# Re-read file and try again # Or use expected_hash to detect concurrent edits
Problem: edit_file_lines edits overlap (e.g., "1-5" and "3-7")
Solution: Use non-overlapping ranges
# BAD: Overlapping
{"line_edits": {"1-5": "...", "3-7": "..."}}
# GOOD: Non-overlapping
{"line_edits": {"1-5": "...", "6-10": "..."}}Problem: Line number out of bounds
Solution: Check file length first
# Read file first to see number of lines
read_file.invoke({"file_path": "test.py"})
# Then edit within boundsProblem: File extension not in allowed list
Solution: Update configuration
file_edit:
allowed_extensions:
- ".py"
- ".yaml" # Add missing extensionProblem: Path in blocked list
Solution: Remove from blocklist if safe
file_edit:
blocked_paths:
# - "/var/www" # Remove if you need to edit hereProblem: Optimistic locking detected concurrent edit
Solution: Re-read file and retry
# Get new hash
new_content = read_file.invoke({"file_path": "test.py"})
new_hash = compute_hash(new_content)
# Retry with new hash
edit_file_lines.invoke({
"file_path": "test.py",
"line_edits": {...},
"expected_hash": new_hash
})Cause: Large files require full content scan
Solutions:
- Use
edit_file_linesif you know line numbers - Split large files into smaller modules
- Increase timeout in config
Cause: Many changes generate large previews
Solutions:
- Break into multiple smaller edits
- Use dry_run sparingly
- Disable preview for batch operations
✅ Use dry_run=True to preview before applying
✅ Start with tolerance="strict" and relax as needed
✅ Use expected_hash for critical edits
✅ Read file first to understand context
✅ Use line-based edits when you know exact line numbers
✅ Use search/replace for pattern-based changes
❌ Edit binary files (use specialized tools) ❌ Bypass security validation ❌ Ignore hash mismatch errors ❌ Use fuzzy matching for exact replacements ❌ Edit system files without understanding risks ❌ Disable approval workflow in production
Other Tools:
- Code Search - Find code before editing it
- Image Analysis - Review changes visually
SDK & API:
- SDK Tools Overview - Using file editing tools programmatically
- Tool Configuration - Configuring file editing in your code
Configuration & Security:
- Configuration Guide - Enable/disable file editing
- Security Policy - Safety controls and best practices