Skip to content
Development
Hook

Hooks

What antigravity runs automatically, and when. A hook is a command Claude Code fires at a fixed moment, without you asking for it.

From plugin
antigravity
4802 skills1 agent11 commands2 hooks

Claude Code

> /plugin marketplace add yuting0624/antigravity-for-claude-code
> /plugin install antigravity@antigravity-for-claude-code

What fires, and when

SessionStart

Fires once when a session begins, and again after a context compaction. It is where a plugin sets up its environment, or restores state the compaction dropped.

  • Matchesstartup"${CLAUDE_PLUGIN_ROOT}/hooks/check-agy.sh"
  • Matchesstartup"${CLAUDE_PLUGIN_ROOT}/hooks/inject-policy.sh"
  • Matchescompact"${CLAUDE_PLUGIN_ROOT}/hooks/inject-policy.sh"

UserPromptSubmit

Fires before Claude sees each prompt you send. A plugin can use it to inject context, so the same instruction reaches the model every turn instead of only at session start.

  • "${CLAUDE_PLUGIN_ROOT}/hooks/nudge-delegation.sh"
Read hooks/hooks.json

Where it lives

  • hooks/check-agy.shRunsGitHub
    Read the script
    #!/usr/bin/env bash
    #
    # SessionStart hook: lightweight check that the Antigravity CLI (`agy`) is usable.
    # Warns on stderr but NEVER fails the session (always exits 0). The full health
    # check lives in scripts/doctor.sh — this one stays fast (no `agy models` network
    # call) so it doesn't slow every session start.
    #
    set -uo pipefail
    
    if ! command -v agy >/dev/null 2>&1; then
      echo "[antigravity] agy not on PATH — install the Antigravity CLI to enable delegation:" >&2
      echo "[antigravity]   https://antigravity.google/docs/cli-using" >&2
      exit 0
    fi
    
    if ! agy --version >/dev/null 2>&1; then
      echo "[antigravity] agy is on PATH but '--version' failed — it may need authentication (run \`agy\` once)." >&2
    fi
    
    exit 0
    
  • hooks/inject-policy.shRunsGitHub
    Read the script
    #!/usr/bin/env bash
    #
    # SessionStart hook: inject this plugin's COST-AWARE routing policy as session
    # context, so the discipline (delegate above the break-even, keep Claude's context
    # lean, always verify agy's output) applies even when the `antigravity` skill isn't
    # explicitly invoked. Prints the hookSpecificOutput JSON on stdout.
    #
    # Toggle off via plugin userConfig `coding_policy` (env CLAUDE_PLUGIN_OPTION_CODING_POLICY:
    # off / false / 0 / no / disabled). Default: on.
    #
    set -uo pipefail
    HERE="$(cd "$(dirname "$0")" && pwd)"
    
    raw="$(printf '%s' "${CLAUDE_PLUGIN_OPTION_CODING_POLICY:-on}" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')"
    case "$raw" in
      off|false|0|no|disabled) exit 0 ;;
    esac
    
    cat "$HERE/policy-context.json"
    exit 0
    
  • hooks/nudge-delegation.shRunsGitHub
    Read the script
    #!/usr/bin/env bash
    #
    # UserPromptSubmit hook: a cheap, deterministic nudge toward delegation when the
    # user's prompt LOOKS like bulk work above the delegation break-even.
    #
    # Design principle: this supplies judgment MATERIAL — the DECISION stays with
    # Claude (per the skill's cost discipline). It never forces a delegation and it
    # never fires the wrapper itself: full automation is a measured net loss below
    # the break-even, so the break-even call must remain a per-task judgment.
    #
    # Heuristic is deliberately conservative (volume/fan-out phrases, EN + JA), and
    # the nudge text is a FIXED string — the user's prompt is never echoed back into
    # the context (no escaping/injection surface).
    #
    # Toggle via plugin userConfig `delegation_nudge`
    # (env CLAUDE_PLUGIN_OPTION_DELEGATION_NUDGE: off/false/0/no/disabled). Default: on.
    #
    set -uo pipefail
    
    raw="$(printf '%s' "${CLAUDE_PLUGIN_OPTION_DELEGATION_NUDGE:-on}" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')"
    case "$raw" in off|false|0|no|disabled) exit 0 ;; esac
    
    IN="$(cat 2>/dev/null || true)"
    [ -n "$IN" ] || exit 0
    
    # Extract ONLY the prompt field (matching on the whole payload would false-positive
    # on cwd/paths). python3 is already a plugin dependency (measure-session, agy-trace).
    PROMPT="$(printf '%s' "$IN" | python3 -c 'import json,sys
    try: print(json.load(sys.stdin).get("prompt",""))
    except Exception: pass' 2>/dev/null || true)"
    [ -n "$PROMPT" ] || exit 0
    
    # Already delegating explicitly? Stay quiet.
    case "$PROMPT" in *antigravity*|*agy-delegate*|*agy-job*) exit 0 ;; esac
    
    shopt -s nocasematch
    HIT=0
    case "$PROMPT" in
      *"all files"*|*"every file"*|*"across the codebase"*|*"entire codebase"*|*"whole repo"*| \
      *migrate*|*migration*|*"generate tests"*|*"test coverage"*|*"exhaustive test"*| \
      *scaffold*|*boilerplate*|*"deep research"*|*"web search"*| \
      *一括*|*全ファイル*|*すべてのファイル*|*網羅*|*移行*|*大量*|*横断*|*リポジトリ全体*)
        HIT=1 ;;
    esac
    shopt -u nocasematch
    [ "$HIT" -eq 1 ] || exit 0
    
    # Fixed nudge. Note the explicit "the judgment is yours" — this is material, not a mandate.
    cat <<'JSON'
    {"hookSpecificOutput":{"hookEventName":"UserPromptSubmit","additionalContext":"[antigravity plugin] This prompt looks like BULK work (mass edits / migration / exhaustive tests / fan-out search) — possibly above the delegation break-even. CONSIDER routing the bulk part to the antigravity-delegate subagent (or agy-delegate --digest) so it runs on the cheap executor, then verify its digest. THE JUDGMENT IS YOURS: if the task is actually small, self-contained, or judgement-heavy, do it yourself — delegating below the break-even is a measured net loss. Decide silently; don't mention this notice."}}
    JSON
    exit 0
    
  • hooks/validate-delegate-bash.shGitHub
    Read the script
    #!/usr/bin/env bash
    #
    # PreToolUse(Bash) gate for the `antigravity-delegate` subagent. Claude Code's
    # subagent `tools:` field can't scope Bash to one command, so this hook is the ONLY
    # thing restricting what that subagent may run — it must allow a Bash call only when
    # it invokes the plugin's delegation wrapper (agy-delegate / agy-job) and nothing else.
    #
    # Hardening (issue #29): the previous version matched the wrapper name as a SUBSTRING
    # anywhere in the command, so payloads like `foo ... # agy-delegate` or
    # `echo $(...) agy-job` slipped through (arbitrary execution under prompt injection).
    # This version instead:
    #   * requires the FIRST command token (argv[0], basename, optional .sh) to be exactly
    #     agy-delegate / agy-job — a token check, not a substring match;
    #   * allows NO pipeline at all. A producer allowlist was the second half of the #29
    #     hardening and it is what GHSA-hwv2-vjgj-8rcv broke, twice over: `git` with
    #     unrestricted arguments is a living-off-the-land binary (`git -c alias.x='!cmd' x`,
    #     `--exec-path=`, `-c core.pager=` all execute arbitrary commands, all measured), and
    #     `cat`/`echo`/`printf` feeding `agy-delegate -` reads any file or `$VAR` and ships it
    #     to the external model. Allowing a command by NAME while ignoring its arguments is
    #     not an identity check. Nothing needed the pipeline: this subagent's own contract
    #     says the gate "blocks every Bash command except the delegation wrapper" and shows
    #     only `agy-delegate [options] "<task>"`, and the documented
    #     `git diff | agy-delegate --tier pro -` in commands/review.md runs as the MAIN
    #     Claude, which this hook does not gate;
    #   * rejects UNQUOTED shell metacharacters bash would act on (`; & | < > ( ) #`,
    #     backticks, `$(`, and a NEWLINE — it separates commands just like `;`), while
    #     permitting them INSIDE a quoted prompt (no false positives on legitimate
    #     prompts — command substitution inside double quotes is still blocked because
    #     bash would expand it). Leading/trailing whitespace is stripped first, so a
    #     trailing newline is fine; an internal one is two commands and stays blocked;
    #   * fails CLOSED (block) if the JSON is unparseable or python3 is unavailable.
    #
    # On a block it prints the SPECIFIC reason to stderr before the generic message
    # (issue #51). Claude Code feeds PreToolUse stderr back to the agent, so the caller
    # can tell "you left a newline in" apart from "you tried to run something else" —
    # previously both produced the same string and the agent retried the same shape.
    #
    # Input: hook JSON on stdin, with .tool_input.command holding the bash command.
    # Exit: 0 = allow, 2 = block.
    #
    set -uo pipefail
    
    input="$(cat)"
    
    BLOCK_MSG="[antigravity-delegate] blocked: this subagent may only run agy-delegate / agy-job / agy-handoff, as a BARE name with no path and no pipeline (and for agy-handoff, without --verify or --no-verify). No other commands, pipes, chaining, redirection, substitution, comments, or unquoted newlines. To give agy a repository, pass --dir <repo-root> rather than piping content in. Delegate file work to agy; verification is the caller's job."
    
    # python3 gives a correct, quote-aware parse. Fail CLOSED if it's missing.
    if ! command -v python3 >/dev/null 2>&1; then
      echo "$BLOCK_MSG (python3 unavailable — failing closed)" >&2
      exit 2
    fi
    
    if AGY_GATE_INPUT="$input" python3 - <<'PY'
    import json, os, shlex, sys
    
    raw = os.environ.get("AGY_GATE_INPUT", "")
    try:
        cmd = json.loads(raw).get("tool_input", {}).get("command", "")
    except Exception:
        sys.exit(2)                      # unparseable payload -> fail closed
    if not isinstance(cmd, str) or not cmd.strip():
        sys.exit(2)
    
    # Leading/trailing whitespace is normalised away BEFORE scanning (issue #51). bash
    # ignores it, so this cannot change what the command does — and a newline with
    # nothing after it cannot begin a second command. Internal newlines are untouched
    # and still rejected below. An unterminated quote still fails the state check:
    # `agy-delegate "hi\n` strips to `agy-delegate "hi`, which is still unbalanced.
    cmd = cmd.strip()
    
    WRAPPERS = {"agy-delegate", "agy-job", "agy-handoff"}
    
    # Say WHY, on stderr, so the caller can self-correct (issue #51). Claude Code feeds
    # PreToolUse stderr back to the agent, which is the same path BLOCK_MSG already takes.
    #
    # NEVER include the command text. This lands in the agent's context and the blocked
    # command routinely carries a delegation prompt the caller would not want quoted back;
    # a character name and an offset are enough to fix it. `argv[0]` is the exception —
    # it is a command name, not content, and naming it is most of the diagnostic value.
    def deny(reason):
        sys.stderr.write("[antigravity-delegate] reason: %s\n" % reason)
        sys.exit(2)
    
    CHAR_NAMES = {";": "';' (command separator)", "&": "'&' (background / chaining)",
                  "<": "'<' (redirection)",       ">": "'>' (redirection)",
                  "(": "'(' (subshell)",          ")": "')' (subshell)",
                  "#": "'#' (comment)"}
    
    # A bare NAME, never a path. This used to be os.path.basename(), which accepted any
    # directory: `./agy-delegate` in a cloned repository passed the gate and ran THAT file.
    # Untrusted repository content is the exact prompt-injection source SECURITY.md names,
    # so the one control it names was defeated by the checkout it was meant to survive.
    #
    # Nothing needs a path. agents/, commands/ and skills/ have invoked these by bare name
    # since 0.14.0 — the plugin puts bin/ on the Bash tool PATH because $CLAUDE_PLUGIN_ROOT
    # is not exported to model-run Bash (issue #11). A bare name resolves through PATH,
    # which the working directory is not on; a path resolves through the working directory,
    # which an attacker controls.
    #
    # Returns None for anything path-shaped, which every caller treats as "not allowed".
    def base(tok):
        if "/" in tok or "\\" in tok:     # backslash too: Git Bash / MSYS accept `.\name`
            return None
      

