Filesystem Tool¶
The Filesystem Tool provides three essential operations for file manipulation: reading, writing, and editing files. These tools work with absolute paths, relative paths, and @mention paths for bundle resources.
Operations¶
| Operation | Tool | Purpose |
|---|---|---|
| Read | read_file | Read file contents or list directories |
| Write | write_file | Create or overwrite entire files |
| Edit | edit_file | Perform surgical string replacements |
Reading Files¶
The read_file tool reads file contents or lists directory contents with line numbers.
Basic File Reading¶
{
"file_path": "./src/main.py"
}
Returns the file with line numbers in cat -n format:
1 import sys
2
3 def main():
4 print("Hello, world!")
Reading Large Files¶
For files with many lines, use offset and limit parameters:
{
"file_path": "./logs/app.log",
"offset": 100,
"limit": 50
}
This reads lines 100-150, useful for: - Inspecting specific sections of large logs - Paginating through long files - Reducing context consumption
Default limits: - Maximum 2000 lines per read - Lines longer than 2000 characters are truncated - Line numbers start at 1 (not 0)
Directory Listing¶
When file_path points to a directory, read_file returns a formatted listing:
{
"file_path": "./src"
}
Returns:
DIR: components/
DIR: utils/
FILE: main.py
FILE: config.py
Path Formats Supported¶
Absolute paths:
{"file_path": "/home/user/project/file.md"}
Relative paths:
{"file_path": "./docs/README.md"}
Bundle resources:
{"file_path": "@mybundle:docs/guide.md"}
Bundle directories:
{"file_path": "@mybundle:docs"}
Writing Files¶
The write_file tool creates new files or overwrites existing ones completely.
Basic Writing¶
{
"file_path": "./src/new_module.py",
"content": "def calculate(x, y):\n return x + y\n"
}
Important Rules¶
⚠️ MUST read existing files first:
// Step 1: Read first
{"file_path": "./src/existing.py"}
// Step 2: Then write
{
"file_path": "./src/existing.py",
"content": "new content"
}
The tool will fail if you attempt to overwrite an existing file without reading it first. This safety mechanism prevents accidental overwrites.
When to Use write_file¶
✅ Good use cases: - Creating entirely new files - Generating configuration files - Writing output from code generation - Creating test fixtures
❌ Avoid for:
- Modifying existing files (use edit_file instead)
- Small changes to files (use edit_file instead)
- Adding single functions to modules (use edit_file instead)
Content Guidelines¶
- Avoid emoji unless explicitly requested by user
- Never proactively create documentation (*.md) files
- Always prefer editing existing files over creating new ones
Editing Files¶
The edit_file tool performs precise string replacements in existing files.
Basic Editing¶
{
"file_path": "./src/main.py",
"old_string": "def calculate(x):\n return x * 2",
"new_string": "def calculate(x, multiplier=2):\n return x * multiplier"
}
Preserving Indentation¶
⚠️ Critical: Preserve exact indentation as shown in read_file output.
The line number format is: spaces + number + tab + content
Example from read_file:
42 def helper():
43 return True
Correct old_string (after the tab):
"def helper():\n return True"
Incorrect (includes line numbers):
" 42 def helper():\n 43 return True"
Ensuring Uniqueness¶
The tool requires old_string to be unique in the file:
❌ Will fail (matches multiple locations):
{
"old_string": "return x"
}
✅ Include context to make it unique:
{
"old_string": "def calculate(x):\n return x"
}
Replace All Occurrences¶
Use replace_all: true for renaming variables or repeated patterns:
{
"file_path": "./src/user.py",
"old_string": "userName",
"new_string": "user_name",
"replace_all": true
}
Perfect for: - Variable renaming across a file - Updating repeated string literals - Changing function names - Updating import paths
When to Use edit_file¶
✅ Preferred for: - Modifying functions or classes - Adding imports - Changing variable names - Updating configuration values - Bug fixes in existing code
✅ Benefits: - Preserves surrounding code - Safer than full file rewrites - More efficient for small changes - Less prone to losing unrelated changes
Directory Listing¶
List directory contents with read_file:
{
"file_path": "./src/components"
}
Output shows:
- DIR: prefix for subdirectories
- FILE: prefix for files
- Alphabetically sorted
- Relative names only (not full paths)
Use cases: - Exploring project structure - Finding configuration files - Discovering test files - Checking bundle contents
Best Practices¶
1. Always Read Before Editing¶
// ✅ Correct workflow
read_file("./src/app.py")
// ... review contents ...
edit_file("./src/app.py", old_string, new_string)
// ❌ Will fail
edit_file("./src/app.py", old_string, new_string)
2. Prefer edit_file Over write_file¶
For existing files, surgical edits are safer:
// ✅ Preferred - surgical edit
edit_file({
"old_string": "version = \"1.0\"",
"new_string": "version = \"2.0\""
})
// ❌ Risky - full rewrite
write_file({
"content": "... entire file contents ..."
})
3. Use Parallel Reads¶
Read multiple files in one message:
[
{"file_path": "./src/main.py"},
{"file_path": "./src/utils.py"},
{"file_path": "./tests/test_main.py"}
]
4. Handle Large Files Efficiently¶
// Check file size first
read_file("./data/large.json", 0, 10)
// Read in chunks if needed
read_file("./data/large.json", 0, 100)
read_file("./data/large.json", 100, 100)
5. Verify Bundle Paths¶
For bundle resources, check directory listing first:
// List available files
read_file("@recipes:examples")
// Then read specific file
read_file("@recipes:examples/workflow.yaml")
Try It Yourself¶
Exercise 1: Read and Edit a File¶
- Create a test file:
write_file({
"file_path": "./test.py",
"content": "def greet(name):\n return f'Hello {name}'"
})
- Read it back:
read_file({"file_path": "./test.py"})
- Edit the function:
edit_file({
"file_path": "./test.py",
"old_string": "def greet(name):\n return f'Hello {name}'",
"new_string": "def greet(name, title='Friend'):\n return f'Hello {title} {name}'"
})
Exercise 2: Explore a Directory Structure¶
read_file({"file_path": "./content"})
read_file({"file_path": "./content/tools"})
Exercise 3: Variable Renaming¶
edit_file({
"file_path": "./src/config.py",
"old_string": "max_retries",
"new_string": "maximum_retry_count",
"replace_all": true
})
Errors and Troubleshooting¶
Error: "Must read file before editing"¶
Cause: Attempted to edit without reading first.
Solution:
read_file({"file_path": "./target.py"})
// Then edit
Error: "old_string not unique"¶
Cause: The string appears multiple times in the file.
Solutions: 1. Add more context to make it unique:
{
"old_string": "return value // More context needed",
"new_string": "function calculate() {\n return value\n}"
}
- Use
replace_all: trueif appropriate:
{"replace_all": true}
Error: "File not found"¶
Cause: Path doesn't exist or is incorrect.
Solutions: - Verify path with directory listing - Check for typos in filename - Use absolute path if relative path is ambiguous - Confirm bundle name for @mention paths
Warning: Empty File Contents¶
If you read a file that exists but is empty, you'll receive a warning. This helps distinguish between: - File doesn't exist (error) - File exists but is empty (warning) - File has content (normal output)
Indentation Mismatch¶
Cause: Including line numbers or wrong whitespace in old_string.
Solution: - Copy exact content after the line number tab - Preserve spaces/tabs exactly as shown - Don't include the line number prefix
Line Truncation¶
Lines longer than 2000 characters are truncated. For such files:
- Use specialized tools for specific content types (JSON parsers, etc.)
- Process in smaller chunks
- Consider using bash tool for stream processing
Advanced Patterns¶
Conditional Edits¶
Read first, decide, then edit based on content:
# 1. Read file
content = read_file("./config.yaml")
# 2. Analyze content
if "debug: false" in content:
# 3. Edit accordingly
edit_file(
old_string="debug: false",
new_string="debug: true"
)
Multi-Step Refactoring¶
# 1. Read original
read_file("./src/api.py")
# 2. First edit - rename function
edit_file(old_string="def old_name():", new_string="def new_name():")
# 3. Second edit - update callers
edit_file(old_string="old_name()", new_string="new_name()", replace_all=True)
Safe Templating¶
# 1. Read template
template = read_file("@mybundle:templates/component.tsx")
# 2. Write new file with modifications
write_file(
file_path="./src/components/MyComponent.tsx",
content=template.replace("{{NAME}}", "MyComponent")
)
Summary¶
The Filesystem Tool provides three complementary operations:
- read_file: View contents, paginate large files, list directories
- write_file: Create new files (read existing files first!)
- edit_file: Surgical replacements (prefer over write_file)
Key principles: 1. Always read before editing 2. Prefer edit over write for existing files 3. Ensure old_string is unique or use replace_all 4. Preserve exact indentation from read_file output 5. Use parallel reads for efficiency
These tools form the foundation of file manipulation in Amplifier, enabling precise control over your project's files.