lazyperm
v1.0.4
Published
PreToolUse hook for Claude Code that auto-allows read-only shell commands
Maintainers
Readme
claude-permission-guard
A PreToolUse hook for Claude Code that auto-allows read-only shell/PowerShell
commands so you stop getting prompted for things like Get-ChildItem,
git status, or ls, while still asking you about anything that writes,
deletes, or changes system state.
How it decides (defense in depth)
- Hard deny patterns (
deny_patterns.json) — checked first, anywhere in the command, including inside a pipeline. If matched, the hook stays silent and you get the normal prompt. Nothing overrides this. - Cache (
cache.json) — if this exact command string was classified before, reuse that decision instantly. - Safe patterns (
safe_patterns.json) — if every piece of a pipe/chain (|,;,&&,||) matches a known read-only verb, allow. - LLM fallback — if
ANTHROPIC_API_KEYis set and nothing above matched, ask a small model ("is this read-only?"). The result is cached so you're never billed twice for the same command. - Default — stay silent, the normal ask-prompt takes over.
The hook never actively denies. It only ever short-circuits to "allow" or does nothing. Worst case if something's misconfigured: you get prompted like normal, you're never blocked from approving manually.
Install
- Copy this folder's
guard.py,safe_patterns.json,deny_patterns.jsoninto.claude/hooks/in your project (or~/.claude/hooks/for a global install across all projects). - Merge
settings-snippet.jsoninto your.claude/settings.json(project) or~/.claude/settings.json(global). - Restart Claude Code. Run
/hooksto confirm it's registered.
cache.json and decisions.log are created automatically next to
guard.py the first time it runs — don't commit them if this lives in a
shared project repo (add both to .gitignore).
Enable the LLM fallback (optional)
export ANTHROPIC_API_KEY=sk-ant-...Without this set, the hook still works — it just won't auto-classify
commands it doesn't recognize; they'll prompt as normal. This is the safer
default for a first install: run rule-based-only for a few days, check
decisions.log for what's still asking, and promote the genuinely safe
ones into safe_patterns.json yourself before turning the LLM on.
Customizing the lists
Both pattern files are plain JSON arrays of regexes, matched against the
start of each subcommand for safe_patterns.json (so ^Get-ChildItem\b
matches Get-ChildItem -Recurse), and anywhere in the full command for
deny_patterns.json (so it catches a destructive call buried mid-pipeline
or a hidden output redirect).
Add your own as you go — e.g. if you keep hitting prompts for
docker ps or kubectl get pods, add "^docker ps\\b" /
"^kubectl get\\b" to safe_patterns.json.
Reviewing what it's doing
tail -f .claude/hooks/decisions.logEvery decision — allowed, asked, or hard-denied — is logged with a timestamp and the full command, so you can audit it or spot a pattern that's too broad.
Tuning the safety margin
If you find the deny list too aggressive (e.g. you genuinely want
git push auto-approved on a specific branch), remove that line from
deny_patterns.json — but the default ships conservative on purpose:
process/service control, file deletion, output redirection, history
rewrites, and non-GET HTTP calls all force a real prompt by default.
