What happened
A user discovered Claude Code consuming roughly 85,000 of a 100,000 token context window on a refactor, most of it spent reading node_modules. The surprising root cause was that bash commands (grep/find) were scanning the whole project tree and bypassing Read() deny rules in the config. In short, Claude Code’s file-read permission system did not apply to shell tool invocations.
The quick fix
The OP added a pre-tool hook that validates Bash commands and rejects ones that reference large or sensitive folders. Their validate-bash.sh (five lines) checks the command payload and fails if patterns like node_modules, .env, __pycache__, .git, dist, or build appear. They wired it into .claude/settings.local.json as a PreToolUse hook. It will not catch every evasive trick (variables, command substitution), but it blocks the common, accidental token drains.
Why this matters
- Silent token waste: Developers who let Claude explore a repo can hit usage limits quickly without realizing why. According to top comments on the Reddit thread (https://www.reddit.com/r/ClaudeAI/comments/1oh95lh/claude_code_usage_limit_hack/), this explains why some users see “insane” usage while others do not.
- Permissions gap: Two permission systems that do not talk to each other are a serious UX and security blind spot. Read() deny lists are ineffective if the LLM just runs a shell command that ignores them.
- Low-cost mitigation: A tiny, local hook stops most accidental scans. That is strong ROI: a few lines of code for major usage savings.
Selected high-value comments
- ZorbaTHut: “This might actually explain why a bunch of people are having insane usage-limit issues while many other people are having no problems at all.”
- bchan7: recommends returning a structured JSON hook response with
permissionDecision: "deny"rather than relying on exit codes, which Claude may ignore for Bash hooks. Their example shows how to respond programmatically to PreToolUse events.
Context and alternatives
Several alternatives and additional mitigations were discussed:
- Ripgrep and .gitignore: Multiple commenters pointed out
ripgrep(rg) respects.gitignoreby default. If Claude is configured to use ripgrep instead of grep,node_modulesscanning should be avoided. See the Claude Code changelog note about ripgrep and theUSE_BUILTIN_RIPGREPtoggle (https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md#1084). - .claudeignore: If available for your setup, explicitly ignoring build and dependency folders is another guard.
- Whitelist vs blacklist: Blacklisting patterns is quick, but whitelisting allowed commands (or restricting to specific, audited tools like
rgorfd) is safer for long-lived projects. - Hardening ideas: Expand regex to catch evasion (command substitution, quoted fragments), log blocked commands for auditing, or use a hook that returns
permissionDecisionexplicitly as commenters suggest. See the Claude Code hooks documentation for details (https://anthropic.mintlify.app/en/docs/claude-code/hooks).
Practical implementation notes
- If you copy the OP’s script, remember edge cases: someone could hide
node_modulesinside a variable or split the string (“node”_”modules”). Consider stronger pattern checks or require a JSONpermissionDecisionresponse. - Prefer
ripgreporfdas replacements forgrepandfind; they are faster and respect ignores by default. - Add logging to the hook so you can see what commands are being blocked and refine patterns safely.
- If you file a bug, include logs and reproduction steps; this feels like a default behavior gap that could be addressed upstream. The original Reddit post includes a short explanatory video as well (https://youtu.be/hgnfrNXiURM?si=xSHz6q0PmZX78YEI).
My takeaways
- If you see unexplained token usage, check tool invocations – shell commands can bypass read policies.
- Implement a PreToolUse hook that either denies risky patterns or explicitly returns
permissionDecisionJSON to the host. - Prefer tools that respect
.gitignore(ripgrep,fd) and consider whitelisting allowed commands for long-term safety.
Actionable checklist
- Add a PreToolUse hook to inspect shell commands or use bchan7’s JSON-style responder.
- Switch to
rgorfdwhere possible and enable ripgrep behavior in Claude Code if available. - Add logging and iterate on blocked patterns rather than over-aggressively denying commands.
Remember that there is often another side to the story: many users never see this problem because ripgrep, .gitignore, or Claude Code defaults already block dependency scans. But if you do run into sudden token spikes during refactors, this post gives you a fast, effective way to stop the bleeding.
Leave a Reply