Read the script before you install anything that runs on your machine. This is the one part of a plugin that acts without being asked.

@yuting0624
Ships withantigravity

Run the Antigravity CLI (Gemini) as a collaborating sub-agent, right inside Claude Code. Claude conducts the judgement; Gemini does the heavy lifting — intelligent model routing across the SDLC.

4802 skills1 agent
Get the whole plugin

Stars on GitHub

Momentum

stars in total
480
Star history of yuting0624/antigravity-for-claude-code on GitHub, a chart by star-history.com

More plugins that ship hooks.

superpowers

superpowers

A development methodology for coding agents that makes the agent brainstorm before building, follow plans, run parallel work and finish branches in a disciplined way. You get fewer skipped steps and more consistent results from your coding assistant.

15 skills1 hook311k
@obra@obra
Install Plugin
ponytail

ponytail

A Claude Code plugin with skills for auditing a whole repo, reviewing a change, listing deferred shortcut comments, and showing measured savings and a quick command reference.

6 skills10 hooks188k
@dietrichgebert@dietrichgebert
Install Plugin
agent-skills

agent-skills

Walks a software change from a written spec through design, testing, code review, CI/CD, and release, applying the matching engineering skill at each stage. Useful for developers using Claude Code on full development workflows.

25 skills+3112k
@addyosmani@addyosmani
Install Plugin
planning-with-files

