Skip to content

feat(antigravity): add support for Antigravity CLI (agy) - #1040

Open
satan9394 wants to merge 7 commits into
Tencent:mainfrom
satan9394:feat/support-antigravity-cli
Open

satan9394 wants to merge 7 commits into
Tencent:mainfrom
satan9394:feat/support-antigravity-cli

Conversation

@satan9394

@satan9394 satan9394 commented Oct 11, 2026 •

Copy link
Copy Markdown

Makes Google DeepMind's Antigravity CLI (agy / antigravity) — the successor to gemini-cli — a first-class teamai target, joining the existing 17+ tools. agy and gemini are accepted as aliases for the tool id.

What it delivers

Resource Project scope User scope
skills .agents/skills ~/.gemini/antigravity/skills
rules .agents/rules ~/.gemini/config/rules
subagents .agents/agents ~/.gemini/antigravity-cli/agents
MCP .agents/mcp_config.json ~/.gemini/config/mcp_config.json
hooks hooks.json ~/.gemini/antigravity-cli/hooks.json
instructions GEMINI.md ~/.gemini/config/GEMINI.md
  • Tool id + aliases. ToolName / ALL_SUPPORTED_TOOLS gain antigravity; a new normalizeToolId maps agy and gemini onto it, applied in normalizeAgentList, so --agent agy / --agent gemini reach the one Antigravity target instead of a phantom tool.
  • Install probe is explicit. A project's .agents/ tree is shared with the central Agent Skills entry, so the generic toolInstallRoot probe would report Antigravity "installed" for anyone with a .agents/ directory. It counts as installed by ~/.gemini/antigravity-cli/, ~/.gemini/config/, an agy / antigravity binary on PATH, an explicit --agent antigravity entry, or Antigravity's own files in the scope's base dir (hooks.json, GEMINI.md, .gemini) — never by the shared .agents/ root alone. Two earlier versions of this probe were wrong in opposite directions (machine-level roots only, which broke the [bug] Team members with different roles overwrite each other's blocks in shared root AGENTS.md #945 "a tool's own paths mean installed" contract on a host without ~/.gemini; then a fallback that accepted the shared .agents/ root, which broke [feat] keep project MCP configs with resolved tokens out of git #882); both were only visible when the suite was run on Linux, which is why it is run there.
  • Own hooks adapter (src/antigravity-hooks.ts). Antigravity's hooks.json is keyed by hook name and carries the events (PreToolUse, PostToolUse, PreInvocation, PostInvocation, Stop) inside each block — a shape the settings renderer (Claude's flat matcher array) cannot express. teamai owns exactly one top-level key, teamai-hooks, deep-merged, so a user's own blocks (plan-readonly-guard, dangerous-commands-guard, …) survive every inject and removal; hooks remove / uninstall deletes only that key. Injection is idempotent (a second pass writes nothing) and creates no hooks.json where the user has none. Wired from reconcileHooksToAllTools and reported by hooks list.
  • Subagents render as Markdown + YAML frontmatter and reverse back into the canonical spec (reusing the Claude parser; reverseByTool re-keys the extras to antigravity).
  • MCP uses the Claude mcpServers shape (CLAUDE_TOOLS).
  • Rules are written as plain .md copies — Antigravity has no rule format of its own — and the instruction blocks go to its claudemd (GEMINI.md).

Tests

New src/__tests__/antigravity.test.ts — 14 cases, all passing (pnpm exec vitest run src/__tests__/antigravity.test.ts → exit 0): scope paths, registry, alias normalization, install probe (each root, the --agent bootstrap, and the shared-.agents/ non-misdetection), MCP format, verbatim rules, subagent render/reverse round trip, and the hooks adapter (event mapping, unknown-event drop, merge-only-our-key, immutability, remove, malformed-JSON read, idempotent inject, clean uninstall, and injection through reconcileHooksToAllTools).

The gate ci.yml runs is npx vitest run --coverage, so the whole suite was compared with exactly that command on the platform CI uses (WSL Ubuntu 24.04, node v22.22.2, pnpm 11.23.0, 8 cores). main advanced 10 commits while this PR was open, so upstream/main is merged in (merge commit 387d5eb, merge rather than force-push) and the comparison was re-run against that new base:

tree Test Files Tests
base 26ca277 (upstream/main) 1 failed / 400 passed / 1 skipped (402) 2 failed / 8592 passed / 33 skipped (8627)
this branch 387d5eb 1 failed / 401 passed / 1 skipped (403) 2 failed / 8614 passed / 33 skipped (8649)

The failure sets are identical — both sides fail only the same dry-run-load-path.test.ts cases with Test timed out in 15000ms (pre-existing, load-dependent; 2 on this run, a third appears on a loaded host). tsc --noEmit, oxlint and tsup all exit 0 on both trees. gitlab-clone-realspawn.test.ts passes on Linux, so its new skipIf(win32) does not hide a real failure.

On Windows, where the suite cannot go green at all (symlink privileges, chmod, POSIX path separators), a 35-file focused set (every test file touching tool registration — 1427 cases) was compared before/after the change with git stash: failure set item-by-item identical (71 failures), all pre-existing platform failures; passed 1331 → 1353.

Real-CLI verification

On Windows (git 2.53.0 / node v24.14.0), against the built dist/index.js:

$ node dist/index.js hook-dispatch session-start --tool antigravity   # exit 0
$ node dist/index.js hook-dispatch prompt-submit --tool antigravity   # exit 0
$ node dist/index.js hook-dispatch stop --tool agy                    # exit 0
$ node dist/index.js hook-dispatch stop --tool gemini                 # exit 0

