Skip to content

feat(tools): support batched edits with optional replaceAll in edit_file#274

Merged
LeeCheneler merged 1 commit intomainfrom
feat/273-batched-edits-edit-file
Apr 9, 2026
Merged

feat(tools): support batched edits with optional replaceAll in edit_file#274
LeeCheneler merged 1 commit intomainfrom
feat/273-batched-edits-edit-file

Conversation

@LeeCheneler
Copy link
Copy Markdown
Owner

Summary

Extend edit_file to accept an array of edits and an optional replaceAll flag per edit, so the model can complete a multi-spot refactor in a single tool call with one combined diff and one confirmation prompt.

GitHub Issue

Closes #273

What Changed

The schema for edit_file now takes path and an edits array. Each edit is { oldString, newString, replaceAll? } (replaceAll defaults to false). The single-pair shape is gone.

Sequential, atomic application. Edits are applied to the file content in order, in memory. Each edit operates on the result of the previous edit, not the original content — this matches how a person would think about a sequence of changes and avoids forcing the model to mentally diff non-overlapping windows in the original. The whole call is atomic per file: if any edit fails the file is left untouched and the error message identifies which edit (1-based) failed and why.

replaceAll opt-in. With replaceAll: false (the default) the existing unique-match guardrail still applies — oldString must appear exactly once or the edit fails. With replaceAll: true every occurrence is replaced and the edit fails only if there are zero matches. This removes the friction of having to add surrounding context to disambiguate when the model legitimately wants to rename every occurrence (variable rename, import path change), without weakening the default safety.

One diff, one prompt. A single combined unified diff is built from the original file content vs. the final post-all-edits content, and the confirmation prompt fires once with that combined diff. Permission semantics are unchanged.

formatCall shows path for a single edit and path (N edits) for N>1, so the collapsed tool header carries the batch size.

Description rewrite. The tool description now explains sequential application, atomicity, replaceAll semantics, and explicitly nudges the model to prefer batching over multiple calls — LLMs follow tool descriptions closely and this is where the speedup actually lands.

The system prompt's read-before-write example (src/prompt/build-system-prompt.ts) was updated to use the new edits array shape so the model sees a consistent contract.

Notes for Reviewers

Regex matching was discussed and intentionally left out — it's a footgun for LLM callers (escaping/flavour confusion, silent corruption on auto-approved edits) and almost everything people reach for regex for is covered by replaceAll with a literal string. If a real need surfaces later it can ship as an opt-in mode: "regex" with stricter confirmation rules.

Replace the single oldString/newString pair with an `edits` array so a refactor
that touches several spots in a file can complete in one tool call, with one
confirmation prompt and one combined diff.

Each edit takes oldString, newString, and an optional replaceAll flag (default
false). Edits are applied sequentially in memory — each operates on the result
of the previous edit, not the original content. The whole call is atomic per
file: if any edit fails, the file is left untouched and the error identifies
which edit (1-based) failed.

Closes #273
@LeeCheneler LeeCheneler merged commit 4be7158 into main Apr 9, 2026
4 checks passed
@LeeCheneler LeeCheneler deleted the feat/273-batched-edits-edit-file branch April 9, 2026 22:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: support batched edits with optional replaceAll in edit_file

1 participant