planning-with-files

Persistent file-based planning for AI coding agents on long-running, multi-step tasks. It keeps task_plan.md, findings.md and progress.md on disk, so work can resume after /clear or context compaction.

6 skills+228k
@othmanadi@othmanadi
Install Plugin
gsd-core

gsd-core

A spec-driven development workflow for Claude Code that guides a project through planning, phases, milestone audits and fixes. Includes skills to generate tests from UAT criteria and to audit completed work.

72 skills+311k
@open-gsd@open-gsd
Install Plugin
superpowers-zh

superpowers-zh

Chinese-language skill pack for Claude Code that adds workflow guidance for brainstorming, TDD, debugging, and code review, plus Chinese conventions for commits, documentation, and domestic Git platforms like Gitee and Coding.net.

21 skills1 hook8.5k
@jnmetacode@jnmetacode
Install Plugin
socraticode

socraticode

Gives Claude Code semantic search, dependency graph and architecture context over a codebase, so an AI assistant can explore and understand a project's code. Includes skills for indexing a project and managing its file watching.

2 skills+33.4k
@giancarloerra@giancarloerra
Install Plugin
cocoindex-code

cocoindex-code

A lightweight code search engine that uses AST-based semantic search with local embeddings and incremental indexing. It helps Claude Code users find code faster and more efficiently.

1 skill1 MCP+13k
@cocoindex-io@cocoindex-io
Install Plugin

Questions about the hooks in antigravity.

Which coding agents can use the hooks in antigravity?
The hooks in antigravity install in Claude Code and Cursor: each runs Claude Code hooks from .claude/settings.json, so copy the hooks there.