Skip to content

feat(cli): native hooks adapter for Antigravity CLI (agy) - #1146

Merged
rohitg00 merged 5 commits into
rohitg00:mainfrom
berthojoris:feature/antigravity-native-hooks
Aug 3, 2026
Merged

feat(cli): native hooks adapter for Antigravity CLI (agy)#1146
rohitg00 merged 5 commits into
rohitg00:mainfrom
berthojoris:feature/antigravity-native-hooks

Conversation

@berthojoris

@berthojoris berthojoris commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

What

Adds agentmemory connect antigravity-cli — a new adapter for the Antigravity agy CLI, with optional native auto-capture hooks behind --with-hooks.

Antigravity ships two products that do not share configuration:

  • the IDE, already wired by connect antigravity (app-support dir);
  • the agy CLI, which reads its customizations out of ~/.gemini/ — MCP servers from ~/.gemini/config/mcp_config.json, hooks from ~/.gemini/config/hooks.json — and until now was not wired at all.

This PR covers the second one. Detection keys off ~/.gemini/antigravity-cli/, which only the CLI creates (~/.gemini/ alone would also match a Gemini CLI install).

Why the Codex merge engine could not be reused

Unlike Droid (#1130), Antigravity's hooks contract differs from the Claude Code / Codex / Droid family in three ways:

  1. hooks.json is a map of named hook bundles at the root, not the { hooks: { <Event>: [...] } } envelope. So src/cli/connect/antigravity-hooks.ts implements a merge that owns top-level keys instead of per-event entries. User-authored bundles are preserved verbatim; a re-install replaces only the bundle whose commands point under the bundled plugin dir.
  2. Only five events existPreToolUse, PostToolUse, PreInvocation, PostInvocation, Stop. There is no SessionStart, SessionEnd or UserPromptSubmit, so the session lifecycle is synthesized from the first PreInvocation and from Stop. PostInvocation is deliberately left unwired — PostToolUse already captures the work, and firing both would double-record every turn.
  3. The stdin payload is camelCase and nested (toolCall.args with PascalCase keys such as AbsolutePath / TargetFile / Query, plus conversationId and workspacePaths instead of session_id / cwd), and stdout must be a JSON objectpre-tool-use.mjs writes raw prose when AGENTMEMORY_INJECT_CONTEXT=true, which Antigravity would fail to parse as a PreToolUse decision.

src/hooks/antigravity-bridge.ts (bundled to plugin/scripts/antigravity-bridge.mjs) bridges all three: it normalizes the payload onto the shape the existing hooks already accept, maps Cascade tool names (view_file, replace_file_content, …) onto the read/edit/write/grep vocabulary the capture heuristics use, pipes to the right script, discards child stdout, and always answers {} so Antigravity's own permission decisions are never overridden. The bridge never blocks or rewrites a tool call.

Event names, tool names and arg keys were verified against the shipped agy binary rather than docs alone (the docs disagree on the global hooks path). The customization dir is ~/.gemini/config/, matching where agy already keeps mcp_config.json and plugins/.

How to verify

npm install
npm run build          # compiles clean; plugin/scripts/*.mjs regenerate byte-identical
npx vitest run test/antigravity-connect-hooks.test.ts test/cli-connect.test.ts test/connect-guidelines.test.ts

test/antigravity-connect-hooks.test.ts (new, 15 cases) covers the merge engine — placeholder resolution, single-named-bundle shape, exact event set, PreToolUse matcher scope, preservation of user bundles, replacement of a stale agentmemory bundle, idempotent re-install — plus bridge payload normalization and event routing.

End-to-end against a real install:

agentmemory connect antigravity-cli --with-hooks --dry-run   # inspect the plan
agentmemory connect antigravity-cli --with-hooks             # backs up an existing hooks.json first

Re-running is idempotent; user-authored bundles in ~/.gemini/config/hooks.json are left untouched.

Notes for the reviewer

  • plugin/skills/agentmemory-rest-api/REFERENCE.md moves 118119 endpoints. That is npm run skills:gen correcting a count that was already stale on main — it is not a side effect of this feature. No REST endpoint is added here.
  • The PreToolUse matcher lists 11 tools while TOOL_NAME_MAP maps 14 (view_line_range, read_url_content, propose_code are mapped but not matched). This is deliberate — PostToolUse uses an empty matcher and catches them anyway — but happy to widen the matcher if you would rather have the two lists identical.
  • No CHANGELOG.md entry, and the README row is kept to one line, matching the review feedback on feat(cli): native hooks adapter for Droid (Factory.ai) #1130.

Summary by CodeRabbit

  • New Features

    • Added Antigravity CLI integration with MCP configuration and optional native hook installation.
    • Added agent memory synchronization across session, tool-use, and stop events.
  • Bug Fixes

    • Improved handling of event payloads, tool names, session identifiers, and existing hook configurations.
    • Added safer, repeatable hook installation while preserving existing settings.
  • Documentation

    • Added Antigravity setup guidance, configuration details, and usage instructions.
  • Tests

    • Added coverage for integration, hook merging, event routing, payload normalization, and repeated installations.

Antigravity ships two products with unrelated configuration: the IDE,
already wired by `connect antigravity`, and the `agy` CLI, which reads
its customizations out of ~/.gemini/ and until now was not wired at all.
This adds `connect antigravity-cli` for the latter — MCP via
~/.gemini/config/mcp_config.json, plus optional native auto-capture hooks
behind --with-hooks.

Unlike Droid (rohitg00#1130), the Codex merge engine could not be reused. The
Antigravity hooks contract differs in three ways:

  * hooks.json is a map of *named* hook bundles at the root, not the
    `{ hooks: { <Event>: [...] } }` envelope, so antigravity-hooks.ts
    implements a merge that owns top-level keys instead of per-event
    entries. User-authored bundles are preserved; a re-install replaces
    only the bundle whose commands point under the bundled plugin dir.
  * only five events exist (PreToolUse, PostToolUse, PreInvocation,
    PostInvocation, Stop) — no SessionStart/SessionEnd/UserPromptSubmit,
    so the session lifecycle is synthesized from the first PreInvocation
    and from Stop. PostInvocation is left unwired to avoid double-capture.
  * the stdin payload is camelCase and nested (`toolCall.args` with
    PascalCase keys, `conversationId`, `workspacePaths`), and stdout must
    be a JSON object — `pre-tool-use.mjs` writes raw prose when context
    injection is on.

plugin/scripts/antigravity-bridge.mjs bridges all three: it normalizes the
payload onto the shape the bundled hooks already accept, maps Cascade tool
names (view_file, replace_file_content, …) onto the read/edit/write/grep
vocabulary the capture heuristics use, pipes to the right script, discards
child stdout and always answers `{}` so Antigravity's own permission
decisions are never overridden.

Event names, tool names and arg keys were verified against the shipped
agy binary rather than docs alone (docs disagree on the global hooks
path); the customization dir is ~/.gemini/config/, matching where agy
already keeps mcp_config.json and plugins/.

Signed-off-by: Bertho Joris <bertho_joris@yahoo.co.id>
@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

@berthojoris is attempting to deploy a commit to the rohitg00's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6b0277e6-71d5-47b2-b27c-8912558617fe

📥 Commits

Reviewing files that changed from the base of the PR and between 6e481cb and 3a095ba.

📒 Files selected for processing (4)
  • plugin/scripts/antigravity-bridge.mjs
  • src/cli/connect/antigravity-cli.ts
  • src/cli/connect/antigravity-hooks.ts
  • src/hooks/antigravity-bridge.ts
💤 Files with no reviewable changes (1)
  • plugin/scripts/antigravity-bridge.mjs
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/cli/connect/antigravity-cli.ts
  • src/hooks/antigravity-bridge.ts
  • src/cli/connect/antigravity-hooks.ts

📝 Walkthrough

Walkthrough

The PR adds an antigravity-cli connector with MCP configuration, optional native hook installation, event bridging, adapter registration, build wiring, documentation, and integration tests.

Changes

Antigravity CLI integration

Layer / File(s) Summary
Hook bridge and event forwarding
src/hooks/antigravity-bridge.ts, plugin/scripts/antigravity-bridge.mjs
The bridge normalizes Antigravity events, maps tools and arguments, routes events, forwards payloads, and returns event-specific responses.
Native hook manifest installation
src/cli/connect/antigravity-hooks.ts, src/cli/connect/antigravity-cli.ts, plugin/hooks/hooks.antigravity.json
The connector merges hook bundles, preserves user entries, replaces agentmemory-owned bundles, supports dry runs, backs up files, and writes atomically.
Adapter registration and configuration
src/cli/connect/index.ts, src/cli/connect/guidelines.ts, tsdown.config.ts, README.md, plugin/skills/*/REFERENCE.md, test/cli-connect.test.ts, test/connect-guidelines.test.ts
The adapter is registered with MCP paths, guideline paths, optional hook support, build entries, and updated reference counts.
Integration validation
test/antigravity-connect-hooks.test.ts
Tests cover payload normalization, event routing, tool matching, bundle replacement, literal path handling, response contracts, and idempotent installation.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested reviewers: rohitg00

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant AgentMemoryCLI
  participant AntigravityConfig
  participant AntigravityBridge
  participant AgentMemoryHooks

  User->>AgentMemoryCLI: connect antigravity-cli --with-hooks
  AgentMemoryCLI->>AntigravityConfig: merge MCP and native hook configuration
  AntigravityConfig-->>AgentMemoryCLI: persist configuration and backup
  AntigravityConfig->>AntigravityBridge: register event commands
  AntigravityBridge->>AgentMemoryHooks: normalize and forward hook payload
  AgentMemoryHooks-->>AntigravityBridge: complete hook execution
  AntigravityBridge-->>AntigravityConfig: emit event response
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding native hooks support for the Antigravity CLI adapter.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@berthojoris

Copy link
Copy Markdown
Contributor Author

The CI failure here is pre-existing on main, not from this branch.

test/consistency.test.ts > documented REST endpoint counts match registered API paths fails because 5023cf3 (#1132) registered a 130th api_path: in src/triggers/api.ts while README.md, AGENTS.md and src/index.ts still say 129. The push CI run for 5023cf3 on main is red for the same reason, and every PR opened since inherits it.

Fix is in #1147 (three-line docs sync, no behaviour change). Once that lands I'll merge main back into this branch so CI here goes green.

Nothing in this PR touches src/triggers/api.ts; the only other test-visible change is plugin/skills/agentmemory-rest-api/REFERENCE.md moving 118 → 119, which is npm run skills:gen correcting a separately stale count.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
test/antigravity-connect-hooks.test.ts (1)

125-140: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extend ARG_KEY_MAP coverage for the remaining mappings.

This test only exercises AbsolutePathfile_path and CommandLinecommand. ARG_KEY_MAP in src/hooks/antigravity-bridge.ts also maps TargetFilefile_path, DirectoryPath/SearchDirectorypath, and Pattern/Querypattern. Add cases for these to catch regressions in the mapping table.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/antigravity-connect-hooks.test.ts` around lines 125 - 140, Extend the
normalizePayload test covering ARG_KEY_MAP to also verify TargetFile and
DirectoryPath/SearchDirectory map to file_path/path, and Pattern/Query map to
pattern. Add representative toolCall argument cases and assertions while
preserving the existing checks for native names and original argument keys.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/cli/connect/antigravity-hooks.ts`:
- Around line 96-119: Update the command expansion in resolveBundle so
String.prototype.replace uses a replacer function that returns pluginRoot,
preserving pluginRoot literally even when it contains replacement tokens such as
$$ or $&.

---

Nitpick comments:
In `@test/antigravity-connect-hooks.test.ts`:
- Around line 125-140: Extend the normalizePayload test covering ARG_KEY_MAP to
also verify TargetFile and DirectoryPath/SearchDirectory map to file_path/path,
and Pattern/Query map to pattern. Add representative toolCall argument cases and
assertions while preserving the existing checks for native names and original
argument keys.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 930756fa-802c-4e17-8ae4-73aa28466f77

📥 Commits

Reviewing files that changed from the base of the PR and between 5023cf3 and 1b43e89.

📒 Files selected for processing (14)
  • README.md
  • plugin/hooks/hooks.antigravity.json
  • plugin/scripts/antigravity-bridge.mjs
  • plugin/skills/agentmemory-agents/REFERENCE.md
  • plugin/skills/agentmemory-rest-api/REFERENCE.md
  • src/cli/connect/antigravity-cli.ts
  • src/cli/connect/antigravity-hooks.ts
  • src/cli/connect/guidelines.ts
  • src/cli/connect/index.ts
  • src/hooks/antigravity-bridge.ts
  • test/antigravity-connect-hooks.test.ts
  • test/cli-connect.test.ts
  • test/connect-guidelines.test.ts
  • tsdown.config.ts

Comment thread src/cli/connect/antigravity-hooks.ts
…mands

resolveBundle() expanded ${CLAUDE_PLUGIN_ROOT} via
String.prototype.replace with a string argument, so a plugin root
containing `$$`, `$&`, "$`" or `$'` was read as a replacement pattern
and rewritten:

  C:/plug$&in  ->  C:/plug${CLAUDE_PLUGIN_ROOT}in/scripts/...
  C:/plug$$in  ->  C:/plug$in/scripts/...

`$1` and `$<name>` are unaffected — the regex has no capture groups.

Switching to a replacer function keeps the path verbatim. The failure
mode this closes is silent: the hook installs with a broken command and
auto-capture simply never fires.

Regression test builds the manifest against a temp plugin root named
`plug$&$$in` and asserts the resolved command contains it literally.

Reported by CodeRabbit on rohitg00#1146.

Signed-off-by: Bertho Joris <bertho_joris@yahoo.co.id>

@rohitg00 rohitg00 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This is well-built. I verified the hook surface against the official docs (antigravity.google/docs/hooks) and the config path, the five events, and the camelCase/PascalCase payload keys all match exactly, so it is not guessing an API. The bridge reuses the bundled hook scripts through a thin normalizer rather than hand-rolling capture, which is the right call given agy's payload and named-bundle hooks.json genuinely differ from the Claude envelope. Supply chain is clean, guidelines.ts shares the same idempotent GEMINI.md block as the existing antigravity entry so there is no double-write, and the generated REFERENCE bumps are exactly what skills:gen produces for the new adapter.

The failing CI is not your fault. main currently has an endpoint-count drift (registers 130 routes while the docs say 129), so consistency.test.ts fails on every branch cut from main right now. Your PR adds no REST endpoint. That failure clears once the count fix lands on main; nothing to do here for it.

One real blocker before merge: the PreToolUse hook emits a bare {}. The docs mark decision as required for PreToolUse output, and there is empirical evidence (cmux #5358 on agy 1.0.5) that a {} response makes agy deny every tool call, which would break the agent instead of passively capturing. agy's own bundled vibe-island plugin registers no PreToolUse at all.

Asks:

  • Either drop the PreToolUse registration (PostToolUse already captures file activity), or have it emit {"decision":"allow"}.
  • Add a test asserting the PreToolUse stdout contract so this can't regress.
  • If you can, confirm against a live agy which allow-payload it actually accepts.

Everything else is approvable.

… hook

Antigravity documents `decision` as a required field of PreToolUse hook
output, and agy treats a response that omits it as a denial: the bare `{}`
the bridge used to write made the agent refuse every matched tool call
(reported against agy 1.0.5 in cmux#5358) instead of passively capturing
it. `responseFor` now answers PreToolUse with `{"decision":"allow"}` and
leaves every other event on `{}`, so no event that carries no permission
decision starts overriding the user's own settings.

The response is written from the `finally` block, so a failed capture or an
unparseable payload still produces the contract rather than empty stdout,
which PreToolUse would read the same way as `{}`.

Tests cover both the pure contract and the built bundled script running
end to end with no server listening. Also extends the ARG_KEY_MAP test to
every mapped key and pins that an explicit canonical key wins over a
PascalCase alias.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/hooks/antigravity-bridge.ts (1)

26-29: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the behavior-description comment.

Lines 26-29 describe work that the bridge performs. Keep only the permission-response rationale that is not clear from identifiers.

As per coding guidelines, "In TypeScript source code, avoid code comments explaining WHAT — use clear naming instead."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/antigravity-bridge.ts` around lines 26 - 29, Remove the
behavior-description portion of the comment near responseFor, including the
statements about running scripts, discarding stdout, auto-capture, and not
blocking or rewriting tool calls. Preserve only the rationale explaining why
PreToolUse cannot return a bare {} response.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/hooks/antigravity-bridge.ts`:
- Around line 26-29: Remove the behavior-description portion of the comment near
responseFor, including the statements about running scripts, discarding stdout,
auto-capture, and not blocking or rewriting tool calls. Preserve only the
rationale explaining why PreToolUse cannot return a bare {} response.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1f94636e-cee4-4ae7-ae21-24e5205051db

📥 Commits

Reviewing files that changed from the base of the PR and between bb74810 and 278fb05.

📒 Files selected for processing (3)
  • plugin/scripts/antigravity-bridge.mjs
  • src/hooks/antigravity-bridge.ts
  • test/antigravity-connect-hooks.test.ts

@berthojoris

Copy link
Copy Markdown
Contributor Author

Thanks for the careful read, and for checking the hook surface against the docs yourself.

You're right about PreToolUse. I went with the explicit-allow option rather than dropping the registration: PostToolUse does cover file activity, but PreToolUse still gives the capture pipeline the tool intent before it runs, and {"decision":"allow"} is what the docs actually ask for. Pushed in 278fb05.

The response now goes through a small responseFor(event):

export function responseFor(event: string): string {
  return event === "PreToolUse" ? '{"decision":"allow"}' : "{}";
}

Every other event stays on {} — none of them carry a permission decision, and emitting decision or terminationBehavior there would override the user's own settings.

One thing I found while fixing it: the bridge wrote its response from the finally block, but a few paths could exit with stdout empty (unparseable payload, a throw during capture). For PreToolUse an empty response is as fatal as {}, so the write now always happens with the right contract regardless of how the capture went.

On the test — two of them:

  • a unit test over responseFor, pinning PreToolUse{"decision":"allow"} and every other event → {};
  • an end-to-end one that spawns the bundled plugin/scripts/antigravity-bridge.mjs with AGENTMEMORY_URL=http://127.0.0.1:1, so every capture fetch fails fast. That's deliberately the failed-capture case, since that's where a swallowed error would produce empty stdout.

I could not confirm against a live agy — it isn't installed on my machine, so the allow payload is what the docs specify plus the cmux#5358 evidence you cited, not something I observed the binary accept. Worth a second pair of eyes if you have agy handy; if it turns out to want a different shape, it's a one-line change in responseFor and the test moves with it.

Also picked up CodeRabbit's nitpick while I was in there: the ARG_KEY_MAP test now covers all seven mappings (TargetFile, DirectoryPath/SearchDirectory, Pattern/Query included) plus a case pinning that an explicit canonical key wins over a PascalCase alias. Its other comment — the $-pattern issue in resolveBundle — was already fixed in bb74810; it reviewed the commit before that one.

And thanks for tracking down the CI failure, that saves me a detour.

Comment thread plugin/scripts/antigravity-bridge.mjs Outdated
default: return [];
}
}
/**

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

remove these comments

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 3a095ba — the artifact is down to the three boilerplate lines every other bundled script has.

The cause was that the bundler strips // comments but preserves JSDoc blocks, so the /** */ docs on the bridge's exported helpers were the only ones surviving into plugin/scripts/. Switched those to line comments: the explanation stays in src/hooks/antigravity-bridge.ts and the generated file now matches pre-tool-use.mjs and friends (3 lines, all of them #region/sourcemap markers).

installHooks: installAntigravityCliHooks,
});

