PreToolUse only
Antigravity also exposes PostToolUse, PreInvocation, PostInvocation, and Stop, but its post payload carries no tool name or input and it has no PermissionRequest event. The single PreToolUse hook is the entire gate.
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
Antigravity (agy) is Google's successor to the Gemini CLI. tool-gates supports it through a single PreToolUse hook with its own wire format: a payload that nests the tool under toolCall, a flat {decision, reason} output, and a hooks.json keyed by hook name. This page covers what tool-gates gates on Antigravity and how the pieces map.
Selection
Antigravity's hook payload carries no hook_event_name, so tool-gates cannot tell it apart from Claude by inspection. The installer bakes --client antigravity into the hook command; that flag selects the wire format. Without it, an Antigravity payload would be misread as Claude.
Payload
Antigravity sends a camelCase envelope where the tool lives under toolCall.name and its arguments use PascalCase keys (the command is at toolCall.args.CommandLine, a write target at toolCall.args.TargetFile). tool-gates rewrites this into its canonical internal shape so the same engine that serves Claude, Codex, and Gemini runs unchanged. The original args are preserved alongside the canonical command / file_path / content keys.
| Antigravity tool | Maps to | Command / path source |
|---|---|---|
run_command | Bash | args.CommandLine |
view_file | Read | args.AbsolutePath |
write_to_file | Write | args.TargetFile + args.CodeContent |
replace_file_content | Edit | args.TargetFile + args.ReplacementContent |
multi_replace_file_content | Edit | args.TargetFile + concatenated args.ReplacementChunks[].ReplacementContent |
grep_search | Grep | args.Query |
find_by_name | Glob | args.Pattern |
Decision
The PreToolUse hook returns a flat JSON object on stdout. decision is required and is one of allow, ask, deny, or force_ask. Antigravity resolves a tool call as the strictest of all candidate decisions (rank order: allow lowest, then ask, force_ask, deny highest), so the hook can only tighten: deny/ask/force_ask win when they outrank agy's native decision, but a hook allow (the floor) never suppresses a prompt agy's own rules would show. Stopping the prompt for safe commands is done with agy's native permissions.allow list (see Allowlisting safe commands).
| tool-gates result | Antigravity output | Effect |
|---|---|---|
| No opinion | empty stdout | Antigravity's own fine-grained permission engine decides. tool-gates never speaks for a command it does not recognize. (decision is required by the schema; this relies on the currently-undocumented behavior that emitting none defers to Antigravity's engine.) |
| Allow (known-safe) | empty stdout | tool-gates recognizes the command as safe, but a hook allow is the lowest rank and inert on agy, so it emits nothing and lets agy's engine decide. The native permissions.allow list is what stops the prompt. |
| Ask (soft) | {"decision":"ask"} | Prompts, respecting the user's "Always Allow" grants. Used for routine mutations and unknown commands. |
| Ask (hard floor) | {"decision":"force_ask"} | Pipe-to-shell, eval, and dangerous substitution. Always prompts, ignoring any "Always Allow" grant, so the floor can never be permanently granted away. |
| Deny | {"decision":"deny"} | Hard block. Remediation text is folded into reason since the Pre output has no additionalContext field. A confirmed source-file recovery names Antigravity's native view_file tool; streamed output never receives file-reader advice. |
The hard-ask safety floor maps to force_ask, not ask: pipe-to-shell and eval are ask-tier (never deny), and Antigravity's plain ask honors a prior "Always Allow" grant, which would let a granted command silently bypass the floor. force_ask always prompts. tool-gates does not emit permissionOverrides: a hook's permissionOverrides does not suppress the current call's prompt, so it would buy nothing.
Scope
Antigravity also exposes PostToolUse, PreInvocation, PostInvocation, and Stop, but its post payload carries no tool name or input and it has no PermissionRequest event. The single PreToolUse hook is the entire gate.
hooks.jsonAntigravity's hooks.json is a top-level object keyed by hook name, so tool-gates owns one entry, tool-gates, and leaves any other named hooks untouched: {"tool-gates": {"PreToolUse": [...]}}.
Write content arrives in the PreToolUse args, so Tier 1 secret scanning runs on the write before it lands. File guards apply on both reads (view_file) and writes for symlinked AI-config files.
Antigravity's MCP tool-name format for hook matchers is not documented, so MCP block rules are not yet applied for Antigravity. Shell, file, grep, and glob tools are all gated.
# shared user/global path (recommended, default) $ tool-gates hooks add --antigravity # preview without writing $ tool-gates hooks add --antigravity --dry-run # project scope $ tool-gates hooks add --antigravity -s project
Antigravity user hooks live at ~/.gemini/config/hooks.json, which is the installer default and the path shared by the CLI backend. Project hooks live at .agents/hooks.json and are available with -s project. The matcher covers run_command, view_file, write_to_file, replace_file_content, multi_replace_file_content, grep_search, and find_by_name.
Plugin-packaged hooks are useful for distribution, but they are not required for hook installs. Treat plugin support as a separate global-install path that should be verified with agy plugin validate and a deny probe before relying on it.
Confirm with tool-gates hooks status, which lists the Antigravity shared user and project hook paths alongside the other clients.
# print a permissions.allow block for safe commands $ tool-gates agy allowlist # merge it into agy's settings (backs up first), then restart agy $ tool-gates agy allowlist --apply # preview the merge without writing $ tool-gates agy allowlist --apply --dry-run
Because a hook allow is inert, the only thing that stops Antigravity prompting for a read-only command is its native permissions.allow list in ~/.gemini/antigravity-cli/settings.json (a different file from the hooks path). tool-gates agy allowlist generates that list from tool-gates' own unconditionally-safe command set, one command(<prog>) rule per program (command(rg), command(cat), command(jq), …).
The tool-gates hook still runs on every call and tightens (deny/ask/force_ask) over any dangerous form, so a native command(find) does not let find . -delete through: the hook asks and the stricter decision wins. Interpreters (bash/sh/zsh), curl, and conditionally-gated programs are excluded from the list. Antigravity reads settings at startup, so restart agy after applying.