You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As an LLM using Tomo,
I want to apply multiple edits to a file in a single tool call and optionally replace all occurrences of a string,
so that I can complete refactors and renames in fewer round-trips with a single confirmation prompt.
Acceptance Criteria
edit_file accepts an edits array instead of a single oldString/newString pair
Each entry in edits has oldString, newString, and optional replaceAll (default false)
When replaceAll is false, oldString must match exactly once in the current file content (existing behaviour)
When replaceAll is true, every occurrence of oldString is replaced; fails only if there are zero matches
Edits are applied sequentially: each edit operates on the result of the previous edit, not the original file content
The whole call is atomic per file: if any edit fails, the file is not modified and the tool returns an error identifying which edit failed
A single combined unified diff is generated from the original content vs. the final content after all edits
Permission check and confirmation prompt happen once for the combined diff, not per edit
formatCall displays the path along with the number of edits (e.g. path/to/file.ts (3 edits))
Tool description is rewritten to explain the new contract: prefer batched edits over multiple calls, explain sequential application, explain replaceAll semantics
Tests cover: single edit, multiple sequential edits, replaceAll true/false, atomic failure when one edit in the batch fails, identical oldString/newString rejection, zero-match and multi-match failures
Additional Context
Today edit_file accepts a single oldString/newString pair and requires the match to be unique. A refactor that touches N spots in a file becomes N tool calls, N round-trips, and N confirmation prompts when the write permission isn't pre-granted. This is slow and noisy.
Batching solves the round-trip and prompt cost. replaceAll removes the friction of having to add surrounding context to disambiguate when the LLM legitimately wants to rename every occurrence (e.g. variable rename, import path change). The unique-match guardrail stays as the default — replaceAll is opt-in per edit so accidental sweeps still require an explicit choice.
Sequential application (each edit sees the previous edit's output) is important: it means the LLM doesn't have to mentally model non-overlapping windows in the original file, which is a common failure mode. Atomicity per file means there's no partial-write state for the model to recover from on failure.
Existing implementation: src/tools/edit-file.ts. The unified diff helper at src/tools/diff.ts already handles (original, final) so the combined-diff requirement is essentially free.
User Story
As an LLM using Tomo,
I want to apply multiple edits to a file in a single tool call and optionally replace all occurrences of a string,
so that I can complete refactors and renames in fewer round-trips with a single confirmation prompt.
Acceptance Criteria
edit_fileaccepts aneditsarray instead of a singleoldString/newStringpaireditshasoldString,newString, and optionalreplaceAll(defaultfalse)replaceAllisfalse,oldStringmust match exactly once in the current file content (existing behaviour)replaceAllistrue, every occurrence ofoldStringis replaced; fails only if there are zero matchesformatCalldisplays the path along with the number of edits (e.g.path/to/file.ts (3 edits))replaceAllsemanticsreplaceAlltrue/false, atomic failure when one edit in the batch fails, identicaloldString/newStringrejection, zero-match and multi-match failuresAdditional Context
Today
edit_fileaccepts a singleoldString/newStringpair and requires the match to be unique. A refactor that touches N spots in a file becomes N tool calls, N round-trips, and N confirmation prompts when the write permission isn't pre-granted. This is slow and noisy.Batching solves the round-trip and prompt cost.
replaceAllremoves the friction of having to add surrounding context to disambiguate when the LLM legitimately wants to rename every occurrence (e.g. variable rename, import path change). The unique-match guardrail stays as the default —replaceAllis opt-in per edit so accidental sweeps still require an explicit choice.Sequential application (each edit sees the previous edit's output) is important: it means the LLM doesn't have to mentally model non-overlapping windows in the original file, which is a common failure mode. Atomicity per file means there's no partial-write state for the model to recover from on failure.
Existing implementation:
src/tools/edit-file.ts. The unified diff helper atsrc/tools/diff.tsalready handles(original, final)so the combined-diff requirement is essentially free.