Skip to content

Commit 290930c

Browse files
laurigatesclaude
andauthored
docs(claude): warn that rg's -r is --replace, not a bundled short flag (#322)
## What Adds a section to `~/.claude/rules/tool-use-patterns.md` on a silent, confident failure mode: `rg -r` is `--replace` and takes an argument, so bundling it into a short-flag cluster makes ripgrep **rewrite every match in its own output**. ``` # Wrong — reads as "recursive + line numbers"; actually means --replace=n rg -rn "yolo" . ./conf/cli_clients/gemini.json: "--n" ← the file says "--yolo" # Right rg -n "yolo" . ./conf/cli_clients/gemini.json: "--yolo" ``` ## Why No error, no warning, and the output is well-formed — it simply does not match the file on disk. That makes it worse than a crash: the fabricated lines read as ground truth. Observed 2026-07 while auditing a `clink` CLI config in `pal-mcp-server`. The doctored output built a false picture of the file that was **nearly acted on**, and was caught only because the rewritten line contradicted an earlier direct `Read` of the same file. The root cause is imported muscle memory from `grep -r`. **ripgrep is recursive by default**, so there is no `-r` to add — the habit produces a flag that means something else entirely. ## The durable fix The rule ends by pointing at the structural answer rather than just "be careful": prefer the **Grep tool** over `rg` in Bash. It has no `--replace` surface, so this class of error cannot occur at all. Also adds the diagnostic heuristic: *if an `rg` result contradicts something you read directly, suspect the flags before you suspect the file.* 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_019wryFZuz4HEs1oqY9EJfGo Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 4aeda1d commit 290930c

1 file changed

Lines changed: 28 additions & 0 deletions

File tree

exact_dot_claude/rules/tool-use-patterns.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,34 @@ limit and recovered fully):
158158
at zero cost and re-runs only the dead ones. Re-dispatching from scratch
159159
re-pays every completed agent's tokens.
160160

161+
## Grep / rg — `-r` is `--replace`, not a bundled short flag
162+
163+
`rg`'s `-r` takes an argument: it **rewrites every match in the output**. Bundling
164+
it into a short-flag cluster silently consumes the next letter as the replacement
165+
string, so the tool prints *fabricated* lines that look like real file contents.
166+
167+
```
168+
# Wrong — reads as "recursive + line numbers"; actually means --replace=n
169+
rg -rn "yolo" .
170+
./conf/cli_clients/gemini.json: "--n" ← the file says "--yolo"; rg rewrote it
171+
172+
# Right
173+
rg -n "yolo" .
174+
./conf/cli_clients/gemini.json: "--yolo"
175+
```
176+
177+
The failure is **silent and confident**: no error, no warning, and the output is
178+
well-formed — it just doesn't match the file on disk. Observed 2026-07 building a
179+
false picture of a config file that was then nearly acted on; caught only because
180+
the doctored line contradicted an earlier direct `Read` of the same file.
181+
182+
- **`rg` is recursive by default** — there is no `-r` to add. The instinct is
183+
imported from `grep -r`, and that's the trap.
184+
- **Never bundle `-r` into a cluster.** If an `rg` result contradicts something you
185+
read directly, suspect the flags before you suspect the file.
186+
- **Prefer the Grep tool** over `rg` in Bash: it has no `--replace` surface, so
187+
this class of error cannot occur.
188+
161189
## WebFetch — do not retry the same failing URL
162190

163191
| Failure | Try |

0 commit comments

Comments
 (0)