// PRODUCT — FEATURED

SWARM

> Agent Development Environment

A desktop IDE for coding agents. Run 1–4 Claude Code or Codex agents in parallel Git worktrees, watch each one in a live PTY, review the diff with per-line comments, then merge or open the PR — all in one window.

agent·1
agent·2
agent·3
agent·4

// 02 — LIVE

Every task plans before it codes.

A task is born pending, with no worktree — creating one costs neither disk nor money. Start creates the worktree, the planner writes the plan and exits, and only then does on_exit bring up the coder. No non-empty plan, no coder.

swarm — task #142 — agent pane
01
02
03
04
05
06
prompt >

// 03 — REVIEW

Attempts side by side, as tabs.

The screen is a tree of splits written into the URL. Any surface becomes a tab — the agent, a terminal, the chat, the diff, the preview, a file, or another attempt at the same prompt. That is how two different CLIs on one prompt end up next to each other.

?panes=agent;(diff|preview) · ; splits · | stacks · , tabs · () nests

swarm/cursor-pagination-a3f2
claude-code2m14s
def index
  @deals = policy_scope(Deal).paginate(cursor: params[:cursor])
  render json: {
    data: @deals,
    next: @deals.next_cursor,
  }
end
3 files · +48 −12
MergeDiscardOpen diff
swarm/cursor-pagination-b7c1
codex1m58s
def index
  scope = policy_scope(Deal).order(:id)
  @deals = scope.after(params[:cursor]).limit(25)
  render json: DealSerializer.wrap(@deals)
end
2 files · +36 −8
MergeDiscardOpen diff
swarm/cursor-pagination-9d0e
claude-code3m01s
def index
  paginator = CursorPaginator.new(
    scope: policy_scope(Deal),
    cursor: params[:cursor],
    per: 25,
  )
  render json: paginator.page
end
5 files · +72 −18
MergeDiscardOpen diff
swarm/cursor-pagination-c204
codex2m47s
def index
  @deals = DealQuery
    .for(current_user)
    .cursor_page(params[:cursor])
  render json: @deals
end
4 files · +52 −22
MergeDiscardOpen diff

// 04 — CAPABILITIES

An environment, not a wrapper around one CLI.

Eight things it does, all on your machine.

Planner, then coder

The handoff is a PTY phase, not a job: the planner runs in the same pane, writes the plan and exits, and on_exit starts the coder pointed at it. “The planner failed” never turns into “code in the dark”.

Claude Code and Codex

Two adapters behind one map — 1 to 4 agents in parallel, each in its own worktree and branch. A new CLI is a map entry plus a class; the validation and the form follow from there.

Real PTYs

PTY.spawn per pane, registered in memory by (task, pane), streamed base64 over ActionCable. Stop escalates HUP → TERM → KILL, and scrollback flushes to disk every 5s so an abrupt death loses at most 5 seconds.

Panes live in the URL

The layout is a tree of splits serialized into the query string. Switching tabs is reordering, closing is removing, splitting is inserting — every action is a link, so reload, back button and bookmarks all just work.

Diff review that talks back

Committed, uncommitted and untracked against merge-base, parsed server-side. Comment on any file:line and the comment goes back to the agent as an instruction.

PR with real evidence

The body is written server-side by a headless agent from the real diff, the original prompt and the actual check results — “no check command” and “never ran” stay distinct from a real result. The golden path is recorded in headless Chrome and embedded as a GIF.

Preview and Design Mode

The worktree's app boots in a PTY behind a proxy whose port detection does not rely on the stack honoring $PORT. Design Mode annotates the live screen like Figma comments — the element's live state is serialized from the DOM and re-rendered with the user's session, not screenshotted.

Loops with guards

Turn, Goal, Time and Proactive, all behind the same non-negotiable guards: cost ceiling, attempt ceiling, global kill switch, judgment timeout, HMAC-SHA256 over the raw webhook body. Trello (bidirectional), Linear and Jira on the tracker side.

// 05 — INSIDE

What happens when you press Start.

A pending task has no worktree. Start creates it, the planner hands off to the coder in the same PTY, and everything the agent produces streams back through a single Puma process — the registry lives in memory, so WEB_CONCURRENCY is forbidden by design.

Task · pendingStart → worktreePlanner (PTY)Claude Code · CodexCoder agentPTY + ActionCablePanes · diffMerge / PR

// 06 — STACK

Built on.

Rails 8.1
Hotwire
Turbo
Stimulus
Importmap
Tailwind v4
Propshaft
SQLite
ActionCable (async)
PTY.spawn
Rouge
xterm.js
CodeMirror 6
Ferrum
Tauri 2
Rust
WebView2
GitHub CLI
Rails 8.1
Hotwire
Turbo
Stimulus
Importmap
Tailwind v4
Propshaft
SQLite
ActionCable (async)
PTY.spawn
Rouge
xterm.js
CodeMirror 6
Ferrum
Tauri 2
Rust
WebView2
GitHub CLI

// 07 — MILESTONES

What is already in.

Everything below is shipped and running — this is the tool as it exists, not a plan.

  1. 01

    Orchestrator

    Isolated worktrees and branches, live PTY streaming, diff review, merge / discard / PR.

  2. 02

    Compare

    N attempts at the same prompt reviewed side by side, per-line comments flowing back into the agent.

  3. 03

    Workspace

    Splittable panes serialized into the URL, with the explorer, editor, preview, chat and any file as draggable tabs.

  4. 04

    Two CLIs, one planner

    Claude Code and Codex adapters, and a planner phase every task goes through before any code is written.

  5. 05

    PR with evidence

    Body written by a headless agent from the real diff and real check results; golden path recorded in headless Chrome as an inline GIF.

  6. 06

    Design Mode

    Annotate the running app like a Figma comment — the element is serialized from the live DOM and re-rendered with the user's session.

  7. 07

    Loops and trackers

    Turn, Goal, Time and Proactive behind cost, attempt and kill-switch guards, plus Trello, Linear and Jira integrations.

  8. 08

    Desktop

    Tauri 2 window on a fixed port with its own process group; WebView2 on Windows, with Rails running inside WSL.

// 08 — STORY

How it started.

I wanted an IDE for coding agents, not a wrapper around one. Register a local repo, fire a prompt, and 1 to 4 agents go at it in parallel — each in its own Git worktree and branch, each in a live PTY, with the file explorer, the editor, the running app, the chat and the diff all in the same window.

It is a Rails 8 app on Hotwire and SQLite, single-process by design: the PTY registry lives in Puma's memory, so WEB_CONCURRENCY is forbidden — two processes mean two registries and a UI showing an empty terminal for an agent that is very much running. Tauri 2 wraps it as a native window; on Windows that is WebView2 with Rails running inside WSL.

One rule ended up running through the whole thing: unknown must never become a value. A model missing from the price table pays the most expensive row. An unreadable transcript returns :unknown and stops the loop instead of counting as zero spent. And a prompt that assembles evidence has to tell the model where the hole is — because a prompt without evidence does not answer “I don't know”, it answers with a plausible invention.

// 09 — TRY IT

Point it at a repo and press Start.

Swarm runs on your machine, against your local repos. The Claude Code plugin ports the same fan-out into the CLI.