fix: improved the fs_patch script description and added improved error handling to it.

This commit is contained in:
2026-06-15 15:05:18 -06:00
parent dc6d736df3
commit 6ce69ee989
2 changed files with 50 additions and 0 deletions
+17
View File
@@ -5,6 +5,23 @@ set -e
# PREFERRED way to modify a file. Prefer this over fs_write whenever the file already exists: it sends less data, # PREFERRED way to modify a file. Prefer this over fs_write whenever the file already exists: it sends less data,
# preserves unchanged content automatically, and is less prone to accidental data loss from full rewrites. # preserves unchanged content automatically, and is less prone to accidental data loss from full rewrites.
# Use fs_write only when you are creating a new file or doing a complete rewrite where most of the content changes. # Use fs_write only when you are creating a new file or doing a complete rewrite where most of the content changes.
#
# CRITICAL — the patch is matched byte-for-byte. There is no fuzzy matching, no whitespace tolerance, and no context shift:
# - Context lines (prefixed with a single space) and removed lines (prefixed with '-') must equal the file content exactly.
# If unsure, fs_cat the file first and copy the bytes verbatim into your patch.
# - JSON-escape the contents string ONCE. Each literal backslash in the file becomes \\ in the JSON contents string. So a
# shell line containing s|\\"|"|g must appear in JSON as s|\\\\\"|\"|g — NOT s|\\\\\\\"|\\\"|g. Over-escaping backslashes
# is the most common cause of "unable to apply patch" failures, especially in files with sed/jq/regex pipelines or
# embedded Python with quoted strings.
# - Hunks are applied in order; the first hunk that fails aborts the whole patch — later hunks are NOT attempted.
# - If you've edited this file in earlier tool calls, fs_cat it again before composing the patch. A stale view of the file
# produces context lines that no longer match.
# - On failure the error message names the failing hunk and shows the expected-vs-actual line. Fix that specific line and
# retry — do not blindly resend a near-identical patch.
#
# For files with heavy escaping (sed/jq/regex pipelines, shell with embedded heredocs, deeply quoted strings), prefer
# fs_write over chained fs_patch hunks to replace the entire file with the full new contents (i.e. original content +
# your changes).
# @option --path! The path of the file to apply the patch to # @option --path! The path of the file to apply the patch to
# @option --contents! The patch to apply to the file # @option --contents! The patch to apply to the file
+33
View File
@@ -600,6 +600,14 @@ patch_file() {
for (i = 2; i <= hunkTotalOriginalLines[hunkIndex]; i++) { for (i = 2; i <= hunkTotalOriginalLines[hunkIndex]; i++) {
if (lines[nextLineIndex] != hunkOriginalLines[hunkIndex,i]) { if (lines[nextLineIndex] != hunkOriginalLines[hunkIndex,i]) {
if (i - 1 > bestPartialLen[hunkIndex]) {
bestPartialLen[hunkIndex] = i - 1
bestPartialAnchorLine[hunkIndex] = lineIndex
bestPartialHunkPos[hunkIndex] = i
bestPartialDivergeLine[hunkIndex] = nextLineIndex
bestPartialExpected[hunkIndex] = hunkOriginalLines[hunkIndex,i]
bestPartialActual[hunkIndex] = lines[nextLineIndex]
}
nextLineIndex = 0 nextLineIndex = 0
break break
} }
@@ -621,7 +629,32 @@ patch_file() {
} }
if (hunkIndex != totalHunks + 1) { if (hunkIndex != totalHunks + 1) {
failingHunk = hunkIndex
print "error: unable to apply patch" > "/dev/stderr" print "error: unable to apply patch" > "/dev/stderr"
print "" > "/dev/stderr"
print "Hunk " failingHunk " of " totalHunks " did not match the file." > "/dev/stderr"
if (bestPartialLen[failingHunk] == 0) {
print "" > "/dev/stderr"
print "The first context/removed line of hunk " failingHunk " was not found anywhere in the file:" > "/dev/stderr"
print " expected: " hunkOriginalLines[failingHunk, 1] > "/dev/stderr"
} else {
print "" > "/dev/stderr"
print "Closest match: anchored at file line " bestPartialAnchorLine[failingHunk] ", matched " bestPartialLen[failingHunk] " of " hunkTotalOriginalLines[failingHunk] " original lines before diverging." > "/dev/stderr"
print "" > "/dev/stderr"
print "At file line " bestPartialDivergeLine[failingHunk] " (hunk original line " bestPartialHunkPos[failingHunk] "):" > "/dev/stderr"
print " expected: " bestPartialExpected[failingHunk] > "/dev/stderr"
print " actual: " bestPartialActual[failingHunk] > "/dev/stderr"
}
print "" > "/dev/stderr"
print "Lines must match byte-for-byte (no fuzzy matching). Check escaping, whitespace, and quoting." > "/dev/stderr"
if (failingHunk < totalHunks) {
print "" > "/dev/stderr"
print (totalHunks - failingHunk) " subsequent hunk(s) were not attempted (patcher aborts on first failure)." > "/dev/stderr"
}
exit 1 exit 1
} }
} }