Skip to content

feat(hook): add native Google Antigravity plugin lifecycle and hook support - #3711

Merged
KuSh merged 4 commits into
rtk-ai:developfrom
Aayushyaash:feat/hook-agy-support
Sep 28, 2026
Merged

KuSh merged 4 commits into
rtk-ai:developfrom
Aayushyaash:feat/hook-agy-support

Conversation

@Aayushyaash

@Aayushyaash Aayushyaash commented Aug 25, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Architectural Rationale: Modular Plugin vs. Direct File Mutation

Antigravity officially specifies plugins as the recommended way to distribute modular capabilities (docs/plugins.md). Rather than parsing and re-serializing the user's root .agents/hooks.json (which risks dropping formatting or corrupting foreign hook configurations):

  1. Zero Blast Radius: RTK is isolated in its own dedicated directory (plugins/rtk/), leaving root user configurations untouched.
  2. Deterministic Uninstallation: rtk init --uninstall cleanly removes the directory without leaving orphaned keys or modifying other tools.
  3. Cross-Surface Discovery: Automatically loaded by Antigravity CLI, IDE, and Antigravity 2.0.

Note on Antigravity Permission Evaluation

Antigravity evaluates its tool permissions.allow rules after the PreToolUse hook rewrites the command. If a user enforces command allowlists, permitted commands should account for rtk (e.g. command(rtk git status) or command(rtk *)). This is documented in hooks/antigravity/README.md and the CLI init output.

Test Plan

  • cargo fmt --all --check
  • cargo clippy --bin rtk -- -D warnings
  • cargo test --bin rtk test_antigravity — all 12 unit & lifecycle tests pass.
  • End-to-end verified across Antigravity CLI, Antigravity Desktop App and Antigravity IDE with transparent rewrite assertion.

@CLAassistant

CLAassistant commented Aug 25, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@Aayushyaash
Aayushyaash force-pushed the feat/hook-agy-support branch from ef02ead to 3e03020 Compare August 25, 2026 22:27
@Aayushyaash Aayushyaash changed the title feat(hook): add Antigravity CLI (agy) transparent hook and init support feat(hook): add native Google Antigravity plugin lifecycle and hook support Sep 14, 2026
@Aayushyaash

Copy link
Copy Markdown
Contributor Author

Update: Rebased & Refactored to Canonical Antigravity Plugin Integration

