I run a lot of coding agents at the same time: Claude Code in one worktree, Codex in another, a dev server, a couple of shells. The agent workspace I was using did the job, but it was an Electron app, and with a dozen terminals open it had quietly become the heaviest thing on my Mac.

So I built Krill: the same parallel-agent workflow in pure Rust. This post is about the architecture, and about a benchmark I ran to check whether the rewrite was actually worth it.

What it does

  • Each task gets its own Git worktree and terminal, so agents don't trip over each other.
  • It works with any CLI agent (Claude Code, Codex, Cursor CLI, OpenCode, Antigravity) or a plain shell.
  • Agent status hooks (working, waiting, done, blocked...) feed a Needs You queue, so the agent waiting on your approval floats to the top.
  • Sessions outlive the app. Quit it, crash it, update it: the agents keep running and you reconnect to the same session IDs.
  • A headless krill CLI drives everything without the GUI (krill new, send, read, wait, attach).

Three processes, not one

The "sessions survive the app" part drove most of the design.

krill-app gpui UI. Renders only the panes you can see. │ IPC krilld session daemon: terminal state (alacritty_terminal), │ 1,000 hot rows + zstd-compressed cold scrollback, │ hooks, git watching, mobile gateway krill-holder tiny PTY parent. Owns the file descriptors. Never panics. 
  • krill-holder is the only process that owns PTYs. It is deliberately small (the macOS build has a 1,300-line budget) and panics are banned there. If it dies, krilld still holds the fds.
  • krilld keeps the terminal state and is the only thing that answers terminal queries. Session work runs under catch_unwind, so one bad session doesn't take down the others. If krilld crashes, the agents survive because the holder keeps the PTYs. Output that krilld had read but not yet written to a segment can be lost, and the UI shows an explicit gap marker rather than pretending nothing happened.
  • krill-app can be killed at any time. On restart it restores the layout and reattaches to the same sessions.

Rebooting, or losing both holder and daemon at once, does end the processes. Krill then respawns shells in the last working directory and keeps the old scrollback above them, but it doesn't pretend the old process survived.

The benchmark

I wanted a number I could defend, so I measured Krill 0.2.2 against Orca 1.4.237 (an Electron agent workspace with a similar feature set) on the same machine:

  • Mac mini (Apple M6, 32 GB), macOS 27.0.1, release builds from /Applications, no tuning
  • fresh isolated profile per run, same 1200×760pt window
  • N login-shell tabs in the same git repo, one visible; each tab prints 20,000 lines (~2.8 MB of colored, wide, Unicode text)
  • every process the app spawns is counted (helpers, GPU, renderer, daemons); shells are excluded on both sides
  • 1, 10 and 20 terminals × 3 runs, alternating which app goes first; medians reported

Krill uses 1/15 of Orca's memory at 10 terminals

At 10 terminals:

Metric Krill Orca
Memory (phys_footprint) 85.9 MiB 1,274.9 MiB 1/15
Idle CPU (30 s average) 1.1% 31.0% 1/27
CPU time to process the output 1.2 s 6.4 s 1/5
Install size 92 MiB 689 MiB 1/7

Where the memory goes

Most of the gap is the UI layer: 30 MiB for the Krill app versus about 994 MiB for Electron's main, GPU and renderer processes. The terminal daemons differ too (46.5 vs 256.3 MiB), mostly because Krill keeps 1,000 hot rows per terminal and compresses the rest.

Where Krill lost, and other caveats

  • Wall-clock output at 20 terminals: Krill took 7.5 s to finish the burst versus Orca's 4.0 s. Krill's runs varied a lot (1.9 to 7.9 s) and the machine was shared, so I can't yet say how much of that is load. It's a real result, though, and it's on my list.
  • Screen locked: all runs were recorded with the display locked. Even then, Orca's GPU process used ~22% CPU. I haven't measured the screen-on case yet, so I'm not claiming the 1/27 holds there.
  • Shared machine: other work (Rust builds) was running. Memory barely moves with load; CPU and timing can.
  • Agent processes themselves are excluded. Claude Code costs the same in either app.

Try it

Apple Silicon macOS is the main platform, and there's a Windows x64 preview. The macOS build isn't notarized yet; the installer verifies the checksum and signature before installing.

curl -fsSL https://krill.page/install.sh -o /tmp/krill-install.sh # read it first bash /tmp/krill-install.sh 

It's Apache-2.0: github.com/craftowen/krill · krill.page

If you try it, the most useful feedback is which step blocked you first: install, opening a repo, starting an agent, creating a second worktree, or reconnecting after an app restart.