Add Antigravity CLI provider#222
Open
gregce wants to merge 3 commits into
Open
Conversation
Add a new `antigravity` provider that converts Antigravity CLI (`agy`) sessions into SpecStory's unified session format, following the same flow as the existing providers (Claude Code, Cursor CLI, Codex CLI, Gemini CLI, Droid CLI, DeepSeek TUI). Antigravity stores each conversation as an append-only JSONL transcript at ~/.gemini/antigravity-cli/brain/<id>/.system_generated/logs/transcript_full.jsonl. The package parses those step-based transcripts, groups them into exchanges, attaches positionally-correlated tool results (including async task output), and renders Antigravity's tools (run_command, view_file, write_to_file, replace_file_content, grep_search, list_dir, ...) to markdown. Notable provider-specific handling: - Binary is `agy`; version via `agy --version`; resume via `--conversation`. - Tool results are separate transcript steps with no call->result id, so results are matched to their tool call by order (FIFO). - Transcripts carry no workspace field and history.jsonl only covers interactive sessions, so the workspace is resolved from history when available and otherwise inferred from tool-arg paths. Unscoped text-only sessions are returned only on explicit by-id retrieval, never in project-filtered bulk listings. - The watcher establishes fsnotify watches level-by-level as the deeply nested transcript directory is created (fsnotify is non-recursive). Also: register the provider in the factory, add antigravity_cmd config support, document the reverse-engineered on-disk format in docs/antigravity-format-spec.md, and update the README and changelog. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds a new Antigravity CLI provider to SpecStory CLI so sessions created by the agy binary can be detected, parsed from Antigravity’s on-disk JSONL transcripts, rendered into SpecStory’s unified markdown/session schema, and watched live for updates. This extends the existing provider ecosystem (Claude Code, Cursor CLI, Codex CLI, Gemini CLI, Droid CLI, DeepSeek TUI) with a new JSONL-based source under ~/.gemini/antigravity-cli/.
Changes:
- Register new
antigravityprovider in the SPI factory and addantigravity_cmdconfig support (README + config template + config accessors/tests). - Add
pkg/providers/antigravitycli/implementation: transcript parsing, workspace inference, tool rendering,agyexecution/resume handling, and fsnotify-based watcher for nested transcript paths. - Add extensive unit tests for the new provider components, plus a reverse-engineered on-disk format spec document.
3 most important improvements:
- Watcher safety/robustness:
processTranscriptFileshould stop early whenresolveTranscriptPath()returns("", nil)to avoid parsing unrelatedtranscript*.jsonlfiles while temporarily watching an ancestor directory (this can lead to incorrect parsing/emission). - Prompt-cleaning correctness: tighten
metadataBlockReso it only removes correctly paired tags (use a backreference or separate regexes) to avoid accidental deletion when mixed blocks appear. - Operational/performance consideration (optional): consider streaming transcript parsing (scanner/decoder) instead of
os.ReadFile+bytes.Splitfor very large transcripts/tool outputs to reduce peak memory usage during sync/watch scans.
Unit test opportunities (targeted):
- Add a watcher test ensuring
processTranscriptFiledoes not parseevent.NamewhenresolveTranscriptPath(conversationID)indicates the conversation has no usable transcript in the expected Antigravity directory layout. - Add a prompt-cleaning test case demonstrating that mismatched/mixed metadata tags don’t cause unintended content removal once the regex is fixed.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| specstory-cli/README.md | Document Antigravity support and add antigravity_cmd config option. |
| specstory-cli/pkg/spi/factory/registry.go | Register the new antigravity provider in the provider registry. |
| specstory-cli/pkg/providers/antigravitycli/watcher.go | fsnotify watcher for Antigravity’s nested transcript directories + dispatch. |
| specstory-cli/pkg/providers/antigravitycli/watcher_test.go | Tests for watcher helpers and modtime dedupe behavior. |
| specstory-cli/pkg/providers/antigravitycli/transcript_parser.go | Parse transcript JSONL + history index + async task logs; build metadata. |
| specstory-cli/pkg/providers/antigravitycli/transcript_parser_test.go | Tests for transcript/history/task parsing and metadata extraction. |
| specstory-cli/pkg/providers/antigravitycli/provider.go | Provider implementation: check/detect/list/get/watch/exec integration. |
| specstory-cli/pkg/providers/antigravitycli/provider_test.go | Provider behavior tests (check helpers, detect/get/list/watch). |
| specstory-cli/pkg/providers/antigravitycli/path_utils.go | Resolve Antigravity disk layout paths and list/locate transcripts. |
| specstory-cli/pkg/providers/antigravitycli/path_utils_test.go | Tests for path resolution, transcript listing, and path containment. |
| specstory-cli/pkg/providers/antigravitycli/markdown_tools.go | Tool classification + input/output formatting + path hint extraction. |
| specstory-cli/pkg/providers/antigravitycli/markdown_tools_test.go | Tests for tool classification/formatting and path hint extraction. |
| specstory-cli/pkg/providers/antigravitycli/antigravity_exec.go | Launch agy, parse custom command, and inject resume args safely. |
| specstory-cli/pkg/providers/antigravitycli/antigravity_exec_test.go | Tests for command parsing, tilde expansion, and resume arg handling. |
| specstory-cli/pkg/providers/antigravitycli/agent_session.go | Convert transcript steps into unified SessionData/Exchanges/Tools schema. |
| specstory-cli/pkg/providers/antigravitycli/agent_session_test.go | Tests for exchange grouping, tool result attachment, workspace inference. |
| specstory-cli/pkg/config/config.go | Add antigravity_cmd to providers config and expose via GetProviderCmd. |
| specstory-cli/pkg/config/config_test.go | Extend config tests to cover deepseek + antigravity provider commands. |
| specstory-cli/docs/antigravity-format-spec.md | Reverse-engineered Antigravity on-disk format spec for maintainers. |
| specstory-cli/changelog.md | Changelog entry for Antigravity CLI support. |
Comment on lines
+191
to
+196
| if preferredPath, err := resolveTranscriptPath(conversationID); err == nil && preferredPath != "" { | ||
| transcriptPath = preferredPath | ||
| } else if err != nil { | ||
| slog.Debug("antigravity: unable to resolve preferred transcript", "conversationId", conversationID, "error", err) | ||
| return | ||
| } |
| // <ADDITIONAL_METADATA> / <USER_SETTINGS_CHANGE> blocks that must not be shown. | ||
| var ( | ||
| userRequestRe = regexp.MustCompile(`(?s)<USER_REQUEST>(.*?)</USER_REQUEST>`) | ||
| metadataBlockRe = regexp.MustCompile(`(?s)<(ADDITIONAL_METADATA|USER_SETTINGS_CHANGE|SYSTEM_MESSAGE)>.*?</(ADDITIONAL_METADATA|USER_SETTINGS_CHANGE|SYSTEM_MESSAGE)>`) |
Comment on lines
+492
to
+505
| func stripFileURI(value string) string { | ||
| trimmed := strings.TrimSpace(value) | ||
| if !strings.HasPrefix(trimmed, "file:") { | ||
| return trimmed | ||
| } | ||
| parsed, err := url.Parse(trimmed) | ||
| if err == nil && parsed.Scheme == "file" { | ||
| if decoded, err := url.PathUnescape(parsed.Path); err == nil { | ||
| return decoded | ||
| } | ||
| return parsed.Path | ||
| } | ||
| return strings.TrimPrefix(trimmed, "file://") | ||
| } |
Comment on lines
+16
to
+55
| // Unknown tools fall back to "generic" (not "unknown") so that tools we know the | ||
| // provider can emit still render sensibly. | ||
| func classifyToolType(name string) string { | ||
| writeTools := map[string]bool{ | ||
| "write_to_file": true, "replace_file_content": true, | ||
| "create_file": true, "edit_file": true, "apply_patch": true, | ||
| } | ||
| readTools := map[string]bool{ | ||
| "view_file": true, "read_file": true, | ||
| "list_dir": true, "read_directory": true, | ||
| } | ||
| searchTools := map[string]bool{ | ||
| "grep_search": true, "codebase_search": true, "file_search": true, | ||
| "find_files": true, "web_search": true, "read_url": true, | ||
| "fetch_url": true, "execute_url": true, | ||
| } | ||
| shellTools := map[string]bool{ | ||
| "run_command": true, "run_terminal_command": true, | ||
| "exec": true, "shell": true, | ||
| } | ||
| taskTools := map[string]bool{ | ||
| "update_plan": true, "todo_write": true, "create_plan": true, | ||
| "task_list": true, | ||
| } | ||
|
|
||
| switch { | ||
| case writeTools[name]: | ||
| return schema.ToolTypeWrite | ||
| case readTools[name]: | ||
| return schema.ToolTypeRead | ||
| case searchTools[name]: | ||
| return schema.ToolTypeSearch | ||
| case shellTools[name]: | ||
| return schema.ToolTypeShell | ||
| case taskTools[name]: | ||
| return schema.ToolTypeTask | ||
| default: | ||
| return schema.ToolTypeGeneric | ||
| } | ||
| } |
Comment on lines
+40
to
+41
| // maxTranscriptLineSize bounds JSONL sidecar scanning well above bufio's 64KB | ||
| // default while still placing a hard cap on malformed lines. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
cc @belucid 👋
What this is (the human version)
Google just shipped Antigravity CLI — the terminal coding agent that replaces Gemini CLI, invoked as the
agybinary (docs). This PR teaches the SpecStory CLI to capture those sessions, so anything you do withagyshows up as clean markdown history (and syncs to SpecStory Cloud) exactly like Claude Code, Cursor CLI, Codex CLI, Gemini CLI, Droid CLI, and DeepSeek TUI already do.In other words:
specstory sync antigravitynow works. Run Antigravity in your project, then sync, and you get a readable transcript of your prompts, the model's reasoning, every tool call, and the results.How it works
Antigravity stores each conversation as an append-only JSONL transcript buried at:
The new
antigravityprovider parses those step-by-step transcripts, groups them into conversation turns, stitches each tool result back onto the tool call that produced it, and renders Antigravity's tools (run_command,view_file,write_to_file,replace_file_content,grep_search,list_dir, …) into the unified SpecStory markdown format.The interesting/tricky bits
A few things made Antigravity different from every provider we've added so far — worth a look in review:
agy(notantigravity); version viaagy --version, resume via--conversation <id>.tasks/task-N.log) is folded back in too.history.jsonlonly logs interactive sessions (notagy -pprint mode). So the workspace is taken from history when available and otherwise inferred from the absolute paths the tools touched. Text-only sessions with no recoverable workspace are returned only on explicit-s <id>requests, never in project-filtered bulk listings (so they can't pollute unrelated projects).The full reverse-engineered on-disk format is documented in
docs/antigravity-format-spec.mdfor future maintainers.What's included
pkg/providers/antigravitycli/package (modeled on thedeepseektuiprovider), with a test file per source file (~70 cases).antigravity_cmdconfig support.docs/antigravity-format-spec.md, plus README + changelog updates.Testing
agysessions (interactiverun_command, file edits/reads/search, multi-turn continuations, failed commands, and text-only sessions) viaspecstory sync antigravity -s <id> --print.specstory check antigravitydetects the real binary (v1.0.2).go build ./...,go vet ./...,golangci-lint run ./...(0 issues), andgo test ./...(all pass) are green.🤖 Generated with Claude Code