I have rebased this PR onto latest develop to resolve all merge conflicts, and consolidated the architecture:

  1. Unified Canonical Target: Standardized on --agent antigravity and rtk hook antigravity to cover all Antigravity surfaces (CLI agy, IDE, and Desktop 2.0) under a single canonical flag rather than splitting agy and antigravity.
  2. Modular Plugin Architecture: Retained the clean .agents/plugins/rtk/ (workspace) and ~/.gemini/config/plugins/rtk/ (global) layout. This keeps user-managed .agents/hooks.json pristine and avoids JSON mutation collisions in shared repositories.
  3. Clean Uninstallation: Maintained deterministic uninstallation via rtk init --agent antigravity --uninstall (resolving rtk init --uninstall -g --agent kilocode (and windsurf/cline/antigravity/kimi) runs the Claude uninstaller instead — removing Claude's config and nothing of the named agent #3404).
  4. Eliminated Prompt Rules: Removed the redundant .md rules file since the PreToolUse hook guarantees transparent execution, which also permanently resolves the Redux Toolkit ("RTK") substring skip bug (Cline/Windsurf/Kilo/Antigravity install skips silently if the project's rules file contains the substring "rtk" — e.g. "RTK Query" in a Redux project #3406).
  5. Aligned with Upstream Permissions: Integrated cleanly with upstream's load_rules_for host architecture and documented Antigravity's post-rewrite permission checking behavior in hooks/antigravity/README.md.

Ready for review!

@Aayushyaash

Aayushyaash commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Update: Rebased on develop, Modularized to init/antigravity.rs, and Expanded Test Suite

  1. Adopted Modular Init Architecture

    • In line with upstream's refactoring of src/hooks/init.rs into per-agent submodules.
    • Antigravity's lifecycle management is now cleanly isolated in src/hooks/init/antigravity.rs.
    • Re-exported via src/hooks/init/mod.rs.
  2. Dedicated Integration Test Suite

    • Added tests/antigravity_hook_test.rs.
    • Covers end-to-end child process execution (rtk hook antigravity).
    • Includes:
      • Transparent rewrite verification
      • UTF-8 BOM toleration
      • Fail-open handling
      • Audit logging
  3. Hardened Edge-Case Coverage

    • Added unit tests in src/hooks/hook_cmd.rs for:
      • Pre-prefixed command idempotency (rtk git status does not get double-prefixed)
      • Shell redirection/subshell deferrals

Verification

  • cargo fmt --all --check — clean (0 diffs).
  • cargo clippy --bin rtk --all-targets -- -D warnings — clean (0 warnings).
  • cargo test --bin rtk test_antigravity — all 15 unit tests pass.
  • cargo test --test antigravity_hook_test — all 5 integration tests pass.
  • cargo test --test hook_decision_protocol_test — all 35 decision protocol tests pass.

Ready for review!

…t like the sibling agents

On develop the rules file handed the agent the full awareness text; the
plugin bundle carried none, so a session got the rewrite and nothing that
says output is condensed or how to recover with rtk proxy. The plugin now
holds rules/AGENTS.md with the file awareness.level selects, plain markdown
with no frontmatter, which Antigravity applies whenever the plugin is
enabled. A re-run rewrites it, so changing the level takes effect.

The plugin files go through the shared atomic_write, and plugin.json, the
file that makes Antigravity discover the directory, is written last: a
write that fails halfway leaves no plugin rather than one missing its
rules. The install output tells the user to restart Antigravity, which only
discovers a plugin directory on startup.

Uninstall reports like Factory Droid: a "not installed (nothing to remove)"
line when there is no plugin, otherwise a header and the removed items, and
the dry-run footer; the helper that removes the directory prints nothing.

README's agent table and the configuration guide no longer describe the
rules file, and the "<1.5ms" figure goes: the hook measures about 7 ms end
to end. Process-level tests pin the printed lines and that both dry runs
change nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@KuSh KuSh left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round 1 of 3 — APPROVED, follow-ups filed

Claim: rtk hook antigravity is a native PreToolUse hook that rewrites run_command calls through overwrite.CommandLine, and rtk init [-g] --agent antigravity [--uninstall | --dry-run] installs and removes an Antigravity plugin bundle at .agents/plugins/rtk/ or ~/.gemini/config/plugins/rtk/, replacing the rules-file mode.
Scope: accept if narrowed to "the native hook and the plugin lifecycle for a fresh install"; migrating a project set up by an earlier release (its .agents/rules/antigravity-rtk-rules.md) is cut and tracked as #4317. #3959 proposes the same hook with a root hooks.json merge and no uninstall; it is a duplicate of this one, and the comment there recommends closing it. #4269 (maintainer branch, every init write through one guarded path) touches the same directory and lands after this PR; routing the two new writes is done there.
Ran: agy 1.2.12 embedded hook and plugin docs (Docker) × the contract this PR implements; rtk hook antigravity × 16 payload shapes (rewrite, already-prefixed, pipe, && chain, cd &&, subshell, redirect, unknown binary, other tool, empty command, invalid JSON, empty stdin, closed stdin, BOM, command key, cwd = plugin dir) × merge-base vs head; init/uninstall × local, global, dry-run, re-run, nothing installed, foreign files in the plugin dir, symlinked plugin dir, root hooks.json present, ~/.claude/settings.json present; upgrade from the merge-base's rules-file install. Invariants: every decision arm driven (Deny is unreachable for Host::Antigravity, as for Host::Vibe); under the hook (rtk rewrite, rtk hook check --agent antigravity); split streams (stdout is the JSON only). Mutation: 5 mutants (BOM strip, command key fallback, uninstall no-op, AgentPath back to rules-only, global root = local root), each killed by a named test. Windows cfg paths type-checked. Savings: n/a (hook, no filtered output).
Matrix: executed this round @064cb2f7 and on the fixup head @0671ac5b.
CI @0671ac5b: all green (11/11, including test on ubuntu, macos and windows); the author's head 064cb2f had never been built by CI.

Blocks merge (1, frozen at round 1; closed by the fixup)

  1. src/hooks/init/antigravity.rs:71 — awareness text dropped (1 site). On develop, rtk init --agent antigravity gives the agent the full awareness text through .agents/rules/antigravity-rtk-rules.md; the plugin this PR installs holds plugin.json and hooks.json only, so an Antigravity session gets the rewrite and nothing that says output is condensed, that rtk proxy <cmd> is the recovery path, or that rtk gain exists. Evidence: find .agents -type f after init on the merge-base lists the rules file; on the head it lists the two JSON files and no rules. Antigravity's plugin docs (agy 1.2.12) give the slot: rules/AGENTS.md, plain markdown, no frontmatter, always on while the plugin is enabled. The fixup writes it with awareness_content(ctx.awareness), so awareness.level applies as it does for Gemini and Vibe. Test: test_antigravity_mode_writes_awareness_rules_at_each_level.

Filed as follow-ups

  • #4317 rtk init --agent antigravity leaves the rules file written by earlier releases in place — cut by the narrowed claim; filed.
  • #913 "No hook installed" warning for non-Claude agents — pre-existing (hook_check::status() reads Claude's settings only; reproduced: rtk gain warns after an Antigravity-only install whenever ~/.claude exists); absorbed, comment added for the Antigravity arm.
  • #3404 and #3406 stay open: this PR fixes their Antigravity arm only (the global uninstall no longer runs the Claude uninstaller, and the rules-file substring check is gone for this agent); Kilo Code, Windsurf, Cline and Kimi are unchanged.

Checked and correct — no need to re-verify

  • Contract matches the CLI's own docs: hook-name namespace with enabled, PreToolUse grouped under matcher/hooks, run_command, timeout in seconds, overwrite shallow-merged at top level, the agent notified of rewritten keys. Plugin discovery from .agents/plugins/<name>/ and ~/.gemini/config/plugins/<name>/ with plugin.json as the marker, hooks loaded from the plugin.
  • Fail-open on every malformed input (invalid JSON, empty, closed stdin, BOM); other tools and an empty CommandLine answer {"decision":"allow"} with no overwrite; already-prefixed commands are not rewritten; chains and pipes are rewritten per segment like every other host.
  • The hook runs with its cwd set to the plugin directory (Antigravity's rule): the decision does not depend on cwd for this host, output identical from the project root and from .agents/plugins/rtk/.
  • rtk init -g --agent antigravity --uninstall no longer reaches the Claude uninstaller: on the merge-base it emptied ~/.claude/settings.json's PreToolUse; on the head that file is untouched.
  • A root .agents/hooks.json with foreign content is never read or written; a foreign file inside .agents/plugins/rtk/ survives install and is removed with the directory on uninstall (the directory is RTK's, same as the Hermes plugin dir).
  • A symlinked .agents/plugins/rtk is written through and only the link is removed on uninstall — the same behaviour every init writer has today, which #4269 changes for all of them.
  • Gate on the head: fmt, clippy, cargo test --all 4052 passed; Windows cfg paths compile. Fixup head: 4057 passed, Windows paths compile, matrix rerun with only the intended output lines changed.

Your questions

  • None posted on this PR. One from me, not holding merge: in your live runs with no permissions.allow rule for git status, does the hook's allow still prompt interactively before the rewritten command runs? The CLI's docs say allow "automatically allows"; the binary's grant reasons have no hook_allow entry (only hook_deny, hook_force_ask, hook_deny_unless_prior_grant, prior-grant, workspace and autoexec grants), and #3959's author measured a headless run still denied by the host's own rules under a hook allow. If a prompt-less run is what you see, say so and the mapping becomes ask on Defer with a config toggle; otherwise the current mapping stands as documented.

Next

fixup 0671ac5 pushed, code-reviewed: the plugin gains rules/AGENTS.md at the configured awareness.level, rewritten on every run so a new level takes effect (the blocker above); the three plugin files go through the shared atomic_write (crash-safe, symlink preserved, mode 600 like every other file rtk init writes), with plugin.json written last so a failed first install leaves no half-plugin; install says to restart Antigravity; uninstall reports like Factory Droid (nothing-to-remove line, header and items, dry-run footer); README's agent table and the configuration guide stop describing the rules file; the "<1.5ms" figure is dropped; process-level tests pin every printed line and that both dry runs change nothing. The PR body's "Resolves #1975, #2803, #3404, and #3406" is narrowed to "Resolves #1975" plus a plain mention of the Antigravity arm of #2803, #3404 and #3406, so merging does not close the four-agent issues. Approving.
Rounds: 1/3. Threads: 0 open, 0 resolved this round.

@KuSh
KuSh merged commit 7efbaef into rtk-ai:develop Sep 28, 2026
12 checks passed
@rtk-release-bot rtk-release-bot Bot mentioned this pull request Sep 28, 2026
@Aayushyaash

Copy link
Copy Markdown
Contributor Author

@KuSh Thanks for the review and the polish in the fixup!

Regarding your question about permissions.allow, I tested this in a live agy session with the three permission cases:

  1. With neither git status nor rtk git status allowed: the hook rewrites the command to rtk git status, but agy still prompts before executing.
  2. With only rtk git status allowed: running git status gets rewritten to rtk git status and executes immediately with no prompt.
  3. With only git status allowed: running git status still gets rewritten to rtk git status, but agy prompts because the rewritten command isn't allowed.

So in the version I tested (live session), the hook's allow doesn't bypass the normal command permission check, and the permission check is applied to the rewritten command.

Based on that, keeping allow for the rewrite/defer path looks correct. Using ask would introduce an extra prompt even when the resulting rtk command is already explicitly allowed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request] Add support to Antigravity CLI

3 participants