Skip to main content
Glama
README.md
# clagy — a Claude Code → Antigravity (`agy`) bridge

An MCP server that exposes Google Antigravity's CLI as sub-agents you can invoke from Claude
Code. Claude orchestrates; Gemini workers execute in parallel.

```
Claude Code
  └─ agy_fanout({tasks:[…]})
       ├─ agy.exe -p --output-format stream-json  → worker 1  ┐
       ├─ agy.exe -p --output-format stream-json  → worker 2  ├─ in parallel
       └─ agy.exe -p --output-format stream-json  → worker 3  ┘
```

Workers report every step while they run, so a delegation looks like this rather than like a
two-minute spinner:

```
agy_fanout(tasks=4)
  2/4 · store ▸ view_file src/db/session.ts
```

The workers also reach outside the repo — they search the web and read pages themselves — so
"why does this library do that" and "how does our code use it" come back as one answer,
without the reading landing in Claude's window. See
**[docs/native-agents.md](docs/native-agents.md)**, which also explains why there is no
`agy-*` entry in `/agents`: a subagent's loop is always Claude, so that route spends the very
thing this bridge exists to save.

## When it's worth using

The benefit here **is not speed** — a worker takes 20–130 s to return something Claude would
write in half the time. The benefit is **context that never enters Claude's window**, which
means a longer session. Hence the only rule that matters:

> **Delegate when the ratio between what the worker consumes and what it hands back is high.**

| case | consumes : returns | |
|---|---|---|
| Research fan-out across a repo | ~300 : 1 | ★★★ |
| Adversarial tests against existing code | ~100 : 1 | ★★★ |
| Cross-file static audit | ~60 : 1 | ★★★ |
| Mechanical edit across ≥4 files | 1 : N | ★★ |
| Spec → code for a new file | ~1 : 1 | neutral |
| One-line fix · debug loop · anything that depends on the conversation | negative | ✗ |

The measured numbers, the reasoning behind each row, and the second axis of benefit — that
workers fail differently than Claude, so they cover blind spots — are in
**[docs/when-to-delegate.md](docs/when-to-delegate.md)**.

## Documentation

| | |
|---|---|
| [docs/when-to-delegate.md](docs/when-to-delegate.md) | which cases show a net benefit, with measured ratios |
| [docs/native-agents.md](docs/native-agents.md) | live step reporting, and why delegation is a tool and not a subagent |
| [docs/async-and-autonomous.md](docs/async-and-autonomous.md) | background jobs + hook, and the permissionless mode |
| [docs/other-repos.md](docs/other-repos.md) | how to use this workflow in any other repository |
| [prompts/](prompts/) | 7 general prompt templates + schemas, so you don't write them from scratch |
| [skills/agy-orchestration/](skills/agy-orchestration/) | the playbook Claude loads on its own; copy to `~/.claude/skills/` |

## Requirements

