> ## Documentation Index
> Fetch the complete documentation index at: https://docs.e2b.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Antigravity CLI

> Run Google Antigravity CLI in a secure E2B sandbox with full filesystem, terminal, and git access.

[Antigravity CLI](https://antigravity.google/docs/cli) (`agy`) is Google's coding agent for the terminal. E2B provides a pre-built `antigravity` template with Antigravity CLI already installed and configured to authenticate with a Gemini API key.

> [!NOTE]
>
> Antigravity CLI is the successor to Gemini CLI. Google [announced the transition](https://developers.googleblog.com/an-important-update-transitioning-gemini-cli-to-antigravity-cli/) on May 19, 2026: since June 18, 2026, Gemini CLI no longer serves free-tier and Google AI Pro and Ultra users, while it remains available to enterprise customers and with paid API keys. To move existing skills, plugins, and MCP servers, see the [migration guide](https://antigravity.google/docs/cli/gcli-migration).

## Authentication

The template sets `"modelProvider": "gemini"` in `~/.gemini/antigravity-cli/settings.json`, so Antigravity CLI authenticates with the `GEMINI_API_KEY` environment variable instead of a browser sign-in. Create a key in [Google AI Studio](https://aistudio.google.com/apikey) and pass it to the sandbox. Antigravity CLI reads only `GEMINI_API_KEY` — it ignores `GOOGLE_API_KEY` and `.env` files — and does not start when the variable is empty.

To sign in with a Google account instead, remove `modelProvider` from the settings file. Antigravity CLI then prints an authorization URL; open it in your local browser and paste the code back into the terminal.

## CLI

Create a sandbox with the [E2B CLI](https://docs.e2b.dev/cli.md) and pass your API key to the terminal session.

```bash theme={"theme":"css-variables"}
e2b sbx create antigravity -e GEMINI_API_KEY=$GEMINI_API_KEY
```

Once inside the sandbox, start Antigravity CLI.

```bash theme={"theme":"css-variables"}
agy
```

## Run headless

Use `-p` for non-interactive mode and `--dangerously-skip-permissions` to auto-approve all tool calls. Without it, headless runs cannot ask for approval: tools that need it are denied, and the run still exits with code 0 and lists them under `denied_actions` in `--output-format json` output.

Agent runs often take longer than the default command timeout, so pass `timeoutMs: 0` (`timeout=0` in Python) to wait until the run completes, and give the sandbox enough lifetime.

> [!NOTE]
>
> Auto-approving tool calls is contained by the sandbox: the agent cannot touch your host machine, local files, or credentials. It can still make outbound network requests — internet access is enabled by default. To limit where an auto-approved agent can connect, configure [outbound network rules](https://docs.e2b.dev/network/internet-access.md) (`allowInternetAccess`, plus allow/deny lists by CIDR or hostname). Hostname rules apply to HTTP(S) traffic only; use CIDR rules for other protocols.

```typescript JavaScript & TypeScript theme={"theme":"css-variables"}
import { Sandbox } from 'e2b'

const sandbox = await Sandbox.create('antigravity', {
  envs: { GEMINI_API_KEY: process.env.GEMINI_API_KEY! },
  timeoutMs: 600_000,
})

const result = await sandbox.commands.run(
  `agy --dangerously-skip-permissions -p "Create a hello world HTTP server in Python"`,
  { timeoutMs: 0 }
)

console.log(result.stdout)
await sandbox.kill()
```

```python Python theme={"theme":"css-variables"}
import os
from e2b import Sandbox

sandbox = Sandbox.create("antigravity", envs={
    "GEMINI_API_KEY": os.environ["GEMINI_API_KEY"],
}, timeout=600)

result = sandbox.commands.run(
    'agy --dangerously-skip-permissions -p "Create a hello world HTTP server in Python"',
    timeout=0,
)

print(result.stdout)
sandbox.kill()
```

> [!TIP]
>
> Run `agy models` in the sandbox to list the models available to your key, and select one with `--model`. Free-tier keys may have no quota for the default Pro model; pick a Flash model in that case.

### Example: work on a cloned repository

```typescript JavaScript & TypeScript theme={"theme":"css-variables"}
import { Sandbox } from 'e2b'

const sandbox = await Sandbox.create('antigravity', {
  envs: { GEMINI_API_KEY: process.env.GEMINI_API_KEY! },
  timeoutMs: 900_000,
})

await sandbox.commands.run(
  'git clone --depth 1 https://x-access-token:$GITHUB_TOKEN@github.com/your-org/your-repo.git /home/user/repo',
  { envs: { GITHUB_TOKEN: process.env.GITHUB_TOKEN! } }
)

const result = await sandbox.commands.run(
  `cd /home/user/repo && agy --dangerously-skip-permissions -p "Add error handling to all API endpoints"`,
  { timeoutMs: 0, onStdout: (data) => { process.stdout.write(data) } }
)

const diff = await sandbox.commands.run('cd /home/user/repo && git diff')
console.log(diff.stdout)

await sandbox.kill()
```

```python Python theme={"theme":"css-variables"}
import os
from e2b import Sandbox

sandbox = Sandbox.create("antigravity", envs={
    "GEMINI_API_KEY": os.environ["GEMINI_API_KEY"],
}, timeout=900)

sandbox.commands.run(
    "git clone --depth 1 https://x-access-token:$GITHUB_TOKEN@github.com/your-org/your-repo.git /home/user/repo",
    envs={"GITHUB_TOKEN": os.environ["GITHUB_TOKEN"]},
)

result = sandbox.commands.run(
    'cd /home/user/repo && agy --dangerously-skip-permissions -p "Add error handling to all API endpoints"',
    timeout=0,
    on_stdout=lambda data: print(data, end=""),
)

diff = sandbox.commands.run("cd /home/user/repo && git diff")
print(diff.stdout)

sandbox.kill()
```

## Streaming output

Use `--output-format stream-json` to get a real-time JSONL event stream: an `init` event, `step_update` events for tool calls and response text, and a final `result` event with the status, response, and token usage.

```typescript JavaScript & TypeScript theme={"theme":"css-variables"}
import { Sandbox } from 'e2b'

const sandbox = await Sandbox.create('antigravity', {
  envs: { GEMINI_API_KEY: process.env.GEMINI_API_KEY! },
  timeoutMs: 600_000,
})

// Output arrives in chunks that can split a JSON line, so buffer until a newline
let buffer = ''
const handleOutput = (data: string) => {
  buffer += data
  const lines = buffer.split('\n')
  buffer = lines.pop() ?? ''
  for (const line of lines.filter(Boolean)) {
    const event = JSON.parse(line)
    const step = event.step_update
    if (step?.step_type === 'tool' && step.state === 'DONE') {
      console.log(`[tool] ${step.tool_name}`)
    } else if (step?.text_delta) {
      process.stdout.write(step.text_delta)
    } else if (event.result) {
      console.log(`\n[done] ${event.result.usage.total_tokens} tokens`)
    }
  }
}

await sandbox.commands.run(
  `agy --dangerously-skip-permissions --output-format stream-json -p "Create hello.py that prints hello, then run it"`,
  { timeoutMs: 0, onStdout: handleOutput }
)

await sandbox.kill()
```

```python Python theme={"theme":"css-variables"}
import json
import os
from e2b import Sandbox

sandbox = Sandbox.create("antigravity", envs={
    "GEMINI_API_KEY": os.environ["GEMINI_API_KEY"],
}, timeout=600)

# Output arrives in chunks that can split a JSON line, so buffer until a newline
buffer = ""

def handle_output(data):
    global buffer
    buffer += data
    *lines, buffer = buffer.split("\n")
    for line in filter(None, lines):
        event = json.loads(line)
        step = event.get("step_update", {})
        if step.get("step_type") == "tool" and step.get("state") == "DONE":
            print(f"[tool] {step['tool_name']}")
        elif step.get("text_delta"):
            print(step["text_delta"], end="")
        elif "result" in event:
            print(f"\n[done] {event['result']['usage']['total_tokens']} tokens")

sandbox.commands.run(
    'agy --dangerously-skip-permissions --output-format stream-json -p "Create hello.py that prints hello, then run it"',
    timeout=0,
    on_stdout=handle_output,
)

sandbox.kill()
```

See the [headless mode reference](https://antigravity.google/docs/cli/headless) for the `json` output envelope, `--json-schema` structured output, and resuming a conversation with `--conversation`.

## Build a custom template

If you need to customize the environment (e.g. pre-install dependencies, add config files), build your own template on top of the pre-built `antigravity` template.

```typescript JavaScript & TypeScript theme={"theme":"css-variables"}
// template.ts
import { Template } from 'e2b'

export const template = Template()
  .fromTemplate('antigravity')
```

```python Python theme={"theme":"css-variables"}
# template.py
from e2b import Template

template = (
    Template()
    .from_template("antigravity")
)
```

```typescript JavaScript & TypeScript theme={"theme":"css-variables"}
// build.ts
import { Template, defaultBuildLogger } from 'e2b'
import { template as antigravityTemplate } from './template'

await Template.build(antigravityTemplate, 'my-antigravity', {
  cpuCount: 2,
  memoryMB: 2048,
  onBuildLogs: defaultBuildLogger(),
})
```

```python Python theme={"theme":"css-variables"}
# build.py
from e2b import Template, default_build_logger
from template import template as antigravity_template

Template.build(antigravity_template, "my-antigravity",
    cpu_count=2,
    memory_mb=2048,
    on_build_logs=default_build_logger(),
)
```

Run the build script to create the template.

```bash JavaScript & TypeScript theme={"theme":"css-variables"}
npx tsx build.ts
```

```bash Python theme={"theme":"css-variables"}
python build.py
```

## Related guides

> [card] [Sandbox persistence](https://docs.e2b.dev/sandbox/persistence.md)
>
> Auto-pause, resume, and manage sandbox lifecycle

> [card] [SSH access](https://docs.e2b.dev/sandbox/ssh-access.md)
>
> Connect to the sandbox via SSH for interactive sessions
