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
12 changes: 11 additions & 1 deletion docs/parser-documentation/access-recipes/14-antigravity.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
## Raw Sources

- Primary root: `~/.gemini/antigravity/`
- Session discovery: `conversations/*.pb`, `brain/<id>/`, and `state.vscdb` trajectory summaries.
- CLI root: `~/.gemini/antigravity-cli/`
- Session discovery: CLI `conversations/*.db`, IDE `conversations/*.pb`, `brain/<id>/`, and `state.vscdb` trajectory summaries.
- Context extraction:
- CLI: read-only SQLite step extraction from `antigravity-cli/conversations/<id>.db`.
- Offline: `brain/<id>/task.md`, `implementation_plan.md`, `walkthrough.md`, and `.resolved*` variants.
- Live: local Antigravity language-server RPC when the app is running.
- Legacy fallback: chat-shaped JSON/JSONL under `code_tracker/`; snapshot-only files are ignored.
Expand All @@ -14,10 +16,17 @@
### Inspect current session IDs

```bash
find ~/.gemini/antigravity-cli/conversations -maxdepth 1 -name '*.db' -print
find ~/.gemini/antigravity/conversations -maxdepth 1 -name '*.pb' -print
find ~/.gemini/antigravity/brain -maxdepth 1 -type d -print
```

### Resume a CLI conversation natively

```bash
agy --conversation <conversation-id>
```

### Inspect offline handoff artifacts

