From 631d24bed5dd035ac2141afe3c929322ce05aeec Mon Sep 17 00:00:00 2001 From: Bet4 <0xbet4@gmail.com> Date: Sun, 14 Jun 2026 14:41:41 +0800 Subject: [PATCH] feat: add support for Antigravity CLI sessions --- DESIGN.md | 5 +- README.md | 4 +- docs/spool-positioning.md | 6 +- packages/app/src/main/acp.ts | 8 +- packages/app/src/main/index.ts | 6 +- packages/app/src/renderer/App.tsx | 2 + .../src/renderer/components/AiAnswerCard.tsx | 2 +- .../renderer/components/FragmentResults.tsx | 2 +- .../src/renderer/components/SettingsPanel.tsx | 7 +- .../app/src/renderer/i18n/locales/de.json | 2 +- .../app/src/renderer/i18n/locales/en.json | 2 +- .../app/src/renderer/i18n/locales/fr.json | 2 +- .../app/src/renderer/i18n/locales/ko.json | 2 +- .../src/renderer/lib/compose-from-session.ts | 1 + packages/app/src/shared/resumeCommand.test.ts | 2 + packages/app/src/shared/resumeCommand.ts | 1 + packages/app/src/shared/sessionSources.ts | 6 + packages/cli/src/commands/list.ts | 4 +- packages/cli/src/commands/search.ts | 4 +- packages/core/README.md | 4 +- packages/core/src/db/db.ts | 1 + packages/core/src/db/migration-v1-v3.test.ts | 2 +- packages/core/src/db/migration-v5.test.ts | 2 +- packages/core/src/db/queries.ts | 2 + packages/core/src/index.ts | 1 + packages/core/src/parsers/antigravity.test.ts | 205 ++++++++++++++++++ packages/core/src/parsers/antigravity.ts | 193 +++++++++++++++++ packages/core/src/sync/source-paths.ts | 42 +++- packages/core/src/sync/syncer.test.ts | 2 + packages/core/src/sync/syncer.ts | 14 +- packages/core/src/sync/watcher.test.ts | 5 +- packages/core/src/sync/watcher.ts | 4 +- packages/core/src/types.ts | 3 +- packages/landing/pages/blog/hello-spool.md | 2 +- .../landing/pages/docs/guides/data-sources.md | 1 + packages/landing/pages/docs/installation.md | 2 +- packages/landing/pages/docs/quick-start.md | 4 +- .../pages/docs/reference/configuration.md | 1 + packages/landing/pages/index.server.ts | 2 +- packages/redact/src/detectors.ts | 2 +- packages/share-kit/src/lib/types.ts | 2 +- skills/spool/SKILL.md | 6 +- 42 files changed, 522 insertions(+), 48 deletions(-) create mode 100644 packages/core/src/parsers/antigravity.test.ts create mode 100644 packages/core/src/parsers/antigravity.ts diff --git a/DESIGN.md b/DESIGN.md index 39d1ea37..55177554 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -1,7 +1,7 @@ # Design System — Spool ## Product Context -- **What this is:** A local AI session library — an Electron macOS app that collects, organizes, and lets you revisit every Claude Code, Codex, and Gemini session you've ever had. +- **What this is:** A local AI session library — an Electron macOS app that collects, organizes, and lets you revisit every Claude Code, Codex, Gemini, Antigravity, and OpenCode session you've ever had. - **Who it's for:** Developers who think with AI daily and have accumulated hundreds of sessions across multiple tools. The persona is overwhelmed by the archive itself, not only by re-explaining context. - **Space/industry:** Developer productivity / local-first tooling. Peers: Raycast, Spotlight, Obsidian, DevonThink — but none of them treat AI sessions as first-class library items. - **Project type:** macOS Electron app — sidebar + main pane shell, the shape of a library client. @@ -89,6 +89,7 @@ Only currently-supported agent sources are listed. New sources arrive via the da | Claude Code | `#C26A4E` | `#E89A7C` | | Codex CLI | `#4A9670` | `#7CC9A2` | | Gemini | `#5887D0` | `#8AB0E5` | +| Antigravity | `#6B5CA5` | `#9B8FD0` | | OpenCode | `#8A6F3D` | `#C9A761` | ### Semantic States @@ -282,5 +283,5 @@ In list contexts (Library Home, Project View, search results), trust the surroun | 2026-05-08 | Warm-tuned status colors replace Tailwind defaults | `green-400 / amber-400 / red-400` were the only un-tuned colors in the system. Replaced with warm green `#6BAF6B`, warm amber `#E4A640`, terracotta `#C95A4F` so status reads as part of the same palette. | | 2026-05-08 | Page title type added (20px) | Library Home and Settings need a real h1 — previous scale topped at sidebar wordmark 16px, leaving the home pane labelless. | | 2026-05-08 | First-person rule softened from "all metadata" to "where it adds signal" | Literal "You discussed this · Mar 15" prefix on every row was repetitive once a user had hundreds of sessions. Library context already conveys ownership; reserve first-person for empty states, detail headers, and confirmations. | -| 2026-05-08 | Source badge list trimmed to active agents only | Twitter / GitHub / YouTube / ChatGPT were carryover from the connector era. Spool ships only Claude Code / Codex CLI / Gemini today; new sources arrive via the daemon and get a row when shipped, not preemptively. | +| 2026-05-08 | Source badge list trimmed to active agents only | Twitter / GitHub / YouTube / ChatGPT were carryover from the connector era. Spool ships only Claude Code / Codex CLI / Gemini / Antigravity today; new sources arrive via the daemon and get a row when shipped, not preemptively. | | 2026-05-26 | Retire the "icons only 12/14/16/20, stroke 1.5" rule — describe reality instead | The 2026-05-08 lock never held: the renderer actually uses 11/12/13/14 (13 & 14 dominant) at strokes 1.6–1.8 (1.6 most common, 1.5 a minority). The aspirational whitelist was actively misleading new work into picking sizes that clashed with adjacent icons. Replaced with a role-based working set + a "match adjacent icons" rule. Sizes/strokes are now chosen for local consistency, not global uniformity. | diff --git a/README.md b/README.md index 56c0a37f..ae7b7305 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Your local AI session library. Spool

