Website: agetor.dev
A local-first kanban board for orchestrating CLI coding agents — Claude Code, OpenAI Codex, and friends — across many tasks and many repos at once.
Agetor turns a kanban board into a control plane for AI coding agents. Each card is a prompt plus a working directory plus an agent choice; starting it spawns the agent as a child process inside an isolated git worktree, streams its output back to the UI, and moves the card through columns as the run progresses. Approvals, clarifying questions, and follow-up messages flow through structured cards in the run panel instead of getting buried in a TUI.
It runs entirely on your machine. No cloud relay, no remote sandbox — agents execute with your shell privileges in your repos, the same way they would if you launched them by hand. Agetor just adds the orchestration, isolation, and UI on top.
- Multi-agent, multi-account. Built-in support for
claude-codeandcodex. Define additional harnesses to run a second Claude or Codex account in parallel — each one gets a dedicated$HOMEso logins, history, and config never collide. - Per-task git worktrees. Every task runs on its own branch (
agetor/<short-id>-<slug>) in a dedicated worktree under~/.agetor/worktrees/. Two agents can hammer the same repo simultaneously without stepping on each other. Base ref is pinned at create time, so re-runs always start from the same commit. - Interactive Claude sessions. Claude Code is hosted in a per-task
tmuxsession that stays alive across multiple turns. Follow up on a task without losing the conversation. Output is streamed by tailing Claude's own JSONL transcript, so assistant text, thinking blocks, tool calls, and tool results all render with their own UI components. - Approvals and questions, lifted out of the TUI. A
PreToolUsehook and a tiny MCP server intercept Claude's tool-permission prompts andAskUserQuestion/ExitPlanModecalls. They surface in the run panel as structured cards — radios, checkboxes, free-text — with an optional "always allow for this task" rule. Codex prompts are detected heuristically from stdout and surfaced the same way. - Live event stream. SSE per task and a global event channel. Status toasts and native notifications fire when a run finishes, succeeds, fails, or blocks waiting on input.
- Reproducible re-runs. Run history persists per task. Cancelling a run keeps the tmux session alive so you can iterate; deleting a task tears down its worktree, branch, and session.
- Local SQLite. All state — tasks, runs, events, projects, harnesses, preferences, approval rules — lives in
~/.agetor/agetor.sqlitewith a versioned migration runner. Nothing leaves your machine.
Agetor is an Electrobun app — a Bun main process that owns the window and a native WebView that renders the UI.
┌─────────────────────────┐ HTTP + SSE ┌─────────────────────────┐
│ React webview │ ◀──────────────────────▶│ Bun main process │
│ (kanban, run panel) │ 127.0.0.1 + token │ (SQLite, orchestrator) │
└─────────────────────────┘ └─────────────┬───────────┘
│ spawn
┌───────────────┴───────────────┐
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ tmux: claude │ │ codex exec │
│ (per task) │ │ (one-shot) │
└──────────────────┘ └──────────────────┘
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ git worktree per │ │ git worktree per │
│ task │ │ task │
└──────────────────┘ └──────────────────┘
Some key design decisions:
- Localhost API, not Electrobun RPC. The webview talks to the main process over a plain HTTP API bound to
127.0.0.1on a configurable port. Every route (except/health) is gated on a per-launch random bearer token passed to the webview via aWKUserScriptpreload. A site you happen to visit can't drive an agent run even if it guesses the port. - One tmux session per Claude task.
clauderuns in interactive mode (so you draw from your normal subscription quota, not the Agent SDK credit). Prompts are delivered as keystrokes viatmux load-buffer + paste-buffer + send-keys. Output is read from the JSONL Claude writes to~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl. - Codex stays one-shot. Each Codex run is a fresh
codex execinvocation; follow-ups create a new run record on the same task. - Boot reconciliation. On startup, any tmux sessions left over from a previous Agetor process are killed, and any rows still marked
runningare flipped toorphaned. The kanban never shows stuck cards.
The full architecture, schema, and lifecycle is documented in CLAUDE.md.
- Bun ≥ 1.1 (install)
- tmux — hard requirement for the Claude Code driver
- macOS:
brew install tmux - Debian/Ubuntu:
apt install tmux
- macOS:
- At least one agent CLI on
PATH:claude-code:npm i -g @anthropic-ai/claude-codecodex:npm i -g @openai/codex
- Git — required for worktree isolation
- Platforms: macOS (primary). Linux and Windows builds are configured in
electrobun.config.tsbut are not currently tested.
Agents are launched with your full shell privileges in whatever directory the task points at. There is no sandbox.
# Clone and install
git clone https://github.com/alamops/agetor.git
cd agetor
bun install
# Build the webview bundle once (required for `bun run dev`)
bun run build
# Run it
bun run devFor UI iteration, run Vite and Electrobun together:
bun run dev:hmrThat starts Vite on port 5173 for hot reload and Electrobun in dev mode in parallel. Main-process changes (anything under src/bun/) do not hot-reload — restart bun run dev:hmr after editing them.
- Click New task in the left rail.
- Pick a workdir (any local folder; if it's a git repo, you'll get worktree isolation for free).
- Choose an agent and a model, and write your prompt.
- Hit Run task to start it immediately, or To backlog to queue it.
Drag cards between columns to override flow manually. Open a card to see the live event stream, send follow-up messages, or answer approval prompts.
All persistent state lives in ~/.agetor/ (override with AGETOR_DATA_DIR):
~/.agetor/
├── agetor.sqlite # tasks, runs, events, projects, harnesses, preferences
├── worktrees/<task-id>/ # per-task git worktrees
├── bin/ # installed approval hook + MCP launcher
└── harnesses/<id>/ # optional per-harness HOME (multi-account)
| Variable | Purpose | Default |
|---|---|---|
AGETOR_DATA_DIR |
Where the SQLite db, worktrees, and bin scripts live. | ~/.agetor |
AGETOR_API_PORT |
Localhost port the main process binds. | 4317 |
AGETOR_CLAUDE_BIN |
Override the claude binary path. |
claude on PATH |
AGETOR_CLAUDE_ARGS |
Extra args appended to every Claude spawn. | (none) |
AGETOR_CODEX_BIN |
Override the codex binary path. |
codex on PATH |
AGETOR_CODEX_ARGS |
Extra args appended to every Codex spawn. | (none) |
AGETOR_TMUX_BIN |
Override the tmux binary path. |
tmux on PATH |
AGETOR_CLAUDE_DRIVER |
Set to fake to skip tmux + the real CLI (test-only). |
unset |
Per-harness bin, home, and env overrides are configured through the Settings dialog and stored in SQLite — they take precedence over the corresponding env vars.
A task is a prompt + workdir + agent. It lives in one of six columns:
| Column | Meaning |
|---|---|
| Backlog | Queued, not started. |
| Ready | Created via "Run task" but waiting on pre-flight. |
| Running | Agent is actively producing output. |
| Blocked | Agent is waiting on an approval, question, or plan review. |
| Review | Last run exited 0. The diff is yours to inspect. |
| Done | You've decided you're satisfied. |
A run is a single invocation of the agent on a task. Tasks accumulate run history; the run panel can replay any past run's events.
A harness is a named agent configuration. The two built-ins (claude-code, codex) wrap each CLI directly. User-defined harnesses are aliases that wrap the same underlying kind with extra env, an alternate binary, or — most usefully — a per-account $HOME override. That last knob lets you run a second Claude or Codex account in parallel without their logins overwriting each other.
Add a harness from Settings → Harnesses. Templates pre-fill common patterns.
Each task picks:
- a mode — how much permission the agent has (
auto,ask,acceptEdits,plan,bypass— exposed per agent), - a model — Opus / Sonnet / Haiku for Claude, GPT-5 / GPT-5 Codex for Codex,
- an effort level — reasoning depth, where the model supports it.
The picker filters incompatible combinations (e.g. effort is hidden on Haiku 4.5 because Anthropic's API doesn't accept it there).
When isolation: "worktree" (the default), starting a task in a git repo:
- Resolves the base ref to a sha (pinned on the row — re-runs reproduce).
- Creates
~/.agetor/worktrees/<task-id>/on a fresh branch off that sha. - Spawns the agent with that path as
cwd.
Worktree teardown happens automatically on task delete: git worktree remove --force plus git branch -D, with an rm -rf fallback for the case where the user moved workdir out from under us.
Set isolation to "none" to run directly in workdir — useful for one-off scripts where a worktree would be overhead.
When Claude is about to use a tool, the Agetor-installed PreToolUse hook posts the tool name and input to 127.0.0.1:<port>/approvals. The run panel renders a card; the user clicks Allow, Allow always, or Deny. "Allow always" persists a (task, tool) rule in SQLite so future fires are auto-allowed without bothering you.
When Claude wants to ask a structured question, it calls the ask_user tool on Agetor's MCP server. The webview renders radios / checkboxes / textarea, and the MCP server's response unblocks the agent.
AskUserQuestion and ExitPlanMode from Claude's built-in tools are intercepted the same way.
The main process exposes a small JSON + SSE API on 127.0.0.1:$AGETOR_API_PORT. All routes except /health require Authorization: Bearer $AGETOR_API_TOKEN (or ?token=... for EventSource).
| Method | Path | Purpose |
|---|---|---|
GET |
/health |
Liveness probe. |
GET |
/info |
App version + boot info. |
GET |
/defaults |
Home dir and other defaults the UI needs to expand ~. |
GET / POST |
/tasks |
List or create tasks. |
GET / PATCH / DELETE |
/tasks/:id |
Inspect, edit allow-listed fields, or delete a task. |
POST |
/tasks/:id/start |
Start a run for the task. |
GET |
/tasks/:id/runs |
Run history for the task (newest first). |
GET |
/tasks/:id/events |
SSE: unified event stream across all runs for the task. |
POST |
/runs/:id/cancel |
Send Ctrl-C to the in-progress run; tmux session survives. |
POST |
/runs/:id/input |
Send a follow-up user message (Claude only). |
GET |
/runs/:id/events |
SSE: events for a single run (replays history, then streams live). |
GET / POST / DELETE |
/harnesses[...] |
Manage harness configs. |
GET |
/agents |
Per-harness availability + version probes. |
GET |
/agent-models |
Models discovered by probing each CLI. |
GET |
/agent-commands |
Slash commands supported by the active agent. |
POST |
/approvals/:id/answer |
Resolve a pending tool approval. |
POST |
/questions/:id/answer |
Resolve a pending ask_user question. |
POST |
/ask-questions/:id/answer |
Resolve an intercepted AskUserQuestion. |
POST |
/plan-approvals/:id/answer |
Resolve an intercepted ExitPlanMode. |
GET |
/tasks/:id/interactions/pending |
Anything currently blocking the task. |
GET / POST / DELETE |
/projects[...] |
Working-directory shortcuts shown in the New Task form. |
GET / PATCH |
/preferences[...] |
Per-user preferences (key/value). |
POST |
/open-path |
Reveal a path in Finder / Files. |
GET |
/events |
SSE: global lifecycle stream (toasts, native notifications). |
The full route list is in src/bun/server.ts.
bun install # install deps
bun run dev # Electrobun, loads packaged webview
bun run dev:hmr # Vite + Electrobun in parallel (preferred for UI work)
bun run build # production bundle (vite build → electrobun build)
bun run build:canary # canary channel build
bun run build:stable # stable channel build
bun run typecheck # tsc --noEmit; must be green
bun test # full test suite (Bun's test runner)
bun test src/bun/orchestrator.test.ts # single file
bun test -t "createTask" # by test namesrc/
├── bun/ # main process: API, orchestration, persistence
│ ├── index.ts # entrypoint: menus, reconcile orphans, start server, open window
│ ├── server.ts # Bun.serve routes (HTTP + SSE)
│ ├── orchestrator.ts # task lifecycle, spawn/cancel/cleanup, event fanout
│ ├── agents.ts # buildCommand / spawnAgent — single source of truth per agent
│ ├── claude-tmux.ts # tmux session lifecycle + JSONL tailer
│ ├── agent-status.ts # availability + version probes per harness
│ ├── agent-discovery.ts # async model-list discovery
│ ├── worktree.ts # git worktree create/remove
│ ├── interactions.ts # approvals + questions registry (in-memory)
│ ├── hook-installer.ts # writes PreToolUse hook + MCP launcher per task
│ ├── hooks/ # bundled hook script + system prompt
│ ├── mcp/agetor-mcp.ts # stdio MCP server exposing `ask_user`
│ ├── commands.ts # slash-command catalogue per agent
│ ├── db.ts # bun:sqlite + bindings
│ ├── migrate.ts # numbered SQL migration runner
│ └── migrations/ # 001_init.sql … 013_harnesses.sql
├── mainview/ # React webview (kanban, run panel, settings)
│ ├── App.tsx
│ ├── components/
│ │ ├── kanban/ # board, columns, cards, run panel, pickers
│ │ ├── settings/
│ │ └── ui/ # shadcn-style primitives (hand-rolled)
│ └── lib/api.ts # typed wrapper around the Bun HTTP API
└── shared/ # types both processes import (no runtime side effects)
└── types.ts # Task, Run, Harness, AGENT_OPTIONS, etc.
- The API binds to
127.0.0.1only and is gated on a per-launch random token. Even ifAccess-Control-Allow-Originwere misconfigured, a foreign origin can't read the token. - The token is delivered to the webview via a
WKUserScriptpreload (not a query string or hash, so it never appears in the URL bar or logs). - Agents run with your full shell privileges in whatever
workdirthe task names. Agetor does not sandbox them. Treat it like running the agent CLI directly — because that's exactly what it's doing. - Approval rules are scoped to a single task, never globally. Each new task starts fresh.
If you find a security issue, please disclose it privately to alamo@alamoweb.com.br rather than filing a public issue.
Issues and pull requests are welcome — including AI-assisted and fully vibecoded ones. If Claude, Codex, Cursor, or any other agent wrote most of the diff, that's fine. Just hold it to the same bar as a hand-written PR: tests green, types clean, and a description that makes the why obvious. (Agetor itself is built to make this kind of contribution easier — eat your own dog food.)
A few conventions worth knowing before opening a PR:
bun run typecheckmust be green. TypeScript is strict (noUncheckedIndexedAccessis on).- Tests are required for behavioural changes. The orchestrator and Claude-tmux modules have particularly thorough coverage; mirror that style. Tests must set
AGETOR_DATA_DIRat the top of the file (not inbeforeAll) to avoid the production-db-pollution trap documented insrc/bun/db.ts. - Never edit an already-applied migration. Add a new numbered
.sqlfile and append it tosrc/bun/migrations/index.ts. The migration runner only tracks ids, so silent edits will diverge from existing user databases. - Shared types are runtime-free.
src/shared/types.tsis imported by both processes — no Node, Bun, React, or DOM imports there. - Agent options live in
AGENT_OPTIONS. To add a model or mode, extend the curated list and teachbuildCommandhow to translate it. Unknown ids are passed through verbatim so users can paste a fresh release before Agetor knows about it.
Adding a new agent kind:
- Extend the
AgentKindunion insrc/shared/types.ts. - Add an entry to
AGENT_OPTIONSwith models / modes / effort. - Add a branch in
buildCommand(src/bun/agents.ts) andspawnAgent. - Surface availability hints in
agent-status.ts. - The orchestrator and UI pick it up automatically.
The architecture and the gotchas worth knowing are documented in CLAUDE.md. Reading that file before your first PR is the highest-leverage thing you can do.
This is an early-stage project. Things on the near-term list:
- An uninstall flow that strips the
PreToolUsehook and MCP-server entries from.claude/settings.local.jsonacross every repo Agetor touched. - First-class Linux and Windows builds (currently configured but untested).
- More agent kinds: Cursor agents, Aider, Gemini CLI.
- Optional Slack / native push notifications on terminal state, not just toasts.
MIT © 2026 Alamo Saravali
- Electrobun — the native-webview-on-Bun runtime this app is built on.
- Claude Code and OpenAI Codex CLI — the agents Agetor was built to orchestrate.
- dnd-kit, shadcn/ui, Tailwind CSS, Lucide, Sonner — the front-end stack.