Patchloom

Patchloom is a Rust CLI for structured file edits, built for AI coding agents.

AI agents are good at reasoning about code but bad at editing config files. When an agent needs to bump a version in config.yaml, it reaches for sed or text replacement. That works until the regex strips a YAML comment, breaks indentation, or produces invalid syntax. When the task touches six files, that means six separate tool calls, each a full round-trip back to the LLM. And on Windows, sed and jq do not exist.

Patchloom fixes all three problems with a single Rust binary.

What it does

  • Structured editing: Edit JSON, YAML, and TOML files by selector path, not regex. Comments and formatting are preserved because the file is parsed, not pattern-matched.
  • Batch operations: Bundle multiple file edits into a single tool call, cutting round-trips from six to one.
  • Cross-platform: Works identically on Linux, macOS, and Windows with zero dependencies.
  • Safe by default: All write operations preview changes without mutating files unless --apply is passed.
  • MCP server: Exposes all operations as structured tool calls for MCP-capable agents.

Quick example

# Edit a YAML value by selector; comments and formatting survive
patchloom doc set config.yaml database.port 5432 --apply

# Version bump across 6 files in a single tool call
patchloom batch --apply <<'EOF'
doc.set package.json version "2.0.0"
doc.set config.yaml app.version "2.0.0"
replace README.md "1.0.0" "2.0.0"
EOF

20 commands

CategoryCommands
Textsearch, replace, patch
Structureddoc (JSON/YAML/TOML), md (Markdown)
Filescreate, delete, rename, read, status
Batchtx (atomic transactions), batch (line-oriented)
Normalizetidy (whitespace, line endings)
Safetyundo (backup restoration)
Agentmcp-server, agent-rules, schema, explain
Setupinit, completions

Get started

Head to Installation to install, then follow the Quickstart to make your first edit.

Installation

From Homebrew (macOS and Linux)

brew install patchloom/tap/patchloom

This installs patchloom with all commands, including the MCP server.

From crates.io

cargo install patchloom

From GitHub Releases

Pre-built binaries for Linux (x64, ARM64), macOS (x64, ARM64), and Windows (x64) are available on the Releases page. Download the archive for your platform, extract, and place patchloom on your PATH.

Shell and PowerShell installer scripts are also available:

# Unix (Linux/macOS)
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/patchloom/patchloom/releases/latest/download/patchloom-installer.sh | sh

# Windows (PowerShell)
powershell -ExecutionPolicy ByPass -c "irm https://github.com/patchloom/patchloom/releases/latest/download/patchloom-installer.ps1 | iex"

Pre-built binaries include all commands, including the MCP server.

From source

Install from source (requires Rust 1.95+):

git clone https://github.com/patchloom/patchloom.git
cd patchloom
cargo install --path .

This builds with MCP support by default. To build without MCP (smaller binary), use cargo install --path . --no-default-features.

If you're contributing from a source checkout, use make check-fast while iterating and make check before committing.

Shell completions

After installing, generate shell completions:

# bash (system-wide; may require sudo and /etc/bash_completion.d in your setup)
patchloom completions bash > /etc/bash_completion.d/patchloom

# zsh (ensure ~/.zfunc is in $fpath, e.g. via oh-my-zsh custom or compinit)
patchloom completions zsh > ~/.zfunc/_patchloom

# fish
patchloom completions fish > ~/.config/fish/completions/patchloom.fish

# elvish
patchloom completions elvish > ~/.config/elvish/rc.elv

# PowerShell
patchloom completions powershell >> $PROFILE

Verify

patchloom --version
patchloom --help

Quickstart

This guide takes you from zero to a working multi-file edit in under 5 minutes.

Prerequisites

  • Patchloom installed (see installation.md)
  • A git repo to work in, or a test directory you can initialize with git init before Step 6

Step 0: Set up your project (optional)

Run init to generate agent rules, get shell completions, and detect MCP setup:

patchloom init

This creates AGENTS.md in a new project or appends the rules to an existing agent instructions file so AI agents know how to use patchloom. Pass -y to skip confirmation prompts. If .vscode/ or .cursor/ already exists, init also prints ready-to-copy .vscode/mcp.json or .cursor/mcp.json snippets.

Step 1: Search for something

Find all TODO comments in your project:

patchloom search 'TODO' src/

Count them:

patchloom search 'TODO' --count src/

Limit a search to a nested subtree with --glob:

patchloom search 'TODO' src/ --glob 'sub/*.rs'

Step 2: Replace text across files

Preview a rename (no files changed yet):

patchloom replace 'old_function' --to 'new_function' src/

The output shows a unified diff. When it looks correct, apply:

patchloom replace 'old_function' --to 'new_function' src/ --apply

Step 3: Edit structured config

Read a value from a JSON file:

patchloom doc get package.json version

Set a new value:

patchloom doc set package.json version "2.0.0" --apply

Step 4: Batch a few file edits into one call

When you need several related edits at once, batch is the fastest path. This example assumes package.json, README.md, and CHANGELOG.md exist, and that CHANGELOG.md contains a ## Unreleased heading:

Preview the grouped edits:

patchloom batch <<'EOF'
doc.set package.json version "3.0.0"
replace README.md "v1.0.0" "v3.0.0"
md.insert_after_heading CHANGELOG.md "## Unreleased" "- Bumped to v3.0.0"
EOF

Apply them once the diff looks right:

patchloom batch --apply <<'EOF'
doc.set package.json version "3.0.0"
replace README.md "v1.0.0" "v3.0.0"
md.insert_after_heading CHANGELOG.md "## Unreleased" "- Bumped to v3.0.0"
EOF

Step 5: Run an atomic transaction with a saved plan

Use tx when the change should live in a reusable plan file, or when you need format/validate lifecycle steps in the same transaction.

Create a plan file called bump.json:

{
  "version": "1",
  "write_policy": { "ensure_final_newline": true },
  "operations": [
    { "op": "doc.set", "path": "package.json", "selector": "version", "value": "2.0.0" },
    { "op": "replace", "path": "README.md", "from": "v1.0.0", "to": "v2.0.0" },
    { "op": "md.insert_after_heading", "path": "CHANGELOG.md", "heading": "## Unreleased", "content": "- Bumped to v2.0.0" }
  ]
}

Preview:

patchloom tx bump.json --diff

Apply all changes atomically:

patchloom tx bump.json --apply

If an operation fails, nothing is written. Format and validate lifecycle steps run after writes, so use "strict": true in the plan if you want those failures to roll back all changes too. Lifecycle failure output includes the failing step number, exit status, and the cwd used for that step.

Step 6: Inspect and undo changes

After any --apply, you can ask patchloom what changed and restore the latest backup session.

patchloom status is git-backed. If you're using a scratch directory, run git init, add the files you want tracked, and make an initial commit before this step.

See pending working-tree changes:

patchloom status

Preview what undo would restore (exit code 2 means files would be restored):

patchloom undo

Restore the most recent backup session:

patchloom undo --apply

Step 7: Use in CI

Check whether a plan would produce changes (exit code 2 = changes pending):

patchloom tx bump.json --check
echo $?  # 0 = clean, 2 = changes detected

Get machine-readable output:

patchloom --json tx bump.json --apply

Returns:

{
  "ok": true,
  "status": "success",
  "files_changed": 3,
  "files_created": 0,
  "files_deleted": 0,
  "changes": [
    { "path": "CHANGELOG.md", "action": "modified" },
    { "path": "README.md", "action": "modified" },
    { "path": "package.json", "action": "modified" }
  ]
}

Troubleshooting

Config file not loading

Patchloom searches for .patchloom.toml starting from the working directory and walking up to the filesystem root. If your config does not seem to take effect:

  1. Verify the file location. Run from the directory containing .patchloom.toml or a subdirectory beneath it.

  2. Check for TOML syntax errors. Patchloom prints a warning to stderr when it finds a .patchloom.toml that cannot be parsed:

    warning: malformed /path/to/.patchloom.toml: expected `=`, found ...
    

    Validate your file with:

    patchloom doc get .patchloom.toml write_policy
    

    If this errors, fix the TOML syntax.

  3. CLI flags override config. Flags like --ensure-final-newline and --normalize-eol always take precedence over .patchloom.toml values.