The e2e suite (it spawns the real CLI) was then run on Linux with a pristine control, over the three files that cover what this change touches — instruction targets (toolPaths / claudemd), hook injection (the new adapter's routing), and the newest tool-addition e2e (Trae #904) as a regression proxy:

tree result duration
base 26ca277 3 files / 45 tests passed 131.3s
this branch 387d5eb 3 files / 45 tests passed 153.9s

AGENTS.md asks for one representative real-CLI run rather than the full provider × agent matrix; these three are that run. (The same e2e file on Windows shows 8 failures — output-string assertions on messages the CLI prints with Windows paths, none involving Antigravity — and they are green on both trees on Linux.)

Caveats (stated, not hidden)

  • Upstream AGENTS.md asks for a worktree; this was done directly on feat/support-antigravity-cli in a clone instead.
  • macOS and node 20 / 24 are left to CI — this host is Linux + node 22 only.
  • docs/guide/providers.md documents Git providers, not AI tools, and docs/guide/getting-started.md carries only an illustrative diagram, so neither lists a tool set to update.

Also fixed

  • src/resources/base.ts — the install probe cost a full synchronous PATH walk per MCP target. isOnPath scans with statSync, and isToolInstalledForConfig runs once per tool per reconciliation (mcp-reconcile.ts). On this host PATH has 58 entries, most on a 9p mount at ~1.6ms per stat, so one probe call was ~116 blocking stats (~185ms) — enough to make mcp-reconcile / pi-mcp ~4.5x slower and push several cases past the 15s timeout under --coverage. The PATH answer is now cached per PATH/PATHEXT value (a stubbed PATH still rescans) and the cheap signals are checked first; the signals are OR'd, so the result is unchanged. Those two files went 456–783s → 118.6s (base: 115.0s) and the full --coverage run 905.8s → 220.9s, with the failure set back to the base's.
  • src/__tests__/gitlab-clone-realspawn.test.ts — Windows-only hazard. It plants a #!/bin/bash fake git through a :-separated PATH, which does not take effect on Windows, so the real git ran against the default https://gitlab.com/org/repo.git; the 401 made Git for Windows' credential helper (credential.helper=manager, system level) open a browser login window and the test hung for 120 s (observed in the run log). It is now describe.skipIf(process.platform === 'win32'). A side-effect on any machine, but a real network call and a credential prompt are exactly what a unit test must not cause.
  • src/__tests__/instruction-targets.test.ts — its mockRejectedValueOnce rejected whichever file happened to be written first; adding a target shifted which file that was. It now rejects only the file under test, so the assertion no longer depends on how many tools are installed.

Antigravity CLI (`agy` / `antigravity`) — Google DeepMind's coding agent and
the successor to `gemini-cli` — becomes a first-class target. Both `agy` and
`gemini` are accepted as aliases for the id (`normalizeToolId`).

Paths, project / user scope:
- skills   `.agents/skills`          / `~/.gemini/antigravity/skills`
- rules    `.agents/rules`           / `~/.gemini/config/rules`
- agents   `.agents/agents`          / `~/.gemini/antigravity-cli/agents`
- mcp      `.agents/mcp_config.json` / `~/.gemini/config/mcp_config.json`
- hooks    `hooks.json`              / `~/.gemini/antigravity-cli/hooks.json`
- instr.   `GEMINI.md`               / `~/.gemini/config/GEMINI.md`

The install probe is explicit: a project's `.agents/` tree is shared with the
central Agent Skills entry, so Antigravity counts as installed by
`~/.gemini/antigravity-cli/`, `~/.gemini/config/`, an `agy` / `antigravity`
binary on PATH, or an explicit `--agent antigravity` entry — never by the
shared project root alone.

New `src/antigravity-hooks.ts`: Antigravity's `hooks.json` is keyed by hook
NAME and carries the events inside each block, a shape the settings renderer
cannot express. teamai owns exactly one top-level key, `teamai-hooks`, and
deep-merges it, so a user's own blocks (`plan-readonly-guard`,
`dangerous-commands-guard`, ...) survive every inject and removal. It is wired
into `reconcileHooksToAllTools` and reported by `hooks list`.

Subagents are Markdown with YAML frontmatter (rendered, and reversed back into
the canonical spec); MCP uses the Claude `mcpServers` shape; rules are written
as plain `.md` copies, since Antigravity has no rule format of its own.

Also fixes a Windows-only hazard in an upstream test:
`gitlab-clone-realspawn.test.ts` plants a `#!/bin/bash` fake `git` through a
`:`-separated PATH, which does not take effect on Windows, so the real git ran
against `https://gitlab.com/org/repo.git` and Git for Windows' credential
helper (the system-level `credential.helper=manager`) opened a browser login
window — the test then hung for 120s. It is skipped on Windows now.
`instruction-targets.test.ts`'s `mockRejectedValueOnce`, which rejected
whichever file happened to be written first, now rejects only the file under
test, so its assertion no longer depends on how many tools are installed.
… tree

The first cut asked `isToolInstalledForConfig` for Antigravity's machine-level
roots only (`~/.gemini/antigravity-cli`, `~/.gemini/config`, an `agy` /
`antigravity` binary on PATH, or an explicit `--agent antigravity`) and
replaced the generic path probe with that answer. `instruction-targets` asks
whether a tool is installed through the paths a project actually has, and its
Tencent#945 suite creates exactly those before asserting delivery — so on any host
without `~/.gemini` (i.e. CI) Antigravity counted as not installed and five
`instruction-targets` cases failed. A Windows box that happens to have
Antigravity installed masks this, which is how it reached the branch.

Falling back to the generic probe is not the answer either: Antigravity's
project skills live in the `.agents/` tree that the central Agent Skills entry
also uses, so the generic root probe reports it installed for every project
that merely has `.agents/` — which hands Antigravity's MCP targets to teams
that do not run it and breaks four Tencent#882 MCP-reconciliation assertions.

The probe now accepts the machine-level roots OR Antigravity's OWN files in the
scope's base dir (`hooks.json`, `GEMINI.md`, `.gemini`) — never the shared
`.agents/`.

Verified on a clean-home Linux host (no `~/.gemini`, no `agy` on PATH):
`instruction-targets` 241/241, `mcp-reconcile` 249/249, `antigravity` 14/14;
and the full suite's failure set is identical to the pristine base commit
(3 pre-existing `dry-run-load-path` 15s timeouts on both sides).
@satan9394

Copy link
Copy Markdown
Author

Verification on Linux (added after the initial push)

The author's Windows box cannot run this suite green (symlink privileges, chmod, POSIX path assertions), and it masked a real defect: five instruction-targets cases only fail on a host without ~/.gemini. So the suite was also run on Linux (WSL Ubuntu 24.04, node v22.22.2, pnpm 11.23.0, 8 cores) — the platform CI uses — comparing the base commit with this branch.

tree Test Files Tests
pristine 2cbf556 1 failed / 398 passed / 1 skipped (400) 3 failed / 8441 passed / 33 skipped (8477)
this branch (2c4ea45) 1 failed / 399 passed / 1 skipped (401) 3 failed / 8463 passed / 33 skipped (8499)

The failure sets are identical — both sides fail only three dry-run-load-path.test.ts cases with Test timed out in 15000ms (load-dependent, pre-existing). Passed went 8441 → 8463 (+14 new cases, +8 new parameterized instruction-targets cases). gitlab-clone-realspawn.test.ts passes on Linux (✓ 1 passed), so its new skipIf(win32) does not hide a real failure.

Two defects this gate caught, both fixed in 2c4ea45

  1. In 02d7d03 itself: the Antigravity install probe replaced the generic path probe with machine-level roots only (~/.gemini/antigravity-cli, ~/.gemini/config, agy/antigravity on PATH, --agent antigravity). instruction-targets asks whether a tool is installed through the paths a project has, and its [bug] Team members with different roles overwrite each other's blocks in shared root AGENTS.md #945 suite creates those before asserting delivery — so on any host without ~/.gemini (CI) Antigravity looked uninstalled and five cases failed. The author's box has Antigravity installed, which hid it.
  2. Introduced by the first fix attempt: falling back to the generic probe made it accept the shared .agents/ root, so every project with .agents/ counted as running Antigravity — breaking four [feat] keep project MCP configs with resolved tokens out of git #882 MCP-reconciliation assertions (reproducible in isolation; pristine passes 249/249).

