# Driving agy (Antigravity) headless via clikae

A focused recipe for one specific job: **route work to your Antigravity (agy) Pro
account instead of burning your main Claude/codex quota.** agy is a real, paid
engine — use it. But it behaves differently from `claude -p` / `codex exec`, and
most wasted agy quota comes from driving it the wrong way. This page is the
canonical how-to so you don't re-learn it (and re-burn it) every session.

> TL;DR — agy is **global, single-account** — one account active across all
> terminals at a time, never two in parallel. As of 2026-07-05 that no longer
> means "not burnable": `clikae agy <tank>` carries the Google login WITH the
> tank via the macOS Keychain (verified on every switch, no interactive OAuth),
> so `clikae burn agy <tank>` can auto-hop to the next agy tank when one runs
> dry — same contract as burn for claude/codex, just sequential (agy still
> can't run two tanks in parallel).

## The one mental-model fact

agy's Google login is a **single global Keychain entry**, not a per-shell env var.
So:

- There is **no `clikae env agy`** and **no per-shell routing** — `clikae env agy`
  fails on purpose and points you here.
- `clikae agy <tank>` swaps `~/.gemini` (and its login, carried via Keychain)
  **machine-wide** — one agy account is active across *all* terminals at once.
  It's exclusive, not parallel: `clikae burn agy <tank>` walks agy's OWN tanks
  sequentially (dry → switch → retry), never two at once, and `clikae conduct`
  still refuses a leg naming a non-active agy tank (parallelism is a structural
  limit, unrelated to the OAuth friction that used to gate sequential switching).
  See `agy-gemini-shared-quota` notes for why same-account agy↔gemini is a fake
  relay (same quota bucket) — that's still true; it's cross-ACCOUNT tanks that
  now hop for free.

## The canonical headless invocation

```bash
# Switch agy to a tank (machine-wide), then run it headless on that account.
# Prompt goes through a FILE — never cram a paragraph into a shell-quoted -p '…'.
clikae agy <tank> -- \
  --print-timeout 900s \
  --add-dir /abs/path/to/repo  `# only if the task must read/write files outside cwd` \
  -p "$(cat /tmp/agy_prompt.txt)"
```

That's it. The rules below are the difference between this returning real work and
returning a blank.

## `clikae burn agy --artifact` — who holds the pen

burn proves a run happened by the **artifact file**, never the exit code. agy's
headless mode **auto-denies the file tools on your paths** — with no terminal it
cannot prompt for permission — so asking agy to write that artifact itself fails
every single time:

```
[ FAIL ] agy/r produced no fresh artifact and shows no limit — a real task failure…
    a tool required the "read_file" permission that headless mode cannot prompt
    for, so it was auto-denied.
```

Those two contracts cannot both be satisfied *by agy*. They differ only in **who
holds the pen**: agy prints perfectly well (that is its whole print mode), and
burn already had the output in hand for its error tail. **So clikae writes the
artifact from agy's stdout**, and says so on the row — you must never be left
believing agy wrote a file it is not allowed to write.

Two things deliberately NOT done:

- **Adding an allow-rule to your agy `settings.json`.** That is clikae widening
  an engine's permissions on your behalf — the same line
  `--dangerously-skip-permissions` sits on, and it is yours to cross, not ours.
- **Refusing `--artifact` for agy.** It would remove the only verification burn
  has, to avoid a problem clikae can simply solve.

⚠️ **The honest limit:** agy buffers a *large* answer into its own brain dir and
prints only a pointer, so a big deliverable may arrive as a pointer-shaped
artifact. Check the file. And a silent run is not proof nothing happened —
look in `~/.gemini/antigravity-cli/brain/` before re-firing.

### `--timeout` now reaches agy

agy enforces its own print budget, **default 5 minutes**, and knows nothing about
clikae's `--timeout`. Until 2026-07-27 that made `burn agy --timeout 1200` a
fiction: agy self-terminated at 5m long before clikae's bound applied. When you
pass `--timeout`, clikae now hands agy the same budget as `--print-timeout`. With
no `--timeout`, agy's own 5-minute default stands — clikae does not invent one.

**A timeout is not evidence of no work.** A run has been observed to time out
with the work already committed and the build already green, only the final reply
unprinted. Check the state before you re-fire.

## Rules that keep agy from firing a blank (hard-won)

1. **Headless agy is `-p` (print mode), never `-i`.** `agy -i` needs a real TTY
   (it's a bubbletea TUI) and dies headless with `could not open TTY`. Any
   agent-driven or background call uses `-p`.

2. **Pass the prompt via a file, not nested quotes.** `-p "$(cat /tmp/foo.txt)"`.
   Nested `sh -c '… -p "…"'` quoting silently eats the prompt and agy answers the
   wrong thing. One layer, file-fed.

3. **Don't collect big output from stdout — let agy write a file.** `agy -p`'s
   stdout return is unreliable for large/structured output: the process stays
   alive at 0% CPU and **buffers everything, emitting nothing**. For anything big,
   tell agy to *write the result to a file itself* (agentic), then read that file.
   Where it lands: `~/.gemini/antigravity-cli/brain/<session-id>/*.md` — `-p`'s
   stdout is often just a one-line pointer to it.

4. **Give a dead task, hard boundaries, and a long timeout.** agy's interactive
   strength — free exploration — is a liability headless: it will wander (e.g. go
   investigate a git diff) and burn the whole timeout writing nothing. Fence the
   prompt explicitly ("don't check git, don't investigate, only do X and write
   Y") and give `--print-timeout 900s` (default ~5 min is too short for real work).

5. **macOS has no `timeout`.** `--print-timeout` doesn't always kill cleanly. For
   unattended jobs, wrap the call in an outer hard kill (e.g. Python
   `subprocess.run(..., timeout=N)`) as a backstop.

6. **`pkill -9 -f "agy -p"` before switching tanks.** `clikae agy <tank>` refuses
   to switch while an `agy` process is live (swapping `~/.gemini` under it would
   corrupt the session). Kill stale/hung print jobs first.

7. **Reading files outside cwd needs `--add-dir <abs path>`** (pre-authorise), or
   feed pure text via stdin (`cat file | agy -p "…"`) so no tool/permission gate
   is hit. A bare `agy -p` that tries to read an un-authorised path **hangs
   forever** waiting on a permission prompt that has no TTY to answer it.

8. **`--dangerously-skip-permissions` is for a *human*, not an agent.** When clikae
   is being driven *by another AI* (e.g. Claude Code), that AI's safety classifier
   blocks an agy call carrying `--dangerously-skip-permissions` — even with a
   `Bash(agy:*)` allow-rule. Plain `agy -p` (no skip flag) passes. If a task genuinely
   needs skip-permissions, the human runs that line (e.g. `!`-prefixed); don't expect
   an agent to self-authorise it.

9. **Rotate tanks across batches to spread the weekly cap.** One global account =
   sequential execution; alternating tanks (`clikae agy 8` / `clikae agy c` / …)
   spreads usage across accounts. Net throughput is round-robin, not parallel.

## What agy is genuinely good at headless

- **One-shot text generation** — translation, transcreation, summaries, copy. Verified:
  it reliably produces single-shot output for well-scoped text tasks. (When asked to
  review zh-TW for Taiwanese voice, attach a Mainland→Taiwan term rubric — agy is
  Gemini underneath and will otherwise drift; treat it as a grader fed an explicit
  rubric, not a native ear.)
- **Live-web QA** — agy has a `read_url_content` tool and really browses (fetches
  URLs, follows links, reads HTML). Good for buyer-journey / i18n / polish sweeps,
  one report file per lens. Smoke-test once (ask it to echo a page's real `<h1>`)
  to confirm it isn't guessing. Plain `curl` is more reliable for a bare liveness
  check — don't spend agy quota on "is the site up?".
- **Big file-writing jobs offloaded from your main quota** — a whole translation
  dictionary, written to a file (rule 3), on the agy bucket, untouched by your
  Claude budget. This is the cost-aware-routing payoff.
- **A cheap breadth leg in `clikae conduct`** — `conduct --leg agy/<tank>` fans a
  read-only audit/analysis prompt to agy alongside claude/codex legs, then hands you
  every leg's output to judge. agy is cheap and fast, so it's a good extra
  perspective for best-of-N. Caveat: the agy leg runs on the **currently active**
  agy tank only (a leg naming another tank is reported, not run — clikae can't
  switch agy in parallel). Its dry state is read from `cli.log`, not stdout.

⚠️ **agy's review suggestions need triage.** It will confidently recommend things
that violate already-decided product calls. Never auto-apply an agy review's fixes —
the orchestrator or a human filters them.

❓ **Unverified (treat with care):** one observation suggests `agy -p` may not run a
fully autonomous multi-step *file-editing* loop the way `claude -p` / `codex exec`
do — it may answer single-shot or defer to an async build and return. Single-shot
text output is confirmed; the agentic edit-and-verify loop is not. If you plan to
use agy as a `burn`-style code-editing worker, validate it with a controlled task
first. See `agy-gemini-shared-quota` for the running ground-truth.

## See also

- `docs/orchestration.md` — the general headless dispatch playbook (burn/conduct/legs).
- `docs/dogfood-agy-headless.md` — the raw dogfooding diaries this recipe distills.
- `clikae agy --help` — the command surface for switching/managing agy tanks.
- `scripts/verify-agy-shapes.sh` — run this after `agy update`. The adapter reads
  agy's own `history.jsonl` and transcripts with grep, and agy updates itself, so
  the format can change with nothing in this repo changing. The bats suite is
  green against fixtures either way; this checks the real files.
