// 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

// IN THE APP

The real app, not a mockup

Swarm with a Claude Code session on the left and the diff review on the right, with a line-comment box under each file
Agent and review side by side: the live session on the left, the diff on the right, where line comments go back to the agent.

// 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 writes a PRD, then coder

The handoff is a PTY phase, not a job: the planner runs in the same pane, writes a PRD — problem, requirements, acceptance criteria, risks, steps — and exits, and on_exit starts the coder pointed at it. A PRD with no acceptance criteria stops the task with that reason: “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. An unreadable transcript stops the loop instead of counting as zero spent.

// 05 — AUTOMATIONS

Work that starts without you.

One Automations page holds everything that fires on its own — schedules, triggers and boards — with the controls, not just the list. Every run still goes through the cost, attempt and kill-switch guards.

Scheduled tasks

A cron expression, parsed by Fugit, fires a prompt into a fresh worktree on schedule. Optional run cap, pause and resume, and three honest states: active, paused and finished. Lowering the cap stops the schedule, so the panel and the scheduler never disagree.

CI failure starts an agent

A failed GitHub check suite opens a task on its own: HMAC-SHA256 over the raw body, actor allowlist, budget, run cap and cooldown. Branch and SHA pass a strict regex and the repo is pinned to the real git origin, so nothing from the payload reaches the prompt raw.

Webhook relay on AWS

Swarm runs behind NAT in WSL, where GitHub cannot reach it. A Lambda provisioned with Terraform keeps the raw body and headers in SQS without validating anything; the app drains the queue every minute over an outbound connection and runs the same HMAC check and deduplication as a direct delivery.

Trello, both ways

A card you are added to becomes a pending task — no worktree, no agent, nothing spent until you press Start. The other way, the card moves to Doing, PR and Done and gets a comment as the task starts, opens a PR and is merged or discarded.

Jira, Linear and GitHub

Tracker panels next to Tasks: Jira Cloud on the current /search/jql endpoint, Linear over GraphQL, and a GitHub panel for issues, PRs and projects in GitHub's own query syntax. An expired credential shows as an error, never as an empty backlog.

Goal loop with a judge

The PRD's acceptance criteria feed the Goal loop. A judge — QA gate, custom shell or evaluator — checks every attempt and retries until it passes, and every stop is named: passed, judge crash, budget exhausted, usage unavailable or attempts exhausted.

// 06 — INSIDE

What happens when you press Start.

A pending task has no worktree. Start creates it, the planner writes a PRD and 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 → PRDClaude Code · CodexCoder agentPTY + ActionCablePanes · diffMerge / PR

// 07 — 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
Solid Queue
Fugit
tmux
AWS Lambda
SQS
Terraform
GitHub Actions
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
Solid Queue
Fugit
tmux
AWS Lambda
SQS
Terraform
GitHub Actions

// 08 — 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, plus a Jump Palette for tasks, projects and commands.

  4. 04

    Two CLIs, one planner

    Claude Code and Codex adapters, and a planner phase that writes a PRD for every task 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

    Automations

    Cron schedules, CI-failure triggers through an AWS relay (Lambda + SQS on Terraform), Trello both ways, and Jira, Linear and GitHub panels.

  8. 08

    Desktop

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

  9. 09

    CI/CD

    CI, infra and release workflows on GitHub Actions: RSpec, RuboCop and a security scan on every PR and push to main, and the Windows .exe built and released by Actions.

  10. 10

    tmux sidecar

    tmux as the backend for agent, shell and preview panes — phase A of ADR 001, shipped behind SWARM_TMUX=1.

// 09 — 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.

// 10 — 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.