-Spool collects every Claude Code, Codex CLI, Gemini CLI, and OpenCode session you've ever had into a sidebar of projects you can browse, pin, and revisit. Press ⌘K to search across the whole archive. +Spool collects every Claude Code, Codex CLI, Gemini CLI, Antigravity CLI, and OpenCode session you've ever had into a sidebar of projects you can browse, pin, and revisit. Press ⌘K to search across the whole archive. > **Early stage.** Spool is under active development — expect rough edges. Feedback, bug reports, and ideas are very welcome via [Issues](https://github.com/paperboytm/spool/issues) or [Discord](https://discord.gg/aqeDxQUs5E). @@ -29,7 +29,7 @@ pnpm build Spool turns the pile of AI sessions sitting on your disk into a browsable library. - **Library shell** — sidebar of projects (derived from working-dir paths across agents) and a main pane that shows recent + pinned sessions for whatever you've selected -- **Session indexing** — watches Claude/Codex/Gemini session dirs and OpenCode's SQLite database in real time, including profile-based paths like `~/.claude-profiles/*/projects`, `~/.codex-profiles/*/sessions`, Gemini's project temp dirs under `~/.gemini/tmp/*/chats`, and `~/.local/share/opencode/opencode.db` +- **Session indexing** — watches Claude/Codex/Gemini/Antigravity session dirs and OpenCode's SQLite database in real time, including profile-based paths like `~/.claude-profiles/*/projects`, `~/.codex-profiles/*/sessions`, Gemini/Antigravity project temp dirs under `~/.gemini/tmp/*/chats` and `~/.gemini/antigravity-cli/brain/`, and `~/.local/share/opencode/opencode.db` - **Pin** — keep important sessions on top of their project and on the global Library Home - **⌘K search** — fast full-text search scoped to All or the current project; AI mode synthesizes answers across fragments - **Agent search** — a `/spool` skill inside Claude Code (and any ACP agent) feeds matching fragments back into your conversation diff --git a/docs/spool-positioning.md b/docs/spool-positioning.md index 151eab09..e9619a82 100644 --- a/docs/spool-positioning.md +++ b/docs/spool-positioning.md @@ -2,7 +2,7 @@ > **The missing search engine for your own AI sessions.** -Search your `[Claude Code sessions · Codex history · Gemini chats]` — locally. +Search your `[Claude Code sessions · Codex history · Gemini chats · Antigravity transcripts]` — locally. --- @@ -10,11 +10,11 @@ Search your `[Claude Code sessions · Codex history · Gemini chats]` — locall ### Your coding agent is already the best search engine you have. -Spool lets Claude Code, Codex, Gemini CLI, and any coding agent search your past sessions from a single search box. +Spool lets Claude Code, Codex, Gemini CLI, Antigravity CLI, and any coding agent search your past sessions from a single search box. ### Every agent session, indexed automatically. -Spool watches `~/.claude/`, `~/.codex/`, and Gemini CLI's `~/.gemini/tmp/*/chats` in real time. Every conversation you have with Claude Code, Codex, or Gemini CLI — searchable the moment it's written. +Spool watches `~/.claude/`, `~/.codex/`, Gemini CLI's `~/.gemini/tmp/*/chats`, and Antigravity CLI's `~/.gemini/antigravity-cli/brain/` in real time. Every conversation you have with Claude Code, Codex, Gemini CLI, or Antigravity CLI — searchable the moment it's written. ### Context that flows back in. diff --git a/packages/app/src/main/acp.ts b/packages/app/src/main/acp.ts index a93459f7..12335117 100644 --- a/packages/app/src/main/acp.ts +++ b/packages/app/src/main/acp.ts @@ -201,7 +201,7 @@ function ensureAgentSearchCwd(): string { * the Spool-authored session write. */ function agentIdToSource(agentId: string): SessionSource | null { - if (agentId === 'claude' || agentId === 'codex' || agentId === 'gemini' || agentId === 'opencode') return agentId + if (agentId === 'claude' || agentId === 'codex' || agentId === 'gemini' || agentId === 'antigravity' || agentId === 'opencode') return agentId return null } @@ -873,12 +873,12 @@ export class AcpManager { // after stripping the prelude only the bare query remains as the first // user message — clean derived title, clean FTS, clean session detail. const systemBody = [ - 'You have access to a local knowledge base called Spool that indexes the user\'s AI coding sessions (Claude Code, Codex CLI, Gemini CLI, OpenCode).', + 'You have access to a local knowledge base called Spool that indexes the user\'s AI coding sessions (Claude Code, Codex CLI, Gemini CLI, Antigravity CLI, OpenCode).', '', 'The database is at ~/.spool/spool.db (SQLite with FTS5). You can query it directly with the `sqlite3` CLI.', '', '── Schema ──', - ' sources(id, name TEXT, base_path TEXT) -- "claude", "codex", "gemini", or "opencode"', + ' sources(id, name TEXT, base_path TEXT) -- "claude", "codex", "gemini", "antigravity", or "opencode"', ' projects(id, source_id, slug, display_path, display_name, last_synced)', ' sessions(id, project_id, source_id, session_uuid TEXT, title TEXT, started_at TEXT, ended_at TEXT, message_count INT, has_tool_use INT)', ' messages(id, session_id, source_id, role TEXT, content_text TEXT, timestamp TEXT, tool_names TEXT)', @@ -893,7 +893,7 @@ export class AcpManager { '', 'Important:', '- Interpret the user\'s intent and decide what to search. Don\'t just match their exact words.', - '- If the user names a specific source (claude/codex/gemini/opencode), only return results from that source unless they explicitly ask for cross-source search.', + '- If the user names a specific source (claude/codex/gemini/antigravity/opencode), only return results from that source unless they explicitly ask for cross-source search.', '- For cross-source questions, first identify the relevant sources, then query each source separately, confirm hits or no-hits per source, and only then merge them into one answer.', '- For temporal queries ("what did I do recently"), use explicit date filters and be conservative when comparing times across different sources.', '- You may run multiple queries to find relevant information.', diff --git a/packages/app/src/main/index.ts b/packages/app/src/main/index.ts index ff3ec1b4..9682e20c 100644 --- a/packages/app/src/main/index.ts +++ b/packages/app/src/main/index.ts @@ -802,7 +802,7 @@ ipcMain.handle('spool:search', (_e, { query, limit = 10, source, onlyPinned, ide if (cached) return cached } - const sessionSource = source === 'claude' || source === 'codex' || source === 'gemini' || source === 'opencode' + const sessionSource = source === 'claude' || source === 'codex' || source === 'gemini' || source === 'antigravity' || source === 'opencode' ? source : undefined const results = searchFragments(db, query, { @@ -824,7 +824,7 @@ ipcMain.handle('spool:search-preview', (_e, { query, limit = 5, source }: { quer const cached = searchCache.get(cacheKey) if (cached) return cached - const sessionSource = source === 'claude' || source === 'codex' || source === 'gemini' || source === 'opencode' + const sessionSource = source === 'claude' || source === 'codex' || source === 'gemini' || source === 'antigravity' || source === 'opencode' ? source : undefined const fragments = searchSessionPreview(db, query, { @@ -983,11 +983,11 @@ ipcMain.handle('spool:force-resync-session', (_e, { sessionUuid }: { sessionUuid ipcMain.handle('spool:resume-cli', (_e, { sessionUuid, source, cwd }: { sessionUuid: string; source: string; cwd?: string }) => { try { + const session = getSessionWithMessages(db, sessionUuid)?.session const command = getSessionResumeCommand(source, sessionUuid) if (!command) { return { ok: false, error: `Session source "${source}" cannot be resumed from the CLI.` } } - const session = getSessionWithMessages(db, sessionUuid)?.session const resumeCwd = session ? resolveResumeWorkingDirectory(session) : resolveResumeWorkingDirectory({ diff --git a/packages/app/src/renderer/App.tsx b/packages/app/src/renderer/App.tsx index 126475d4..fb488d07 100644 --- a/packages/app/src/renderer/App.tsx +++ b/packages/app/src/renderer/App.tsx @@ -907,6 +907,7 @@ export default function App() { claudeCount={status?.claudeSessions ?? null} codexCount={status?.codexSessions ?? null} geminiCount={status?.geminiSessions ?? null} + antigravityCount={status?.antigravitySessions ?? null} opencodeCount={status?.opencodeSessions ?? null} themeEditor={themeEditor} onThemeEditorChange={setThemeEditor} @@ -1103,6 +1104,7 @@ export default function App() { claudeCount={status?.claudeSessions ?? null} codexCount={status?.codexSessions ?? null} geminiCount={status?.geminiSessions ?? null} + antigravityCount={status?.antigravitySessions ?? null} opencodeCount={status?.opencodeSessions ?? null} themeEditor={themeEditor} onThemeEditorChange={setThemeEditor} diff --git a/packages/app/src/renderer/components/AiAnswerCard.tsx b/packages/app/src/renderer/components/AiAnswerCard.tsx index 99014f33..81216339 100644 --- a/packages/app/src/renderer/components/AiAnswerCard.tsx +++ b/packages/app/src/renderer/components/AiAnswerCard.tsx @@ -109,7 +109,7 @@ export default function AiAnswerCard({ answer, streaming, agentName, sources, er )} {/* CTA — only shown when a resume target is wired (i.e. agent has a - source we persisted a session row for: claude/codex/gemini/opencode). */} + source we persisted a session row for: claude/codex/gemini/antigravity/opencode). */} {!streaming && answer && onResume && (