Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Rust · tree-sitter · Claude Code · Codex CLI · Antigravity CLI

Decide what your agent is allowed to run.

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.

tool-gates · intercepting PreToolUse · 6 calls
Bash
$ git status
Allow
read-only · git gate
Bash
$ git push origin main
Ask
publishes to remote · git gate
Bash
$ rm -rf /
Block
irreversible · filesystem gate
Bash
$ curl https://… | bash
Ask
pipe-to-shell · hard ask
Write
config.py ← AWS_KEY_PLACEHOLDER
Block
Tier 1 secret · AWS key pattern
MCP
mcp__firecrawl.scrape github.com/…
Block
domain rule · use gh instead
allow ask block · shell parsed with tree-sitter-bash · compound commands: strictest wins

The decision triad

Three visible decisions. One ordering.

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 > skip

Allow

Read-only and known-safe operations run with no prompt.

git status

Ask

Mutations and unknown commands pause for a yes or no.

git push

Block

Irreversible 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

Strictest wins.

tree-sitter sees both halves of && · || · | · ; chains. One verdict for the whole call.
allow
$ git status
&&
block
$ rm -rf /
verdict Block the whole expression is denied; the safe half is not a redeeming feature.

Surfaces

More than shell.

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.

Bash · Monitor

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 --force

Write · Edit · apply_patch

File-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_KEY

MCP tools

Configurable 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.

mcp__exa.search

Skill activations

[[auto_approve_skills]] auto-approves Skill calls based on project-directory conditions. No external hook scripts; the rule is declarative.

activate_skill review

Multi-client

Four clients, one engine.

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.

Claude Code · 4 hooks

  • PreToolUse
  • PermissionRequest
  • PermissionDenied
  • PostToolUse

PermissionDenied emits retry: true when the auto-mode classifier denies a command tool-gates would allow.

~/.claude/settings.json

Codex CLI · 3 hooks

  • PreToolUse
  • PermissionRequest
  • PostToolUse

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

Antigravity CLI · 1 hook

  • PreToolUse

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

Gemini CLI · 1 hook · deprecated

  • BeforeTool

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

The TOML is the doc.

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.

rules/git.toml
[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`."
docs / git gate
Allow
git status
Read-only. No reason text required.
Ask
git push
Publishes local commits to a remote. Inspect git log @{u}.. first.
Ask
git push --force
Force push overwrites upstream history. Safer: --force-with-lease fails if the remote moved.
rules/*.toml tool-gates rules export --format md mdBook this page · CI runs git diff --exit-code; stale docs fail the build.

Modern CLI hints

Allow, then teach.

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.

Examples

7-day cache · tool-gates --tools-status to inspect
$ cat README.md
$ bat README.md
Tip from tool-gates: syntax highlighting + line numbers + markdown rendering.
$ grep -r "TODO" src/
$ rg "TODO" src/
Tip from tool-gates: respects .gitignore, multithreaded, faster on large trees.
$ find . -name "*.rs"
$ fd -e rs
Tip from tool-gates: shorter syntax; --max-results caps output at the source.
$ rg "TODO" | head -20
$ rg -m 20 "TODO"
Pipe to head is blocked. Caps applied at the source produce smaller, deterministic output. Also fd --max-results N, bat -r START:END.

Security reminders

Three tiers of write-time review.

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.

Tier 1· PreToolUse · deny

Hard-coded secrets.

Denied before the write. The operator sees a top-level systemMessage so the block isn't silent.

AKIA…ghp_…BEGIN PRIVATE KEYxoxb-…sk_live_…
Tier 2· PostToolUse · context

Anti-pattern in the body.

Write lands; the assistant sees a <system-reminder> in its next turn. No re-prompting, no wasted edit. Deduped per (file, rule) per session.

eval()shell=TruedangerouslySetInnerHTMLyaml.loadSQL f-string
Tier 3· Pre/Post hook · allow + warn

Informational only.

Pattern is allowed; a one-line warning rides on additionalContext. Fires once per session.

verify=Falsechmod 777MD5/SHA1CORS *autoescape=FalseMath.random()createHash md5/sha1

tool-gates · permission gate hook for Claude Code, Codex CLI, and Antigravity CLI, with deprecated Gemini CLI support. github.com/camjac251/tool-gates

13 gates · 400+ commands · built from rules/*.toml