```bash
Expand All @@ -41,6 +50,7 @@ find ~/.gemini/antigravity/code_tracker -type f \( -name '*.json' -o -name '*.js
## Current Parser Comparison

- The parser now indexes current Antigravity installs even when `code_tracker` contains only file snapshots.
- Antigravity CLI conversations are indexed from `~/.gemini/antigravity-cli/conversations/*.db`.
- Offline handoffs are artifact-backed and explicitly note that full raw transcript extraction requires live Antigravity.
- Legacy JSONL remains supported only for files with real user/assistant chat entries.

Expand Down
6 changes: 5 additions & 1 deletion docs/parser-documentation/storage-format/14-antigravity.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ Accessed: 2026-04-28
## Observed Storage

- Root: `~/.gemini/antigravity/`
- CLI root: `~/.gemini/antigravity-cli/`
- CLI conversations: `antigravity-cli/conversations/<conversation-id>.db`
- Persisted conversation IDs: `conversations/*.pb`
- Handoff artifacts: `brain/<conversation-id>/task.md`, `implementation_plan.md`, `walkthrough.md`, plus `.resolved*` variants.
- UI/index metadata: Antigravity global storage SQLite at `~/Library/Application Support/Antigravity/User/globalStorage/state.vscdb` on macOS.
- Auxiliary local data: `code_tracker/`, `browser_recordings/`, `implicit/`, `knowledge/`, and account/setting files.

## Parser Behavior

- `src/parsers/antigravity.ts` now discovers sessions from the union of `conversations/*.pb`, `brain/<id>/`, `state.vscdb` trajectory summaries, and optional live language-server RPC.
- `src/parsers/antigravity.ts` now discovers sessions from the union of CLI `conversations/*.db`, IDE `conversations/*.pb`, `brain/<id>/`, `state.vscdb` trajectory summaries, and optional live language-server RPC.
- CLI `.db` extraction reads `steps` read-only through `node:sqlite` and decodes embedded protobuf/JSON strings best-effort for messages and tool activity.
- `code_tracker/` is no longer treated as canonical. It is parsed only as a legacy fallback when a file actually contains chat-shaped `{type, content, timestamp}` records.
- Offline extraction does not decrypt `.pb`; it builds a useful handoff from brain artifacts and state metadata.
- When Antigravity is running, the parser attempts read-only RPC extraction for full steps, messages, tool activity, and modified files.
Expand All @@ -26,6 +29,7 @@ Accessed: 2026-04-28
## Direct Access Recipe

```bash
find ~/.gemini/antigravity-cli/conversations -name '*.db'
find ~/.gemini/antigravity/conversations -name '*.pb'
find ~/.gemini/antigravity/brain -maxdepth 2 -type f | head -n 80
sqlite3 "$HOME/Library/Application Support/Antigravity/User/globalStorage/state.vscdb" \
Expand Down
81 changes: 81 additions & 0 deletions src/__tests__/antigravity-parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ function makeRoot(): string {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'continues-antigravity-'));
tempRoots.push(root);
vi.stubEnv('ANTIGRAVITY_HOME', root);
vi.stubEnv('ANTIGRAVITY_CLI_HOME', path.join(root, 'missing-antigravity-cli'));
vi.stubEnv('ANTIGRAVITY_STATE_DB', path.join(root, 'missing-state.vscdb'));
vi.stubEnv('ANTIGRAVITY_DISABLE_RPC', '1');
return root;
Expand Down Expand Up @@ -120,6 +121,28 @@ function createStateDb(dbPath: string, key: string, value: string): void {
}
}

function createCliConversationDb(
dbPath: string,
rows: Array<{ idx: number; stepType: number; payload: string }>,
): void {
const sqliteModule = require('node:sqlite') as {
DatabaseSync: new (database: string) => SqliteDatabase;
};
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
const db = new sqliteModule.DatabaseSync(dbPath);
try {
db.exec(
'CREATE TABLE steps (idx integer PRIMARY KEY, step_type integer NOT NULL DEFAULT 0, metadata blob, task_details blob, render_info blob, step_payload blob)',
);
const insert = db.prepare('INSERT INTO steps (idx, step_type, step_payload) VALUES (?, ?, ?)');
for (const row of rows) {
insert.run(row.idx, row.stepType, Buffer.from(row.payload, 'utf8'));
}
} finally {
db.close();
}
}

afterEach(() => {
vi.unstubAllEnvs();
spawnMock.mockClear();
Expand Down Expand Up @@ -237,6 +260,64 @@ describe('Antigravity parser', () => {
expect(sessions[0].updatedAt.toISOString()).toBe(updatedAt.toISOString());
});

it('discovers Antigravity CLI sqlite conversations under antigravity-cli', async () => {
const root = makeRoot();
const cliRoot = path.join(root, 'antigravity-cli');
const id = 'dddddddd-bbbb-4ccc-8ddd-eeeeeeeeeeee';
vi.stubEnv('ANTIGRAVITY_CLI_HOME', cliRoot);
writeFile(path.join(cliRoot, 'cache', 'last_conversations.json'), JSON.stringify({ '/home/user/project': id }));
createCliConversationDb(path.join(cliRoot, 'conversations', `${id}.db`), [
{ idx: 0, stepType: 14, payload: 'Fix the CLI resume flow for Antigravity' },
{
idx: 1,
stepType: 15,
payload:
'I will inspect the resume adapter. run_command {"CommandLine":"git status --short","Cwd":"/home/user/project"}',
},
]);

const sessions = await parseAntigravitySessions();

expect(sessions).toHaveLength(1);
expect(sessions[0]).toMatchObject({
id,
source: 'antigravity',
cwd: '/home/user/project',
repo: 'user/project',
lines: 2,
summary: 'Fix the CLI resume flow for Antigravity',
originalPath: path.join(cliRoot, 'conversations', `${id}.db`),
});
});

it('extracts Antigravity CLI context from sqlite steps without launching the IDE', async () => {
const root = makeRoot();
const cliRoot = path.join(root, 'antigravity-cli');
const id = 'eeeeeeee-bbbb-4ccc-8ddd-eeeeeeeeeeee';
vi.stubEnv('ANTIGRAVITY_CLI_HOME', cliRoot);
createCliConversationDb(path.join(cliRoot, 'conversations', `${id}.db`), [
{ idx: 0, stepType: 14, payload: 'Fix the Antigravity CLI parser' },
{
idx: 1,
stepType: 15,
payload:
'I will inspect the registry. run_command {"CommandLine":"rg antigravity src/parsers/registry.ts","Cwd":"/home/user/project"}',
},
]);

const [session] = await parseAntigravitySessions();
const context = await extractAntigravityContext(session);

expect(context.recentMessages.map((message) => message.role)).toContain('user');
expect(context.recentMessages.map((message) => message.role)).toContain('assistant');
expect(
context.toolSummaries.some((summary) =>
summary.samples.some((sample) => sample.summary.includes('rg antigravity')),
),
).toBe(true);
expect(spawnMock).not.toHaveBeenCalled();
});

it('extracts an offline handoff from brain artifacts when live RPC is unavailable', async () => {
const root = makeRoot();
const id = 'cccccccc-bbbb-4ccc-8ddd-eeeeeeeeeeee';
Expand Down
32 changes: 32 additions & 0 deletions src/__tests__/forward-flags.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,37 @@ describe('cross-tool forwarding', () => {
expect(resolved.passthroughArgs).toEqual([]);
});

it('maps antigravity cli forwarding and resume commands to agy', () => {
const session: UnifiedSession = {
id: 'abc123456789',
source: 'antigravity',
cwd: '/tmp/project',
lines: 10,
bytes: 120,
createdAt: new Date('2026-02-20T00:00:00.000Z'),
updatedAt: new Date('2026-02-20T00:00:00.000Z'),
originalPath: '/tmp/session.db',
};

const resolved = resolveCrossToolForwarding('antigravity', {
rawArgs: ['--allow-all', '--sandbox', 'false', '--model', 'gemini-3-flash'],
});

expect(adapters.antigravity.binaryName).toBe('agy');
expect(adapters.antigravity.binaryFallbacks).toEqual(['antigravity']);
expect(adapters.antigravity.nativeResumeArgs(session)).toEqual(['--conversation', 'abc123456789']);
expect(getResumeCommand(session)).toBe(
'agy --conversation abc123456789 (or: antigravity --conversation abc123456789)',
);
expect(resolved.mappedArgs).toEqual([
'--dangerously-skip-permissions',
'--sandbox=false',
'--model',
'gemini-3-flash',
]);
expect(resolved.passthroughArgs).toEqual([]);
});

it('consumes unsupported approval and permission forwarding for opencode', () => {
const resolved = resolveCrossToolForwarding('opencode', {
rawArgs: ['--approval-mode', 'plan', '--permission-mode', 'plan'],
Expand All @@ -149,6 +180,7 @@ describe('cross-tool forwarding', () => {
expect(getDefaultHandoffInitArgs('kiro')).toEqual([]);
expect(getDefaultHandoffInitArgs('crush')).toEqual([]);
expect(getDefaultHandoffInitArgs('qwen-code')).toEqual([]);
expect(getDefaultHandoffInitArgs('antigravity')).toEqual([]);

expect(getDefaultHandoffInitArgs('codex')).toEqual([
'-c',
Expand Down
Loading