Skip to main content

Crate patchloom

Crate patchloom 

Source
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

FeatureDefaultDescription
cliyesCLI parser (clap) and all subcommand implementations. Disable for pure library use.
mcpyesMCP server support (adds tokio, rmcp, schemars)
astyesAST-aware operations using tree-sitter (20 language grammars)
filesnoFile scanning helpers + library plan execution + search_directory + append etc. for pure-library use (no CLI/clap).
fullnoEverything: 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 (flexible AbsolutePathPolicy via builder for temp dirs/extra roots in library use; strict Reject for MCP)
  • exec – shell command execution with process-tree management
  • fallback – multi-strategy edit recovery (exact, anchor, similarity)
  • filesis_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_signature with FunctionSigEdit
  • On disk: api::ast_rewrite_signature(path, name, &edit, new_signature, mode, guard?)
  • Plans / MCP: op ast.rewrite_signature / tool ast_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::RegexBuilder with bounded compilation limits.
is_verbose
Returns true if verbose mode is enabled.
run
Run the patchloom CLI. Returns the exit code as a u8.