The probe now accepts the machine-level roots or Antigravity's own files in the scope's base dir (hooks.json, GEMINI.md, .gemini) — never the shared .agents/.

Targeted re-verification on that clean-home Linux host: instruction-targets 241/241, mcp-reconcile 249/249, antigravity 14/14. On Windows: tsc --noEmit, oxlint and tsup all exit 0.

@jeff-r2026 jeff-r2026 self-assigned this Oct 12, 2026
@github-actions

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/antigravity-hooks.ts:96 — Invalid or non-object JSON is converted to {}, after which both injection and removal rewrite the file. Running hooks inject/remove on a malformed user hooks.json therefore destroys its contents despite logging that it will be left untouched.
  • [P1 blocking] src/hooks.ts:2674 — The adapter appends every team definition without teamDefsForTool. A hook restricted to tools: [claude], for example, is installed and executed by Antigravity.
  • [P1 blocking] src/hooks.ts:2672 — This path ignores teamHookProjectRoot and the managed-hook manifest. In non-self project scope, raw project commands are placed in the global Antigravity file, run outside that project, and are overwritten when another project reconciles the single teamai-hooks block.
  • [P1 blocking] src/hooks.ts:2673 — During a builtinsOnly recovery pass, this explicitly omits built-ins and passes an empty definition list, deleting the existing teamai-hooks block. A temporary team-hook resolution failure therefore removes working hooks instead of preserving team hooks and installing/checking built-ins.
  • [P1 blocking] src/types.ts:555 — Registering Antigravity as a normal settings-backed tool sends HTTP install_hook_rule operations through generic applyAgentHook, which writes Claude’s { hooks: { Event: [...] } } schema. Antigravity requires named top-level blocks, so these remotely installed hooks never run and uninstall uses the wrong parser too.
  • [P2 non-blocking] src/hooks.ts:2658 — hooks list has no Antigravity-specific status reader. Because the tool has a settings path, it falls through to getHookStatus, which searches for Claude’s top-level hooks property and always reports a correctly installed teamai-hooks block as missing.
  • [P2 non-blocking] src/utils/tool-names.ts:52 — Alias normalization is only called by normalizeAgentList. Advertised aliases inside team configuration, such as tools: [agy] on an MCP server or hook, remain unmatched against the antigravity target and are silently skipped.
  • [P2 non-blocking] src/__tests__/gitlab-clone-realspawn.test.ts:17 — This Windows-only GitLab test change is unrelated to Antigravity support and violates the review rule requiring surgical changes. It should be moved to a separate PR.

Testing

  • The PR description includes representative real-CLI and E2E execution, so it satisfies the runtime-change testing-record requirement.

…ed by it

isToolInstalledForConfig runs once per MCP target per reconciliation
(mcp-reconcile.ts), and Antigravity's probe reached a synchronous
isOnPath walk of PATH inside that loop. Measured on WSL: PATH holds 58
entries, most on a 9p mount where one statSync costs ~1.6ms, so a single
uncached call is ~116 blocking stats (~185ms). That made mcp-reconcile
and pi-mcp ~4-5x slower and pushed several of their cases past the 15s
test timeout under --coverage.

The PATH answer only changes when PATH does, so cache it per PATH/PATHEXT
value (a stubbed PATH still gets a fresh scan), and check the cheap
signals first so the scan is only reached when nothing else matches.
Result is unchanged: the signals are OR'd.
@satan9394

Copy link
Copy Markdown
Author

Aligning with ci.yml: the --coverage gate, and a performance regression it caught

ci.yml runs npx vitest run --coverage (coverage instrumentation changes timing), so the comparison above was repeated with exactly that command on the same Linux host, base commit vs this branch:

tree --coverage result tests phase duration
pristine 2cbf556 3 failed / 8441 passed (400 files) 1202.9s 355.7s
2c4ea45 5 failed / 8461 passed (401 files) 2591.1s (2.15x) 905.8s

The two extra failures were Test timed out in 15000ms in mcp-reconcile.test.ts and pi-mcp.test.ts. They were not assertion regressions — they were a real performance regression this branch introduced, which the --coverage gate surfaced as timeouts:

  1. Running just those two files, 3x per tree: both trees 254/254 green — nothing was asserted differently.
  2. Alternating A/B (P→F→P→F) with a constant pure-CPU control: the pattern was fast/slow/fast/slow — 144.9s / 114.6s on the base vs 782.7s / 456.3s on the branch — while the CPU control was identical every round. So the cost was I/O, not CPU, and it was the branch.
  3. Root cause, measured: isOnPath walks PATH with synchronous statSync. On this host PATH has 58 entries, most on a 9p mount, where one stat costs ~1.6ms — so a single isOnPath('agy') || isOnPath('antigravity') is ~116 blocking stats (~185ms). isToolInstalledForConfig runs once per MCP target per reconciliation (mcp-reconcile.ts), so the Antigravity probe sat inside that loop.

Fix in 7a8afce

  • cache the PATH result per PATH/PATHEXT value (a stubbed PATH still gets a fresh scan), and
  • check the cheap signals first, so the expensive scan is only reached when nothing else matches.

The signals are OR'd, so the boolean result is unchanged; tsc --noEmit exits 0.

Re-verification with the same command on the same host:

before after 7a8afce
two-file A/B (--coverage) branch 456–783s base 114.97s vs branch 118.61s (1.03x)
full --coverage branch had 2 extra failures 3 failed / 8463 passed — failure set identical to base
full --coverage duration 905.8s 220.9s

So all four ci.yml steps (npm run lint, npx tsc --noEmit, npx vitest run --coverage, npm run build) pass locally on the platform CI uses, with zero difference from the base commit; the only failures on either side are the three pre-existing dry-run-load-path timeouts. macOS and node 20/24 are left to CI.

