[Feature] Edit tool: add dry-run preview and line-number targeting to prevent blind substitutions

Resolved 💬 4 comments Opened Mar 27, 2026 by casriraj Closed May 23, 2026

Problem

The Edit tool has two related gaps that cause real bugs in large codebases:

1. replace_all performs blind substitution with no preview

When using replace_all=True, the tool replaces every match without showing where they are. In one incident, replacing self.company_frame.pack(...) with self._show_header() across a 32K-line file also replaced the pack() call inside _show_header() itself -- creating infinite recursion. The user had to manually rollback via git.

There is no way to see all match locations before committing the replacement.

2. Targeted edits require unique strings, with no line-targeting fallback

The Edit tool requires old_string to be unique in the file. In large files (10K+ lines), common patterns like cursor.execute("DROP TABLE IF EXISTS") or self.root.after(0, appear dozens of times. The only workaround is adding 5-10 lines of surrounding context to make the match unique -- which is error-prone and slow.

There is no way to say "replace the match at line 1793" or "replace the 3rd occurrence."

Impact

  • replace_all caused infinite recursion in production code (required git rollback)
  • "Found 2 matches" errors occur multiple times per session on large files, requiring repeated Read + re-craft of old_string with more context
  • Estimated 10-15% of edit attempts on files over 5K lines fail on first try due to uniqueness issues
  • Over 3 months of daily use on a 67K-line codebase, this is the single most frequent source of workflow friction

Suggested Solutions

  1. dry_run=True parameter -- returns all match locations (file, line number, surrounding context) without applying changes. User/model can review before committing.
  2. line_number parameter -- disambiguates when old_string has multiple matches. "Replace the match nearest to line 1793."
  3. occurrence parameter -- "Replace the Nth occurrence" as a simpler alternative.
  4. For replace_all: show a count + first/last match location in the response so the model knows what was changed and can verify.

Environment

  • Claude Code VS Code extension, Opus model
  • Python codebase with files ranging from 1K to 32K lines
  • Encountered across 3 months of daily use

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