The patch tool
Editing a file by naming the region instead of quoting it back, and every reason it can refuse.
Edit asks for old_string to reproduce the target text byte for byte. That is
the one field where a model re-emits, as output tokens, text it already paid for
as input tokens. A thirty-line old_string costs thirty lines of output and
carries no information the file did not already have.
The patch tool removes that repetition. Claude names the region; the tool
reads the region off disk and replaces it.
Naming a region
Two selectors, and only one may be used at a time.
| Selector | Example | Means |
|---|---|---|
lines |
"42:58" |
Lines 42 through 58 inclusive, counted by newline. |
regex |
{"pattern": "def run\\(.*?\\n\\n", "flags": "s"} |
The single region the expression matches. |
Lines are counted by \n and nothing else. A form feed in a C or Emacs Lisp
source would otherwise shift every line number, because the usual line splitter
also breaks on \f, \v and several Unicode separators.
It runs on your machine
The tool is served by culp mcp, a stdio server inside the CLI that Claude Code
starts. It is local because a remote server cannot read your disk, and routing
the file's text through the model's output would re-emit exactly what the
feature avoids.
The tool cannot check that the file was read first, because it is a separate
process with no view of the transcript. Edit can and does. That is a real
reduction in safety, taken deliberately for the tokens, and the receipt is what
makes a mistargeted region visible. Read the receipt.
What it will not do
Nothing is authored. The region replaced is always a substring of the file, byte for byte. Regions are never widened, because widening changes what the replacement has to contain, and a region that cannot be told apart is refused by name rather than guessed at.
The replacement is spliced at the resolved offsets, never searched for. An earlier version applied it by string replacement, which found the first copy of the region's text, so a line range could resolve correctly and then edit a different line whenever the file repeated itself.
Refusals
| Reason | What happened |
|---|---|
line_out_of_range |
The range names a line the file does not have. |
no_match |
The expression matched nothing. |
ambiguous_match |
The expression matched in more than one place. |
divergent_matches |
The matches disagree about what the region is. |
invalid_regex |
The pattern did not compile. |
A refusal costs a turn and never a file. The replacement is encoded in full first, written to a sibling temporary, and only then moved over the original, so a failure part-way through cannot leave the file truncated. Symlinks are followed rather than replaced.
Text handling
The stream is decoded as UTF-8 by the tool itself rather than by the platform,
because a piped standard input is otherwise decoded with the system locale. On a
Windows console that turned a Japanese character into three wrong ones, and
turned é into mojibake silently, under a receipt reporting success.
Line breaks inside the replacement are converted to the file's own convention. A file with no newline, or one that already mixes both, has shown no convention and gets nothing imposed on it.