The e2e suite was re-run after the fix too, with a pristine control, over the three files that cover what this change touches (instruction targets, hook injection, and the Trae #904 tool-addition e2e): base 3 files / 45 passed in 90.7s, this branch 3 files / 45 passed in 100.8s — it was 449.4s before the fix.

@github-actions

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/antigravity-hooks.ts:96 — Malformed or non-object JSON returns {}, so inject/remove subsequently overwrites the existing hooks.json despite warning it will remain untouched.
  • [P1 blocking] src/hooks.ts:2674 — Team definitions are appended without teamDefsForTool; a hook restricted to tools: [claude] is therefore installed and executed by Antigravity.
  • [P1 blocking] src/hooks.ts:2672 — The adapter ignores teamHookProjectRoot and managed manifests. In non-self project scope, project hooks enter the global file without cwd gating, and reconciling project B replaces project A’s single teamai-hooks block.
  • [P1 blocking] src/hooks.ts:2673 — A builtinsOnly recovery pass has empty defs and explicitly omits built-ins, causing the adapter to delete the existing block instead of preserving team hooks and restoring built-ins.
  • [P1 blocking] src/types.ts:555 — Antigravity remains accepted by install_hook_rule, which routes through generic applyAgentHook and writes Claude’s top-level hooks schema. The remotely installed hook never runs, and removal uses the wrong parser.
  • [P1 blocking] src/hooks.ts:2658 — Only normal reconciliation uses the new adapter. uninstall --agent antigravity discovers hooks through generic hasTeamaiHooks, which cannot see the top-level teamai-hooks block, so uninstall leaves active TeamAI hooks behind.
  • [P2 non-blocking] src/hooks.ts:2658 — hooks list still falls through to getHookStatus, which searches for Claude’s top-level hooks property and reports a correctly installed Antigravity block as missing.
  • [P2 non-blocking] src/utils/tool-names.ts:52 — Alias normalization is only applied to CLI agent arguments. Aliases in team configuration, such as tools: [agy] or agent targets: [gemini], remain unmatched and are silently skipped.
  • [P2 non-blocking] src/__tests__/gitlab-clone-realspawn.test.ts:17 — This unrelated Windows GitLab-test change violates the requirement that every changed line trace directly to the PR goal; it belongs in a separate PR.

No earlier finding is resolved in the current head. The PR description includes representative real-CLI and E2E records, so it satisfies the runtime-change testing requirement.

Resolve the conflict in src/resources/base.ts: upstream Tencent#1017 added
toolGateRoot to the installed-tool gate. The Antigravity probe stays ahead
of the generic call — Antigravity is not in RELOCATABLE_TOOLS, so a
recorded toolRoots entry does not apply to it - and the generic call now
uses upstream's toolGateRoot form.

Switch the Antigravity hooks install gate in hooks.ts to the same
toolGateRoot shape upstream adopted, which is what upstream's import list
expects (toolInstallRoot is no longer imported there).
@github-actions

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/antigravity-hooks.ts:96 — Invalid or non-object JSON becomes {}, so inject/remove overwrites the malformed user file despite warning that it will remain untouched.
  • [P1 blocking] src/hooks.ts:2802 — Team hooks are appended without teamDefsForTool; a definition restricted to tools: [claude] is still installed and executed by Antigravity.
  • [P1 blocking] src/hooks.ts:2802 — The adapter ignores teamHookProjectRoot and managed manifests. Non-self project hooks are installed globally without cwd gating, and reconciling another project replaces the same teamai-hooks block.
  • [P1 blocking] src/hooks.ts:2801 — A builtinsOnly recovery pass supplies no built-ins and empty team definitions, deleting the existing block instead of restoring built-ins while preserving team hooks.
  • [P1 blocking] src/types.ts:563 — Registering Antigravity as settings-backed makes install_hook_rule use generic applyAgentHook, which writes Claude’s top-level hooks schema; these remote hooks never run and removal uses the wrong parser.
  • [P1 blocking] src/types.ts:563 — uninstall --agent antigravity discovers hooks through generic hasTeamaiHooks, which cannot recognize the named teamai-hooks block, leaving active hooks behind.
  • [P1 blocking] src/utils/tool-names.ts:52 — Alias normalization is only used by init. After init --agent agy, the matching uninstall --agent agy passes the raw ID, finds no agy target, and leaves Antigravity resources installed.
  • [P1 blocking] src/utils/tool-names.ts:48 — gemini was already a distinct supported known agent targeting ~/.gemini/skills; remapping it means existing init --agent gemini users silently receive Antigravity paths instead of Gemini CLI skills.
  • [P2 non-blocking] src/types.ts:563 — hooks list falls through to generic getHookStatus, which looks for Claude’s top-level hooks property and reports a valid Antigravity block as missing.
  • [P2 non-blocking] src/utils/tool-names.ts:52 — Aliases in team configuration are not normalized; tools: [agy] and targets: [gemini] do not match the antigravity target.
  • [P2 non-blocking] src/__tests__/gitlab-clone-realspawn.test.ts:17 — This unrelated Windows GitLab-test change violates the surgical-change rule and should be moved to a separate PR.

No earlier finding is resolved in the current diff. The PR description includes representative real-CLI and E2E records, so it satisfies the runtime-change testing requirement.

@satan9394

Copy link
Copy Markdown
Author

Re-verified against the new base (main moved while this PR was open)

main advanced 10 commits while this PR was open, so the PR went dirty. Rather than force-push, upstream/main is merged in (merge commit 387d5eb). The only conflict was src/resources/base.ts, where upstream #1017 added toolGateRoot to the installed-tool gate: the Antigravity-specific probe stays ahead of the generic call (Antigravity is not in RELOCATABLE_TOOLS, so a recorded toolRoots entry does not apply to it), and the generic call now uses upstream's toolGateRoot form. The Antigravity hooks install gate in hooks.ts was switched to the same shape, which is what upstream's import list now expects.

Re-run with the gate ci.yml uses (npx vitest run --coverage) on the same Linux host, new base vs this branch, each side after its own pnpm install + build:

tree Test Files Tests failing set e2e
base 26ca277 1 failed / 400 passed / 1 skipped (402) 2 failed / 8592 passed / 33 skipped (8627) 2 dry-run-load-path timeouts 3 files / 45 passed
this branch 387d5eb 1 failed / 401 passed / 1 skipped (403) 2 failed / 8614 passed / 33 skipped (8649) identical (the same 2) 3 files / 45 passed

Zero difference from the base; passed went +22 (the 14 new Antigravity cases and 8 new parameterized instruction-targets cases), and the duration is the same order (306.4s vs 282.1s). tsc --noEmit, npm run lint and npm run build all exit 0.

(One Windows-only note, not introduced here: builtin-agents.test.ts > the deployed recall agent marks every recall it runs… fails on Windows after upstream #1033's recall wording change, and passes on both trees on Linux.)

CI and Code Erosion still need a maintainer to approve the run; the PR is mergeable again.

@jeff-r2026

Copy link
Copy Markdown
Collaborator

Thanks for the hook-dispatch check. When you have a moment, could you also try this with the real Antigravity CLI (agy)? Opening an Agent should run teamai hook-dispatch session-start through PreInvocation, and teamai pull should sync the team data. A short note with the agy command, the installed hooks.json entry, the session log, and the pull result would be very helpful.

@satan9394

satan9394 commented Oct 14, 2026 •

Copy link
Copy Markdown
Author

Real agy verification on Windows -- the session-start hook never fires, and I think three things in the diff need to change

Thanks for the pointer -- I ran it against the real agy CLI. Short version: teamai pull and the resource delivery are fine, but on this platform the injected lifecycle hook is inert, for two independent reasons, and the SessionStart mapping isn't needed. Details, all reproducible from the logs below.

Environment: Windows 11, agy 1.3.4 (Antigravity CLI), git 2.53.0, node v24.14.0, teamai built from this PR's head 387d5eb and npm linked. Install: teamai init https://github.com/<private-throwaway-repo> --scope user --agent antigravity --force (user scope, so the hooks land in the file the maintainer asked about). Every agy run below is a plain agy -p "<prompt>" --log-file ..., with the CLI's own log compared against teamai's ~/.teamai/debug.log.

1. ~/.gemini/antigravity-cli/hooks.json is loaded but never executed

agy-customizations (bundled with 1.3.4) documents two customization roots: .agents/ in the workspace and ~/.gemini/config/ globally. There is no antigravity-cli/ customization root -- that directory is the CLI's app data (log/, conversations/, brain/, builtin/, cache/).

Measured with the same probe hook written into both files (identical type/command, one writes a line per event):

I1014 07:55:13.066677  1 hooks_manager.go:53] loaded 5 named hooks from 2 hooks.json file(s)
  • ~/.gemini/config/hooks.json copy -> ran: SessionStart, PreInvocation, Stop all recorded.
  • ~/.gemini/antigravity-cli/hooks.json copy -> never ran (no line written), and when the teamai block was in that file alone, agy never even attempted it -- zero jsonhook__teamai-hooks_* lines in its log, and zero hook-dispatch entries in teamai's log for the whole session:
I1014 07:42:17.543192  1 hooks_manager.go:53] loaded 3 named hooks from 2 hooks.json file(s)   <- block present in antigravity-cli/hooks.json
(grep "hook-dispatch" in ~/.teamai/debug.log for the session window -> no matches)

