A fast, native macOS desktop client for talking to AI agents.
Circulo is built for marketers, product people, and designers — not for people who live in a terminal. The goal is a clean, structured chat that feels instant, plus a simple way to keep work in sessions and (when you want) group those sessions into projects.
Enough tools to do great work.
This repository is in pre-MVP planning. Product and engineering docs exist; the app binaries are not implemented yet. We build through OpenSpec changes, not by dumping an entire MVP into one branch.
Most agent clients are made for developers. Streaming is a wall of plain text. Organizing work means a flat dump of chats. The apps themselves often feel heavy.
Circulo is the opposite bet:
- A rich chat: Markdown, tool-call cards, diffs, and basic task lists — rendered while the agent is still streaming.
- Sessions first. A new session has no project. It lives in Circulo’s special Sessions folder and shows Without Folder until you pick a project folder in the composer.
- Sidebar sections. Today lists sessions you worked on today; Earlier lists older sessions. Each card shows name, folder (or Without Folder), and relative duration.
- Native and fast. GPUI + Rust. No Electron shell. Custom window chrome (traffic lights live in the sidebar).
- Modular. The UI never talks to an agent CLI. A small local daemon owns persistence and adapters. The first adapter is OpenCode.
| Item | State |
|---|---|
| Product definition | Current (Circulo-Project-Definition.md v0.8) |
| PRD / TRD / UX / Flows / Implementation | Current in docs/ |
| Engineering contract | AGENTS.md |
| Application code | MVP feature-complete: app, daemon, OpenCode adapter, persistence, settings, sidebar, composer, streaming. 153 unit/integration tests passing. |
| Platform | macOS first (Windows / Linux later) |
| MVP agent | OpenCode only |
Circulo is two of our processes. OpenCode is a third, external process.
circulo-app (GPUI) process 1
│
│ HTTPS + SSE
│ Circulo protocol (localhost)
▼
circulo-daemon process 2
SQLite · session store · adapters
│
│ HTTP + SSE
│ OpenCode server API
▼
opencode serve external
The app does not call OpenCode. The daemon does, through circulo-adapter-opencode. OpenCode’s own server (opencode serve) already speaks HTTP and SSE; we translate that into Circulo’s model.
If you only remember one sentence: UI → Circulo daemon → OpenCode.
- Native macOS window, hidden title bar, traffic lights + sidebar hide control aligned in the sidebar (a min rail when collapsed).
- Dark theme. English UI, with every string in a locale catalog so more languages can land later.
- New session → unassigned → appears in Today with Without Folder, unless the composer assigns a folder.
- Sidebar: Today and Earlier sections; each row shows name, folder or Without Folder, and relative session duration.
- Project folder is chosen in the composer when the chat starts, then locked. No worktree switching in this phase.
- Delete a project and its sessions go with it. Archive a project and restore it from Settings.
- Rich streaming chat: Markdown, tool cards, diffs, basic tasks.
- Local SQLite store.
- One provider: OpenCode.
Explicitly out of the MVP: other agents, interactive question cards, a plugin marketplace, collaboration, cloud sync, deep theming, Windows/Linux.
AGENTS.md How we work (read this before coding)
Circulo-Project-Definition.md Product source
README.md
LICENSE MIT
Cargo.toml Workspace
rust-toolchain.toml Pinned stable Rust
crates/
circulo-app/ UI process (no-op until app-shell)
circulo-daemon/ Daemon process (no-op until local-daemon-api)
circulo-core/
circulo-protocol/
circulo-adapter/
circulo-adapter-fake/
circulo-adapter-opencode/
circulo-persist/
circulo-i18n/
circulo-markdown/
docs/
openspec/
scripts/check-crate-boundaries.py
macOS. Install rustup if you do not have Rust. The repo pins stable via rust-toolchain.toml.
Building circulo-app needs Xcode (Metal). If xcrun metal fails, point at the full Xcode app:
export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developercargo build --workspace
cargo test --workspace
python3 scripts/check-crate-boundaries.pyBinaries:
cargo run -p circulo-app
cargo run -p circulo-daemoncirculo-app opens a dark GPUI window (custom title bar, sidebar rail). It does not talk to the daemon yet.
circulo-daemon listens on http://127.0.0.1:7432 (override with CIRCULO_DAEMON_ADDR, loopback only). It uses the fake adapter. TLS is not enabled yet. GET /v1/health is the smoke check.
OpenCode is not required until the OpenCode adapter change. UI work should use the fake adapter so the app is not hostage to a live provider. Bun is only for scripts/tooling, not the app runtime.
Circulo is spec-first. Every feature is:
- An OpenSpec change (
proposal→specs→design→tasks) - One git branch:
feature/<change-name> - Implementation only after investigation and explicit permission
- Automated tests + manual checks
- Granular conventional commits — after those checks, not before
Rules of the road live in AGENTS.md. Product requirements live in docs/PRD.md.
If you use an AI coding agent, point it at AGENTS.md and the OpenSpec skills under .agents/ / .cursor/ / .agent/.
The project is public and early. Useful contributions right now:
- Review the docs and open issues on contradictions or missing decisions.
- Do not send a giant “here is the app” PR.
- One OpenSpec change per PR, mapped to one feature branch.
Please file issues in English or Spanish. User-facing product copy is English.
MIT.