Releases: patchloom/patchloom
Release list
patchloom: v0.9.0
Patchloom 0.9.0
This release closes parity gaps across all three channels (CLI, MCP, library API) and fixes a security-relevant path traversal bypass. 3 new features, 6 bug fixes, and 54 new tests across 19 commits.
Highlights
Fuzzy edit matching, context-anchored replacements, and the prepend operation each had one channel missing. This release fills all the gaps: fuzzy fallback reaches the library API, context anchoring reaches both the CLI and library API, and prepend reaches the CLI. Every replacement feature is now available in all three channels. A path traversal bypass in the containment module was also identified and patched, hardening the security boundary for all channels.
New features
- Fuzzy fallback for
replace_in_content(library API parity). The in-memory replace API now acceptsfuzzy: trueinReplaceOptions. When exact match fails, patchloom automatically tries Jaro-Winkler similarity matching, then returns suggestions if fuzzy also fails. This eliminates ~15 lines of manual fallback glue that library embedders previously needed. The tx engine (CLI, MCP, and plans) already had this capability. (#1292) before_context/after_contextfor replace (CLI and library API parity). Anchor text that disambiguates which match to target when a pattern appears multiple times in a file. The MCPreplace_texttool and tx plans already supported these fields; this release adds CLI flags (--before-context,--after-context) and exposes them in the library API'sReplaceOptions. Supports fuzzy anchor matching as a fallback. (#1290, #1310)prependcommand (CLI parity). New CLI command that prepends content to the beginning of an existing file. The MCPprepend_filetool and libraryfile_prependAPI already existed; this completes the CLI surface. Accepts--contentor--stdin(mutually exclusive). (#1290)
Bug fixes
- Path traversal bypass in containment.
canonicalize_or_ancestorsilently dropped..components from non-existent path segments, allowing paths likeworkspace/nonexistent/../../outside/secret.txtto escape the workspace boundary. Fixed by adding lexical normalization of.and..before the ancestor walk. (#1296) - Markdown
replace-sectionduplicated headings. When replacement content started with the same heading being replaced, the heading appeared twice in the output. Now automatically stripped. (#1306) - Markdown
replace-sectiondropped blank lines. Replacing a section's content removed the trailing blank line separator before the next heading, producing tightly packed markdown. Now preserves the original spacing. (#1307) - YAML
doc setstripped quote styles. Setting a value on a double-quoted or single-quoted YAML scalar lost the quotes, producing a plain scalar. Now uses in-place value replacement that preserves the original quoting style. (#1283) - Per-extension formatters were dead code. The
[format.by_extension]table in.patchloom.tomlwas parsed and validated but never wired through to the format runner. Now correctly discovers modified files and applies extension-specific formatters. (#1285) - Patch, doc, and replace exit code fixes. Creation patches no longer falsely report the target as missing;
doc moverejects moving a key to its own descendant; replace preview mode returns consistent exit codes. (#1297)
Library API changes
- Bline issues #1287, #1288, #1289.
ast.renamein tx plans now supports directory paths (recursive rename across all source files); numeric dot-notation selectors (e.g.,env.0.value) resolve as array indices; operation field descriptions are now included in theexecute_planJSON schema for better agent tooling. (#1291) selectorreplaceskey. Thekeyfield on doc operations has been renamed toselectorfor consistency with selector-based commands. Old plans usingkeycontinue to work via serde aliases. (#1293)
Internal improvements
- Deduplicated append/prepend implementation via shared
ContentPositionenum. (#1299) - Removed all 45
#[non_exhaustive]attributes and tightenedpubtopub(crate)onwrite.rsinternals. (#1300) - Extracted the agent-rules generator from the oversized
cmd/mod.rsinto its own module. (#1301) - Removed backward-compatibility serde aliases and legacy error prefixes. (#1298)
- Unified divergent
parse_line_rangeimplementations. (#1304)
Numbers
patchloom: v0.8.0
Patchloom 0.8.0
This release focuses on library API expansion, MCP usability for AI agents, and multi-language AST support. 4 new features, 3 bug fixes, and 49 new tests across 11 commits.
Highlights
Better MCP tool discovery for AI agents
AI models connected via MCP now receive a categorized tool guide in the server instructions, grouping all 54 tools into 7 categories (Document, Markdown, Text, File, AST, Plan, Server). This steers models toward doc_* tools for JSON/YAML/TOML mutations instead of falling back to verbose replace_text calls. Path parameter descriptions now explicitly state that paths are relative to the working directory, and a new server_info tool returns the server's working directory for path discovery. (#1272, #1275)
Library API for embedders
Two new public APIs let library embedders (like Bline) drop separate dependencies:
text_diff(original, modified, path)generates unified diffs in memory, exposing patchloom's internal diff engine as a standalone function.parse_unified_diff(input)exposes patchloom's diff parser, re-exportingPatchFile,Hunk, andPatchLinetypes through the public API. (#1269, #1272)
Replace gains unique mode and match_count
The replace operation now supports unique: true, which fails with an error when the search pattern matches more than once. This prevents accidental multi-site replacements that corrupt files. A companion match_count field in the result reports how many matches were found, even in dry-run mode. (#1269)
New features
- Multi-language
rewrite_function_signature. The AST operation now supports all languages with tree-sitter grammars, not just Rust. Rust retains full-reconstruction logic (preserving async/unsafe/const/extern qualifiers); all other languages use surgical node replacement that preserves surrounding code exactly. (#1271) server_infoMCP tool. Zero-argument tool that returns the MCP server's working directory, letting agents discover the correct base path for relative file operations. (#1272)text_diffandparse_unified_diffpublic APIs. In-memory diff generation and unified diff parsing for library embedders. (#1269, #1272)uniquemode andmatch_countfor replace. Fail-safe single-match enforcement and match counting for both CLI and library API. (#1269)
Bug fixes
- Permission-based tests no longer fail as root. Tests that set file permissions to
000now detect when running as root (common in Docker containers) and skip gracefully instead of failing. (#1277) - Clarified misleading MCP code comments. Fixed a doc comment that incorrectly described validation behavior, renamed a misleading variable (
log_clonetolog_path), and simplified an error conversion that discarded type information. (#1278) - MCP no-match error signaling. Operations that find zero matches now return proper error responses instead of silent success, making failures visible to connected agents. (#1272)
Test quality
- Replaced bare
assert!(x.is_ok())calls with.expect()for actionable panic messages on failure. (#1280) - Strengthened weak assertions that matched substrings too broadly (e.g.,
contains("a")passing on error messages). (#1279) - Updated
clap_completedependency for shell completion generation. (#1279) - Added missing MCP integration tests and corrected stale test counts in documentation. (#1262)
Numbers
| Metric | v0.7.0 | v0.8.0 | Delta |
|---|---|---|---|
| Unit tests | 1,938 | 1,983 | +45 |
| Integration tests | 834 | 838 | +4 |
| PTY tests | 10 | 10 | -- |
| Total tests | 2,782 | 2,831 | +49 |
| CLI commands | 22 | 22 | -- |
| MCP tools | 53 | 54 | +1 |
| Commits | -- | 11 | -- |
patchloom: v0.7.0
Patchloom 0.7.0
The 0.7.0 release is the most thoroughly tested version of patchloom to date. 98 bug fixes from 480+ rounds of runtime testing, 15 refactoring PRs that unified the execution engine, and 9 new features. 2,782 tests (up from 1,816 in 0.6.0), 140 commits, 50,946 lines added.
Highlights
API field names aligned with LLM priors
Breaking change. CLI arguments and plan field names were renamed to match what LLMs naturally generate. Agents that previously struggled with parameter names now get them right on the first try.
| Before | After | Why |
|---|---|---|
--from / --to |
positional OLD + --new |
"replace X with Y" maps to replace X --new Y |
--file |
positional FILE |
Natural first argument |
hygiene |
tidy |
Clearer intent |
--plan |
positional PLAN |
Direct argument |
Serde aliases preserve backward compatibility for plan files: both "from" and "old" are accepted in JSON/YAML plans.
480+ rounds of runtime testing
Every command was exercised with real files, edge-case inputs, and adversarial patterns through systematic runtime testing. Combined with 12 rounds of code audit and 8 multi-perspective improvement cycles, this surfaced 98 bug fixes across all major modules.
Areas of highest bug density:
- YAML operations: quote style preservation, comment migration after key deletion, multi-document detection, CST trailing whitespace cleanup
- AST operations: C/C++ pointer-returning function extraction, Ruby symbol extraction, shell/bash word node handling, template interpolation traversal
- Transaction engine: strict rollback restoring collateral files, validation step label reporting, context-filtered replace accuracy
- Regex handling: consistent
multi_line(true)across all regex builders (search, replace, tx engine, library API, AST replace), phantom EOF match filtering
Execution engine unification
All write commands now route through the transaction engine. Previously, some commands used atomic_write() directly, bypassing backup, rollback, and format/validate lifecycle steps. The unification delivered 110 new tests and eliminated a class of bugs where new commands forgot a write mode.
Three execution paths serve different needs:
| Path | Use case | Example |
|---|---|---|
execute_via_engine() |
Single-operation writes | doc, md, create, delete, append |
execute_operations() |
Multi-file writes with pre-filtering | ast rename |
execute_precomputed() |
Parallel scan + batch commit | replace (multi-file regex) |
Post-write formatter hook
New --format flag runs an external command after successful writes. Supports per-extension configuration via .patchloom.toml:
[format]
rs = "rustfmt {file}"
py = "black {file}"
go = "gofmt -w {file}"The formatter integrates with --confirm interactive mode and transaction plans (via format steps). Format commands run after writes but before validation steps.
Symbol verification for transaction plans
Transaction plans can now include verify checks that run before and/or after operations:
{
"verify": [
{"symbol": "MyClass", "file": "src/lib.rs", "when": "before"},
{"symbol": "MyClass::new", "file": "src/lib.rs", "when": "after"}
]
}Pre-checks confirm symbols exist before modification. Post-checks confirm the result compiles or the expected symbols appear. Failed checks trigger rollback in strict mode.
New features
- Windows ARM64 and Linux musl release targets (#947): Pre-built binaries now cover
aarch64-pc-windows-msvcandx86_64-unknown-linux-musl. tidy --dedentand--indent(#1034): Adjust indentation levels across files.--dedent 4removes 4 spaces of leading indentation;--indent 2adds 2 spaces.- AST Phase C operations (#1039, #1040):
ast.insert,ast.wrap,ast.imports,ast.group,ast.reorder,ast.move,ast.extract,ast.split, andfor_eachglob batching. - Library API parity (#1047):
FilePrepend,replace_in_content(), andSearchOptionswithWalkBuildersupport for exclude patterns and custom ignore files. - Template interpolation traversal (#1090):
ast renameandast refsnow follow identifiers inside template literals (${varName}in JS/TS,f"{var}"in Python). - MCP
md_dedupe_headingstool (#1097): Deduplicate repeated markdown headings in a single MCP call.
Refactoring
- Consolidated shared logic into ops layer, separating business logic from CLI wiring (#970).
- Extracted inline tests to companion files and organized by concern category (#1010, #1023).
- Split
cmd/mcp/mod.rsinto focused submodules (#1024). - Routed all API write functions (AST, doc, file, replace, tidy, patch) through the tx engine (#1027-#1032).
- Reduced
DocActionand batch parsing boilerplate (#1026). - Extracted
InsertContextstruct to eliminateclippy::too_many_arguments(#1123). - Migrated test helpers to
TxStateFixturefor consistent setup (#1124).
Bug fixes (selected)
98 bug-fix commits span every major module. Selected highlights:
- Regex anchors:
^and$now behave consistently across search, replace, tx engine, library API, and AST replace. All regex builders usemulti_line(true). Phantom EOF matches atcontent.len()are filtered (#1254). - YAML fidelity: doc delete preserves quote styles and key order (#1239). Orphaned comments that migrate inline after key deletion are stripped (#1238). Trailing whitespace from YAML CST after key deletion is cleaned (#1237).
- Multi-document YAML: improved detection and multi-line context matching (#1248, #1252).
- Context-filtered replace: before/after context lines now compare multiple lines instead of single-line matching (#1248).
- Nested predicates: doc select/delete-where predicates support nested paths (#1248).
- Patch apply: supports file creation and deletion within unified diffs (#1235).
- Create with empty content:
createnow actually creates the file instead of silently doing nothing (#1233). - Symlink handling:
atomic_writeresolves symlinks before writing (#1232). - Undo advancement: sequential undo now advances through backup sessions instead of replaying the same one (#1201).
- EditorConfig:
tidy check --respect-editorconfigdetects EOL mismatches and honorstrim_trailing_whitespace(#1199, #1203). - AST: C/C++ pointer-returning functions extracted correctly (#1242). Ruby gets a dedicated symbol extractor (#1241). Shell/bash
wordnode kind included in AST rename (#1243). - Strict rollback: restores collateral files modified by format steps (#1116).
Test quality
- Removed 14 duplicate test functions accumulated across audit sessions (#1255).
- Replaced 2 dead defensive guards (unreachable through any API path) with
debug_assert!()(#1255). - PTY test expect timeout increased from 10s to 30s to eliminate flakiness under load (#1256).
- Replaced 19 bare
assert!(x.is_ok())with.unwrap()for proper panic messages (#955). - Strengthened weak
contains("a")assertions that passed by matching substrings in error messages (#1125).
Dependency upgrades
rmcp1.8.0 to 2.0.0 (#1221)axum-server0.7 to 0.8 (#953)expectrl0.7 to 0.9 (#953)- GitHub Actions group update (#971)
Numbers
| Metric | v0.6.0 | v0.7.0 | Delta |
|---|---|---|---|
| Unit tests | 1,056 | 1,938 | +882 |
| Integration tests | 750 | 834 | +84 |
| PTY tests | 10 | 10 | -- |
| Total tests | 1,816 | 2,782 | +966 |
| CLI commands | 22 | 22 | -- |
| MCP tools | 43 | 43 | -- |
| Bug fixes | -- | 98 | -- |
| Commits | -- | 140 | -- |
Upgrading
The API field name changes (#1214) are the only breaking change. If you use patchloom as a library or construct plan files programmatically:
- CLI:
--from/--toare now positionalOLD+--new NEW - Plans: serde aliases accept both old and new field names (
"from"and"old"both work) - MCP: tool parameter names follow the new convention
No action needed if you only use the MCP server or transaction plans with the alias support.
patchloom: v0.6.0
Patchloom 0.6.0
The 0.6.0 release rebuilds the MCP server architecture from the ground up, adds 11 new AST tools for code-aware agent workflows, and delivers measurable performance gains for concurrent MCP workloads. 41 PRs, 112 files changed, and 1,816 tests (up from 1,678 in 0.5.0).
Highlights
MCP auto-generation: add a tool in 6 lines
The MCP server's internal architecture was redesigned around a declarative registry. 19 of the 43 tools are now auto-generated from Operation enum variants via MCP_TOOL_REGISTRY, with input schemas derived directly from Rust types using schemars. Adding a new tool that maps 1:1 to an existing operation takes 6 lines of metadata instead of a 40-line handler function.
McpToolMeta {
tool_name: "new_tool",
op_name: "new_op",
description: "Short description. Example: {\"path\": \"file.txt\"}",
has_strict: true,
validations: &[FieldValidation::Path("path")],
},The old mcp_tool! macro and per-tool params structs are gone. Unknown fields are rejected at the MCP layer. Schema drift between CLI operations and MCP tools is caught automatically by a new params drift test (#921).
11 AST tools for code-aware agents
All AST commands are now available as MCP tools, bringing the total from 32 to 43:
| Tool | What it does |
|---|---|
ast_list |
List symbol definitions (functions, classes, structs) across 20 languages |
ast_read |
Read a specific symbol's source code by name |
ast_rename |
Rename identifiers across files (skips strings and comments) |
ast_validate |
Check syntax and report parse errors with line numbers |
ast_search |
Structural search using tree-sitter queries and code patterns |
ast_refs |
Find all references to a symbol, distinguishing definitions from uses |
ast_deps |
Extract import/dependency statements from source files |
ast_map |
Generate a ranked repository map using PageRank over the symbol graph |
ast_diff |
Structural diff showing added, removed, and modified symbols |
ast_impact |
Transitive impact analysis: trace dependents through the reference graph |
ast_replace |
Replace text only within a specific symbol's body |
AST handlers are extracted into a dedicated ast_tools.rs module (633 lines), keeping mcp/mod.rs focused on infrastructure.
Performance: spawn_blocking, tree caching, PageRank convergence
Three changes improve MCP server responsiveness under concurrent load:
-
spawn_blockingfor all sync I/O. Every MCP handler that touches the filesystem or runs tree-sitter parsing now executes on Tokio's blocking thread pool. This was harmless for stdio transport (single-threaded) but is critical for HTTP/HTTPS transport under concurrent requests. -
Tree-sitter parse tree caching. Repeated AST queries against the same file reuse the cached parse tree instead of re-parsing. This is especially impactful for
ast_refsandast_impact, which traverse the same files multiple times. -
PageRank convergence.
ast_mapnow uses L1-norm convergence (threshold 1e-6) instead of a fixed 20 iterations. Most graphs converge in 8-12 iterations; complex ones that need more get them automatically.
Deep structural refactoring
18 refactoring PRs restructured the codebase for long-term maintainability:
- Write-command state machine (#912): The repeated preview/check/apply/confirm branching logic was extracted into a shared state machine used by all write commands, eliminating a class of bugs where new commands forgot a mode.
- Ops module decomposition (#911, #917): Pure data operations (
doc,search) were moved fromcmd/toops/, separating business logic from CLI wiring. - WritePolicy unification (#916): Two parallel
WritePolicytypes and theEolNormalizationenum were merged into a single source of truth. - Schema derivation (#918, #919, #920): Operation schemas are now derived from Rust types via
schemars+ a centralOPERATION_REGISTRY, replacing hand-maintained schema definitions. - Doc mutation dispatch (#888, #910): All
doc_*mutations now route through a singleDocMutationdispatcher with shared validation. - Integration test modularization (#878): The 20,935-line
integration.rsmonolith was split into 28 focused test modules.
Bug fixes
doc flattennow includes empty arrays and empty objects in output instead of silently dropping them (#897).- Transaction engine preserves idempotent delete semantics: deleting a file that was already removed in the same plan no longer fails (#889).
exit_code_to_resultuses the fallback error path for all non-zero exit codes, not just known ones (#941).delete_wherepredicates are validated before execution, catching malformed selectors early (#888).search --jsonlwith zero matches now returns exit code 3 (NO_MATCHES) consistently (#907).ast_renamewith a no-op rename (old name equals new name) returns early instead of rewriting files identically (#926).- Git argument injection is blocked across all commands that accept paths (#925, #926).
- Concurrent-write warnings added to all MCP tool descriptions so agents avoid lost-update races (#908).
- Quickstart and concepts documentation updated with missing exit code 9 (
OPERATION_FAILED) and corrected ordering.
Numbers
| Metric | v0.5.0 | v0.6.0 | Delta |
|---|---|---|---|
| CLI commands | 22 | 22 | -- |
| MCP tools | 32 | 43 | +11 |
| Unit tests | 948 | 1,056 | +108 |
| Integration tests | 720 | 750 | +30 |
| PTY tests | 10 | 10 | -- |
| Total tests | 1,678 | 1,816 | +138 |
| PRs in this release | -- | 41 | -- |
| Files changed | -- | 112 | -- |
Install
# Homebrew (macOS/Linux)
brew install patchloom/tap/patchloom
# crates.io
cargo install patchloom
# Pre-built binaries
# https://github.com/patchloom/patchloom/releases/latestpatchloom: v0.5.0
Patchloom 0.5.0
The 0.5.0 release brings network-accessible MCP, a richer library API for embedders, and a major internal quality push: 55 PRs, +10,900 lines across 67 files, and 1,678 tests (up from ~1,350 in 0.4.0).
Highlights
Streamable HTTP/HTTPS transport for the MCP server
The MCP server is no longer limited to stdio. The new --http flag starts a Streamable HTTP server that any remote MCP client can connect to, bringing patchloom in line with the MCP specification's network transports.
# HTTP on localhost
patchloom mcp-server --http --port 3000
# HTTPS with TLS termination (rustls)
patchloom mcp-server --http --tls-cert cert.pem --tls-key key.pem
# Ephemeral port (OS-assigned, printed in the startup banner)
patchloom mcp-server --http --port 0Supports graceful shutdown (Ctrl+C), configurable bind address (--host), and automatic host header restriction for loopback-only binds. Gated behind the mcp-http Cargo feature, enabled by default.
execute_plan MCP tool
Agents can now submit a full multi-operation transaction plan in a single MCP tool call instead of chaining individual calls. This is the MCP equivalent of patchloom tx and supports all 25 operation types with atomic rollback.
{
"tool": "execute_plan",
"arguments": {
"plan": "op: doc.set\npath: config.yaml\nselector: version\nvalue: \"2.0.0\"\n---\nop: replace\npath: README.md\nold: \"1.0.0\"\nnew: \"2.0.0\""
}
}Library API for Rust embedders
Building on 0.4.0's PathGuard foundation, this release fills the remaining gaps for downstream Rust projects embedding patchloom as a library:
filesfeature: Pure file helpers (binary detection, text reading, parallel file processing) available without pulling the full CLI.api::search_directory: Recursive grep-like search with fullSearchOptions(glob, ignore files, context lines, multiline).PlanReportreturn type:execute_plannow returns structured results, not just exit codes.- Search ignore customization:
custom_ignore_filenames,exclude_patterns, andmax_resultsacross CLI, MCP, and library surfaces. - AST rewrite helpers:
ast::rename_symbolandast::validate_syntaxavailable under theastfeature withoutcli.
Internal quality overhaul
16 refactoring PRs restructured the MCP server internals:
- A
mcp_tool!macro eliminated ~800 lines of per-handler boilerplate, with all 35 MCP tools now using a consistent pattern. src/api.rswas decomposed into focused submodules.- Transaction execution was unified between CLI (
cmd/tx) and library (src/tx) paths, sharingTxState,CachedDoc, andTxExecResulttypes. - GlobalFlags construction boilerplate was reduced across the codebase.
20 test-focused PRs added 300+ new tests and strengthened existing ones: bare .success() assertions were upgraded to .code(0), weak contains("a") substring checks were replaced with precise JSON field matches, and edge cases for MCP error paths, guard rejection, and TLS validation were added.
Bug fixes
- HTTPS server banner now shows the actual bound port when using
--port 0(previously showed:0). - File MCP tools (
create_file,delete_file,move_file,append_file) now route through the tx engine for structured JSON responses, consistent with all other write tools. - YAML nested key creation and markdown move-section body attachment fixed.
PathGuard::allow_temp_directory()now handles macOS/private/tmpsymlink resolution.- Crates.io publish workflow made resilient to lock/index drift.
Numbers
| Metric | v0.5.0 |
|---|---|
| CLI commands | 22 |
| MCP tools | 35 (was 31) |
| Unit tests | 948 |
| Integration tests | 720 |
| PTY tests | 10 |
| Total tests | 1,678 |
| PRs in this release | 55 |
| Lines changed | +10,954 / -3,864 |
Install
# Homebrew
brew install patchloom/tap/patchloom
# Cargo
cargo install patchloom
# Shell installer (macOS/Linux)
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/patchloom/patchloom/releases/latest/download/patchloom-installer.sh | shAll install methods ship with every feature enabled: CLI, stdio MCP, HTTP/HTTPS MCP, and AST operations.
patchloom: v0.4.0
Patchloom 0.4.0
This is a major release focused on making Patchloom an excellent, safe, and ergonomic library for Rust applications and AI coding agents, while continuing to polish the CLI and MCP surfaces.
Highlights
PathGuard for safe library embedding
The biggest addition in 0.4.0 is first-class support for PathGuard (and the flexible AbsolutePathPolicy builder) across the entire public library API.
- All high-level functions in
patchloom::api(replace_text,doc_*,md_*,file_*,tidy,execute_plan, etc.) now accept an optionalguard: Option<&PathGuard>as the final parameter. execute_planperforms upfront validation using the centralizeddeclared_pathshelper.- The builder makes common relaxed policies easy:
PathGuard::builder(root).allow_temp_directory().build(). - Strict
Rejectpolicy, cross-file operation safety (rename, md.move_section, etc.), and symlink-aware checks are all enforced at the right time.
This was driven by real downstream usage (e.g. bline) and closes a long tail of PathGuard-related tech debt (#748–#750, #755–#759, #762).
Library modularity and semver safety
- The
clifeature is now optional. Usedefault-features = false+features = ["mcp", "ast"](or any subset) for a dramatically smaller dependency tree when embedding Patchloom. - Many public types were moved to more reusable locations (
ops,write, etc.) and re-exported. - All public structs and enums are now
#[non_exhaustive]for future-proofing.
Other notable improvements
- Shared
declared_pathshelper eliminates duplicated path collection logic betweencmd/txupfront guard checks and MCP validation (#762). - Extensive new tests for guard behavior (relaxed policies, destination rejection, cross-file operations, cfg combinations).
- Hygiene wins: removal of now-dead
TxState.guardcode, direct unit coverage for the path declaration helper, stronger test assertions in several areas. - Numerous fixes for atomicity, confirm flows, Windows path handling, and edge cases from the recent improvement cycles.
- Better documentation and examples for the library API surface.
Migration notes
If you were already using the library API, the new guard parameter is trailing and optional. Update call sites:
// Before
api::replace_text(path, old, new, &opts, mode)?;
// After (no guard)
api::replace_text(path, old, new, &opts, mode, None)?;
// With guard
let guard = PathGuard::builder(cwd).allow_temp_directory().build()?;
api::replace_text(path, old, new, &opts, mode, Some(&guard))?;See the patchloom::api module documentation for full details and examples.
Thank you
Thanks to everyone who filed issues, reviewed, and used the library in real agent tooling. This release makes the "library first" story much stronger.
Full changelog and commit history are in the release PR.
patchloom: v0.3.0
Patchloom v0.3.0
This is the biggest patchloom release yet. It adds a full AST layer powered by tree-sitter, a new file.append command, post-write formatting hooks, smarter error recovery for failed edits, and a major internal cleanup.
AST-aware operations (new ast feature)
Patchloom can now parse source code. The new ast command uses tree-sitter grammars for 20 languages (Rust, Go, Python, TypeScript, JavaScript, Java, C, C++, C#, Ruby, Kotlin, Swift, Scala, PHP, Lua, Zig, Elixir, Haskell, Bash, HCL) and provides 11 subcommands:
| Subcommand | What it does |
|---|---|
ast list |
List symbol definitions in a file or directory |
ast read |
Read a specific symbol by name |
ast rename |
Rename identifiers, skipping strings and comments |
ast validate |
Check syntax of source files |
ast search |
Structural search using tree-sitter queries |
ast refs |
Find all references to a symbol across files |
ast deps |
Extract import/dependency statements |
ast map |
Generate a ranked repository map (PageRank) |
ast replace |
Replace text only within a specific symbol's body |
ast impact |
Transitive impact analysis of changing a symbol |
ast diff |
Structural diff between two file versions |
All subcommands support --json and --jsonl output. The ast feature is enabled by default; build with --no-default-features for a smaller binary without tree-sitter.
AST operations are also available as MCP tools (ast_list, ast_read, ast_rename, ast_validate) and in transaction plans.
Post-write formatting
All write commands (replace, create, append, md, doc, tidy, patch) now accept a --format flag that runs a shell command after a successful --apply:
patchloom replace --from "old_fn" --to "new_fn" src/main.rs \
--apply --format "cargo fmt --all"The formatter runs after the file is written but before the command exits, so you get a clean result in one step. A --format-timeout flag (default 30s) prevents hanging formatters. Also works through --confirm interactive mode and in transaction plans via the format lifecycle step.
file.append
New append command adds content to the end of an existing file:
patchloom append src/lib.rs --content "pub mod new_module;"
echo "new entry" | patchloom append CHANGELOG.md --stdinSupports all standard modes (--diff, --check, --apply, --confirm), --format post-write hooks, and the --json/--jsonl output flags. Also available as the file_append MCP tool.
Smarter error recovery for edits
The fallback module, previously built but unwired, is now integrated at three levels:
- Similarity hints. When a literal replace finds no matches, patchloom suggests similar strings using Jaro-Winkler scoring:
no matches for 'proccess_data' in main.rs (did you mean: process_data?). - Structural validation. The MCP
replace_texttool pre-validates edits to JSON, YAML, and TOML files, warning about broken syntax or unbalanced brackets before the replacement is applied. - Context-anchored matching. The
replace_textMCP tool and transaction plans now accept optionalbefore_contextandafter_contextparameters. When exact matching fails, patchloom uses surrounding lines as anchors to locate the intended edit target, recovering from whitespace drift or minor formatting changes.
Word-boundary matching
New --word-boundary flag on replace prevents partial-word matches. patchloom replace --from "File" --to "Document" --word-boundary replaces SetupFile but not BenchSetupFile. Available on the CLI, in transaction plans, and via the MCP replace_text tool.
Library API for downstream consumers
The fallback and ast modules now expose their core building blocks as public API, letting library consumers (e.g., bline) use patchloom's edit recovery and tree-sitter infrastructure directly.
Fallback module (patchloom::fallback):
| Function/type | What it does |
|---|---|
resolve_with_fallback() |
Full fallback chain: exact, anchor, similarity, structured error |
anchor_match() |
Anchor-based matching using surrounding context lines |
validate_edit() |
Pre-validate a replacement against JSON/YAML/TOML syntax |
AnchorMatchResult |
Return type with matched_text, start_offset, strategy |
MatchStrategy |
Enum: Exact, Anchor, Similarity |
ValidationResult |
Return type with valid, errors, warnings |
AST module (patchloom::ast, requires features = ["ast"]):
All high-level AST functions (extract_symbols, search_query, validate_source, rename_in_source, find_refs_in_source, structural_diff, compute_impact, replace_in_symbol, generate_map) were already public.
Internal cleanup
- ops.rs split. The 4,369-line monolithic
src/ops.rswas split intosrc/ops/{doc,md,patch,replace}.rs. - Transaction engine refactoring. Extracted
build_full_tx_output(eliminating 6x duplication),validate_and_prepare_plan(shared validation), andexecute_doc_op/execute_file_op(breaking up a 460-line match). - MCP validator consolidation. Unified
validate_content_size/validate_param_sizeinto a sharedvalidate_sizehelper. - Visibility tightening.
diffandexitmodules narrowed topub(crate). All 28 MCP param structs narrowed topub(crate). - Dead code removal. Removed
DiffResult::total_files_changed(set at 20+ sites but never read).
Breaking changes
diffandexitmodules are no longer public. Code importingpatchloom::diff::*orpatchloom::exit::*must use the library API instead.Operation::Replacehas three new fields:word_boundary,before_context,after_context. Code constructing this variant via struct literals must include them (use..Default::default()or add explicit values).DiffResult::total_files_changedfield removed.- Internal helper
truncate_strin the fallback module narrowed frompubtopub(crate). - All MCP param structs narrowed from
pubtopub(crate).
Test coverage
1,588 tests (887 unit + 694 integration + 7 PTY), up from 1,476 in v0.2.0.
Links
patchloom: v0.2.0
Patchloom v0.2.0
Patchloom has always been a CLI and an MCP server. Starting with this release, it is also a Rust library. This release exposes the core editing engine as a public API, hardens transaction safety, and adds three-way patch merging.
Library API
Patchloom's structured editing engine is now available as a Rust library. Add it as a dependency with no async overhead:
[dependencies]
patchloom = { version = "0.2", default-features = false }The api module exposes doc, replace, markdown, file, and patch operations. All types are Send + Sync. Four utility modules are also public:
containment-- workspace path guard that prevents directory traversal attacks. Two-layer defense: syntactic depth check plus symlink-aware canonicalization.exec-- shell command execution with timeout and process-tree management.files-- file-walking, SIMD-accelerated binary detection, and text reading helpers.write-- atomic file writes via tempfile with write-policy transformations.
All public structs and enums are marked #[non_exhaustive] for forward-compatible evolution. Cargo-semver-checks runs in CI on every release PR.
Three-way patch merge
New patch merge subcommand handles stale diffs gracefully:
# Check if a patch applies cleanly, merges cleanly, or has conflicts
patchloom patch merge stale.patch --check
# Apply with three-way merge
patchloom patch merge stale.patch --apply
# Allow conflict markers in output
patchloom patch merge stale.patch --apply --allow-conflictsAlso available in tx plans via on_stale: "merge" and allow_conflicts: true, and in the MCP server's apply_patch tool.
Conflicts produce familiar <<<<<<< patchloom (ours) / >>>>>>> patch (theirs) markers. Exit code 8 (CONFLICTS) signals unresolved conflicts.
Transaction rollback hardening
strictnow defaults totrue. Format or validation failures roll back all writes automatically. Override with"strict": falsein the plan,[tx] strict = falsein.patchloom.toml, or--no-stricton the CLI.- Mid-commit recovery. If a write fails partway through, patchloom restores already-written files from the backup session. Exit 7 for clean rollback, exit 1 if restore is incomplete.
- Clearer exit semantics. Staging failures exit 4 (
operation_failed), distinct from parse errors.
Other improvements
- Config validation. Invalid values in
.patchloom.toml(e.g.,normalize_eol = "unix") now emit stderr warnings instead of silently ignoring. - Batch quoting docs. Expanded guidance for JSON string values in batch format, with Unix and Windows examples.
- Schema fix.
md.move_sectionexamples inpatchloom schemaoutput now include the requiredopfield. - Test coverage. 1,476 tests (807 unit + 669 integration), up from 1,300 in v0.1.7.
Breaking changes
All public structs and enums are now #[non_exhaustive]. Code that constructs these types via struct literals must add ..Default::default(). Serde deserialization (the primary construction path for plans and MCP params) is unaffected.
Links
patchloom: v0.1.7
0.1.7 (2026-06-16)
Features
- add --whole-line, --range, and --collapse-blanks to replace (#564) (5651320), closes #563
- close #573 and #574 - complete API parity and edge case tests (#576) (d6fc1a9)
- md.move-section -- move a heading section between files (#554) (d6f42e7), closes #553
Bug Fixes
- improvement cycle 11 — config, schema, MCP tests, docs (#568) (ea4967b)
- improvement cycle 11b - docs, CI hardening (#569) (5041287)
- improvement cycle 12 - Windows CI, fuzz CI matrix (#572) (c24792f)
- improvement cycle 13 - tests, inline refactor, error context (#575) (6208177)
- improvement cycle 14 - strengthen weak test assertions (#577) (2ba2396)
- make unit tests portable in Docker and pseudo-TTY environments (#579) (591b4d8)
- md move-section same-file path detection and cross-file --check mode (#556) (da76cc5)
- rename same-file detection via path canonicalization (#557) (a1b5573)
- replace broken shields.io badges with gist endpoints (#578) (23b14f3)
- update MCP bench to use individual tool calls (#570) (655a1d2)
patchloom: v0.1.6
0.1.6 (2026-06-08)
Features
Bug Fixes
- add error context to backup restore and rename cross-device paths (#543) (69018e7)
- ci: use App token in update-branches to trigger CI on updated PRs (#523) (e51cdae)
- correct pinned action SHAs in docs workflow (#549) (b1fabf6)
- improvement cycle (UTF-8 truncate, doc_set double-parse, docs freshness) (#531) (a8dffb9)
- md silent default mode, search empty-pattern guard, strengthen assertions (#542) (45d3239)
- md/doc --check produce stdout output and doc --json errors use structured JSON (#546) (819fb7c), closes #544 #545
- propagate read errors in file_create and extract inline conditional (#533) (26ab09c)
- propagate YAML serialization error and remove unnecessary borrows in ops.rs (#537) (24e67f4)
- remove documentation field so crates.io auto-links to docs.rs (#547) (f6bbd10)