Note it loads both files and dedupes by hook name, so a stale dangerous-commands-guard in ~/.gemini/antigravity-cli/hooks.json also shadows the real one in ~/.gemini/config/hooks.json by name.

Effect: with the current userScope.settings default, the injected block is written to a file agy reads and discards, so session start never pulls and no team rule/context is ever delivered at session start.

2. In the file agy does execute, the Windows command form never runs either

agy runs hook commands with cmd /c on Windows (its own docs; confirmed from inside a hook -- %COMSPEC% expands to C:\WINDOWS\system32\cmd.exe). The command this PR writes:

"D:/.../Git/bin/bash.exe" -lc "teamai hook-dispatch session-start --tool antigravity 2>/dev/null" || true

reaches cmd with its quote escapes intact and fails on every invocation:

I1014 08:05:38.086106  776 command_hook_executor.go:75] JSON hook command stderr: '\"D:/Technology_application/Git/bin/bash.exe\"' <cmd: is not recognized as an internal or external command...>
E1014 08:05:38.086106  776 prehooks.go:43] failed to call custom pre-invocation hook jsonhook__teamai-hooks_PreInvocation_0_0: JSON hook "..." failed: command failed: exit status 1
E1014 08:05:38.175419  776 prehooks.go:43] failed to call custom pre-invocation hook jsonhook__teamai-hooks_PreInvocation_0_1: ... exit status 1
E1014 08:05:44.016084  776 stophooks.go:62] failed to call custom stop hook jsonhook__teamai-hooks_Stop_0_0: ... exit status 1

That is the os/exec -> cmd /c quoting pitfall: Go escapes the inner " as \", which cmd does not treat as an escape, so the first token is literally \"D:/.../bash.exe\". The agent keeps working (agy just logs the failed hook), but nothing of teamai's executes -- silently, because the wrapper ends in 2>/dev/null || true anyway.

Two forms did work in the same real agy session (verified, exit 0, teamai actually ran):

hook command result
teamai hook-dispatch session-start --tool antigravity [hook-dispatch] session-start/antigravity background: 18311ms total, 2 handlers, slowest pull=18311ms | pull=18311ms(ok)
cmd /c teamai hook-dispatch prompt-submit --tool antigravity ran on each invocation: prompt-submit/antigravity foreground: 92ms total, 5 handlers ...

So a Windows form for agy should avoid the quoted git-bash wrapper (and the POSIX 2>/dev/null), like the ZCode/DSH adapters already do for their platform-specific runners.

3. SessionStart is a real agy event, so it does not need to ride PreInvocation

antigravity-hooks.ts maps SessionStart -> PreInvocation with the comment "Antigravity has no session-start event of its own". 1.3.4 documents SessionStart ("once when a new conversation session starts", flat handler list) and it fires: in one session a SessionStart probe fired once while a PreInvocation probe fired once per model call. As written, the full session-start dispatch -- including the pull -- is repeated before every invocation.

What does work (verified, same run)

  • init --scope user --agent antigravity cloned the repo, registered the member, and delivered the team skill and rule: ~/.gemini/antigravity/skills/agy-e2e-marker-skill/SKILL.md, ~/.gemini/config/rules/agy-e2e-rule.md.
  • teamai pull after a second skill landed on the team repo: Team repo: 1 file(s) changed -> Synced 2 skills (1 new, 1 updated) / new: agy-e2e-marker-skill-2 -> Synced 1 rule(s), file present on disk.
  • teamai hooks inject merged into an existing, hand-written ~/.gemini/config/hooks.json and kept both of the user's own named blocks (dangerous-commands-guard, plan-readonly-guard) -- the one-key ownership contract holds for this adapter.

Two more paths worth checking (same root cause, not benchmarked yet)

  • User-scope skills: the delivered skill lands in ~/.gemini/antigravity/skills, but on this machine that directory is the Antigravity 2.0 app's skill set (antigravity/ is its data dir per agy's docs, while the CLI's is antigravity-cli/). If the CLI's global root is ~/.gemini/config/, it should be ~/.gemini/config/skills (and agents likewise).
  • Project scope: the docs place hooks.json inside a customization root, i.e. <project>/.agents/hooks.json, not <project>/hooks.json.

Happy to push a follow-up for 1-3 and re-run this exact loop to show the corrected block executing inside agy if that's useful.

…on Windows

Verified against the real Antigravity CLI (`agy` 1.3.4, Windows) rather than
its documentation alone; the previous paths and command form left every
lifecycle hook inert, so session start never pulled and no team resource was
delivered at session start.

* Roots: the CLI discovers customizations in a project's `.agents/` and, for
  the machine, `~/.gemini/config/`. `~/.gemini/antigravity-cli/` is its
  application data (logs, conversations, built-in skills) and
  `~/.gemini/antigravity/` belongs to Antigravity 2.0 - a hooks file or skill
  written there is loaded-but-ignored or not discovered at all. User-scope
  hooks therefore move to `~/.gemini/config/hooks.json`, skills to
  `~/.gemini/config/skills`, subagents to `~/.gemini/config/agents`, and the
  project hooks file to `.agents/hooks.json` (a project's root `hooks.json` is
  never executed).