Backups filling up disk

Backup sessions are stored under .patchloom/backups/ and are automatically pruned after 7 days. If you need to free space immediately:

rm -rf .patchloom/backups/

This is safe; the next --apply run will create a fresh backup directory.

Exit codes

CodeMeaning
0Success
1General failure
2Changes detected (used by --check)
3No matches found
4Parse error
5Ambiguous match
6Validation failed
7Rollback (transaction failed and was rolled back)

Next steps

  • Browse the examples directory for more tx plan patterns
  • See the full reference guide for command, operation, and notable mode guidance
  • Read concepts.md for write modes, exit codes, and glob filtering

Core Concepts

Commands

Patchloom has 20 commands:

  • search / replace -- text-level find and replace across files
  • patch -- apply unified diffs
  • md -- markdown-aware editing (sections, bullets, tables, headings)
  • doc -- parser-backed JSON, YAML, and TOML mutations
  • tidy -- whitespace and line-ending normalization
  • create / delete / rename -- file lifecycle
  • read -- file content inspection with optional line range (supports multiple files)
  • status -- uncommitted change summary from git
  • tx -- atomic multi-operation transactions
  • batch -- line-oriented multi-operation format (delegates to tx engine)
  • completions -- shell completion generation
  • agent-rules -- print end-user agent documentation for patchloom
  • schema -- export operation schemas with tier filtering and system prompts
  • explain -- summarize a tx plan in plain English before applying
  • undo -- restore files from a backup created by --apply
  • init -- set up patchloom in a project (agent rules, completions, MCP)
  • mcp-server -- MCP protocol server exposing patchloom tools for AI agents

For feature-by-feature Use when guidance on commands, operations, and notable modes, see the reference guide.

Write modes

Every write command supports four modes:

FlagBehaviorUse case
--diff (default)Print a unified diff of what would changePreview before applying
--checkExit 0 if clean, exit 2 if changes detectedCI pipelines, dry-run validation
--applyWrite changes to diskActual mutation
--confirmShow the diff, then prompt before writingInteractive preview-then-apply

These modes are mutually exclusive. Patchloom is safe by default: nothing is written unless you pass --apply or confirm an interactive prompt.

Write policy

A write policy controls transformations applied to all content before it reaches disk:

  • --ensure-final-newline -- non-empty files always end with \n
  • --normalize-eol <lf|crlf> -- standardize line endings
  • --trim-trailing-whitespace -- remove trailing spaces on every line
  • --respect-editorconfig -- read policy from .editorconfig if present

Standalone write commands use these flags directly. In tx, the same flags act as defaults for all writes, and plan-level write_policy entries override conflicting CLI flags for self-contained plans.

In tx plans, set these at the plan level:

{
  "version": "1",
  "write_policy": { "ensure_final_newline": true },
  "operations": [...]
}

Project configuration

Create a .patchloom.toml in your project root to set per-project defaults. CLI flags override config values.

[write_policy]
ensure_final_newline = true
normalize_eol = "lf"
trim_trailing_whitespace = true

[exclude]
globs = ["target/**", "node_modules/**"]

[output]
color = "auto"

The config file is searched from the working directory upward, so it works in subdirectories too.

Undo safety net

Before any --apply write, patchloom saves the original content of each affected file to .patchloom/backups/. If something goes wrong:

patchloom undo --list          # see available backups
patchloom undo                 # dry-run: show what would change
patchloom undo --apply         # actually restore files

Backups older than 7 days are auto-pruned.

Color output

Patchloom colorizes diffs and search results when stdout is a terminal. Override with:

  • --color=always -- force color (useful when piping to a pager like less -R)
  • --color=never -- disable color
  • NO_COLOR=1 -- environment variable that disables color for all tools (no-color.org)

Machine-readable modes (--json, --jsonl, --quiet) never produce color.

Transaction plans

The tx command runs multiple operations atomically. If any operation fails, all changes are rolled back and no files are written.

Plans are JSON objects with three lifecycle arrays:

  1. operations -- the mutations (replace, doc.set, md.replace_section, etc.)
  2. format -- shell commands that run after writes (e.g., cargo fmt)
  3. validate -- shell commands that verify correctness (e.g., make check)

With "strict": true, a format or validation failure reverts all writes (exit 7). Without strict mode, writes stay on disk (exit 6).

Exit codes

Every command returns a specific exit code:

CodeMeaning
0Success
1General error
2Changes detected (with --check)
3No matches found
4Parse error in input
5Ambiguous (multiple replace matches, or stale patch context)
6Validation failed (writes may remain)
7Rollback (strict mode, no writes remain)

These codes let CI pipelines and agent frameworks branch on outcomes without parsing output.

Glob filtering

Most commands accept --glob <pattern> (repeatable) to restrict which files are processed:

patchloom replace "old" --to "new" --glob "*.rs" --glob "*.toml" --apply

Glob patterns match either the basename or the path relative to the input root. For example, if you search src/, then --glob 'sub/*.txt' matches src/sub/file.txt.

In tx plans, individual operations can use "glob" instead of "path" to target multiple files.

Security model

Patchloom runs with the privileges of the invoking user and treats all inputs (command-line arguments, plan files, stdin) as trusted. This is the same trust model as make, sh, or cargo.

What this means in practice:

  • Plans can execute arbitrary shell commands. The format and validate lifecycle steps pass their cmd field to sh -c (or cmd /C on Windows) with the user's full privileges. Only load plans you trust.
  • File operations are unrestricted. create, delete, read, replace, patch, and all tx operations accept any path the invoking user can access. There is no sandbox, chroot, or path restriction.
  • Plan cwd overrides the working directory. A plan's cwd field changes the working directory for all subsequent operations and lifecycle steps. Relative values resolve from the invocation root, not from the plan file location. In normal CLI use this still runs with the invoking user's filesystem access; in MCP mode the resolved directory must stay under the server root.

For AI agent authors: Do not construct plans from untrusted conversational input without validation. A plan is equivalent to a shell script. Treat plan files with the same care you would treat a Makefile or a bash script from an unknown source.

MCP Setup Guide

Patchloom includes an MCP (Model Context Protocol) server for structured tool calls. MCP-capable AI agents can call Patchloom tools directly with JSON parameters, with no shell command construction, no quoting issues, and no --apply flag needed.

Verify MCP support

The MCP server is included by default in all pre-built binaries, Homebrew, and crates.io installs. Verify it works:

patchloom mcp-server --help

Configure your agent

Grok (config.toml)

Add to ~/.grok/config.toml:

[mcp_servers.patchloom]
command = "/path/to/patchloom"
args = ["mcp-server"]
# Add "--allow-shell" to args to enable format/validate lifecycle steps:
# args = ["mcp-server", "--allow-shell"]

Claude Desktop (JSON)

Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

{
  "mcpServers": {
    "patchloom": {
      "command": "/path/to/patchloom",
      "args": ["mcp-server"]
    }
  }
}

VS Code (.vscode/mcp.json)

Create .vscode/mcp.json in your workspace root:

{
  "servers": {
    "patchloom": {
      "command": "/path/to/patchloom",
      "args": ["mcp-server"]
    }
  }
}

Cursor (.cursor/mcp.json)

Create .cursor/mcp.json in your workspace root:

{
  "servers": {
    "patchloom": {
      "command": "/path/to/patchloom",
      "args": ["mcp-server"]
    }
  }
}

Or use the Patchloom VS Code extension to configure MCP automatically via the Patchloom: Configure MCP command.

Generic stdio MCP

Any MCP client that supports stdio transport can connect by spawning patchloom mcp-server as a subprocess. The server communicates via JSON-RPC over stdin/stdout.

Available tools

