Allow
Read-only and known-safe operations run with no prompt.
git statusPress ← or → to navigate between chapters
Press S or / to search in the book
Press ? to show this help
Press Esc to hide this help
tool-gates is a permission hook for AI coding assistants. On the hook surfaces each client exposes, it gates shell commands, file edits, MCP calls, and Skill activations, parses commands with a real tree-sitter AST, and resolves them against thirteen declarative gates covering 400+ command patterns. One result per call: allow, ask, defer, or block.
The decision triad
The green / amber / red triad maps to the visible decisions: allow, ask, block. Defer is the prompt-friendly fourth output described below.
block > ask > allow > skipRead-only and known-safe operations run with no prompt.
git statusMutations and unknown commands pause for a yes or no.
git pushIrreversible or hostile patterns are denied regardless of settings.
rm -rf /A fourth output, defer, omits the decision and lets the assistant's own resolver populate the "don't ask again for X" button. Used for benign asks not covered by an explicit rule.
Compound resolution
&& · || · | · ; chains. One verdict for the whole call.
Surfaces
The same engine fires across every tool surface the assistant exposes. Shell is the loudest case; the other three are where the agent slips past most policy systems.
Shell commands are parsed with tree-sitter and resolved per-program. Compound chains decompose; raw-string passes catch pipe-to-shell and eval first.
git push --forceFile-edit bodies are scanned for 28 anti-patterns. Tier-1 secrets deny before write; symlinked AI config files (CLAUDE.md, .cursorrules) are guarded so the agent can't read through them.
config.py ← AWS_KEYConfigurable block rules cover Glob, Grep, and firecrawl/ref/exa calls to GitHub. In acceptEdits mode, named MCP tools auto-approve, filling the gap Claude Code leaves open.
[[auto_approve_skills]] auto-approves Skill calls based on project-directory conditions. No external hook scripts; the rule is declarative.
Multi-client
The same Rust binary serves Claude Code, Codex CLI, Antigravity CLI, and the deprecated Gemini CLI. The client is auto-detected from the hook payload, or selected with --client codex / --client antigravity where event names collide or are absent. Each gets exactly the wire format it expects.
PermissionDenied emits retry: true when the auto-mode classifier denies a command tool-gates would allow.
~/.claude/settings.json
apply_patch is the canonical edit tool; tool-gates parses the unified diff so file-guards and secret scans run per affected path.
~/.codex/hooks.json
Google's Gemini CLI successor (agy). Selected via --client antigravity; a flat decision output and a hooks.json keyed by hook name. PreToolUse drives the whole gate.
~/.gemini/config/hooks.json
Deprecated compatibility: Google's consumer Gemini CLI sunset date was 2026-06-18. Existing tool-gates integrations remain available, but new setups should use Antigravity. Requires v0.36.0+ for ask support. No PermissionRequest, no approval tracking; tool-gates emits "block" for hard blocks, and Gemini also accepts "deny".
~/.gemini/settings.json
One binary. Routing is via hook_event_name, or the --client flag where event names collide or are absent; the same gate engine returns the right shape for the right client.
Single source of truth
Every rule below is generated from the same files build.rs already reads to build the binary. Editing a reason changes the published page; CI fails when the committed docs drift.
[meta] name = "git" priority = 10 # Read-only. Pass through [[programs.allow]] subcommand = "status" # Mutation. Pause for approval [[programs.ask]] subcommand = "push" reason = "Publishes local commits to a remote. Inspect `git log @{u}..` first." # Force push. Same name, narrower flag [[programs.ask]] subcommand = "push" if_flags_any = ["--force", "-f"] warn = true reason = "Force push overwrites upstream history. Safer: `--force-with-lease`."
git log @{u}.. first.--force-with-lease fails if the remote moved.Modern CLI hints
When the agent reaches for a legacy tool that has a sharper modern alternative, tool-gates allows the call and attaches a one-line suggestion via additionalContext. Hints only fire when the modern tool is actually installed on this machine.
tool-gates --tools-status to inspect
.gitignore, multithreaded, faster on large trees.--max-results caps output at the source.fd --max-results N, bat -r START:END.Security reminders
28 anti-patterns watch every Write/Edit body. The hard floor denies before the file ever lands; the middle tier nudges Claude and Codex after the write so the next action can self-correct; the top tier just informs.
Denied before the write. The operator sees a top-level systemMessage so the block isn't silent.
Write lands; the assistant sees a <system-reminder> in its next turn. No re-prompting, no wasted edit. Deduped per (file, rule) per session.
Pattern is allowed; a one-line warning rides on additionalContext. Fires once per session.