Skip to content

feat(tools): Add btw_tool_files_patch for diff-style file editing#192

Merged
gadenbuie merged 6 commits into
mainfrom
fix/190-add-patch-style-file-edit-tool
May 12, 2026
Merged

feat(tools): Add btw_tool_files_patch for diff-style file editing#192
gadenbuie merged 6 commits into
mainfrom
fix/190-add-patch-style-file-edit-tool

Conversation

@gadenbuie
Copy link
Copy Markdown
Collaborator

@gadenbuie gadenbuie commented May 12, 2026

Closes #190

Summary

Adds a new files tool, btw_tool_files_patch(), that applies a structured diff-style patch envelope to make coordinated changes across multiple files in a single call. One envelope can add, update, delete, and rename files; every operation is validated (parser, path safety, filesystem preconditions, hunk matching) before any file is written, so a partial failure leaves the working tree untouched.

Key design choices:

  • Hunks must include at least one context or delete line as an anchor. Pure-insert hunks are rejected so every edit has a concrete location; use *** Add File for new files.
  • Internal hunk representation is a flat tagged-line list (type = "context"|"delete"|"insert") rather than the spec's separate context/deletes/inserts decomposition; both matching and applying become one-pass operations.
  • Returns BtwToolResult rather than BtwFileDiffToolResult because a patch can touch multiple files, and the single-file diff viewer shape doesn't map cleanly.

Verification

withr::with_tempdir({
  writeLines(c("a", "b", "c"), "x.txt")
  patch <- "*** Begin Patch
*** Update File: x.txt
@@
 a
-b
+B
 c
*** End Patch
"
  btw::btw_tool_files_patch(patch)
  readLines("x.txt")  # c("a", "B", "c")
})

Tests cover the parser, syntax/filesystem validators, hunk matcher, applier (add/update/delete/move + atomic failure), and tool-integration snapshots. 69 tests pass via testthat::test_file("tests/testthat/test-tool-files-patch.R").

gadenbuie added 4 commits May 12, 2026 15:02
Adds a new files tool that applies structured patch envelopes
(apply_patch wire format) to one or more files atomically. Models
can produce edits without a prior read because hunks are anchored
by context lines rather than hashlines.

The parser rejects pure-insert hunks (no context or delete line)
so every edit has a concrete anchor; use *** Add File for new
files.

Fixes #190
`seq(search_start, n_file - seq_len_val + 1L)` produced a descending
range when the match sequence was longer than the file, causing an
out-of-bounds read and a confusing "missing value where TRUE/FALSE
needed" error. Guard with `search_start <= last_start` so the
not-found path runs cleanly. Adds regression tests for shorter-than-
hunk and empty files.
@gadenbuie gadenbuie marked this pull request as ready for review May 12, 2026 19:35
gadenbuie added 2 commits May 12, 2026 16:30
R CMD check --as-cran with error_on='warning' rejects any non-ASCII
in R source. Replace em dashes with -- and the moved-arrow with ->
so the file is portable.
Add a display block to btw_tool_files_patch_impl that shows the
input patch in a diff fence followed by the operations summary,
and hides the raw tool call.
@gadenbuie gadenbuie merged commit ec997d0 into main May 12, 2026
9 checks passed
@gadenbuie gadenbuie deleted the fix/190-add-patch-style-file-edit-tool branch May 12, 2026 21:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add patch-style file editing tool

1 participant