ToolDescription
doc_setSet a value by selector in a JSON, YAML, or TOML file
doc_deleteDelete a value by selector from a structured file
doc_mergeDeep-merge an object into a document
doc_appendAppend a value to an array
doc_prependPrepend a value to an array
doc_ensureSet a value only if the selector path does not exist
doc_delete_whereDelete array elements matching a predicate
doc_updateUpdate array elements matching a predicate
doc_moveMove a value from one selector path to another
doc_getRead a value by selector (read-only)
doc_queryQuery a structured file: has, keys, len, select, or flatten (read-only)
doc_diffCompare two structured files (read-only)
search_filesSearch text files for a pattern, including literal, case-insensitive, count, file-only, multiline, invert-match, and assert-count modes. Binary and invalid UTF-8 files are skipped (read-only)
git_statusShow uncommitted file changes vs git HEAD (read-only)
read_fileRead file contents with optional line range
replace_textReplace text in a text file (literal or regex). Binary and invalid UTF-8 files are skipped
md_upsert_bulletAdd a bullet under a markdown heading
md_table_appendAppend a row to a markdown table
md_replace_sectionReplace a markdown section by heading
md_insert_after_headingInsert content after a markdown heading
md_insert_before_headingInsert content before a markdown heading
md_lintLint an AGENTS.md file for common issues
fix_whitespaceFix whitespace and line endings in a text file. Binary and invalid UTF-8 files are skipped
create_fileCreate a new file with content
delete_fileDelete a file
move_fileMove or rename a file (binary-safe)
apply_patchApply a unified diff
batch_replaceReplace the same text across multiple files atomically
batch_tidyFix whitespace in multiple files atomically

How MCP mode differs from CLI mode

AspectCLI modeMCP mode
Apply behaviorRequires --apply flagAuto-applies (writes are the default)
Input formatShell argumentsStructured JSON parameters
Path securityNo restrictionPaths must stay within working directory
Error formatstderr textMCP error response with structured content
DiscoveryAgent reads AGENTS.mdAgent discovers tools via MCP protocol

Debugging and logging

The MCP server can log every tool call to a JSONL file for debugging and performance analysis. Each line records the tool name, duration, and success/failure status.

Enable logging with the --log flag:

patchloom mcp-server --log /tmp/patchloom-mcp.log

Or set the PATCHLOOM_MCP_LOG environment variable (the --log flag takes precedence):

export PATCHLOOM_MCP_LOG=/tmp/patchloom-mcp.log
patchloom mcp-server

Each line is a JSON object:

{"ts":1749123456789,"tool":"replace_text","duration_ms":3,"ok":true}
{"ts":1749123456800,"tool":"doc_set","duration_ms":5,"ok":false,"error":"file not found"}
FieldTypeDescription
tsnumberUnix timestamp in milliseconds
toolstringTool name that was called
duration_msnumberExecution time in milliseconds
okbooleanWhether the call succeeded
errorstringError message (only present on failure)

Configuring logging in your MCP client

Grok (config.toml) -- pass the env var to the MCP server subprocess:

[mcp_servers.patchloom]
command = "/path/to/patchloom"
args = ["mcp-server"]
env = { PATCHLOOM_MCP_LOG = "/tmp/patchloom-mcp.log" }

Or use --log in the args:

[mcp_servers.patchloom]
command = "/path/to/patchloom"
args = ["mcp-server", "--log", "/tmp/patchloom-mcp.log"]

Claude Desktop / VS Code / Cursor (JSON) -- use --log in the args:

{
  "mcpServers": {
    "patchloom": {
      "command": "/path/to/patchloom",
      "args": ["mcp-server", "--log", "/tmp/patchloom-mcp.log"]
    }
  }
}

Or pass the env var (Claude Desktop supports env in server config):

{
  "mcpServers": {
    "patchloom": {
      "command": "/path/to/patchloom",
      "args": ["mcp-server"],
      "env": { "PATCHLOOM_MCP_LOG": "/tmp/patchloom-mcp.log" }
    }
  }
}

Security model

The MCP server enforces path containment: all file paths must resolve within the working directory where patchloom mcp-server was started. Absolute paths, ../ traversal, and symlinks escaping the working directory are rejected. This prevents an agent from accidentally (or maliciously) editing files outside the project.

Each individual tool validates every path before execution.

Example tool call

An MCP-capable agent sends:

{
  "method": "tools/call",
  "params": {
    "name": "doc_set",
    "arguments": {
      "path": "config.yaml",
      "selector": "database.port",
      "value": 5432
    }
  }
}

Patchloom parses the YAML, changes database.port to 5432, preserves all comments and formatting, and writes the file. The agent receives a success response with no further action needed.

Patchloom Reference

This is the reference for Patchloom's meaningful commands, actions, operations, and notable command modes.

  • Start with Quickstart if you want a first success.
  • Read Core Concepts for shared semantics like write modes, exit codes, and transaction behavior.
  • Use this file when you need to choose the right feature or mode for a job, or when a pull request adds meaningful CLI surface and the docs coverage test expects it here.

Global behaviors

Patchloom has a small set of global features that shape how other commands behave.

Write modes

Patchloom write commands default to preview mode. The canonical semantics live in Core Concepts. The sections below focus on when to choose each mode.

--diff

  • What it does: Prints the unified diff for a write command without mutating files.
  • Use when: You want a human review step before applying a change, or you want to inspect the exact patch Patchloom would write.
  • Prefer instead: Use --check for CI pass or fail behavior, or --apply to actually write files.

--apply

  • What it does: Writes the requested change to disk.
  • Use when: You have already previewed the change, or you trust the command and want the mutation to happen now.
  • Prefer instead: Use --diff when reviewing, or --check when you only need a clean or dirty signal.

--check

  • What it does: Calculates whether a write command would change files and returns exit code 2 when changes are pending.
  • Use when: You are wiring Patchloom into CI, pre-commit validation, or agent workflows that should fail on drift.
  • Prefer instead: Use --diff when you need the actual patch text, or --apply when you want the mutation.

--confirm

  • What it does: Shows the diff preview, then prompts Apply? [Y/n] on stderr. If confirmed, applies the change; if declined, exits without writing.
  • Use when: You want a single-command preview-then-apply workflow instead of running the command twice.
  • Prefer instead: Use --apply when scripting (no interactive prompt), or --diff when you only want the preview.

--diff, --apply, --check, and --confirm are mutually exclusive. Passing more than one is rejected with an error. When none is specified, --diff is the default. When --confirm is used and stdin is not a TTY, the command shows the diff without prompting (same as --diff).

Write policy flags

These flags shape how written content is normalized before it reaches disk.

--ensure-final-newline

  • What it does: Ensures non-empty written files end with \n.
  • Use when: You want simple newline hygiene on every touched file without running a separate cleanup command.
  • Prefer instead: Use tidy fix when the goal is repo cleanup, not just normalization of files already being edited.

--normalize-eol

  • What it does: Normalizes written line endings to keep, lf, or crlf.
  • Use when: A repo or downstream tool expects a specific line ending convention.
  • Prefer instead: Use --respect-editorconfig when the repo already declares the desired convention there.

--trim-trailing-whitespace

  • What it does: Removes trailing spaces and tabs from touched lines before writing.
  • Use when: You want text cleanup to happen automatically as part of another write command.
  • Prefer instead: Use tidy fix when the goal is to sweep existing files for whitespace problems.

--respect-editorconfig

  • What it does: Reads .editorconfig when present and applies matching write policy.
  • Use when: The repo already encodes formatting policy in .editorconfig and Patchloom should follow it automatically.
  • Prefer instead: Use explicit write flags, or tx write_policy, when the command should be self-contained and not depend on repo metadata.

Output and scope flags

These flags affect how Patchloom reports results or chooses which files to touch.

--json

  • What it does: Emits one machine readable JSON document for the command result.
  • Use when: Another tool, script, or agent needs structured output instead of human oriented text.
  • Prefer instead: Use --jsonl when you want one JSON object per result line for streaming style consumers.

