Expand description
Patchloom: agent-grade repo operations as a Rust library.
This crate provides both a CLI binary and a library API for structured
file editing operations. The api module is the main entry point for
library consumers.
§Feature flags
| Feature | Default | Description |
|---|---|---|
cli | yes | CLI parser (clap) and all subcommand implementations. Disable for pure library use. |
mcp | yes | MCP server support (adds tokio, rmcp, schemars) |
ast | yes | AST-aware operations using tree-sitter (20 language grammars) |
files | no | File scanning helpers + library plan execution + search_directory + append etc. for pure-library use (no CLI/clap). |
full | no | Everything: cli + mcp + ast |
§Embedding as a library
To use patchloom as a library (no CLI, no MCP):
[dependencies]
patchloom = { default-features = false }Or with AST support:
patchloom = { default-features = false, features = ["ast"] }(Update the version number in these examples when the next release-please PR bumps the crate version. See the release checklist.)
This gives you the api module (primary editing interface), ops,
and utility modules:
containment– workspace path guard (flexibleAbsolutePathPolicyvia builder for temp dirs/extra roots in library use; strictRejectfor MCP)exec– shell command execution with process-tree managementfallback– multi-strategy edit recovery (exact, anchor, similarity)files–is_binary,read_text_file, and (with “files” feature) parallel scanning helpers- [
write] – atomic file writes with write-policy transformations
With “files” feature you also get api::search_directory, api::execute_plan,
api::file_append/file_prepend, and full plan execution for library use.
For advanced search ignore (e.g. .blineignore on top of .gitignore) + custom walkers:
Use SearchOptions::exclude_patterns and custom_ignore_filenames with search_directory/search_file,
or collect paths with files::collect_file_paths_with_ignores (or your own WalkBuilder) then
pair with the low-level api::search_one_file inside par_process_files + format_search_results / build_context_lines.
See api::search_one_file (and its docs for custom WalkBuilder use), api::SearchOptions, and files module.
Example (pure library with plans):
use patchloom::api::{execute_plan, parse_plan, ApplyMode, file_append};
use patchloom::containment::PathGuard;
use std::path::Path;
let guard = PathGuard::builder(std::env::current_dir().unwrap())
.allow_temp_directory()
.build()?;
// Simple append via api
let _ = file_append(Path::new("log.txt"), "entry\n", ApplyMode::Apply, Some(&guard))?;
// Or via plan for atomic multi-op
let plan_json = r#"{"version":1,"ops":[{"op":"file.append","path":"log.txt","content":"more\n"}]}"#;
let plan = parse_plan(plan_json)?;
let report = execute_plan(plan, Path::new("."), Some(&guard))?;
assert!(report.ok);For AST signature edits (bline #1459 / #821 follow-through):
- In-memory:
ast::rewrite::rewrite_function_signaturewithFunctionSigEdit - On disk:
api::ast_rewrite_signature(path, name, &edit, new_signature, mode, guard?) - Plans / MCP: op
ast.rewrite_signature/ toolast_rewrite_signature
CLI ast rewrite-signature is still optional; library + plan + MCP cover embedders.
For several ordered text edits on one buffer then a single write (agent intent engines):
use api::apply_content_edits / api::apply_content_edits_to_file with
ContentEdit::{Replace, InsertBefore, InsertAfter, Append, Prepend} (all-or-nothing).
Multi-file multi-op remains execute_plan.
Note on results: Single-file ops return EditResult (with action, dest_path,
match_count for replace, and removed for doc.delete / doc.delete_where).
execute_plan (library) returns PlanReport (typed TxOutput) with ok, changes,
searches, reads, error, plus mutations / aggregate changed / removed for
deletes (including idempotent removed: 0 no-ops) (#811, #1439, #1459).
See api::PlanReport, api::execute_plan, and embedding docs. CLI/MCP retain (code, json) for compatibility.
For library users needing relaxed containment (e.g. agents like Bline using –yolo or temp files):
use patchloom::containment::PathGuard;
let guard = PathGuard::builder(std::env::current_dir().unwrap())
.allow_temp_directory() // includes /tmp and handles macOS /tmp -> /private/tmp
.build()
.expect("guard");
// pass to high-level api functions, e.g.
let _ = patchloom::api::replace_text(
std::path::Path::new("foo.txt"),
"old",
"new",
&patchloom::api::ReplaceOptions::default(),
patchloom::api::ApplyMode::Preview,
Some(&guard),
);The files module (pure helpers like is_binary, read_text_file, and scanning tools when “files” feature enabled) is always available.
The cli and cmd modules require the cli feature.
For pure library use with plans and execution (post #792), prefer
features = ["ast", "files"] (or “files”). execute_plan is available
under any(feature = "cli", "files") and delegates to the tx module.
§Migration for high-level api::* signature changes (PathGuard, #758)
The addition of the trailing guard: Option<&PathGuard> parameter to all mutating
functions (replace_text, doc_, md_, file_*, tidy, apply_patch, etc.) and to
execute_plan is a source-breaking change from pre-#749 usage.
// Before
patchloom::api::doc_set(&p, "k", v, ApplyMode::Apply)?;
// After (pass None to keep previous strict-root behavior, or a guard for relaxed)
patchloom::api::doc_set(&p, "k", v, ApplyMode::Apply, None)?;See the “Using with PathGuard” section in the api module docs, the builder
for relaxed policies, and AGENTS.md “High-level library API signature changes”
for the full checklist (doctests, greps, examples, tests). execute_plan now
also accepts the guard (threaded into tx; #755).
With features = ["ast"], the ast module provides tree-sitter parsing,
symbol extraction, structural search, rename, and more for 20 languages.
No clap, tokio or other heavy dependencies are pulled in when cli and mcp are disabled.
§Thread safety
All public API types (api::EditResult, api::ApplyMode, etc.) are
Send + Sync. Library functions are safe to call concurrently from
multiple threads with one constraint:
- Different files: fully safe. Multiple threads can edit different files simultaneously with no coordination.
- Same file: the caller must serialize access. Concurrent writes to the same file are inherently racy (last writer wins). Use a mutex or other synchronization if you need to coordinate edits to a single file.
Backup sessions use unique directory names (nanosecond timestamp + monotonic counter) so concurrent backup creation never collides.
Configuration can be loaded once with config::CachedConfig and reused
across threads, avoiding repeated disk reads.
Re-exports§
pub use api::search_one_file;pub use api::ApplyMode;pub use api::ContentEditResult;pub use api::EditResult;pub use api::Hunk;pub use api::PatchFile;pub use api::PatchLine;pub use api::ReplaceOptions;pub use api::SearchOptions;pub use api::SearchResult;pub use api::WritePolicyOptions;pub use api::build_context_lines;pub use api::format_search_results;pub use api::parse_unified_diff;pub use api::search_file;pub use api::text_diff;pub use plan::Plan;
Modules§
- api
- Public library API for embedding patchloom in Rust applications.
- ast
- AST-aware operations using tree-sitter grammars.
- backup
- Backup session management for undo safety net.
- cli
- cmd
- config
- Project configuration file support (.patchloom.toml).
- containment
- Workspace path containment.
- exec
- Shell command execution with proper process tree management.
- fallback
- Multi-strategy fallback chain for edit resolution.
- files
- ops
- plan
- Transaction plan format parsing.
- schema
- Intent format specification with versioning, tier filtering, and schema export.
- selector
- write
- Atomic write, final newline, EOL normalization, trailing-whitespace trimming.
Macros§
- verbose
- Print a verbose diagnostic message to stderr.
Functions§
- bounded_
regex_ builder - Create a
regex::RegexBuilderwith bounded compilation limits. - is_
verbose - Returns
trueif verbose mode is enabled. - run
- Run the patchloom CLI. Returns the exit code as a u8.