Fuzzy matching
How editkit locates a SEARCH block when the model's quote drifted from the file.
The applier doesn't require byte-exact matches. It runs three strategies in order; the first one to match wins.
Strategy 1: exact match
If the SEARCH text appears verbatim in the file exactly once, use that. Fast, always correct, no fuzzing.
Strategy 2: indent-shift
Drop a common leading-whitespace prefix from every SEARCH line, search for that, and re-apply the file's actual indentation to the REPLACE. This handles the very common case where the model quoted a nested block with no indentation:
// File has:
function outer() {
function inner() {
return 42;
}
}
// Model emits:
<<<<<<< SEARCH
function inner() {
return 42;
}
=======
function inner() {
return 43;
}
>>>>>>> REPLACEThe SEARCH lacks the four-space indent that's actually in the file, but editkit still
locates the block and indents the REPLACE to match.
Strategy 3: trim trailing whitespace
Strip trailing spaces from every line on both sides, then exact-match. Handles files where the editor left trailing whitespace the model didn't quote.
Use fuzzyReplace directly
The matching primitive is exposed if you want it without the parsing layer:
import { fuzzyReplace } from "editkit";
const out = fuzzyReplace(originalFileText, searchText, replaceText, {
fuzzyWhitespace: true, // default
});
// Result shapes:
// { kind: "ok", text, strategy: "exact" | "indent-shift" | "trim-eol" }
// { kind: "ambiguous", count: 3 }
// { kind: "not-found" }Turning fuzzing off
Pass { fuzzyWhitespace: false } to applyEdits if you want strict byte-exact matching.
Useful when you're applying machine-generated diffs and want any drift to be a hard error
(rather than silently picked up by a fuzzy strategy).
Ambiguous matches
If the SEARCH block appears more than once in the file, fuzzing won't pick for you — you'll
get ambiguous-match back as a failure. The fix is in the model's hands: re-prompt asking
for additional surrounding context to make the SEARCH unique. The error message editkit
returns is already written to be model-readable; feed it straight back into the next
prompt.