--jsonl

  • What it does: Emits one compact JSON value per result line instead of one aggregate document.
  • Use when: A command naturally yields multiple result records, or you want compact machine-readable output from single-result commands like create, delete, rename, status, tx, explain, or undo.
  • Prefer instead: Use --json when you want one aggregate document for the whole command.

--quiet

  • What it does: Suppresses non-JSON human readable output.
  • Use when: Only the exit code or the file mutation matters and extra stdout noise would get in the way.
  • Prefer instead: Use --json when another tool still needs structured output.

--cwd

  • What it does: Sets the working directory used to resolve relative paths.
  • Use when: You are invoking Patchloom from outside the target repo, or you want scripts to behave predictably regardless of the caller's current directory.
  • Prefer instead: Use a plan level cwd in tx when the directory choice should travel with the plan itself, but keep it inside the invocation root. Relative plan cwd values resolve from the caller's working directory (--cwd or the process cwd), not from the plan file location.

--glob

  • What it does: Restricts candidate files by one or more glob patterns. Patterns match either the basename or the path relative to the input root, so sub/*.txt matches files under a searched sub/ directory.
  • Use when: A command should only see a narrow file type or subtree, even if the input path is broader.
  • Prefer instead: Use --files-from when another tool has already determined the exact file list.

--files-from

  • What it does: Reads the target file list from a file, or from stdin when passed -.
  • Use when: Another tool already selected the exact paths and Patchloom should operate only on that set.
  • Prefer instead: Use --glob for pattern based scoping, or direct path arguments when the target set is already small and obvious.

--color

  • What it does: Controls when ANSI color codes appear in output. auto (default) enables color when stdout is a terminal and the NO_COLOR environment variable is not set. always forces color even when piped. never disables color unconditionally.
  • Use when: You need to override the default terminal detection, for example forcing color into a pager or disabling it in a terminal that renders escape codes literally.
  • Prefer instead: Set the NO_COLOR environment variable when you want a global, tool-agnostic way to disable color across all CLI tools.

--verbose

  • What it does: Prints diagnostic messages to stderr prefixed with [patchloom]. Shows which operations are running, search parameters, selector evaluation steps, and MCP tool call timing. Can also be enabled by setting the PATCHLOOM_LOG environment variable to any value.
  • Use when: A command produces unexpected results and you need to see what Patchloom is doing internally without reading source code.
  • Prefer instead: Use --json when you need machine-readable output for downstream tools.

Exit codes

Use Core Concepts as the canonical exit code table. When integrating Patchloom into CI or agent workflows, branch on exit codes instead of parsing human readable output.

Commands

These are the main entry points. If you are deciding between commands, start here.

  • What it does: Searches text files with literal or regex matching, optional context, counts, and file only results. Binary and invalid UTF-8 files are skipped.
  • Use when: You need to locate candidate edits, audit repo state, or narrow inputs before changing files. For AI agents, native search/grep tools are typically faster for simple pattern matching.
  • Prefer instead: Use replace for actual text mutation, or doc, md, or patch when you already know the structured change you want.
  • Related: --glob, --files-from, replace

replace

  • What it does: Performs mechanical string replacement across one or many text files, with literal or regex matching. Binary and invalid UTF-8 files are skipped.
  • Use when: You are doing a rename, version bump, boilerplate rewrite, or another string level change where plain text semantics are enough. For AI agents doing single-file replacements, native search_replace tools are typically faster; use patchloom replace inside tx plans when batching multiple file edits.
  • Prefer instead: Use doc for structured data, md for heading aware markdown, or patch when you already have a unified diff.
  • Related: search, tx

patch

  • What it does: Checks or applies a unified diff.
  • Use when: The change already exists as a patch, or you want stale context detection instead of search and replace semantics.
  • Prefer instead: Use replace, doc, or md when you want to describe the mutation directly instead of carrying a diff artifact.
  • Related: patch check, patch apply, tx patch.apply

md

  • What it does: Performs heading aware markdown edits for sections, bullets, tables, and AGENTS linting.
  • Use when: Documentation needs semantic markdown edits that should not depend on raw byte offsets.
  • Prefer instead: Use replace for simple line level edits, or patch for exact diff application.
  • Related: md actions, tx markdown operations

doc

  • What it does: Performs parser backed JSON, YAML, and TOML queries and mutations.
  • Use when: Config or metadata changes should operate on keys and arrays instead of brittle text matching.
  • Prefer instead: Use replace for plain text, md for markdown, or patch for existing diffs.
  • Related: doc actions, tx document operations

tidy

  • What it does: Checks or fixes trailing whitespace, line endings, and final newlines in text files. Binary and invalid UTF-8 files are skipped.
  • Use when: You need repo text normalization, or a CI guard for basic text tidiness.
  • Prefer instead: Use write policy flags when the cleanup should only apply to files already being touched by another command.
  • Related: tidy check, tidy fix, tx tidy.fix

create

  • What it does: Creates a file from literal content or stdin. Exactly one of --content or --stdin is required. Passing both is rejected with --content and --stdin cannot be combined, and passing neither is rejected with either --content or --stdin must be provided. Directory targets are rejected in all modes. When combined with --confirm and --json or --jsonl, the structured output includes applied: true|false so callers can tell whether the prompt was accepted.
  • Use when: Generating a new tracked file is the whole task, or one step in a larger transaction. For AI agents creating a single file, native file creation tools are typically faster; use file.create inside tx plans when bundling with other edits.
  • Prefer instead: Use doc, md, or replace when the file already exists and only needs edits.
  • Related: delete, tx file.create

delete

  • What it does: Removes a file. Directory targets are rejected in all modes. When combined with --confirm and --json or --jsonl, the structured output includes applied: true|false so callers can tell whether the prompt was accepted.
  • Use when: A file should disappear outright and no other atomic edits are needed. For AI agents deleting a single file, native delete tools are typically faster; use file.delete inside tx plans when bundling with other edits.
  • Prefer instead: Use tx file.delete when the removal must be bundled atomically with other changes.
  • Related: create, tx file.delete

rename

  • What it does: Moves (renames) a file from one path to another. Source and destination must both be file paths, not directories. When combined with --confirm and --json or --jsonl, the structured output includes applied: true|false so callers can tell whether the prompt was accepted.
  • Use when: A file needs to be relocated and no other atomic edits are needed. Use file.rename inside tx plans when bundling with other edits.
  • Prefer instead: Use tx file.rename when the rename must be bundled atomically with other changes.
  • Related: create, delete, tx file.rename

tx

  • What it does: Runs multiple operations atomically, then optional format and validate steps.
  • Use when: Editing 3 or more files in one task. Batches N operations into 1 tool call, eliminating agent round-trips. Also provides atomicity, rollback, and format/validate lifecycle. For AI agents, this is the primary speed advantage: one call instead of N.
  • Prefer instead: Use standalone commands when one direct operation is enough.
  • Related: examples, tx fields, tx operations

batch

  • What it does: Executes multiple operations from a simple line-oriented format. Each line is one operation with positional arguments (e.g., doc.set config.json version "2.0.0"). Internally builds a tx plan and delegates to the tx engine.
  • Use when: Editing multiple files and the JSON tx plan format is too verbose. The line format covers 21 operations (doc.set, doc.delete, doc.merge, doc.ensure, doc.append, doc.prepend, doc.update, doc.move, doc.delete_where, replace, file.create, file.delete, file.rename, md.upsert_bullet, md.table_append, md.replace_section, md.insert_after_heading, md.insert_before_heading, md.dedupe_headings, md.lint_agents, tidy.fix) with minimal syntax. For AI agents, this is faster to generate than a full JSON plan.
  • Prefer instead: Use tx when you need format/validate lifecycle steps, strict mode, or operations not supported by the line format (patch.apply, replace with regex/nth, search, read).
  • Related: tx

read

  • What it does: Prints the contents of one or more files, optionally restricted to a line range. Multiple files get ==> path <== separators in text mode, a JSON array in --json mode, and one object per line in --jsonl mode. If at least one requested file is read successfully, the command still exits successfully and reports errors only for the missing files.
  • Use when: An agent needs to inspect one or several files before deciding on an edit. For AI agents, native read_file tools are typically faster for single-file reads.
  • Prefer instead: Use search when you need pattern matching, or doc get when the file is structured and you want a single value.
  • Related: search, doc get

status

  • What it does: Shows which files have uncommitted changes compared to git HEAD. This command is git-backed, so it must run inside a git repository.
  • Use when: An agent needs a quick summary of the working tree before committing, staging, or choosing which files to process. For AI agents, native git status or terminal commands are typically equivalent.
  • Prefer instead: Use git status directly when you need full git porcelain output or staging details.
  • Related: search, read

undo

  • What it does: Restores files from a backup created by a previous --apply operation. Before any --apply write, patchloom saves the original content of affected files to .patchloom/backups/<timestamp>/. In dry-run mode, undo reports what would be restored and exits with code 2 (CHANGES_DETECTED). --json or --jsonl emit that preview as structured output.
  • Use when: An --apply operation produced an undesirable result and you want to revert. Especially useful when the working tree was not committed before applying changes.
  • Notable flags:
    • --list shows available backup sessions. --json emits the full session list as one array, while --jsonl emits one session object per line.
    • --session <timestamp> targets a specific session (defaults to most recent).
    • --apply actually restores files (dry-run by default, showing what would change).
  • Prefer instead: Use git checkout or git stash when working in a committed git repo.
  • Related: tx, replace, tidy

explain

  • What it does: Parses a tx plan (JSON, YAML, or TOML) and prints a numbered, human-readable summary of each operation. Supports --json and --jsonl for structured output, plus --stdin for piped input. If both a path and --stdin are provided, stdin takes precedence and the path is ignored.
  • Use when: A user or agent wants to review what a tx plan will do before running tx --apply. Converts machine-readable plan format into plain English descriptions.
  • Prefer instead: Use tx directly (without --apply) to see the actual diff preview. Use explain when you want a quick overview without touching any files.
  • Related: tx, batch

schema

  • What it does: Exports the complete registry of patchloom operations with JSON Schemas, tier-filtered subsets, and LLM-ready system prompt fragments. Each operation is annotated with a minimum capability tier (weak, medium, strong).
  • Use when: You are building an AI agent that uses patchloom programmatically and need machine-readable operation schemas, or you want to generate a system prompt tailored to a specific model tier.
  • Notable flags:
    • --format json|prompt (default: json): json outputs operation schemas as JSON, prompt outputs markdown suitable for LLM system prompts.
    • --tier weak|medium|strong: Filter operations by minimum capability tier.
    • --examples: Include usage examples in JSON output (omitted by default).
  • Prefer instead: Nothing; this is the only programmatic way to discover available operations and their schemas.
  • Related: agent-rules, mcp-server

agent-rules

  • What it does: Prints an end-user AGENTS.md that teaches AI agents how to use patchloom. Includes command reference, exit codes, write modes, transaction plan format, and usage examples.
  • Use when: You are setting up a project where agents should use patchloom for file operations and need an AGENTS.md or SKILL.md that describes patchloom's interface.
  • Notable flags:
    • --mode cli|mcp|all (default: all): cli omits MCP section, mcp omits CLI shell examples, all includes everything.
    • --platform linux|windows|all (default: all): linux uses heredocs and single-quote syntax, windows uses file arguments and double-quote escaping, all shows both.
  • Prefer instead: Nothing; this is the only way to generate the end-user agent documentation.
  • Related: completions, mcp-server

init

  • What it does: Sets up patchloom in the current project: creates AGENTS.md if needed, otherwise appends the rules to an existing agent instructions file, prints shell completion instructions, and detects MCP configuration opportunities. When .vscode/ or .cursor/ already exists, it prints ready-to-copy .vscode/mcp.json or .cursor/mcp.json snippets.
  • Use when: You just installed patchloom and want a single command to configure a project instead of running agent-rules, completions, and MCP setup separately.
  • Notable flags:
    • -y, --yes: Skip confirmation prompts and auto-accept all actions.
  • Prefer instead: agent-rules if you only need the rules text, or completions if you only need shell completions.
  • Related: agent-rules, completions, mcp-server

mcp-server

  • What it does: Starts an MCP (Model Context Protocol) server on stdio, exposing patchloom operations as structured tool calls. Included by default in all builds.
  • Use when: An MCP-capable AI agent can call patchloom tools directly via structured tool calls instead of constructing shell commands. This eliminates the shell-syntax construction tax and reduces agent errors.
  • --allow-shell: By default, the MCP server rejects tx plans that contain format or validate lifecycle steps (which execute shell commands). Pass --allow-shell to permit these steps. This flag has no effect on the CLI tx command, which always allows lifecycle steps.
  • Prefer instead: Use the CLI directly when the agent does not support MCP, or when patchloom is invoked from scripts and CI.
  • Related: batch, tx

completions

  • What it does: Generates shell completion scripts for bash, zsh, fish, or elvish.
  • Use when: You are installing Patchloom into an interactive shell and want faster command discovery.
  • Prefer instead: Nothing, if Patchloom is only used from scripts or ephemeral CI runners.
  • Related: installation guide

Command modes

These are meaningful command-specific modes that change how a top-level command behaves, even though they are not separate subcommands.

search --files-with-matches

  • What it does: Emits only file paths that contain at least one match.
  • Use when: You need a path list to feed into another tool or command instead of the matching lines themselves.
  • Prefer instead: Use search --count when per-file match totals matter, or plain search when the matching lines matter.

search --count

  • What it does: Emits match counts per file instead of full matching lines.
  • Use when: You are auditing prevalence, comparing files, or gating on how many matches remain.
  • Prefer instead: Use plain search when you need the matching text, or search --files-with-matches when only file membership matters.

search --invert-match

  • What it does: Shows lines that do not match the pattern.
  • Use when: You are looking for non-conforming lines or excluding content that matches a known pattern.
  • Prefer instead: Use plain search when you want the matching lines themselves.

search --multiline

  • What it does: Lets regex matches span multiple lines by making . match newlines.
  • Use when: The pattern you care about is inherently block-shaped, such as a function body or multi-line stanza.
  • Prefer instead: Use plain search for line-oriented patterns because it is simpler and easier to reason about.

search --before-context

  • What it does: Shows N lines before each match but none after (unless combined with -A).
  • Use when: You need to see what precedes a match (function signature before a body, imports before usage) without cluttering output with lines after.
  • Prefer instead: Use --context (-C) when symmetric context is fine, or combine -B and -A for independent before/after counts.

search --after-context

  • What it does: Shows N lines after each match but none before (unless combined with -B).
  • Use when: You need to see what follows a match (function body after signature, error handling after a call) without lines before.
  • Prefer instead: Use --context (-C) when symmetric context is fine, or combine -B and -A for independent before/after counts.

search --case-insensitive

  • What it does: Matches regardless of case.
  • Use when: The target text may appear in inconsistent capitalization across files.
  • Prefer instead: Use case-sensitive search when exact spelling matters and false positives would be noisy.

search --assert-count

  • What it does: Succeeds (exit 0) only if the total match count equals the given number. Exits 2 otherwise.
  • Use when: An agent or CI pipeline needs to verify an invariant (e.g. "exactly 18 markers exist") in one call instead of searching and then comparing the count manually.
  • Prefer instead: Use plain search --count when you want to see counts without a pass/fail assertion.

replace --regex

  • What it does: Treats the pattern as a regex instead of a literal string.
  • Use when: The change is pattern-based, or capture groups should shape the replacement.
  • Prefer instead: Use literal replace for fixed text because it is simpler and less error-prone.

replace --if-exists

  • What it does: Returns success even when no matches are found.
  • Use when: The replacement is intentionally idempotent and should not fail if the repo is already in the desired state.
  • Prefer instead: Use default replace behavior when a missing match should be treated as drift or an error.

replace --nth

  • What it does: Replaces only the Nth occurrence of the target.
  • Use when: Replacing every occurrence would be too broad and the exact positional match matters.
  • Prefer instead: Use plain replace when every occurrence should change, or regex when the target can be narrowed semantically.

replace --insert-before

  • What it does: Inserts text before each match instead of replacing it. The matched text is preserved.
  • Use when: You need to add a line or annotation above an existing anchor without repeating the anchor in the replacement text.
  • Prefer instead: Use --to when the matched text should actually change, not just receive a prefix.

replace --insert-after

  • What it does: Inserts text after each match instead of replacing it. The matched text is preserved.
  • Use when: You need to append content after an existing anchor, such as adding a comment or tag after a specific line.
  • Prefer instead: Use --to when the matched text should actually change, not just receive a suffix.

replace --multiline

  • What it does: Lets regex replacement span multiple lines by making . match newlines.
  • Use when: The target pattern is a multi-line block rather than a single line.
  • Prefer instead: Use line-oriented replace when the match should stay local and easy to inspect.

replace --case-insensitive

  • What it does: Matches regardless of case during replacement.
  • Use when: The target text appears with inconsistent capitalization and should still be updated uniformly.
  • Prefer instead: Use case-sensitive replace when exact spelling is part of the safety boundary.

create --stdin

  • What it does: Reads the new file content from stdin instead of --content.
  • Use when: Another tool is generating the content, or shell composition is cleaner than embedding the full text in one argument.
  • Prefer instead: Use create --content for short inline content that should stay visible in the command itself.

create --force

  • What it does: Overwrites an existing file instead of failing.
  • Use when: File recreation is intentional and should replace previous contents deterministically.
  • Prefer instead: Use default create behavior when accidental overwrite would be dangerous.

patch FILE

  • What it does: Reads the unified diff from a file path (positional argument).
  • Use when: The patch already exists as a saved artifact that should be reviewed, reused, or passed around directly.
  • Prefer instead: Use patch --stdin when another tool is piping the patch text dynamically.

patch --stdin

  • What it does: Reads the unified diff from stdin instead of a file argument.
  • Use when: Another tool is generating or piping the patch text directly.
  • Prefer instead: Use patch FILE when the diff should be stored as a tangible artifact.

doc --predicate

  • What it does: Supplies the key-value predicate used by doc delete-where.
  • Use when: Array cleanup should target matching objects instead of deleting by fixed index or selector path alone.
  • Prefer instead: Use doc delete when one direct selector can remove the target without predicate filtering.

doc --stdin

  • What it does: Reads merge payload content from stdin for doc merge.
  • Use when: The object being merged is generated by another tool or is awkward to express inline.
  • Prefer instead: Use doc merge --value for short, self-contained object literals.

md --stdin

  • What it does: Reads replacement or inserted markdown content from stdin for the section-editing commands.
  • Use when: The markdown payload is generated, large, or easier to stream than to quote inline.
  • Prefer instead: Use --content when the inserted text is small and should stay visible in the command.

tx -

  • What it does: Reads the transaction plan from stdin instead of a plan file. Defaults to JSON; use --plan-format for YAML or TOML.
  • Use when: The plan is generated on the fly or piped from another tool.
  • Prefer instead: Use tx FILE when the plan should be stored, reviewed, or reused.

tx --plan-format yaml

  • What it does: Tells tx to parse the plan as YAML (or TOML) instead of JSON. Auto-detected from file extension for plan files; required when piping YAML from stdin.
  • Use when: The plan is easier to write or generate in YAML/TOML, or when JSON verbosity is friction for inline agent-generated plans.
  • Prefer instead: Use JSON plans when interoperability or strict schema validation matters more than writability.

doc actions

Use these when the top level doc command is right, but you need a specific structured operation.

Comment preservation: All doc write operations preserve inline comments, section comments, and formatting in YAML and TOML files. The parser edits the concrete syntax tree (CST) directly, so only the changed values are rewritten while surrounding comments and whitespace stay intact. This includes operations that change array length (append, prepend, delete-where), which use text-level splicing to preserve comments on the affected file.

doc get

  • What it does: Reads the value at a selector path from a JSON, YAML, or TOML file.
  • Use when: You need one precise value without mutating the document.
  • Prefer instead: Use doc flatten when you are exploring an unfamiliar file and need a broader map of its contents.

doc has

  • What it does: Checks whether a selector path exists.
  • Use when: A script or workflow needs a presence check before choosing a later action.
  • Prefer instead: Use doc ensure when the real goal is to create the value if it is missing.

doc keys

  • What it does: Lists the keys of an object at a selector path.
  • Use when: You want to inspect the shape of a structured object before choosing an edit.
  • Prefer instead: Use doc get when you already know the exact selector you want.

doc len

  • What it does: Counts items in an array or object.
  • Use when: You need a quick cardinality check in scripts, CI, or exploratory work.
  • Prefer instead: Use doc select or doc get when the actual values matter more than the count.

doc set

  • What it does: Sets or creates a value at a selector path.
  • Use when: One exact selector path should be updated deterministically.
  • Prefer instead: Use doc merge for multi field updates, or doc ensure when existing values should be preserved.

doc delete

  • What it does: Removes the value at a selector path.
  • Use when: A selector path or node is obsolete and should disappear cleanly.
  • Prefer instead: Use doc delete-where when the target is a subset of array items instead of one direct selector.

doc delete-where

  • What it does: Deletes array items that match a predicate.
  • Use when: You need to remove selected objects from a list without rebuilding the whole array by hand.
  • Prefer instead: Use doc delete when one direct selector can remove the target.

doc merge

  • What it does: Deep merges an object payload into an existing document.
  • Use when: Several related fields should be added or updated together.
  • Prefer instead: Use doc set when one exact path should change and merge semantics are unnecessary.

doc append

  • What it does: Appends a value to an array.
  • Use when: New items should appear at the end of the list.
  • Prefer instead: Use doc prepend when order or precedence means the new item should come first.

doc prepend

  • What it does: Inserts a value at the front of an array.
  • Use when: The new item should win by order, or defaults should be introduced at the front of the list.
  • Prefer instead: Use doc append when simple chronological growth is enough.

doc select

  • What it does: Reads only the values that match a selector or predicate.
  • Use when: You need a filtered read view of a larger structure.
  • Prefer instead: Use doc update or doc delete-where when the end goal is mutation rather than inspection.

doc update

  • What it does: Updates all matching nodes to the same value.
  • Use when: A broad but uniform change should apply across many selected elements.
  • Prefer instead: Use doc set when the change only targets one path.

doc move

  • What it does: Moves or renames a selector path.
  • Use when: Schema cleanup or path migration should preserve the value while changing the selector.
  • Prefer instead: Use doc set plus doc delete only when the move semantics are not a clean fit.

doc ensure

  • What it does: Creates a value only if it is currently missing.
  • Use when: You need idempotent config bootstrapping and must not overwrite existing values.
  • Prefer instead: Use doc set when the desired value should win even if the selector path already exists.

doc flatten

  • What it does: Lists leaf selector paths and their values.
  • Use when: You are discovering the shape of an unfamiliar structured file.
  • Prefer instead: Use doc get for one targeted read, or doc keys when only the object shape matters.

doc diff

  • What it does: Compares two structured files by their semantic content.
  • Use when: You care about structural value changes more than raw formatting differences.
  • Prefer instead: Use patch or ordinary diff tooling when the exact textual patch matters.

md actions

Use these when markdown structure matters more than raw text matching.

md replace-section

  • What it does: Replaces the body of a heading section.
  • Use when: A section should be treated as authoritative content that can be rewritten in one step.
  • Prefer instead: Use md insert-after-heading when existing section content should stay and you only need to add more text.

md insert-after-heading

  • What it does: Inserts content immediately after a heading.
  • Use when: You want to add a note, release entry, or status line while preserving the existing section body.
  • Prefer instead: Use md replace-section when the whole section should be regenerated.

md insert-before-heading

  • What it does: Inserts content immediately before a heading.
  • Use when: You want to add a preface or a new section boundary before an existing heading.
  • Prefer instead: Use md insert-after-heading when the addition belongs inside the section that starts at the heading.

md upsert-bullet

  • What it does: Ensures a bullet exists under a heading, without duplicating it.
  • Use when: Rules, checklists, or recurring notes should be added idempotently.
  • Prefer instead: Use md replace-section when the entire list should be rewritten.

md dedupe-headings

  • What it does: Removes duplicate headings.
  • Use when: Generated markdown or hand edited docs have accumulated repeated sections that should collapse to one.
  • Prefer instead: Use md lint-agents when the goal is diagnosis rather than mutation.

md lint-agents

  • What it does: Checks AGENTS style markdown for common problems.
  • Use when: You want a CI style guard for agent instruction files before they drift into invalid or confusing structure.
  • Prefer instead: Use md dedupe-headings when you already know the file should be auto corrected.

md table-append

  • What it does: Appends a row to the markdown table under a heading.
  • Use when: A docs table should grow without manually rebuilding its existing rows.
  • Prefer instead: Use md replace-section when the whole table should be regenerated from source data.

patch actions

Use these when the change already exists as a unified diff.

patch check

  • What it does: Verifies whether a patch applies cleanly, without writing files.
  • Use when: CI or review should fail early on stale patch context.
  • Prefer instead: Use patch apply when the patch should be written, or replace and doc when you do not actually need to carry a diff file.

patch apply

  • What it does: Applies a unified diff.
  • Use when: The desired change is already available as patch text and should be replayed directly.
  • Prefer instead: Use replace, md, or doc when you would rather describe the desired mutation at a higher level.

tidy actions

Use these when newline and whitespace correctness is the main concern.

tidy check

  • What it does: Reports missing final newlines, mixed line endings, and trailing whitespace in text files. Binary and invalid UTF-8 files are skipped.
  • Use when: You want a non mutating tidy audit for CI or local review.
  • Prefer instead: Use tidy fix when the goal is to normalize the files immediately.

tidy fix

  • What it does: Applies newline and whitespace normalization to text files. Binary and invalid UTF-8 files are skipped.
  • Use when: Existing files already need cleanup and the cleanup itself is the task.
  • Prefer instead: Use write policy flags when normalization should only apply to files already being touched by another write command.

tx reference

tx is the place where Patchloom's features compose. Use Core Concepts for the canonical explanation of rollback and exit codes, and examples for plan templates.

Plan fields

version

  • What it does: Declares the plan schema version. Patchloom rejects plans whose version does not match the version it supports.
  • Use when: Every plan must include this field. It ensures forward-compatibility safety so an old patchloom build does not silently misinterpret a plan written for a newer schema.
  • Required: Yes. Plans without a version field are rejected.

cwd

  • What it does: Sets the base directory used to resolve relative paths inside the plan.
  • Use when: You need plan operations and lifecycle steps to run from a specific subdirectory under the invocation root.
  • Important: Relative values resolve from the invocation working directory (--cwd or the process cwd), not from the plan file's directory. In MCP mode, the resolved directory must stay inside the server root. If the resolved path does not exist or is not a directory, the plan is rejected with PARSE_ERROR (exit 4).
  • Prefer instead: Use the CLI --cwd flag when the directory choice is a caller concern rather than part of the plan itself.

write_policy

  • What it does: Applies newline, EOL, and whitespace normalization across all pending writes in the plan.
  • Use when: Every write in the transaction should share the same normalization policy.
  • Fields: Supports ensure_final_newline (bool), normalize_eol (keep, lf, or crlf), and trim_trailing_whitespace (bool).
  • Precedence: Patchloom starts from the invocation's per-file write policy, including CLI flags and any --respect-editorconfig values, then overrides only the keys set here.
  • Prefer instead: Use CLI write flags when one invocation needs defaults, but the plan itself should stay generic.

strict

  • What it does: Rolls back file writes when a format or validation step fails.
  • Use when: Partial writes are unacceptable and post write failure should behave like a full transaction failure.
  • Prefer instead: Leave strict mode off when writes may stay on disk even if later validation reports a problem.

operations

  • What it does: Lists the ordered mutations that make up the transaction.
  • Use when: One logical change spans several steps or several mutation types.
  • Prefer instead: Use a standalone command when one direct operation is enough.

format

  • What it does: Runs shell commands after writes are staged to disk but before validation.
  • Use when: Generated or edited files should be normalized by tools like cargo fmt, prettier, or black as part of the same workflow.
  • Step fields: Each entry accepts cmd (required shell command) and timeout (seconds, default 60).
  • Failure behavior: Any non-zero exit or timeout fails the transaction. Error output reports the failing step number, exit status, the lifecycle working directory (cwd), and a truncated snippet of the command's stderr when available. With strict: true, Patchloom rolls back the staged writes.
  • Prefer instead: Run formatting outside tx when it does not need to participate in the transaction's success criteria.

validate

  • What it does: Runs shell commands that decide whether the transaction should be reported as valid.
  • Use when: Build, test, or policy checks are part of the definition of success for the change.
  • Step fields: Each entry accepts cmd (required shell command), required (bool, default false), and timeout (seconds, default 60).
  • Failure behavior: required: true makes the step gate transaction success. required: false still reports the validation problem to stderr. Error output reports the failing step number, exit status, the lifecycle working directory (cwd), and a truncated snippet of the command's stderr when available.
  • Prefer instead: Use standalone verification outside tx when the mutation and the validation lifecycle should stay separate.

Transaction operations

The operations below are the building blocks inside operations.

replace

  • What it does: Runs text replacement inside a transaction.
  • Use when: A text rewrite needs to share atomic rollback, formatting, or validation with other operations.
  • Requires: Exactly one of to, insert_before, or insert_after, matching top level replace.
  • Regex insert semantics: In regex mode, insert_before and insert_after preserve the matched text, they do not insert the raw pattern string.
  • Optional fields: case_insensitive (bool, default false), multiline (bool, default false), and if_exists (bool, default false) match the top level replace --case-insensitive, --multiline, and --if-exists flags.
  • Related: top level replace

doc.set

  • What it does: Runs a targeted structured set inside a transaction.
  • Use when: A precise config update must be bundled atomically with other repo changes.
  • Field naming: Use selector (not key) for the path expression in doc.set, doc.delete, doc.append, doc.prepend, doc.update, doc.ensure, and doc.delete_where.
  • Related: top level doc set

doc.delete

  • What it does: Removes a structured value inside a transaction.
  • Use when: Schema cleanup should happen as one step in a larger atomic change.
  • Related: top level doc delete

doc.merge

  • What it does: Deep merges structured content inside a transaction.
  • Use when: Several related structured fields should change together as part of one plan.
  • Related: top level doc merge

doc.append

  • What it does: Appends to an array inside a transaction.
  • Use when: List growth must stay atomic with other edits in the same plan.
  • Related: top level doc append

doc.prepend

  • What it does: Prepends to an array inside a transaction.
  • Use when: Ordered config precedence should change as part of a larger atomic mutation.
  • Related: top level doc prepend

doc.update

  • What it does: Updates all matching structured nodes inside a transaction.
  • Use when: A broad structured rewrite should be coupled to other edits and validations.
  • Related: top level doc update

doc.move

  • What it does: Moves or renames a structured selector path inside a transaction.
  • Use when: Schema migration must stay atomic with related code or docs edits.
  • Related: top level doc move

doc.ensure

  • What it does: Adds a structured value only if it is missing, inside a transaction.
  • Use when: Idempotent bootstrapping should happen together with other plan steps.
  • Related: top level doc ensure

doc.delete_where

  • What it does: Deletes array items matching a predicate inside a transaction.
  • Use when: Targeted list cleanup must be coordinated with other transactional edits.
  • Related: top level doc delete-where

md.replace_section

  • What it does: Replaces a markdown section inside a transaction.
  • Use when: Docs regeneration should be part of a larger all or nothing repo change.
  • Related: top level md replace-section

md.insert_after_heading

  • What it does: Inserts markdown content after a heading inside a transaction.
  • Use when: A release note or docs annotation must be added atomically with code or config changes.
  • Related: top level md insert-after-heading

md.insert_before_heading

  • What it does: Inserts markdown content before a heading inside a transaction.
  • Use when: Docs structure must change as one step in a broader plan.
  • Related: top level md insert-before-heading

md.upsert_bullet

  • What it does: Ensures a markdown bullet exists inside a transaction.
  • Use when: Idempotent docs or checklist updates should stay coupled to other edits.
  • Related: top level md upsert-bullet

md.table_append

  • What it does: Appends a markdown table row inside a transaction.
  • Use when: Documentation tables should be updated together with the code or metadata they describe.
  • Related: top level md table-append

md.dedupe_headings

  • What it does: Removes duplicate markdown headings inside a transaction.
  • Use when: Cleanup of generated docs should stay atomic with the rest of the plan.
  • Related: top level md dedupe-headings

md.lint_agents

  • What it does: Lints an AGENTS.md file for common problems (duplicate headings, dangerous commands outside code fences, missing final newline) inside a transaction.
  • Use when: Agent rules validation should be part of a larger plan, e.g., lint before and after markdown edits to confirm no new issues.
  • Related: top level md lint-agents, MCP md_lint

tidy.fix

  • What it does: Applies tidy normalization inside a transaction.
  • Use when: Text cleanup should be part of the same atomic success criteria as other edits.
  • Related: top level tidy fix

file.create

  • What it does: Creates a file inside a transaction.
  • Use when: New files must appear only if the full plan succeeds.
  • Related: top level create

file.delete

  • What it does: Deletes a file inside a transaction.
  • Use when: File removal should roll back if later format or validation steps fail.
  • Related: top level delete

file.rename

  • What it does: Renames (moves) a file inside a transaction.
  • Use when: File renames should roll back if later format or validation steps fail. More efficient than read + file.create + file.delete as a single operation.
  • Related: top level rename

search

  • What it does: Searches a file for a pattern inside a transaction and includes match results in the JSON output without writing anything.
  • Use when: An agent needs to locate patterns before replacing them in the same plan, enabling locate-then-edit in a single call.
  • Optional fields: regex (bool, default false), case_insensitive (bool, default false), multiline (bool, default false), context (lines around matches), before_context, and after_context match the top level search flags.
  • Related: top level search

read

  • What it does: Reads a file inside a transaction and includes its content in the JSON output without writing anything. The JSON read result carries the same line metadata as top level read (start_line, end_line, total_lines), and when no line range is requested it preserves the raw file content exactly.
  • Use when: An agent needs to inspect file content before or after other operations in the same plan, enabling "understand then edit" in a single call.
  • Related: top level read

patch.apply

  • What it does: Applies a unified diff inside a transaction.
  • Use when: Patch replay needs to compose with earlier in plan edits and share the same rollback or validation behavior.
  • Related: top level patch apply

Patchloom: structured file edits for AI agents

AI coding agents are remarkably good at reasoning about code. They are remarkably bad at editing config files.

When an agent needs to bump a version in config.yaml, it reaches for sed or text replacement. That works until the regex strips a YAML comment, breaks indentation, or produces invalid syntax. When the task touches six files, that means six separate tool calls, each a full round-trip back to the LLM. And on Windows, sed and jq do not exist, so the agent falls back to verbose PowerShell or makes errors with unfamiliar syntax.

We built Patchloom to fix all three problems with a single Rust binary.

What it does

Patchloom edits JSON, YAML, and TOML files by selector path, not regex. It preserves comments and formatting because it parses the file instead of pattern-matching it. It batches multiple file edits into one tool call, cutting round-trips from six to one. And it works identically on Linux, macOS, and Windows with zero dependencies.

# Edit a YAML value by selector; comments and formatting survive
patchloom doc set config.yaml database.port 5432 --apply

# Version bump across 6 files in a single tool call
patchloom batch --apply <<'EOF'
doc.set package.json version "2.0.0"
doc.set config.yaml app.version "2.0.0"
doc.set config.toml project.version "2.0.0"
replace README.md "1.0.0" "2.0.0"
replace CHANGELOG.md "1.0.0" "2.0.0"
file.create VERSION "2.0.0"
EOF

20 commands cover structured document editing, search and replace, markdown section editing, multi-file batching, atomic transactions with rollback, diff patching, file lifecycle operations, operation schema export, and an MCP server that exposes everything as structured tool calls for MCP-capable agents.

The honest benchmark

We ran 11 real agent tasks, three times each, comparing Patchloom MCP, Patchloom CLI, and native editor tools. The agent was Claude Opus 4 via Grok Build.

Method      Total time (11 tasks)   Wins
──────────  ─────────────────────   ────
MCP mode    228.5s                  5/11
Native      233.8s                  3/11
CLI mode    321.9s                  0/11

MCP mode wins overall because structured tool calls skip shell syntax construction entirely. CLI mode is slowest because the agent must construct and quote shell commands for every call.

But Patchloom is not faster than native tools for everything. We are upfront about that. The agent instructions Patchloom generates include this table:

TaskUse Patchloom?Why
Edit JSON/YAML/TOML by selectorYesParser-backed, comments preserved
Batch edits across multiple filesYesOne tool call instead of N
Append a row to a markdown tableYesHeading-aware, no line number guessing
Read a single fileNoNative read_file is faster
Simple text searchNoNative grep is faster
Single-file text replacementNoNative search_replace is faster

Patchloom tells agents when not to use it. The right tool for the right job.

Why it matters

Comments survive. doc set config.yaml database.port 5432 parses the YAML as a concrete syntax tree, changes the value at the selector path, and writes valid output. Inline comments, section comments, indentation, and key ordering are all preserved. A sed command cannot do this.

Round-trips disappear. Six file edits via native tools means six round-trips to the LLM. One batch call does the same work in a single round-trip. In our benchmarks, multi-file batch operations completed in under half the time of sequential native calls.

Failures roll back. tx plans group multiple operations with format and validate lifecycle hooks. Set strict: true and every file reverts if any step fails. No more partial edits to clean up after a broken CI run.

One binary everywhere. Same commands, same flags, same behavior on Linux, macOS, and Windows. No dependency on sed, jq, grep, or any Unix-specific tooling.

Self-documenting. Run patchloom agent-rules to generate an AGENTS.md file that teaches the agent exactly when and how to use each command. The agent reads it, learns the tool surface, and knows which tasks to handle natively and which to hand to Patchloom.

Try it

cargo install patchloom                    # crates.io
brew install patchloom/tap/patchloom       # macOS / Linux (Homebrew)

Or download a prebuilt binary from GitHub Releases.

Set up your project in one command:

patchloom init

This creates AGENTS.md in a new project or appends the rules to an existing agent instructions file, offers shell completions, and detects MCP configuration. If .vscode/ or .cursor/ exists, it prints ready-to-copy .vscode/mcp.json or .cursor/mcp.json snippets. Or generate just the agent instructions:

patchloom agent-rules >> AGENTS.md

For MCP mode (structured tool calls, no shell syntax):

cargo install patchloom
patchloom mcp-server

There is also a VS Code extension that handles binary detection, AGENTS.md generation, and MCP configuration from the command palette.

By the numbers

  • 1,318 tests, zero unsafe code
  • 20 commands including MCP server with 29 structured tool calls
  • Agent-tested with Grok 4.3, GPT-5.4, and Claude Opus 4.6
  • Cross-platform: Linux (x64, ARM64), macOS (x64, ARM64), Windows (x64)
  • MIT OR Apache-2.0 licensed
  • Rust 1.95+, single static binary with no runtime dependencies

What comes next

This is v0.1.0. We would love feedback on:

  • Which agent workflows hit friction that Patchloom could smooth
  • Missing operations or formats (.env? .ini? HCL?)
  • MCP integration with agents we have not tested yet
  • Performance reports from real-world projects

File issues, start discussions, or send PRs on GitHub.