Website · Download .dmg · All releases
You can run one Claude Code session in a terminal. GraphCode lets you run ten — connected, unattended, and still yours to attach to and correct mid-run. Each node in the graph is a unit of work running inside a real CLI coding-agent session; each edge is a hand-off, message, or spawn between them. The sessions are real terminals you can attach to, watch, and steer — not headless jobs that report back when they're done.
Graph Engineering, simplified → — the full article: the mental model, then the machinery underneath it.
Every loop type is "an agent runs repeatedly" — they differ in what you stop doing:
| Loop type | You hand off | Runs until | For example |
|---|---|---|---|
| Turn-based | the check | you end it — each turn pauses for your review inside the session | a refactor you want to eyeball step by step |
| Goal-based | the stop condition | a goal is met (optionally a shell predicate exits 0) | "fix the build" — done when make test passes |
| Time-based | the trigger | you stop it — cadence lives in the prompt (/loop 1h …) |
hourly issue triage |
| Proactive | the prompt | a composite sub-graph runs it end to end | a pipeline that plans its own steps |
Two design choices explain most of the rest:
- GraphCode schedules nothing. A time-based loop's recurrence lives inside its
session, written into the prompt with the agent's own
/loopor/scheduleskill. The daemon only makes sure the session is alive. That's what keeps a running loop something you can attach to and correct, rather than a job that already finished somewhere. - Sessions outlive everything. Each loop's terminal is a
zmxsession, so it survives closing the window, quitting the app, and restarting the daemon — reattaching restores full scrollback.
So a working graph might look like: a time-based loop runs /loop 1h Triage new bug reports, a hand-off edge fires a goal-based fixer loop for anything it finds (done when
the test suite passes), and a second edge hands the fix to a turn-based reviewer loop where
you approve each change yourself. All three are live terminals the whole time — click any
node and you're in that session, scrollback and all.
Every project you add hangs off Start, and each carries its own graph — the screenshot above chains goal-based, turn-based, and time-based loops on one canvas, each color-coded by kind.
Requires macOS 15+ on Apple Silicon (arm64), with Claude Code on your PATH —
GraphCode launches it, it doesn't bundle it.
brew install --cask scgopi/graphcode/graphcodeThat pulls the same signed release, and puts the graphcode CLI on your PATH while it's
there — the long name adds the tap by itself, so brew tap scgopi/graphcode first is
equivalent and makes later commands just graphcode. brew upgrade --cask graphcode from
then on. The tap is
scgopi/homebrew-graphcode — its own
repository, since Homebrew's cask index takes only projects past a notability threshold
GraphCode hasn't reached yet.
Homebrew 6 warns that a third-party tap is untrusted. It's a gate on taps in general, not
a finding about this one; brew trust --tap scgopi/graphcode settles it, and gets ahead of
the release that stops making it optional.
Or download the latest
graphcode-macos-arm64.dmg
(all versions under Releases), open it,
and drag GraphCode to Applications. Releases are Developer ID signed and notarized, so
a browser download opens on a double-click.
Either way the app carries graphcoded, graphcode (the CLI), and zmx inside it, and
puts them in ~/.graphcode/bin on first launch, along with the launchd agent that keeps
the daemon running. If you installed from the DMG, add that directory to your PATH for
the CLI; Homebrew links it for you.
Holding a pre-0.0.9 build?
Builds before 0.0.9 were ad-hoc signed and picked up a quarantine flag that made macOS
refuse them with a misleading "graphcode.app is damaged".
xattr -dr com.apple.quarantine /Applications/graphcode.app clears it — or just grab the
current release.
- Add a project — the sidebar's ⊕ menu: open a local folder, clone a repository from a URL, or add a remote repository over SSH (key auth and zmx on the server required — loops then run on the server while this Mac steers them). Each becomes a project with its own graph. Whatever was open is restored next launch; right-click a project to Close, Remove, or delete its loops.
- Create a loop — ⊕ on the canvas. Write the prompt and hit Create: the form opens
on goal-based, and the title is optional — leave it blank and GraphCode asks the loop's
own backend for a name. For a time-based loop put the cadence in the prompt itself:
/loop 1h Check for new reports. - Open it — click the node. You get its terminal workspace: tabs (⌘T), splits (⌘D / ⌘⇧D — split as many times as you like), ⌘1–9 to switch tabs, ⌘]/⌘[ to move between a split's panes, and ⇧⌘]/⇧⌘[ to step between loops. Every loop type opens the same way, including ones the daemon started on its own — you're attaching to the live session, not a copy of its output.
- Connect loops — drag between nodes. An edge is a hand-off by default (fires when the source resolves); it can also be a message or a spawn, with a condition and a cycle guard.
A turn-based loop resolves when its session exits — the per-turn review happens inside the session itself, so there's no separate approve step in the app.
| Piece | What it is |
|---|---|
graphcode.app |
The UI — project sidebar, graph canvas, and a per-loop terminal workspace with tabs and splits |
graphcoded |
Background daemon (launchd agent). Owns every project's graph, fires hand-off edges, polls goal predicates, and keeps unattended sessions alive whether or not the app is open |
graphcode |
CLI for the same daemon — graphcode status <project>, graphcode node create …, graphcode node send … (type a message into another loop's live session) |
zmx |
Third-party session daemon that keeps each loop's PTY alive (zmx.sh) |
| GhosttyKit | Third-party terminal engine rendering each surface (ghostty.org) |
State lives in ~/.graphcode/ — per-project graphs, recents, terminal layouts, the daemon
socket and logs, and the installed binaries. Nothing is ever written inside a project
folder you open.
Needs mise (Xcode, tuist, swiftlint, zig come through it) and the submodules:
git submodule update --init --recursive
make doctor # checks every prerequisite and prints the fix for anything missing
make third-party # builds zmx and GhosttyKit (zig)
make install-zmx install-cli daemon-install
make run-appmake doctor is the fastest way to find a missing piece — it checks the toolchain, the
submodules, the macOS 15 SDK zig needs, and the Metal toolchain GhosttyKit compiles shaders
with.
make test # unit tests
make check # swiftlint + swift-format, both strict
make format # apply formattingDesign docs live in docs/ and are kept local (gitignored) for now.
- Apple Silicon only. GhosttyKit is built for the native architecture; there is no x86_64 slice, so a universal build won't link.
- Claude Code is the most complete backend. Copilot CLI and Codex loops launch, run, fan out, and receive message edges like Claude ones; what stays Claude-only is hook-reported presence and usage (the others read as heuristics or "not reported") — and the picker refuses pairings a backend can't host (time-based needs the session to re-trigger itself; composites need verified sub-agents).
- A new folder stops at Claude's trust prompt. An unattended loop started by the daemon
in a folder Claude hasn't seen waits at "Do you trust this folder?" and shows as
runningwhile doing nothing. Attach once and answer it. - First session start is slow if your shell profile is slow (conda's hook, for instance) — the queued command runs only after the profile finishes.
- Sessions aren't reaped. Long-lived
graphcode-*zmx sessions accumulate; list them withzmx listand remove dead ones withzmx kill. - Remote repositories are attach-first. Loops on an SSH remote launch and attach fine, but deleting one doesn't kill its remote session, message edges into remote loops report undelivered, presence/usage read "not reported", and worktrees and loop fan-out aren't available there yet. And a sleeping Mac fires no edges — the daemon stays local by design.
Inspired by Supacode — same spine of daemon-kept terminal sessions;
its unit is the worktree, GraphCode's is the graph of loops. Built on
Ghostty and zmx, vendored under ThirdParty/.
MIT.