/**

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

lot of similar comments

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Trimmed in 3a095ba. The file header and the per-function block were stating the same things twice — kept one statement of each and dropped the rest. Comment lines: antigravity-cli.ts 26 → 12, antigravity-hooks.ts 65 → 46, antigravity-bridge.ts 70 → 41, which puts them in line with droid.ts.

What I deliberately kept is the agy behaviour I verified against a live 1.0.15 — the two event shapes, the no-quoting rule, the PreToolUse decision contract. That part isn't derivable from the code, and each of those was a real defect here, so a future reader changing the manifest needs the reason. Happy to cut further if you'd rather that lived only in the PR description.

…t 1.0.15

Three defects found by probing a live agy 1.0.15 with an instrumented hook,
each of which stopped the adapter from capturing anything at all.

Lifecycle events take a flat handler list, not the tool-event wrapper. agy
parses `PreToolUse`/`PostToolUse` as `[{matcher, hooks: [...]}]` but
`PreInvocation`/`PostInvocation`/`Stop` as a bare `[{type, command}]`, since
there is no tool name to match on. Wrapping a lifecycle event makes agy read
the wrapper itself as a handler and reject the *whole file* with
`invalid hook "agentmemory": command hook must specify 'command'` — so the
mis-shaped Stop entry disabled every hook in the bundle, and would have
disabled hooks other tools had written to the same file.

`command` is not run through a shell and quotes are not stripped, so the
quoted path resolved to a module name that literally began with a double
quote: `Cannot find module 'C:\Users\…\.gemini\config\"C:\…\bridge.mjs"'`.
Commands are now bare. That also means a path containing spaces cannot be
expressed at all — quoted and unquoted both fail — so the installer refuses
with an explanation instead of writing hooks that can only fail at tool time.

The merge engine reads both shapes when deciding which bundles agentmemory
owns, so a re-install over the old wrapped layout still replaces it rather
than leaving a second copy behind.

Tests pin both event shapes, the absence of quotes, the space check, and
normalization of a payload captured verbatim from the live run — which also
confirms `conversationId`, PascalCase `toolCall.args`, and that agy sends no
`cwd` key at all.
@berthojoris

Copy link
Copy Markdown
Contributor Author

Correction to my last comment: I did get access to a live agy (1.0.15) and ran the verification you asked for. You were right about PreToolUse, and the probe turned up two further defects that would have stopped this adapter from capturing anything at all. Fixed in 6e481cb.

Method: a probe hook that logs the payload agy sends and replies with whatever I put in a file, driven by agy --print runs, cross-checked against the exa.hooks_pb descriptor embedded in the binary.

1. PreToolUse — confirmed, on 1.0.15 as well as 1.0.5

Three runs, same prompt, only the hook's stdout differing:

stdout result in transcript
{} tool call denied with reason: (empty reason)
{"decision":"deny","reason":"probe: deny path check"} tool call denied with reason: probe: deny path check
{"decision":"allow"} tool ran, 0 denials

So it is not specific to 1.0.5 — current agy still denies on a bare {}. The descriptor agrees: PreToolHookResult is decision (string), reason, overwrite, permission_overrides, plus deprecated allow_tool/deny_reason. PostToolHookResult is an empty message, which confirms {} is right for the other events.

2. Lifecycle events use a different shape, and getting it wrong killed the whole file

This is the one that matters most. agy parses the two event families differently:

"PreToolUse":  [ { "matcher": "", "hooks": [ { "type": "command", "command": "" } ] } ],
"Stop":        [ { "type": "command", "command": "" } ]

Tool events take the {matcher, hooks} wrapper; PreInvocation, PostInvocation and Stop take a flat handler list. Our manifest wrapped all four. agy then reads the wrapper itself as a handler, finds no command on it, and rejects the entire file:

Failed to parse hooks file …\.gemini\config\hooks.json:
  invalid hook "agentmemory": command hook must specify 'command'

Not just the bad event — the whole bundle, so nothing fired. Worse, hooks.json is shared, so a mis-shaped entry of ours would take out hooks any other tool had installed there. I bisected event by event to confirm: PreToolUse alone loads, PostToolUse alone loads, PreInvocation or Stop alone fails.

3. Commands must be unquoted, and paths with spaces are impossible

command is not run through a shell and agy does not strip quotes before splitting, so node "<root>/scripts/antigravity-bridge.mjs" produced:

Error: Cannot find module 'C:\Users\…\.gemini\config\"C:\…\antigravity-bridge.mjs"'

Note both the literal quotes and the resolution base — hooks run with cwd set to the hooks.json directory, not the workspace. Commands are bare now. The corollary is that a plugin path containing a space cannot be expressed at all; I tested quoted and unquoted with a space in the path and neither executes. So connect antigravity-cli --with-hooks now refuses with an explanation rather than writing a bundle that can only fail at tool time. MCP is unaffected.

Verified end to end: with 6e481cb, the real bundle loads (loaded 1 named hooks from 1 hooks.json file(s)), the bridge runs, and the tool proceeds with no denials.

One payload note for you, not fixed here because I'd be guessing at the right behavior. A real PreToolUse payload:

{"artifactDirectoryPath":"…/brain/53642203-…","conversationId":"53642203-…",
 "modelName":"gemini-3.6-flash-high","stepIdx":3,
 "toolCall":{"args":{"DirectoryPath":"C:\\Users\\u\\.gemini\\antigravity-cli"},"name":"list_dir"},
 "transcriptPath":"…/transcript_full.jsonl","workspacePaths":[]}

conversationId and the PascalCase args match what the bridge expects. But there is no cwd key, and workspacePaths came back empty in every headless run — combined with the cwd being the hooks.json dir, the bridge's process.cwd() fallback would attribute captures to ~/.gemini/config rather than the repo. I don't know yet whether workspacePaths is populated in interactive sessions; if it usually is, this only affects --print, and if it isn't, the fallback needs rethinking. Happy to take direction, or to leave it for a follow-up.

The environment I tested on is restored — the probe hooks.json is removed and nothing else was touched.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/cli/connect/antigravity-hooks.ts`:
- Around line 127-136: Update isAgentmemoryBundle to require a stable
AgentMemory ownership marker or an exact generated bridge command, rather than
checking whether any handler command merely contains normalizedScriptsDir.
Ensure user-authored bundles that reference the scripts directory remain
untouched while genuinely generated AgentMemory bundles are still identified for
removal.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 469578bc-6336-4c59-8148-e350a825650b

📥 Commits

Reviewing files that changed from the base of the PR and between 278fb05 and 6e481cb.

📒 Files selected for processing (4)
  • plugin/hooks/hooks.antigravity.json
  • src/cli/connect/antigravity-cli.ts
  • src/cli/connect/antigravity-hooks.ts
  • test/antigravity-connect-hooks.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • plugin/hooks/hooks.antigravity.json
  • test/antigravity-connect-hooks.test.ts
  • src/cli/connect/antigravity-cli.ts

Comment on lines +127 to +136
function isAgentmemoryBundle(bundle: unknown, scriptsDir: string): boolean {
if (!bundle || typeof bundle !== "object" || Array.isArray(bundle)) {
return false;
}
const normalizedScriptsDir = normalizePathForCommandMatch(scriptsDir);
return allHandlers(bundle as NamedHook).some((handler) =>
normalizePathForCommandMatch(handler?.command ?? "").includes(
normalizedScriptsDir,
),
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Use an exact ownership marker before deleting a bundle.

Line 132 treats any command that contains scriptsDir as AgentMemory-owned. A user-authored bundle can invoke another script under that directory or pass that directory as an argument. The merge then removes the complete bundle, and installAntigravityCliHooks persists the loss.

Match a stable AgentMemory bundle marker or the exact generated bridge command before removal.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli/connect/antigravity-hooks.ts` around lines 127 - 136, Update
isAgentmemoryBundle to require a stable AgentMemory ownership marker or an exact
generated bridge command, rather than checking whether any handler command
merely contains normalizedScriptsDir. Ensure user-authored bundles that
reference the scripts directory remain untouched while genuinely generated
AgentMemory bundles are still identified for removal.

The bundled script carried 24 comment lines where every other script in
plugin/scripts has three. The bundler strips `//` comments but preserves
JSDoc blocks, so the fix is to document the bridge's exported helpers with
line comments: the explanations stay in source and the generated artifact
comes out as clean as its siblings.

The connect adapter and merge engine restated the same facts in a file
header and again in a per-function block. Kept one statement of each,
dropped the repetition, and left the verified agy behaviour in place since
that is the part not derivable from the code.
@rohitg00
rohitg00 merged commit d60652a into rohitg00:main Aug 3, 2026
1 of 10 checks passed
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.

2 participants