fs_read vs fs_patch in Forge: When to Use Each File System Tool
Use fs_read to inspect file contents without modification and fs_patch to apply controlled edits with automatic snapshots and validation.
Forge, the AI coding assistant from the antinomyhq/forgecode repository, provides two primary file system tools for LLM interactions: fs_read for safe file inspection and fs_patch for controlled modifications. Understanding the architectural differences between these services ensures you choose the right tool for reading source code versus applying programmatic edits.
Core Architectural Differences
The tools operate through distinct service traits with separate responsibilities in the Forge engine.
FsReadService Implementation
According to forge_app/src/services.rs, the FsReadService trait defines read-only operations. The concrete implementation ForgeFsRead<F> in forge_services/src/tool_services/fs_read.rs handles MIME type detection, enforces size limits, and returns a ReadOutput struct containing either Content::text or Content::image alongside file metadata. This service guarantees immutability—no file system modifications occur during execution.
FsPatchService Implementation
The FsPatchService trait, also defined in forge_app/src/services.rs, powers file modifications through ForgeFsPatch<F> in forge_services/src/tool_services/fs_patch.rs. Unlike the read service, this implementation creates snapshots for undo functionality, computes edit ranges using exact or fuzzy string matching, and runs remote validation before finalizing writes. It returns a PatchOutput containing both the original and modified content states.
When to Use fs_read for File Inspection
Use fs_read whenever you need to display file contents to the LLM without altering the underlying data. According to the source code in forge_services/src/tool_services/fs_read.rs, the service automatically enforces max_file_size_bytes and max_image_size_bytes limits, rejects binary files exceeding visual thresholds, and truncates output to max_read_lines (defaulting to 2,000 lines).
Typical use cases include:
- Showing source code to the LLM for analysis or code review
- Displaying image or PDF previews inside chat contexts
- Fetching specific line ranges (e.g., lines 10-20) for targeted discussion
When to Use fs_patch for File Modifications
Use fs_patch when you need to programmatically change file contents. The implementation in forge_services/src/tool_services/fs_patch.rs supports single edits, batch operations via multi_patch, and operations like replace, append, and prepend. Before writing changes, the service stores a snapshot enabling rollback and runs remote validation to ensure file integrity.
Typical use cases include:
- Fixing bugs automatically by replacing deprecated API calls
- Adding imports or comments to specific locations
- Performing batch refactors across multiple locations in a single file
Code Examples
Reading Files with FsReadService
The read method accepts optional start and end line parameters to limit output size:
use forge_app::{FsReadService, ReadOutput};
async fn demo_read<S: FsReadService>(services: &S) -> anyhow::Result<()> {
// Read the first 50 lines of `src/main.rs`
let output: ReadOutput = services
.read(
"src/main.rs".to_string(),
Some(1), // start_line
Some(50), // end_line
)
.await?;
println!("File hash: {}", output.info.hash());
println!("Content:\n{}", output.content.as_text()?);
Ok(())
}
Source: forge_services/src/tool_services/fs_read.rs (lines 87-115, 162-170)
Applying Single Edits with FsPatchService
The patch method performs string replacement with optional fuzzy matching when exact matches fail:
use forge_app::{FsPatchService, PatchOutput};
async fn demo_patch<S: FsPatchService>(services: &S) -> anyhow::Result<()> {
let result: PatchOutput = services
.patch(
"src/lib.rs".to_string(),
"old_api()".to_string(), // search string
"new_api()".to_string(), // replacement
false, // replace_all = false
)
.await?;
println!("Before:\n{}", result.before);
println!("After:\n{}", result.after);
Ok(())
}
Source: forge_services/src/tool_services/fs_patch.rs (lines 31-42, 86-106)
Batch Operations with multi_patch
For multiple edits in a single transaction, use multi_patch with a vector of PatchEdit structs:
use forge_domain::PatchEdit;
use forge_app::FsPatchService;
async fn demo_multi_patch<S: FsPatchService>(services: &S) -> anyhow::Result<()> {
let edits = vec![
PatchEdit {
old_string: "use std::fmt;".to_string(),
new_string: "use std::fmt::{self, Display};".to_string(),
replace_all: false,
},
PatchEdit {
old_string: "// TODO".to_string(),
new_string: "// ✅ Fixed".to_string(),
replace_all: true,
},
];
let output = services
.multi_patch("src/main.rs".to_string(), edits)
.await?;
println!("Applied edits to {} lines", output.after.matches('\n').count() + 1);
Ok(())
}
Source: forge_services/src/tool_services/fs_patch.rs (lines 15-19, 30-44)
Safety Mechanisms and Validation
Both tools implement safety guards appropriate to their risk profiles.
FsRead enforces read-only constraints through size limits and line truncation. The service in forge_services/src/tool_services/fs_read.rs detects MIME types to handle text versus visual content differently, ensuring binary files cannot overwhelm the LLM context window.
FsPatch implements write-safety through snapshot creation and validation. Before any modification, the service creates a recoverable snapshot enabling undo operations. After applying edits, it runs remote validation via the validation API (with graceful fallback) and returns any errors in the PatchOutput.errors vector without necessarily failing the entire operation.
Summary
- Use
fs_read(viaFsReadService) for immutable file inspection, leveraging MIME detection and configurable line limits to safely present content to the LLM. - Use
fs_patch(viaFsPatchService) for mutable operations, relying on automatic snapshots, fuzzy search capabilities, and remote validation to ensure safe, reversible edits. - Reference
forge_app/src/operation.rsfor the dispatch logic that routesToolOperation::FsReadandToolOperation::FsPatchto their respective service implementations. - Consider
multi_patchfor batch operations to minimize filesystem round-trips and ensure atomic edit application.
Frequently Asked Questions
Can fs_patch read files without modifying them?
No. While fs_patch internally reads the original file to compute the before state for the PatchOutput, its primary purpose is modification. For read-only operations, use fs_read (via FsReadService) which enforces stricter size limits and provides optimized text or image content handling without write-side overhead.
What happens if fs_patch cannot find the exact search string?
According to the implementation in forge_services/src/tool_services/fs_patch.rs, the service falls back to fuzzy search when an exact match is not found. This allows edits to succeed even with minor whitespace or formatting differences. If fuzzy matching still fails, the operation returns an error without modifying the file, preserving data integrity.
How does Forge handle large files when using fs_read?
The fs_read implementation enforces three layers of protection: max_file_size_bytes for general files, max_image_size_bytes for visual content, and max_read_lines (default 2,000) for text truncation. Binary files exceeding visual limits are rejected, while text files are truncated to prevent LLM context overflow, as defined in forge_services/src/tool_services/fs_read.rs.
Is it possible to undo changes made by fs_patch?
Yes. The FsPatchService automatically creates a snapshot before writing any modifications, storing the original content for undo operations. While the API client manages the undo lifecycle, the underlying infrastructure in forge_services/src/tool_services/fs_patch.rs guarantees that every patch or multi_patch operation retains the pre-edit state for recovery.
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 →