Skip to content

Repository files navigation

search-replace-patch

Apply LLM-written SEARCH/REPLACE blocks to a file, tolerantly.

Asking a model to edit a large file by re-emitting the whole file is slow and expensive (output tokens dominate the bill). Asking it for small SEARCH/REPLACE blocks is far cheaper, but models are bad at copying the surrounding text character for character: wrong indentation, a tag re-wrapped over two lines, \r\n vs \n. Most appliers just fail on those. This one accepts the near-misses and stays strict where it matters.

  • Zero dependencies, one file, TypeScript types included.
  • Tolerant matching: exact match → ignore indentation and line endings → same words in the same order.
  • Strict on ambiguity: an anchor matching more than one place is refused (block_N_ambiguous), never guessed.
  • All-or-nothing, but returns partialCode (the blocks that did apply) so you can ask the model for only the missing blocks instead of regenerating everything.
  • Keeps the file's own indentation around the replaced span.
import { applyPatches, hasPatchBlocks, PATCH_INSTRUCTIONS } from "search-replace-patch";

const reply = await callYourModel({
  system: "You edit code.\n\n" + PATCH_INSTRUCTIONS,
  user: `Current file:\n${code}\n\nChange the price to 6500.`,
});

if (hasPatchBlocks(reply)) {
  const r = applyPatches(code, reply);
  if (r.ok) {
    save(r.code);                        // r.applied blocks applied
  } else if (r.partialCode) {
    // some blocks applied: ask the model only for the rest, on r.partialCode
  } else {
    // nothing applied (reason: "block_1_missing", "block_2_ambiguous", "no_blocks"…)
    // escalate to a stronger model, or regenerate the file
  }
}

Install from GitHub (builds on install):

npm install github:hypesecret/search-replace-patch

Block format

<<<<<<< SEARCH
exact lines copied from the current file
=======
lines that replace them
>>>>>>> REPLACE

Empty REPLACE deletes. To insert, use an existing anchor line as SEARCH and repeat it in REPLACE followed by the new code.

Why it exists (measured, with limits)

Extracted from a prompt-to-app builder. In one benchmark of 8 models × 15 edit requests on a single-file React app (≈100 real calls), the failure causes were: a block whose anchor was not found (missing), an anchor matching several places (ambiguous), and edits that applied but broke compilation. Both anchor failures are reported distinctly so a caller can react differently (repair vs. escalate).

What was NOT measured: the effect of the tolerant matching itself. We never compared "tolerant" against "exact-only" on the same replies, so we make no claim about how many failures the tolerance saves. It is a sample from one app, not a general result: measure on your own files.

What it does not do

  • It does not validate the result (compile, lint, render). Do that after applying; if validation fails, that is your escalation trigger.
  • It matches text, not syntax trees. A SEARCH that is textually unique but semantically the wrong place will still be applied.
  • Regex-free inputs only: SEARCH is matched literally (special characters are escaped).

Résumé en français

Applique des blocs SEARCH/REPLACE écrits par un LLM sur un fichier, en tolérant l'indentation, les fins de ligne et les balises coupées sur plusieurs lignes, en refusant les ancrages ambigus, et en renvoyant le code partiel (partialCode) quand seuls certains blocs s'appliquent. Sans dépendance, un seul fichier.

Development

npm install
npm test          # vitest
npm run typecheck
npm run build

MIT © hypesecret

About

Tolerant applier for LLM SEARCH/REPLACE edit blocks: accepts wrong indentation and re-wrapped lines, refuses ambiguous anchors, returns partial code. Zero dependencies.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages