mirror of
https://github.com/coder/coder.git
synced 2026-06-03 04:58:23 +00:00
32a894d4a7
## Problem The `edit_files` tool used `strings.ReplaceAll` for exact substring matches, silently replacing **every** occurrence. When an LLM's search string wasn't unique in the file, this caused unintended edits. Fuzzy matches (passes 2 and 3) only replaced the first occurrence, creating inconsistent behavior. Zero matches were also silently ignored. ## Investigation Investigated how **coder/mux** and **openai/codex** handle this: | Tool | Multiple matches | No match | Flag | |---|---|---|---| | **coder/mux** `file_edit_replace_string` | Error (default `replace_count=1`) | Error | `replace_count` (int, default 1, -1=all) | | **openai/codex** `apply_patch` | Uses first match after cursor (structural disambiguation via context lines + `@@` markers) | Error | None (different paradigm) | | **coder/coder** `edit_files` (before) | Exact: replaces all. Fuzzy: replaces first. | Silent success | None | ## Solution Adopted the mux approach (error on ambiguity) with a simpler `replace_all: bool` instead of `replace_count: int`: - **Default (`replace_all: false`)**: search string must match exactly once. Multiple matches → error with guidance: *"search string matches N occurrences. Include more surrounding context to make the match unique, or set replace_all to true"* - **`replace_all: true`**: replaces all occurrences (opt-in for intentional bulk operations like variable renames) - **Zero matches**: now returns an error instead of silently succeeding Chose `bool` over `int` count because: 1. LLMs are bad at counting occurrences 2. The real intent is binary (one specific spot vs. all occurrences) 3. Simpler error recovery loop for the LLM ## Changes | File | Change | |---|---| | `codersdk/workspacesdk/agentconn.go` | Add `ReplaceAll bool` to `FileEdit` struct | | `agent/agentfiles/files.go` | Count matches before replacing; error if >1 and not opted in; error on zero matches; add `countLineMatches` helper | | `codersdk/toolsdk/toolsdk.go` | Expose `replace_all` in tool schema with description | | `agent/agentfiles/files_test.go` | Update existing tests, add `EditEditAmbiguous`, `EditEditReplaceAll`, `NoMatchErrors`, `AmbiguousExactMatch`, `ReplaceAllExact` |