Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@ docs/
/.ripgreprc
/.zprofile
/.zshrc

# Local runtime state from memory/sidecar processes (not source)
/data/
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,19 @@ When you're happy with the result, merge the branch back to main from the sideba
- **macOS** — `.dmg` (universal)
- **Linux** — `.AppImage` or `.deb`

2. **Install at least one AI coding CLI:** [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [Codex CLI](https://github.com/openai/codex), [Gemini CLI](https://github.com/google-gemini/gemini-cli), or [Copilot CLI](https://docs.github.com/en/copilot/concepts/agents/about-copilot-cli)
2. **Install at least one AI coding CLI:** [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [Codex CLI](https://github.com/openai/codex), [Gemini CLI](https://github.com/google-gemini/gemini-cli), [Antigravity CLI](https://antigravity.google/), or [Copilot CLI](https://docs.github.com/en/copilot/concepts/agents/about-copilot-cli)

3. **Open Parallel Code**, point it at a git repo, and start dispatching tasks.

<details>
<summary><strong>Antigravity CLI: run natively, not in Docker isolation</strong></summary>

Antigravity (`agy`) signs in interactively and caches credentials in your OS keyring (Keychain/libsecret). The keyring cannot be reached from inside a Linux container — it needs a secret-service daemon the agent container doesn't run — and `agy` has no API-key fallback, so **Docker-isolated Antigravity tasks cannot authenticate. Run Antigravity as a native (non-Docker) task.**

The bundled image still ships the `agy` binary, and `~/.gemini/antigravity-cli` (settings/plugins) is shared when "Share agent auth" is enabled, so Docker support is forward-compatible if a file-based or API-key auth path appears later.

</details>

<details>
<summary><strong>Build from source</strong></summary>

Expand Down
8 changes: 8 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ RUN ln -sf "$(command -v fdfind)" /usr/local/bin/fd 2>/dev/null || true
# AI agent CLIs — must be present so Docker-mode tasks can execute them
RUN npm install -g @anthropic-ai/claude-code @openai/codex @google/gemini-cli opencode-ai

# Antigravity CLI (agy) — distributed as a Go binary via the official installer
# (not on npm). The installer's `--dir` flag drops the binary straight into a
# system path on PATH, so the non-root `agent` user that runs containers resolves
# it. A failed fetch makes the build step fail, surfacing the break at image build
# time rather than at task launch.
RUN curl -fsSL https://antigravity.google/cli/install.sh | bash -s -- --dir /usr/local/bin \
&& agy --version

# Default git config so commits work out of the box inside containers
RUN git config --system init.defaultBranch main \
&& git config --system advice.detachedHead false
Expand Down
11 changes: 11 additions & 0 deletions electron/ipc/agents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ const DEFAULT_AGENTS: AgentDef[] = [
// settle before sending, without being so long that the user notices the wait.
prompt_ready_delay_ms: 1_000,
},
{
id: 'antigravity',
name: 'Antigravity CLI',
command: 'agy',
args: [],
resume_args: ['-c'],
skip_permissions_args: ['--dangerously-skip-permissions'],
description: "Google's Antigravity CLI agent (successor to Gemini CLI)",
// Antigravity paints a TUI that needs a beat to settle before auto-send.
prompt_ready_delay_ms: 1_000,
},
];

async function isCommandAvailable(command: string): Promise<boolean> {
Expand Down
1 change: 1 addition & 0 deletions electron/ipc/pty.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ describe('spawnAgent docker mode', () => {
['gemini', '.gemini'],
['opencode', '.config/opencode'],
['copilot', '.config/github-copilot'],
['agy', '.gemini/antigravity-cli'],
])(
'%s bind-mounts a user-owned host directory when shareDockerAgentAuth is enabled',
(command, relDir) => {
Expand Down
1 change: 1 addition & 0 deletions electron/ipc/pty.ts
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,7 @@ const AGENT_CONFIG_DIRS: Record<string, string[]> = {
gemini: ['.gemini'],
opencode: ['.config/opencode'],
copilot: ['.config/github-copilot'],
agy: ['.gemini/antigravity-cli'],
};

// Config files (not directories) each agent CLI uses for auth, relative to HOME.
Expand Down
17 changes: 16 additions & 1 deletion electron/mcp/agent-args.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { describe, expect, it } from 'vitest';

import { buildCodexMcpConfigOverride, buildMcpLaunchArgs, isCodexCommand } from './agent-args.js';
import {
buildCodexMcpConfigOverride,
buildMcpLaunchArgs,
isAntigravityCommand,
isCodexCommand,
} from './agent-args.js';

const config = {
mcpServers: {
Expand Down Expand Up @@ -50,4 +55,14 @@ describe('MCP agent launch args', () => {
'/tmp/config.json',
]);
});

it('detects antigravity commands by executable name', () => {
expect(isAntigravityCommand('agy')).toBe(true);
expect(isAntigravityCommand('/home/agent/.local/bin/agy')).toBe(true);
expect(isAntigravityCommand('claude')).toBe(false);
});

it('emits no --mcp-config for Antigravity', () => {
expect(buildMcpLaunchArgs('agy', '/tmp/config.json', config)).toEqual([]);
});
});
8 changes: 8 additions & 0 deletions electron/mcp/agent-args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export function isCodexCommand(command: string): boolean {
return command.split('/').pop()?.includes('codex') === true;
}

export function isAntigravityCommand(command: string): boolean {
return command.split('/').pop() === 'agy';
}

function tomlString(value: string): string {
return JSON.stringify(value);
}
Expand Down Expand Up @@ -41,5 +45,9 @@ export function buildMcpLaunchArgs(
if (isCodexCommand(command)) {
return ['--config', buildCodexMcpConfigOverride(config)];
}
// Antigravity (agy) has no `--mcp-config` flag; emit nothing so launch is not broken.
if (isAntigravityCommand(command)) {
return [];
}
return configPath ? ['--mcp-config', configPath] : [];
}
2 changes: 2 additions & 0 deletions openspec/changes/add-antigravity-agent/.openspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-05-29
169 changes: 169 additions & 0 deletions openspec/changes/add-antigravity-agent/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
## Context

parallel-code launches coding-agent CLIs in node-pty terminals, one per git
worktree. The agent catalog is a static `DEFAULT_AGENTS` array in
`electron/ipc/agents.ts`; each entry is an `AgentDef` (`{ id, name, command,
args, resume_args, skip_permissions_args, description, prompt_ready_delay_ms?,
mcp_config_flag? }`). `AgentDef` is **duplicated** — the canonical copy lives in
`src/ipc/types.ts` and a structurally identical copy in `electron/ipc/agents.ts`.

At launch the agent is started **interactively** with `args` (the initial prompt
is typed into the TUI afterward via bracketed paste, not passed as a CLI argument).
`buildTaskAgentArgs` appends `resume_args` on resume and `skip_permissions_args`
when the task opts into skip-permissions. Availability is probed with `which
<command>` (TTL-cached). The renderer's `AgentSelector` renders each agent as a
text button keyed off `agent.id` with **no per-agent icon or color map**, so a new
agent needs no front-end asset.

The Electron main process resolves the user's full login+interactive shell PATH
(`zsh/bash -ilc`, `electron/main.ts:52`), so binaries on `~/.local/bin` are
discoverable as long as the user's shell profile adds that directory.

For Docker-isolated tasks, `AGENT_CONFIG_DIRS` (`electron/ipc/pty.ts:707`) maps a
command basename to host config dirs that are bind-mounted into the container when
"Share agent auth" is enabled. The bundled `docker/Dockerfile` installs the other
agents via `npm install -g`.

Ground truth for `agy` (verified against the installed binary, v1.0.3 — `agy
--help`):

- Interactive launch: `agy`
- Resume most recent conversation: `-c` / `--continue`
- Skip permissions: `--dangerously-skip-permissions`
- Sandbox (`nsjail`/`sandbox-exec`) is **opt-in** via `--sandbox`; off by default.
- Non-interactive print mode: `-p` / `--print` (not used by this app).
- Config/app-data dir is `~/.gemini/antigravity-cli` (settings.json, mcp_config.json,
plugins/, conversations/) — verified against the installed binary; it is **not**
`~/.config/antigravity`. Interactive auth is OAuth cached in the OS keyring
(Keychain/libsecret); `agy` has **no** API-key environment fallback (the
`ANTIGRAVITY_API_KEY` variable is not consumed — confirmed behaviorally).

## Goals / Non-Goals

**Goals:**

- Add `agy` as a built-in default agent with correct launch/resume/skip-permission
args, fully working on native (macOS/Linux) tasks.
- Ship the Docker bits that are additive and forward-compatible (bundle the `agy`
binary, mount its config dir), while documenting clearly — in user-facing docs and
the PR — that Docker-isolated Antigravity cannot authenticate and must be run
natively.
- Keep Gemini CLI fully intact.
- Make the integration data-driven — no new IPC, no per-id special casing.

**Non-Goals:**

- Removing or deprecating Gemini CLI (separate future change).
- Wiring `agy`'s OAuth keyring credentials into Docker containers (not file-mountable).
- Supporting `agy --sandbox` inside Docker (nested sandbox; left off by default).
- A custom agent-branding/icon system.

## Decisions

**1. Launch interactively with `command: "agy"`, `args: []`.**
Matches every other agent: the app types the prompt into the live TUI rather than
passing `-p`. `-p`/`--print` is one-shot and prints-then-exits, which would defeat
the interactive session model. Rejected `-i`/`--prompt-interactive` because the app
already delivers the prompt via terminal input after a readiness delay.

**2. `resume_args: ["-c"]`, `skip_permissions_args: ["--dangerously-skip-permissions"]`.**
Both verified against the installed binary. `--dangerously-skip-permissions` is
real (it coincidentally matches Claude's flag); the Gemini `--yolo` flag does **not**
carry over.

**3. `prompt_ready_delay_ms` set to a modest stability delay (~1000 ms), mirroring
Copilot.** `agy`'s first interactive run shows onboarding (Google sign-in, theme,
file-permission prompts); steady-state runs still paint a TUI that needs a beat to
settle before auto-send. This only tunes the readiness recheck; it does not bypass
first-run OAuth, which is interactive regardless (same caveat as other agents).

**4. Omit `mcp_config_flag` AND exclude `agy` from the `--mcp-config` paths.** `agy`
has no `--mcp-config` flag (MCP is configured via plugins/config), so passing it would
break launch. Two code paths can emit it and both must exclude `agy`:

- `legacyMcpConfigArgs` (`src/lib/agent-args.ts`) injects `--mcp-config <path>` for any
non-Codex command when a task carries a legacy `mcpConfigPath` and no `mcpLaunchArgs`
— reachable only by older persisted tasks.
- `buildMcpLaunchArgs` (`electron/mcp/agent-args.ts`, imported by `coordinator.ts`) is
the modern per-agent builder used by coordinator/MCP tasks.
Excluding `agy` (the same way Codex is excluded via `isCodexCommand`) is the safe
default: it guarantees `agy` is never handed an unsupported flag regardless of whether
a given task takes the legacy or modern path. Leaving `mcp_config_flag` unset keeps it
off the flag-driven path as well.

**5. Mount the config dir; Docker auth is unsupported (native only).** Add
`agy: ['.gemini/antigravity-cli']` to `AGENT_CONFIG_DIRS` so settings/plugins are
shared. Auth, however, cannot work in Docker: login credentials live in the host OS
keyring (Keychain/libsecret), which needs a secret-service daemon the agent container
does not run, and `agy` has no API-key environment fallback (the `ANTIGRAVITY_API_KEY`
claim was unverified and is false — behaviorally `agy` still enters OAuth with it set).
So Antigravity is documented as a native-only agent. The mount + bundled binary are
kept because they are additive and forward-compatible if a file-based or API-key auth
path appears.

**6. Install `agy` in the Docker image via the official installer, not npm.** `agy`
ships as a Go binary from `https://antigravity.google/cli/install.sh`, so the existing
`npm install -g` line cannot add it. The installer supports `-d|--dir`, so the step
installs straight to a system PATH dir:
`RUN curl -fsSL … | bash -s -- --dir /usr/local/bin && agy --version`. Verified the
script runs non-interactively when piped and `mkdir -p`s its target. The trailing
`agy --version` makes the build assert the install at image-build time.

## Risks / Trade-offs

- **Docker auth is impossible, not just limited** → `agy` login is keyring-only OAuth,
the container has no secret-service daemon, and there is no API-key fallback, so an
in-container `agy` cannot authenticate at all. Mitigation: native tasks work fully;
docs and PR state plainly that Antigravity is native-only and Docker isolation is
unsupported for it. The config-dir mount + bundled binary are kept as additive,
forward-compatible scaffolding, not a working auth path.
- **PATH discovery** → If the user never ran the installer's PATH line, `~/.local/bin`
is absent from the resolved shell PATH and `agy` shows "(not installed)". Mitigation:
this is the same behavior as any un-PATHed agent; the availability probe degrades
gracefully. Document running `agy install`.
- **`--mcp-config` injected by either MCP path** → `legacyMcpConfigArgs` (old tasks)
or `buildMcpLaunchArgs` (coordinator tasks) could hand `agy` an unknown flag and
break launch. Mitigation: exclude `agy` from both, the way Codex is excluded via
`isCodexCommand` — chosen as the safe default rather than relying on the legacy path
being unreachable. **Functional consequence (accepted):** `buildMcpLaunchArgs`
returning `[]` for `agy` means Antigravity tasks receive no parallel-code MCP
coordinator wiring — no in-`agy` subagent spawning. `agy` configures MCP via
plugins/config rather than a CLI flag, so coordinator integration is a separate
follow-up if desired; this change intentionally ships without it.
- **Installer-script drift in Docker builds** → Piping a remote installer into the
image is less reproducible and could break builds if the URL/format changes.
Mitigation: pin behavior to the documented installer; treat a failed fetch as a
build error so it is caught at image-build time, not at task launch.
- **Duplicate `AgentDef` definitions drift** → The entry and the type live in two
files. Mitigation: update both `electron/ipc/agents.ts` and `src/ipc/types.ts` in
lockstep; TypeScript strict build catches shape mismatches.

## Migration Plan

Additive and behind agent availability detection — no data migration. `agy` appears
in the selector only when the binary is on PATH; existing Gemini tasks are untouched.
Rollback is removing the `DEFAULT_AGENTS` entry (and the Dockerfile/ config-dir
lines); no persisted state depends on it.

## Resolved Decisions

- **Antigravity is native-only; Docker isolation is unsupported for it.** The earlier
`ANTIGRAVITY_API_KEY` Docker auth path was an unverified claim and is false. We keep
the additive Docker scaffolding (bundled `agy` binary, mounted
`~/.gemini/antigravity-cli` config dir) but document Antigravity as native-only.
Decided with the user after the Codex review surfaced the wrong config path and the
non-functional env var.
- **`agy` is excluded from both `--mcp-config` paths** (`legacyMcpConfigArgs` and
`buildMcpLaunchArgs`), as the safe default — no dependence on the legacy path being
unreachable.

## Open Questions

- In-container PATH is resolved: the installer's `--dir` flag installs `agy` to
`/usr/local/bin` (on PATH for the non-root `agent` user); the build-step
`agy --version` verifies it. A full `docker build` is the only step needing a Docker
daemon.
- **Untested:** whether `agy` can read a file-based credential it shares with the
Gemini CLI (`~/.gemini/gemini-credentials.json` / `google_accounts.json` sit in the
parent dir). If it does, that could become a real Docker auth path later. Not claimed
or relied on here.
50 changes: 50 additions & 0 deletions openspec/changes/add-antigravity-agent/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
## Why

Google is transitioning the Gemini CLI to the new **Antigravity CLI** (`agy`). On
**June 18, 2026** Gemini CLI stops serving Google AI Pro/Ultra and free Code Assist
users; it remains only on paid/enterprise API keys. Users who pick the "Gemini CLI"
agent today will increasingly hit a dead end, while `agy` becomes the supported
Google terminal agent. parallel-code should offer Antigravity CLI as a first-class
agent so users have a working migration path — without yet removing Gemini CLI,
which paid/enterprise users still rely on.

## What Changes

- Add **Antigravity CLI** (`agy`) as a built-in default agent alongside Claude Code,
Codex, Gemini CLI, OpenCode, and Copilot.
- Register its launch contract: interactive launch (`agy`), resume (`-c`), and
skip-permissions (`--dangerously-skip-permissions`).
- Add its config directory (`~/.gemini/antigravity-cli`) to the Docker shared-auth
mount map for settings/plugins, and document that Antigravity is **native-only**:
its login credentials live in the OS keyring (no secret-service daemon in the
container) and `agy` has no API-key env fallback, so Docker-isolated Antigravity
cannot authenticate.
- Install `agy` in the bundled Docker agent image (via the official installer script,
not npm) so the binary is present and the integration is forward-compatible.
- Keep Gemini CLI unchanged. **No removal, no breaking change.**

## Capabilities

### New Capabilities

- `agent-cli-registry`: The set of coding-agent CLIs parallel-code can launch, and the
per-agent contract (command, launch/resume/skip-permission arguments, availability
detection, config-directory auth mounting for Docker isolation) that governs how each
agent is started, resumed, and authenticated.

### Modified Capabilities

<!-- None: no existing capability spec governs the agent registry today. -->

## Impact

- **Code:** `electron/ipc/agents.ts` (`DEFAULT_AGENTS`), `src/ipc/types.ts` +
`electron/ipc/agents.ts` (`AgentDef` — duplicated, both updated),
`electron/ipc/pty.ts` (`AGENT_CONFIG_DIRS`), `docker/Dockerfile` (install `agy`).
- **No new IPC channels or payload-type changes** — Antigravity reuses the existing
data-driven `AgentDef` plumbing; the selector renders it from the agent list with no
per-id branding.
- **Dependencies:** the Docker image gains the `agy` binary fetched from
`https://antigravity.google/cli/install.sh`.
- **Tests:** agent-registry and persistence tests that enumerate agents may need the
new entry accounted for.
Loading
Loading