Read-only commands (ls/grep/find/wc/cat/tail) should return exit 0 from `rtk rewrite`, not exit 3
#1232 opened on Apr 11, 2026
Description
Summary
`rtk rewrite` returns exit code 3 ("ask rule matched") for read-only file-inspection commands like `ls`, `grep`, `find`, `wc`. These are non-destructive and should return exit 0 ("auto-allow") so the Claude Code hook can rewrite and auto-execute them without requiring user approval on every call.
Reproduction
Running against the standard `rtk-rewrite.sh` hook (`rtk-hook-version: 3`):
```bash $ rtk rewrite 'ls -la /tmp' rtk ls -la /tmp $ echo $? 3
$ rtk rewrite 'grep -n foo /tmp/x' rtk grep -n foo /tmp/x $ echo $? 3
$ rtk rewrite 'ls -la /tmp; echo done' rtk ls -la /tmp; echo done $ echo $? 3
$ rtk rewrite 'ls /tmp && echo ok' rtk ls /tmp && echo ok $ echo $? 3 ```
All four return exit 3. Per the hook protocol documented in `rtk-rewrite.sh`:
```
Exit code protocol for `rtk rewrite`:
0 + stdout Rewrite found, no deny/ask rule matched → auto-allow
1 No RTK equivalent → pass through unchanged
2 Deny rule matched → pass through
3 + stdout Ask rule matched → rewrite but let Claude Code prompt the user
```
Why this matters
Read-only commands don't warrant a user prompt. But more importantly, exit 3 has a silent-failure mode in permissive Claude Code Bash configurations (filed separately): when exit 3 is returned, the hook outputs `updatedInput` without `permissionDecision`, expecting Claude Code to prompt. In a session where Bash is auto-approved, no prompt is shown and the mutation is silently dropped — the original unrewritten command runs.
Because of this interaction, classifying `ls`/`grep`/`find`/`wc` as exit 3 means in many real-world Claude Code sessions, RTK provides ~0% savings on the very commands that should be its hot path.
Empirically on my setup, `rtk discover` reports across 30 days of Claude Code usage:
| Command | Count |
|---|---|
| `grep -n` | 60 |
| `ls -la` | 27 |
| `find` | 24 |
| `tail -20` | 11 |
| `wc -l` | 5 |
All listed as "already handled by RTK" — yet the corresponding session JSONLs show raw command output, not rewritten format, proving the hook never successfully mutated them.
Suggested fix
In `src/discover/registry.rs`, classify these as auto-allow (exit 0):
- `ls`, `ll`
- `grep`, `rg`
- `find`
- `wc`
- `cat`, `head`, `tail`
- `stat`, `file`
Keep exit 3 for commands that modify state: `rm`, `mv`, destructive `git`/`npm` operations, etc.
Environment
- rtk: 0.23.0+ (verified via `rtk verify`: 141/141 tests pass)
- Claude Code: current
- Platform: macOS Darwin 25.5.0
- Hook: `rtk-hook-version: 3`