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
{{ message }}
Repository navigation
Read-only commands (ls/grep/find/wc/cat/tail) should return exit 0 from rtk rewrite, not exit 3 #1232
`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`):
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)
Implemented in PR #1332.\n\nMain change: read-only command rewrites now resolve to allow verdict (exit 0) while deny/ask precedence and default safety remain intact.\n\n@pszymkowiak please review when convenient.
Update: Most commands fixed, head and tail still exit 3
Tested on rtk 0.36.0 — most read-only commands from this issue now correctly return exit 0:
exit=0 ls -la /tmp
exit=0 grep -n foo /tmp/x
exit=0 find . -name "*.js"
exit=0 wc -l file
exit=0 cat file.txt
exit=3 head -5 file ← still ask
exit=3 tail -20 file ← still ask
exit=1 stat file ← no RTK equivalent
head is the biggest remaining gap.rtk discover on my Claude Code setup shows 401 head -5 commands across 30 days — the single most common rewritable command that's still blocked by exit 3.
head and tail are pure read-only. They should be exit 0 (auto-allow) like cat, ls, grep, find, and wc now are.
Confirming on rtk 0.39.0 (28 days after the April 16 update above): PR #1332 holds — ls, grep, find, wc, cat, gh api, git log, curl standalone all return exit 0. The remaining gap is head / tail AND any pipe terminating in them.
rtk discover --all on this Claude Code account (macOS arm64, rtk 0.39.0, hook v3, 1,282 sessions, 30,435 Bash commands over 30 days):
Pattern
Count
rtk equivalent
Status on 0.39.0
grep -n ... (standalone)
1,483
rtk grep -n ...
✅ exit 0 — but historically exit 3 before PR #1332
cat <path> (standalone)
1,444
rtk read <path>
✅ exit 0
`...
head -N/...
tail -N`
(subset of above)
head -N <file> / tail -N <file>
counted under "TOP UNHANDLED"
rtk read --head/--tail
❌ exit 3
The pipe-to-head/tail subset is the residual leak in real-world agentic sessions where many invocations chain through pagination (grep ... | head -20 to get the first match cluster, ls -la | head -10 for top-10 newest, find ... | head -50 for first 50 hits). On this account it's the single largest unfixed pattern.
Worth flagging that the head/tail gap is amplified by #1233 for any account running in permissive Bash mode. My account has Bash(rtk:*) plus per-command allowlists, so exit-3 mutations from ... | head aren't prompted — they're silently dropped and the bare form executes. That's about ~750K input tokens / 30 days that the diagnosis-half of the fix recovers, with the execution-half pending #1233.
Suggested patch shape (head/tail)
Adding head/tail to the read-only allow list in src/discover/registry.rs should be a one-line extension of the PR #1332 change. The pipe-chain case (<anything> | head -N) is harder — needs the analyzer to walk the AST and verify the terminal command is read-only. Could ship in two passes: (a) standalone head/tail first, (b) pipe-terminal head/tail recognition second.
Happy to send a PR if helpful — wanted to confirm the diagnosis is still current before duplicating someone else's in-flight work.
The request is valid, but the solution as envisioned is too simple to ship. A command that only reads can become a write through what surrounds it: ls > file, find . | xargs rm, cat f | tee g, grep x $(...), process substitution, and more shell constructs than a list of read-only names can cover, so an intrinsic allow verdict would have to understand the whole command line, not the first word. That needs real design work. It is also less pressing than it was: agents now validate commands themselves and offer their own auto-approve modes, so the hook deliberately asserts allow only when one of the user's explicit allow rules matches and never grants it on its own. Leaving this open as the design request.
Activity
pszymkowiak commentedon Apr 12, 2026
🔍 Automated Triage
bugmediumSummary
Read-only commands (ls/grep/find/wc/cat/tail) incorrectly return exit 3 from
rtk rewriteinstead of exit 0, causing silent hook failures in auto-approved Bash sessions; PR #1332 is in progress.📁 Relevant files
src/discover/registry.rsTriaged automatically by wshm · This is an automated analysis, not a human review.
TechWizard9999 commentedon Apr 15, 2026
Taking this now. I will adjust rewrite permission behavior for read-only commands and open a focused PR shortly. @pszymkowiak
TechWizard9999 commentedon Apr 15, 2026
Implemented in PR #1332.\n\nMain change: read-only command rewrites now resolve to allow verdict (exit 0) while deny/ask precedence and default safety remain intact.\n\n@pszymkowiak please review when convenient.
migelizaga-rgb commentedon Apr 16, 2026
Update: Most commands fixed,
headandtailstill exit 3Tested on
rtk 0.36.0— most read-only commands from this issue now correctly return exit 0:headis the biggest remaining gap.rtk discoveron my Claude Code setup shows 401head -5commands across 30 days — the single most common rewritable command that's still blocked by exit 3.headandtailare pure read-only. They should be exit 0 (auto-allow) likecat,ls,grep,find, andwcnow are.Environment
acpatelmd commentedon May 11, 2026
Confirming on rtk 0.39.0 (28 days after the April 16 update above): PR #1332 holds —
ls,grep,find,wc,cat,gh api,git log,curlstandalone all return exit 0. The remaining gap ishead/tailAND any pipe terminating in them.Probe on 0.39.0
My 30-day data point
rtk discover --allon this Claude Code account (macOS arm64, rtk 0.39.0, hook v3, 1,282 sessions, 30,435 Bash commands over 30 days):grep -n ...(standalone)rtk grep -n ...cat <path>(standalone)rtk read <path>/...head -N <file>/tail -N <file>rtk read --head/--tailThe pipe-to-head/tail subset is the residual leak in real-world agentic sessions where many invocations chain through pagination (
grep ... | head -20to get the first match cluster,ls -la | head -10for top-10 newest,find ... | head -50for first 50 hits). On this account it's the single largest unfixed pattern.Interaction with #1233 silent-mutation-drop
Worth flagging that the head/tail gap is amplified by #1233 for any account running in permissive Bash mode. My account has
Bash(rtk:*)plus per-command allowlists, so exit-3 mutations from... | headaren't prompted — they're silently dropped and the bare form executes. That's about ~750K input tokens / 30 days that the diagnosis-half of the fix recovers, with the execution-half pending #1233.Suggested patch shape (head/tail)
Adding
head/tailto the read-only allow list insrc/discover/registry.rsshould be a one-line extension of the PR #1332 change. The pipe-chain case (<anything> | head -N) is harder — needs the analyzer to walk the AST and verify the terminal command is read-only. Could ship in two passes: (a) standalonehead/tailfirst, (b) pipe-terminal head/tail recognition second.Happy to send a PR if helpful — wanted to confirm the diagnosis is still current before duplicating someone else's in-flight work.
KuSh commentedon Sep 27, 2026
The request is valid, but the solution as envisioned is too simple to ship. A command that only reads can become a write through what surrounds it:
ls > file,find . | xargs rm,cat f | tee g,grep x $(...), process substitution, and more shell constructs than a list of read-only names can cover, so an intrinsic allow verdict would have to understand the whole command line, not the first word. That needs real design work. It is also less pressing than it was: agents now validate commands themselves and offer their own auto-approve modes, so the hook deliberately asserts allow only when one of the user's explicit allow rules matches and never grants it on its own. Leaving this open as the design request.