* Events: agy fires a `SessionStart` of its own, so the session-start dispatch
  rides it instead of `PreInvocation`, which fires before every model call.
* Windows: agy hands each hook command to `cmd /c`, where the POSIX
  `"<git-bash>" -lc "…"` wrapper's quotes arrive backslash-escaped and the
  command never starts (`exit status 1` on every invocation). On Windows the
  adapter now writes the bare `teamai hook-dispatch …` command.
* The hooks install gate is the settings file itself, so the project path
  change keeps "a project with no hooks file gets none written".

Verified end to end in the real CLI: session-start, prompt-submit and stop all
run with no hook failures, a pulled skill is discovered from the corrected
skills root, and a project-scope `.agents/hooks.json` handler executes while a
root `hooks.json` one does not.
@satan9394

Copy link
Copy Markdown
Author

Fix pushed: ad4db09 -- roots, Windows command form, and SessionStart

The verification above turned up three things; all three are fixed in ad4db09 and re-verified against the real CLI (agy 1.3.4, Windows) end to end.

1. Roots. toolPaths.antigravity now targets the customization roots the CLI actually uses:

  • user scope: ~/.gemini/config/{hooks.json,skills,rules,agents} (was ~/.gemini/antigravity-cli/hooks.json, ~/.gemini/antigravity/skills, ~/.gemini/antigravity-cli/agents)
  • project scope: .agents/hooks.json (was hooks.json at the project root)

Both old locations are ones agy loads but never runs (its app-data hooks file) or never discovers at all (skills under ~/.gemini/antigravity/skills, which belongs to Antigravity 2.0) -- measured above with an identical probe written to both files.

2. Events. SessionStart now maps to agy's own SessionStart event instead of PreInvocation; UserPromptSubmit keeps riding PreInvocation (no equivalent event).

3. Windows command. New getAntigravityDispatchCommand writes the bare teamai hook-dispatch <event> --tool antigravity on Windows and keeps the POSIX bash -lc wrapper on Unix. agy hands every hook command to cmd /c there, where the wrapper's escaped quotes never resolve.

Verification after the fix

