Combining All Four Antigravity Surfaces in One Project — Up to Running Your Own SDK Agent
How to split a single project across Antigravity 2.0, CLI, IDE, and SDK, and how to bridge between them — from diverging on design to converging on production, all the way to running a small custom agent with the Python SDK, with implementation included.
In the previous article I sorted Antigravity's four surfaces by the question of when to reach for which. In a real project, though, switching the entry point per phase flows much better than committing to a single surface. Here I will show you the exact combination I run as an indie developer, implementation and all.
The subject is a mundane indie scene: semi-automating the research behind Dolice Labs blog posts and the code checks that come with them. It is not a flashy setup, but it makes it easy to see how the four surfaces mesh, each in its own lane.
Split one project into four phases and assign a door to each
The first move is to cut the work into phases of different character. My baseline is this four-way split.
Diverge — widen what to build and research. Throw several research tasks in parallel with Antigravity 2.0.
Converge — actually write and tidy the code. Lock it down in the IDE, approving line by line.
Operate — verify behavior on the server. Run it headless with the CLI.
Automate — hand the repeating routine to your own agent. Build it with the SDK.
This split works because each surface's strength lines up cleanly with the phase's demand. Diverging wants parallelism, converging wants visual review, operating wants headless, automating wants code. Because they share a harness, crossing phases costs almost no re-setup.
Mind the bridge between diverging and converging
When I carry research from 2.0 into IDE implementation, slipping one step in between cuts down on accidents. My rule is to write the conclusions from 2.0 into a short NOTES.md in the repo before opening the IDE.
The reason: if the conclusions of several parallel tasks live only in my head, the IDE phase drops "which option did I settle on a moment ago." I skipped this a few times and paid with rework. The humble act of dumping the diverge output into text is what most reduces churn in the converge phase.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦A blueprint that splits one project into four phases and assigns 2.0 for diverging, IDE for converging, CLI for ops, SDK for automation
✦Minimal Python code to launch a local agent with the Antigravity SDK and run a repository-inspection task
✦Exception handling to stop an async agent safely, plus three production-deploy pitfalls and how to avoid them
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
The SDK sits at the center of the automate phase. The official Antigravity SDK is a Python library; you write an agent locally and run it on the same shared harness. I recommend starting from the smallest thing that just asks "what files are in the current directory?"
import asynciofrom google.antigravity import Agent, LocalAgentConfigasync def main(): config = LocalAgentConfig( system_instructions="You are an expert assistant for codebase navigation.", # api_key="your_api_key_here", ) async with Agent(config) as agent: response = await agent.chat("What files are in the current directory?") print(await response.text())if __name__ == "__main__": asyncio.run(main())
The point is wrapping the agent lifecycle in async with. An agent holds background sessions and file handles, so a context manager guarantees cleanup the moment the work ends. I recommend keeping your first one in exactly this shape.
Grow it toward your own routine
Once the minimal version runs, bend system_instructions toward your repeated work. In my case, I grew it into an agent that checks whether the code examples in an article actually run. The idea is to replace generic instructions with concrete checklist items.
INSTRUCTIONS = """\You are a code-example reviewer for a technical blog. Follow these steps.1. Extract the code blocks and detect the language2. Confirm the syntax holds together3. Flag clearly broken dependencies or undefined variables4. Propose a fix as the smallest possible diffReply as a bulleted list, avoid over-asserting, and give your reasoning."""async def review_snippet(code: str) -> str: config = LocalAgentConfig(system_instructions=INSTRUCTIONS) async with Agent(config) as agent: response = await agent.chat(f"Please review the following code:\n\n{code}") return await response.text()
Even a wrapper this thin automates one step I used to do by hand every time. I folded it into my pre-publish check and cut the per-article review time roughly in half by feel. The realistic on-ramp is not to chase a grand autonomous agent, but to offload exactly one small chore you already do daily.
Stop an async agent safely
The thing people stumble on most with the SDK is stopping the agent. When you cut a long-running async task short, neglecting cleanup leaves the session dangling. Alongside async with, keep explicit cancellation and a timeout on hand.
async def review_with_timeout(code: str, timeout: float = 30.0) -> str: try: return await asyncio.wait_for(review_snippet(code), timeout=timeout) except asyncio.TimeoutError: return "Review timed out. Split the code and retry." except Exception as e: return f"An error occurred during review: {e}"
Wrapping with asyncio.wait_for avoids the case where no response comes back and the process locks up. If you fold this into a production pipeline, the guarantee that it always returns in finite time is non-negotiable.
Three pitfalls that bite on production deploy
Moving a locally working agent straight to production, here are the three I actually hit.
First, API key handling. Always switch the commented-out api_key from the sample to reading an environment variable in production. Pushing it hardcoded trips secret scanning and invites accidents. I keep keys in environment variables and leave only placeholders in the repo.
Second, concurrency control. Throwing many snippets at once opens too many agent sessions in parallel and hits rate limits. Capping concurrency with asyncio.Semaphore is the practical workaround.
Third, failure fallbacks. Assume the agent's response may be empty or malformed, and prepare hold-and-retry paths. In my setup, if a review returns an error, I stop the article and route it to manual review — the safe side. I consider automation production-grade only when it is designed not to cause damage when it stops.
Where to start building
Trying to master all four surfaces at once will, paradoxically, stall you. My recommended order: run your daily work as a round trip between 2.0 and the IDE, add the CLI when server checks appear, and once a daily-repeated step comes into view, automate exactly one of them with the SDK.
I myself tried to build a grand agent platform up front, failed, and rebuilt from "delete one small daily chore." The strength of sharing a harness is precisely how easy this incremental growth becomes. Picture the one step you find most tedious today, and start building there.
Share
Thank You for Reading
Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.