Claude Code hooks that block find/sed/cat, redirect to native tools A set of Claude Code PreToolUse hooks blocks specific Bash command patterns that duplicate built-in Claude Code tools, such as find with -exec or piped grep, sed -i, and cat/head/tail on single files, redirecting the model to use native tools instead. The hooks enforce the restriction at the harness level, preventing permission prompts and ensuring consistent behavior across sessions and subagents. The installer, requiring jq, copies hook scripts into the config directory and adds PreToolUse entries to settings.json without affecting existing hooks. A set of Claude Code PreToolUse hooks that deny specific Bash command patterns which duplicate a built-in Claude Code tool, and tell Claude to use that tool instead. Each hook is a standalone script; install one, some, or all of them. Telling Claude in a CLAUDE.md file "don't use find -exec , use Grep/Glob" doesn't reliably stick — instructions are a suggestion, not an enforcement mechanism, and a model will still reach for a familiar shell one-liner under the right prompt. It also means every Bash call built out of one of these patterns shows up as a permission prompt for the user to approve, when the native tool call wouldn't have needed approval at all. A PreToolUse hook enforces it at the harness level instead: the hook runs before the Bash tool call reaches you, denies it if it matches, and hands Claude a reason it can act on — so it retries with the right tool rather than just getting stuck. Configured once in settings.json , it applies to every session and every subagent, so you're not approving or fixing the same pattern repeatedly across parallel agents. Blocks: find . -name ' .log' -exec rm {} \; find . -name ' .ts' | grep -v node modules find . -type f | xargs grep -l TODO Doesn't touch: plain find with no -exec /piped grep , plain grep , or "find"/"grep" appearing as substrings of another word or inside quoted arguments e.g. grep -rn "findUser" src , a path like my-find-exec-project/ . Blocks: sed -i 's/foo/bar/' file.txt sed -i.bak 's/foo/bar/' file.txt sed -i '' 's/foo/bar/' file.txt BSD/macOS gsed -i ... / sed --in-place ... Doesn't touch: sed without -i printing to stdout , or sed used to transform piped text git diff | sed 's/^/ /' . Blocks: cat notes.txt head -n 20 notes.txt tail -c 100 notes.txt sudo cat /etc/hosts Doesn't touch: tail -f / --follow streaming , cat file1 file2 concatenating multiple files — Read handles one file at a time , anything piped elsewhere cat notes.txt | grep TODO , head -n 5 file | wc -l , and anything redirected to a new file cat notes.txt copy.txt . These are text-pattern heuristics on the raw Bash command string, not a shell parser — deliberately simple. They won't catch every possible obfuscation e.g. combined short-option clusters like sed -ni where -i isn't the first letter after the dash, or a quoted file path containing a space in deny-cat-head-tail.sh 's naive whitespace tokenizer , and deny-find-chains.sh / deny-sed-inplace.sh can't distinguish a real invocation from a command that merely mentions the pattern in a comment or string — both get denied. If you need airtight enforcement, that's a different, heavier tool. git clone https://github.com/