The installed block (user scope, merged into a ~/.gemini/config/hooks.json that already held two of the user's own blocks -- both preserved):

"teamai-hooks": {
  "enabled": true,
  "SessionStart":   [ { "command": "teamai hook-dispatch session-start --tool antigravity", "type": "command" } ],
  "PreInvocation":  [ { "command": "teamai hook-dispatch prompt-submit --tool antigravity", "type": "command" } ],
  "Stop":           [ { "command": "teamai hook-dispatch stop --tool antigravity", "type": "command" } ],
  "PostToolUse":    [ ... ]
}

Real agy session (agy -p "Reply with exactly: PONG-FIX"): no failed to call custom ... hook line in the CLI's log at all, and teamai's own log shows the handlers running:

[hook-dispatch] session-start/antigravity foreground: 3916ms total, 6 handlers ... team-rules=1ms(ok)
[hook-dispatch] prompt-submit/antigravity foreground: 53ms total, 5 handlers ...
[hook-dispatch] stop/antigravity foreground: 336ms total, 2 handlers ...
  • Skills root: after teamai pull --force the two marker skills land in ~/.gemini/config/skills/, and the real CLI activates them -- asked to use them it replies with their exact marker strings, whereas the same question with those skills present only under ~/.gemini/antigravity/skills replies NO-SUCH-SKILL.
  • Project scope: a SessionStart handler in <project>/.agents/hooks.json executes; the same handler in <project>/hooks.json does not.
  • Tests: antigravity.test.ts 15/15, instruction-targets.test.ts 241/241, builtin-hooks and hooks-golden green; tsc --noEmit, pnpm lint, pnpm build exit 0.
  • Not re-run: the Linux leg (the WSL checkout on this box has stale platform binaries in node_modules, so I left it to CI), plus macOS and node 20/24 as before.

Still open (not touched by this commit)

  • User-scope rules (~/.gemini/config/rules/) and agents are aligned with the documented global root, but I could not confirm agy honours a delivered rule file or subagent there -- worth a check before claiming delivery.
  • The two PostToolUse matcher passes (Skill, TodoWrite) never match agy's tool names (run_command, view_file, ...), so they are inert.
  • agy's hook stdout contract is {"injectSteps": [...]} while teamai's dispatcher emits its own JSON shape, so it is worth checking what the CLI does with that output before calling session-context delivery complete.

Two things I hit that look like separate issues, not this PR: teamai hooks remove ignores enabledAgents and strips hooks from every tool (on this machine it removed a Hermes hook and reported an OpenClaw entry), and a git-mode project-scope install saved under ~/.teamai/projects/<slug>/ was not found by teamai status / hooks list from inside that project afterwards.

The branch is still behind main (it reports as conflicting again); happy to merge upstream next if you want it fresh for review.

@github-actions

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/antigravity-hooks.ts:101 — Malformed or non-object JSON returns {}, which reconciliation then writes back. Injecting or removing hooks therefore destroys the existing user file despite warning that it will remain untouched.
  • [P1 blocking] src/hooks.ts:2803 — Team definitions bypass teamDefsForTool. A hook restricted to tools: [claude] is consequently installed and executed by Antigravity.
  • [P1 blocking] src/hooks.ts:2805 — The adapter ignores project scoping and managed manifests. In non-self project scope, project commands enter the global file without cwd gating; reconciling project B also replaces project A’s teamai-hooks block.
  • [P1 blocking] src/hooks.ts:2802 — A builtinsOnly recovery pass supplies neither built-ins nor team definitions. Because the generic status reader cannot recognize the existing block, recovery reaches this adapter and deletes working hooks instead of preserving them.
  • [P1 blocking] src/types.ts:566 — Antigravity remains settings-backed for HTTP install_hook_rule, whose generic writer produces Claude’s top-level hooks schema. These remotely installed hooks never execute, and removal uses the wrong parser.
  • [P1 blocking] src/types.ts:566 — Uninstall discovery still uses hasTeamaiHooks, which checks Claude’s top-level hooks property rather than teamai-hooks. An installed Antigravity block is therefore missed and remains active after uninstall --agent antigravity.
  • [P1 blocking] src/utils/tool-names.ts:52 — Alias normalization is still limited to normalizeAgentList. After init --agent agy, uninstall --agent agy uses the raw, nonexistent target ID and leaves Antigravity resources installed.
  • [P1 blocking] src/utils/tool-names.ts:48 — gemini remains a distinct registered agent targeting ~/.gemini/skills. Remapping it silently redirects existing init --agent gemini users to Antigravity instead of Gemini CLI.
  • [P2 non-blocking] src/types.ts:566 — hooks list still uses generic getHookStatus, which requires a top-level hooks property. A correctly installed Antigravity block is always reported as missing.
  • [P2 non-blocking] src/utils/tool-names.ts:52 — Team-configuration selectors are not normalized. Advertised aliases such as MCP tools: [agy] or agent targets: [gemini] do not match the antigravity target and are silently skipped.
  • [P2 non-blocking] src/__tests__/gitlab-clone-realspawn.test.ts:17 — The Windows GitLab-test skip is unrelated to Antigravity support and violates the surgical-change rule. Move this independently useful fix to a separate PR.

Earlier Findings
None of the previously raised findings is resolved in the current diff.

Testing
The description includes real-CLI and representative E2E records, satisfying the testing-record requirement. Their older commit references are a note, not blocking. No PR code was run, built, or installed during this review.

A rule file without frontmatter `trigger` is rejected by the CLI -- it logs
`Invalid rule trigger: CORTEX_MEMORY_TRIGGER_UNSPECIFIED` and the rule never
reaches the model -- so the verbatim `.md` copy this adapter wrote delivered
nothing. Verified against agy 1.3.4 with the same rule twice: verbatim it was
dropped (and logged), with `trigger: always_on` the model followed it.

* New `ANTIGRAVITY_RULE_FORMAT`: a team rule renders as
  `---\ntrigger: always_on\n---` plus the team body, and a rule that declares a
  trigger of its own keeps that value.
* The verbatim render stays in `previousRenders`, so copies an earlier teamai
  wrote are still recognized as teamai's.
@satan9394

Copy link
Copy Markdown
Author

Follow-up: team rules never reached the model -- fixed in dc4f6ed

Chasing the open item from my last comment (does a delivered rule actually reach the agent?) turned up a second real defect, now fixed and verified in the real CLI.

Root cause. agy parses a rule file's frontmatter and requires a trigger. A rule without one is rejected outright -- the CLI logs Invalid rule trigger: CORTEX_MEMORY_TRIGGER_UNSPECIFIED and the rule never reaches the model. The adapter's "Antigravity has no rule format of its own, so a team rule is written as a plain .md copy" therefore delivered nothing.

Evidence.

  • Removing the delivered file from ~/.gemini/config/rules/ dropped the CLI's Invalid rule trigger count from 2 to 1 in otherwise identical sessions -- the rule was being scanned, and rejected.
  • The same rule file, run twice in a real session: verbatim the model answered PONG-RULE with no trace of the rule; with trigger: always_on prepended, it answered PONG-RULE2 followed by TEAMAI-PROJECT-RULE-YES, the marker the rule tells it to append. (Project scope, .agents/rules/<name>.md.)
  • After the fix, delivered to user scope: teamai pull --force writes ~/.gemini/config/rules/agy-e2e-rule.md as ---\ntrigger: always_on\n--- + the team body, and the real CLI answers OK4 followed by TEAMAI-AGY-RULE-OK -- the team rule now changes the agent's behaviour. The Invalid rule trigger count is back to the 1 baseline that no delivered rule contributes to.

Fix (dc4f6ed). New ANTIGRAVITY_RULE_FORMAT: a team rule renders with trigger: always_on and keeps a trigger it declares itself; the verbatim render stays in previousRenders so copies an earlier teamai wrote are still recognised as teamai's. Both root paths are now covered -- .agents/rules/*.md in a project, ~/.gemini/config/rules/*.md for the user.

Tests. antigravity.test.ts 15/15 (the rule case now asserts the trigger is added, and that a declared trigger is kept). Note rules.test.ts reports 10 failures on this Windows box; they are platform failures unrelated to this change -- I verified by stashing the working tree and re-running it on pristine, which fails the exact same 10 (#993 symlink cases and the OpenCode instructions globs).

Open items after this (the rules channel is now off the list):

  • User-scope agents (~/.gemini/config/agents/) is doc-aligned but still unverified against the CLI.
  • The two PostToolUse matcher passes (Skill, TodoWrite) still never match agy's tool names (run_command, view_file, ...), so they are inert.
  • agy's hook stdout contract is {"injectSteps": [...]}; teamai's dispatcher writes nothing to stdout for this tool (its user-scope delivery is file-based, which is how the rule above arrives). Whether the project-scope instruction blocks need that channel is worth checking separately -- I did not want to claim either way from a manual invocation, since a manual hook-dispatch without a host payload prints nothing for other tools too.

@github-actions

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/antigravity-hooks.ts:101 — Malformed or non-object JSON becomes {}, which reconciliation writes back during injection or removal. A malformed user hooks.json is therefore destroyed despite the warning that it will remain untouched.
  • [P1 blocking] src/hooks.ts:2803 — Team definitions bypass teamDefsForTool. A hook restricted to tools: [claude] is consequently installed and executed by Antigravity.
  • [P1 blocking] src/hooks.ts:2805 — The adapter ignores teamHookProjectRoot and managed manifests. Non-self project hooks enter the global file without cwd gating, and reconciling project B replaces project A’s teamai-hooks block.
  • [P1 blocking] src/hooks.ts:2802 — A builtinsOnly recovery pass supplies neither built-ins nor team definitions. The generic status reader misses the existing block, so recovery deletes working hooks instead of preserving team hooks and restoring built-ins.
  • [P1 blocking] src/types.ts:566 — HTTP install_hook_rule still routes this settings-backed target through generic applyAgentHook, producing Claude’s top-level hooks schema. Those Antigravity hooks never execute, and removal uses the wrong parser.
  • [P1 blocking] src/types.ts:566 — Uninstall discovery uses hasTeamaiHooks, which cannot recognize the named teamai-hooks block. uninstall --agent antigravity consequently leaves installed hooks active.
  • [P1 blocking] src/utils/tool-names.ts:52 — Alias normalization remains limited to normalizeAgentList. After init --agent agy, uninstall --agent agy uses the nonexistent raw target ID and leaves Antigravity resources installed.
  • [P1 blocking] src/utils/tool-names.ts:48 — gemini remains a separately registered agent targeting ~/.gemini/skills. Remapping it silently redirects existing init --agent gemini users to Antigravity instead of Gemini CLI.
  • [P2 non-blocking] src/types.ts:566 — hooks list still uses generic getHookStatus, which requires a top-level hooks property. A correctly installed Antigravity block is reported as missing.
  • [P2 non-blocking] src/utils/tool-names.ts:52 — Team-configuration selectors are not normalized. Advertised aliases such as MCP tools: [agy] or agent targets: [gemini] do not match the antigravity target.
  • [P2 non-blocking] src/resources/base.ts:42 — .gemini and GEMINI.md also belong to the separately supported Gemini CLI. A Gemini-only project therefore counts as having Antigravity installed, enabling unsolicited Antigravity resource delivery despite the explicit-probe goal.
  • [P2 non-blocking] src/__tests__/gitlab-clone-realspawn.test.ts:17 — The Windows GitLab-test skip is unrelated to Antigravity support and violates the surgical-change rule. Move it to a separate PR.

Earlier Findings
None of the previously raised findings is resolved in the current diff.

Testing
The supplied description includes representative real-CLI and E2E records, satisfying the testing-record requirement. Older commit references are not blocking. No PR code was run, built, or installed.

Brings the branch up to `27a026f` so the PR is mergeable again. Conflict
resolutions:

* `src/resources/agent-format.ts`: keep both the new `muse` tool and
  `antigravity` in `ToolName`, `ALL_SUPPORTED_TOOLS` and `tool_extras`.
* `src/resources/base.ts`: keep Antigravity's own install probe (it must not
  use the `..agents/`-based generic probe) and upstream's new
  `HOME_INSTALL_ROOTS` check; Antigravity is not in that table, so the order of
  the two checks does not change either answer.
* Docs: upstream rewrote `docs/guide/advanced.md` into several pages
  (`dashboard`, `diagnostics`, `distribution`, `http-contract`, `reporting`,
  `skill`, `workflow`) and removed it, and rewrote `member-guide.md` and
  `sharing.md`. Resolved to upstream's structure, since the page our
  Antigravity section lived on no longer exists, and the tool's own
  documentation is carried by the README matrix (an `Antigravity CLI` row, added
  to all five language versions) plus a row in `docs/guide/team-culture.md` and
  its zh-CN twin for where the culture blocks land.
@satan9394

Copy link
Copy Markdown
Author

Merged upstream/main (27a026f) -- the PR is mergeable again

Head is now 0fe701a. Conflict resolutions:

  • src/resources/agent-format.ts -- kept both the new muse tool and antigravity in ToolName, ALL_SUPPORTED_TOOLS and tool_extras.
  • src/resources/base.ts -- kept Antigravity's own install probe (it must not use the generic .agents/-based one, or a project that merely uses .agents/ is counted as running Antigravity) alongside upstream's new HOME_INSTALL_ROOTS check. Antigravity is not in that table, so the order of the two checks does not change either answer.
  • Docs -- upstream rewrote docs/guide/advanced.md into several pages and deleted it, and rewrote member-guide.md / sharing.md. I resolved those to upstream's structure, because the page the Antigravity section lived on no longer exists and re-homing several paragraphs of tool documentation is your call, not a merge decision. What the PR does carry is the advertised contract: an Antigravity CLI row in the supported-tools matrix of all five READMEs, and a row in docs/guide/team-culture.md (+ zh-CN) for where the culture blocks land (~/.gemini/config/GEMINI.md, GEMINI.md in a project). Say where you want the detailed tool page and I will write it there.

Re-verified on the merged base with the real CLI (agy 1.3.4, Windows):

  • teamai init + teamai pull delivers the team rule as ---\ntrigger: always_on\n--- + body into ~/.gemini/config/rules/, and the hooks block into ~/.gemini/config/hooks.json with SessionStart: teamai hook-dispatch session-start --tool antigravity.
  • agy -p "Say exactly: OK-MERGE" answers OK-MERGE followed by TEAMAI-AGY-RULE-OK -- the delivered rule still reaches the model.
  • Zero failed to call custom ... hook lines in the CLI log; teamai's own log shows the dispatch running (session-start/antigravity ... pull=10291ms(ok), stop/antigravity ...).
  • Gate: tsc --noEmit 0, pnpm lint 0, and antigravity (15), instruction-targets (241), builtin-hooks, hooks-golden, agent-format all green. agents.test.ts reports 2 failures on this box, both EPERM: operation not permitted, symlink -- the Windows symlink-privilege class the #993 cases already fail with here, unrelated to this branch. The Linux leg is left to CI.

One upstream behaviour change worth noting for anyone following along: init no longer copies skills/rules at the end (its own message now says they sync on the next session start or on teamai pull), so the verification above runs pull explicitly before starting agy.

@github-actions

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/antigravity-hooks.ts:109 — Malformed or non-object JSON becomes {}, which reconciliation writes back during injection or removal. A malformed user hooks.json is therefore destroyed despite the warning that it will remain untouched.
  • [P1 blocking] src/hooks.ts:3149 — Team definitions bypass teamDefsForTool. A hook restricted to tools: [claude] is consequently installed and executed by Antigravity.
  • [P1 blocking] src/hooks.ts:3151 — The adapter ignores project scoping and managed manifests. Non-self project hooks enter the global file without cwd gating, and reconciling project B replaces project A’s teamai-hooks block.
  • [P1 blocking] src/hooks.ts:3148 — A builtinsOnly recovery pass supplies neither built-ins nor resolved team definitions. The generic status reader misses the existing block, so recovery deletes working hooks rather than preserving team hooks and restoring built-ins.
  • [P1 blocking] src/types.ts:634 — HTTP install_hook_rule still routes this settings-backed target through generic applyAgentHook, writing Claude’s top-level hooks schema. Those Antigravity hooks never execute, and removal uses the wrong parser.
  • [P1 blocking] src/types.ts:634 — Uninstall discovery uses hasTeamaiHooks, which cannot recognize the named teamai-hooks block. uninstall --agent antigravity consequently leaves installed hooks active.
  • [P1 blocking] src/utils/tool-names.ts:48 — Alias normalization remains limited to normalizeAgentList. After init --agent agy, uninstall --agent agy uses the nonexistent raw target ID and leaves Antigravity resources installed.
  • [P1 blocking] src/utils/tool-names.ts:49 — gemini remains a separately registered agent targeting ~/.gemini/skills. Remapping it silently redirects existing init --agent gemini users to Antigravity instead of Gemini CLI.
  • [P2 non-blocking] src/types.ts:634 — hooks list still uses generic getHookStatus, which requires a top-level hooks property. A correctly installed Antigravity block is reported as missing.
  • [P2 non-blocking] src/utils/tool-names.ts:52 — Team-configuration selectors are not normalized. Advertised aliases such as MCP tools: [agy] and agent targets: [gemini] do not match the antigravity target.
  • [P2 non-blocking] src/resources/base.ts:48 — .gemini and GEMINI.md also belong to the separately supported Gemini CLI. A Gemini-only project therefore counts as having Antigravity installed, enabling unsolicited Antigravity resource delivery.
  • [P2 non-blocking] src/__tests__/gitlab-clone-realspawn.test.ts:17 — The Windows GitLab-test skip is unrelated to Antigravity support. It violates the surgical-change rule and should move to a separate PR.

Earlier Findings
None of the previously raised findings is resolved in the specified diff.

Testing
The supplied PR description includes representative real-CLI and E2E records, satisfying the testing-record requirement. Older commit references are not blocking. No PR code was run, built, or installed during this review.

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