- Node 18+
- [Claude Code](https://claude.com/claude-code)
- Google Antigravity's CLI (`agy`) installed and **already authenticated**. The bridge reuses
  the OAuth credentials in `~/.gemini/`; it does not need `GEMINI_API_KEY`. If `agy` is not in
  `%LOCALAPPDATA%\agy\bin\`, set `AGY_BIN` to the executable's path.
Nothing else: internet research needs no extra skill, because the workers carry `search_web`
and `read_url_content` in every mode.

## Installation

In the examples below, `C:\path\to\clagy` is wherever you cloned this repo — substitute your own.

**1. The bridge.**

```powershell
git clone https://github.com/mateo-cuello/orchestrator-claude--agy.git C:\path\to\clagy
cd C:\path\to\clagy
npm install
node mcp/agy-bridge.mjs --selftest    # must report 16/16 before you continue
claude mcp add --scope user agy -- node C:\path\to\clagy\mcp\agy-bridge.mjs
```

Scope `user` = available from every project, with nothing to install per repo.

**2. The `Stop` hook**, so async jobs report back on their own. In `~/.claude/settings.json`:

```json
"hooks": {
  "Stop": [
    { "hooks": [
      { "type": "command", "command": "node C:\\path\\to\\clagy\\hooks\\agy-stop-hook.mjs" }
    ] }
  ]
}
```

**3. The skill**, so Claude loads the orchestration playbook by itself: copy
[`skills/agy-orchestration/`](skills/agy-orchestration/) into `~/.claude/skills/` and replace
the `C:\path\to\clagy` placeholders inside with your real path.

That's all three. There is deliberately no step for `~/.claude/agents/` — see
[docs/native-agents.md](docs/native-agents.md#why-there-are-no-agy--subagents). If you
installed the `agy-*` agents from an earlier version, delete them: they run a Claude loop per
delegation, which is the cost this bridge exists to avoid.

```powershell
Remove-Item $HOME\.claude\agents\agy-*.md
```

MCP servers, hooks and agents are loaded at session startup: restart Claude Code after
registering them. To use this from other repositories, see
[docs/other-repos.md](docs/other-repos.md).

## Verification

```powershell
node mcp/agy-bridge.mjs --selftest
```

Runs 16 checks: round trip, structured output, **silent-failure detection** (and that the
denial names the tool and its arguments), **that a deny is not diagnosed as exhausted quota**,
tool-contract effectiveness, that `verify` executes a command that is allowlisted exactly,
**that the allowlist matches the entire command line and not the binary**, **the `cwd`
regression** (that writes land in your project and not in `agy`'s scratch), **the
autonomous-mode guard**, the full lifecycle of an async job, that the hook **delivers** a
result and **does not repeat it**, that 21 consecutive registry writes lose none and leave no
`.tmp` behind (this was a real bug: see
[async-and-autonomous.md](docs/async-and-autonomous.md)), that a hung job gets detected,
**that workers report their steps live and leave a trace**, and that the stream parser
survives chunks split mid-JSON without duplicating steps or leaking tool output into the
trace. It must report `16/16 checks OK`.

## Tools

### `agy_dispatch` — a single task

| field | default | notes |
|---|---|---|
| `prompt` | — | required |
| `model` | `gemini-3.6-flash-high` | `gemini-3.1-pro-high` for depth |
| `mode` | `read` | `read` / `write` / `verify` (see Modes) |
| `cwd` | the bridge's cwd | working root; added to the workspace automatically. Scope it anyway |
| `schema` | — | JSON Schema; arrives parsed in `structured` |
| `timeout` | `10m` | |
| `resumeFrom` | — | a previous `conversationId`, to continue the thread |

Returns `{ok, response, structured, conversationId, usage, trace}` — plus `denials[]` when it
failed, and `warnings[]` when it succeeded *despite* a tool being denied along the way (a case
that was simply invisible before the bridge started reading `agy`'s step stream).

`trace` is what the worker actually did, taken from that stream rather than from its own
account of itself:

```json
"trace": { "steps": 14, "toolCalls": 9,
           "toolsUsed": ["list_dir", "grep_search", "view_file"],
           "filesTouched": ["src/db/session.ts", "src/db/index.ts"] }
```

Use `filesTouched` to check citations: a finding that names a file the worker never opened is
invented. Tool *outputs* are deliberately not kept — they are exactly the context the worker
exists to keep out of your window.

### `agy_fanout` — N in parallel

`tasks[]` + `maxConcurrency` (default 4). Returns `{summary, results[]}`.

### `agy_start` / `agy_result` / `agy_jobs` — in the background

`agy_start` takes the same fields as `agy_dispatch` (plus `label`) but **does not wait**: it
returns a `jobId` immediately while the worker keeps running. When it finishes, the `Stop`
hook injects the result at the end of the next turn — no polling required. `agy_result`
fetches it manually; `agy_jobs` lists everything.

Use it when you have something else to do meanwhile — the notification lands on a turn
boundary, so launching one and immediately going idle buys you nothing. If your next step
depends on the result, a blocking `agy_dispatch` is simpler. Details in
**[docs/async-and-autonomous.md](docs/async-and-autonomous.md)**.

### Modes

- **`read`** — read-only. This is the default and needs no extra permissions. Note that
  read-only **includes the internet**: `search_web` and `read_url_content` are in the toolset
  of every mode, so a plain `read` worker can already answer "what does the changelog say".
- **`write`** — also edits files. `write_file(*)` is already permitted in headless mode.
- **`verify`** — also runs allowlisted commands (`npm test`, `pytest`, `ruff check`,
  `npx tsc --noEmit`, `make`, `git status|diff|log`, …), **with one sharp limitation**: `agy`
  matches the *entire command line*, literally, with no wildcards. Measured 2026-08-09:
  `git status` runs, `git log --oneline -3` is denied despite `command(git log)` being present;
  `command(yt-dlp)`, `command(yt-dlp *)` and `command(yt-dlp:*)` all fail to authorize
  `yt-dlp --version`. So an allowlist entry only covers a **fixed** invocation. Anything
  carrying a variable argument — a path, a URL, a test name — cannot be reached from `verify`
  at all; that's `autonomous` or nothing. Note this also means alternation entries like
  `npm run build|lint|test|typecheck` match nothing: they read as prose to the model, not as a
  permission. Two selftest checks pin both halves of this behaviour.
- **`autonomous`** — ⚠️ **no permission system** (`--dangerously-skip-permissions`):
  arbitrary commands, iterating on its own until tests pass. It exists so the
  write→test→fix cycle doesn't run through the orchestrator's context window. It requires an
  explicit `cwd`, rejects dangerous directories, and the contract forces the worker to paste
  raw test output. **Run the tests yourself once anyway.** Read
  [docs/async-and-autonomous.md](docs/async-and-autonomous.md) before using it.

## The five `agy` traps this bridge solves

1. **Silent failure on permissions.** If a tool needs a permission it doesn't have, `agy`
   exits with **code 0** and **`status:"SUCCESS"`** but an empty `response`, sending the
   notice to stderr. A naive orchestrator counts that as success. The bridge turns it into
   `ok:false` with `denials[]` — and since it reads the step stream, the denial names the tool
   and its arguments (`run_command {"CommandLine":"whoami"}: User denied permission…`) instead
   of the bare "a tool required the command permission" that stderr gives you. That's check #3.
2. **Silent failure on quota, which looks just like it.** `agy` never reports a 429 in print
   mode: it silently retries until `--print-timeout` runs out and exits equally empty. The
   bridge passes `--log-file`, looks for `RESOURCE_EXHAUSTED` and returns
   `quotaExhausted: true` with a different message — otherwise the diagnosis sends you off to
   fix permissions that were fine.
3. **`agy` ignores the process cwd.** Its default workspace is
   `~/.gemini/antigravity-cli/scratch`, so a `write_to_file` with a relative path lands there
   instead of in your project — with no error at all. Measured across 4 variants: `cwd` alone
   fails; `--add-dir <cwd>` works; an absolute path in the prompt works. The bridge does
   **both**: it puts `cwd` in the workspace and forbids relative paths in the contract. That's
   check #5.
4. **Unpredictable tool selection.** The model may wander into `run_command` and die
   auto-denied. Every prompt is prefixed with a contract pinning the mode's toolset.
5. **Shell escaping.** An inline `--json-schema` breaks in PowerShell (the quotes get eaten).
   The bridge uses `spawn` with an argv array and passes the schema via file.

Also: `-p` does not accept stdin, so prompts longer than 8000 chars are written to a temporary
`BRIEF.md` for the worker to read. And `--agent` is never used: a nonexistent name raises no
error, it silently falls back to the default agent.

## `agy`'s permission model in headless mode

| Category | Default | Effect |
|---|---|---|
| `read_file(*)` | allowed | reads with no configuration |
| `write_file(*)` | allowed | **can edit code** |
| `command(*)` | `ask` → auto-denied | only what's in `permissions.allow` |

Config lives in `~/.gemini/antigravity-cli/settings.json` (`permissions.allow`, which supports
alternation: `command(npm run (build|lint|test))`) and `~/.gemini/config/config.json`
(`userSettings.globalPermissionGrants`, where the UI accumulates every "Always allow").

Sensitive paths (`.env*`, `.git`, `.npmrc`, `.netrc`, `.git-credentials`) are already in
`ask`/`denied` by default — except in `mode:"autonomous"`, which turns all of that off.

> **Review `permissions.allow` every so often: it grows on its own.** `agy`'s UI accumulates
> there every "Always allow" you accepted in an interactive session. Each one is stored as an
> **exact command line**, so it never widens `verify` into a category — the risk is not that
> `verify` runs more than the contract enumerates (it runs less), but that one concrete
> dangerous line ends up authorized. `command(git checkout)`, `command(git add)` and
> `command(git commit)` were removed from that list once — they let a non-autonomous worker
> discard uncommitted changes. Only read-only `git` remains.

## Cost

A floor of ~18–26k input tokens per call (`agy`'s system prompt), even for a "hello". A real
research task measured 328k input + 1.4M cache-read + 10.5k output in 75s. Aggressive fan-out
burns quota fast: use `flash-high` for broad sweeps and reserve `gemini-3.1-pro-high` for
depth.

## Quality

`gemini-3.6-flash-low` gave an **incorrect** answer on a trivial file-reading task during
testing. Rule of thumb: schemas should always ask for `files_read[]` and `confidence`, and no
claim about the code is accepted without opening the file it cites.

## License

MIT — see [LICENSE](LICENSE).