Multi-session coding-agent orchestrator

TUI Agentic IDE

Run Claude Code, Codex, Antigravity, opencode, aider, GitHub Copilot CLI, Vibe, pi, Oh My Pi — or any CLI you define — side by side in persistent tmux panes that survive crashes and restarts.

And every pane you see is a Lua file in a directory you own — the session list included. Turn one off, reorder them, or write your own.

One command Install it, then run thurbox — that is the whole setup. Sessions, agents, themes and the interface itself are seeded on first launch.

Linux & macOS, and the recommended route. Auto-detects your platform, verifies checksums, and installs both thurbox and thurbox-cli to ~/.local/bin .

bash
curl -fsSL https://raw.githubusercontent.com/Thurbeen/thurbox/main/scripts/install.sh | sh

Then run it:

bash
thurbox
thurbox

Built-in agent support for Claude Code Codex Antigravity opencode aider Copilot Vibe pi Oh My Pi plus any CLI agent

MIT License Built with Rust

Every pane is a plugin

The interface is a directory you can edit

The session list, the agent terminal, the search strip — each one is a Lua file under ui/plugins/. Move it, turn it off, replace it with your own, and the arrangement closes up around what is left. No recompile, no restart.

thurbox

ui/layout.lua — arrangement

ui/plugins/ — click to turn off

Autoplaying — click anything to take over.

That last one is not shipped. thurbox-cli plugin new git-status writes a file that already loads, and it is a pane the moment you save it. A plugin gets no filesystem and no network — it draws, and the kernel does the rest. Adding or removing a pane is two edits, as it is above: the plugin, and the slot in ui/layout.lua that says where it goes.

“Stacked right” is a demo you can run. Those two panes are real, distributed panes — a tasks list, and CPU and memory read from top — with an arrangement that puts them in a column beside the agent. Install them, copy the arrangement, and press F10 :

bash
thurbox-cli plugin install tasks
thurbox-cli plugin install top
cp examples/lua/layout.lua ~/.config/thurbox/ui/layout.lua

Each install records itself in plugins.toml beside your panes and prints the layout.lua line the new pane needs — because a pane whose slot nothing places loads perfectly and draws nothing, and thurbox-cli plugin check is what fails on it. The arrangement is copied rather than installed on purpose: the manager never writes that file.

The tasks pane needs no permission — it reads the same records thurbox-cli task does and writes back as commands. The top one asks to run a program , so it draws nothing until you trust the file (settings → Interface → t ). It then reads the machine the selected session runs on , which means picking a session over SSH shows you that host's load — without the plugin knowing what SSH is.

Or just ask the agent. You do not have to learn Lua to change any of this. thurbox runs coding agents, and the interface is a directory of plain text files those agents can read — so point a session at it and say what you want:

bash
thurbox-cli session create --name ui --repo-path ~/.config/thurbox/ui
  • Install the info panel plugin.
  • Add a new pane on the left with CPU and RAM usage. There is a top example plugin — install it and give it a slot in the layout.
  • Move the search strip to the bottom, and make the session column 30% wide.

It works because the directory ships an AGENTS.md that any coding CLI loads as context without being asked — so “install a plugin” is read as thurbox-cli plugin install rather than an npm request, and the agent knows thurbox-cli plugin check is how it verifies its own work. Press F10 and the change is on your screen. The worked example →

Not a config format

This is the whole plugin

A plugin is a table. It names a slot to occupy, declares the chords it wants, and returns what it drew — and there are only ever four node kinds (text, box, input, surface). Lists, panels and tables are not kinds; they compose from those, in Lua, in ui/lib/.

ui/plugins/90_git.lua
local theme = require("lib.theme")
local widgets = require("lib.widgets")

return {
  name  = "git",
  slot  = "center",
  order = 90,
  focusable = true,

  -- Declaring it is not being granted it: until you
  -- trust this file, the `run` global is *absent*.
  capabilities = { "run" },

  keys = {
    { key = "f8", action = "git.open",
      desc = "repo at a glance", scope = "global" },
  },

  render = function(ctx)
    -- `run` is nil until the file is trusted, so this
    -- checks rather than assuming.
    if not run then
      return { type = "text", text = theme.dim("  not trusted") }
    end

    local id = store.selected
    if not id then
      return { type = "text", text = theme.dim("  no session") }
    end

    -- Asked every frame, on purpose. A worker answers;
    -- a fresh answer is a map lookup, not a process.
    run("status", "git status --porcelain",
        { session = id, ttl = 2 })

    local got  = thurbox.runs["status"]
    local rows = {}

    if got and got.ok then
      for line in got.stdout:gmatch("[^\n]+") do
        rows[#rows + 1] = { spans = { {
          text  = "  " .. widgets.truncate(line, ctx.width - 4),
          style = { fg = line:find("^%s*M") and theme.warn
                        or theme.secondary },
        } } }
      end
    end

    return {
      type  = "box",
      frame = widgets.panel("git", ctx.focused),
      children = {
        widgets.list({ rows = rows, height = ctx.height - 2 }),
      },
    }
  end,
}

→ and that is a pane

thurbox
  • It never blocks. ctx.run asks; a worker answers next frame. Calling it every frame is the intended pattern, because a fresh answer is a map lookup rather than a process.
  • It gets no more than it asked for. No filesystem, no network, no os or io — those are absent from the sandbox, not blocked in it. Running a program is a capability, and it stays inert until you trust the file.

Features

A fixed engine under an interface that is entirely yours

Band 1 — compiled, fixed

The engine

Processes, git, hosts, schedules, the database. This half is Rust and it is not yours to edit — which is the point: it is what a plugin is allowed to be careless in front of.

Persistent Sessions

Run multiple coding-agent CLIs side-by-side, each in its own tmux pane. Sessions persist across crashes, restarts, and multiple concurrent Thurbox instances.

Git Worktrees & Sync

Spawn sessions in isolated git worktrees for branch-level isolation. One-key sync ( Ctrl+S ) rebases all worktrees from origin/main, with automatic conflict resolution and worktree cleanup on session close.

Any Coding Agent

A session runs one agent of your choice — Claude Code, Codex, Antigravity, opencode, aider, GitHub Copilot CLI, Vibe, pi, Oh My Pi, or any CLI you describe. Each agent runs with its own default config. Mix different agents across the sidebar.

Agent Definitions

Agents are declared as data in ~/.config/thurbox/agents.toml , seeded with built-ins on first run. Add or tweak an agent — command and argument templates — with no recompile.

Automations

Named, scheduled agent runs — one-shot or recurring cron. On schedule they send a prompt to a session, spawn a fresh one, or run a plain shell command. A detached tmux keeper fires them whether or not thurbox is open .

Tasks & issue sync

A todo list whose items connect to a coding agent — send one into a running session or spawn a fresh one seeded with it. Syncs Records carry a source and an external id, so a sync script of your own can import from a tracker without thurbox knowing which one.

Remote & WSL Hosts

Run an agent on another machine while the interface stays local — over SSH, or in a WSL distro that is discovered with no config . The agent, its tmux window and its worktrees all live on that host.

Headless CLI

The thurbox-cli binary scripts everything from the terminal: create and drive sessions, schedule commands, pass messages between agents — all against the same SQLite database the interface reads.

How It Works

Three steps from launch to coding

1

Launch

Start Thurbox. The session sidebar and terminal panel appear.

bash
thurbox
2

Create a Session

Press Ctrl+N to pick repos, name the session, then choose an agent.

thurbox
Ctrl+N  New session
# Pick repo: ~/projects/my-app
# Name: my-app   Agent: claude
3

Start Coding

Spin up sessions (optionally on a fresh git worktree) and start working with the agent. Navigate, sync, and fork with Vim-inspired keys.

thurbox
Ctrl+N  New session
Ctrl+J  Next session      Ctrl+K  Previous
Ctrl+/  Search sessions and their screens
Ctrl+S  Sync worktrees with origin/main
Ctrl+R  Restart active session
Ctrl+F  Fork active session
Ctrl+O  Open repos in editor
Ctrl+,  Settings, incl. the Interface tab

You are already in a coding agent

Just ask for it

thurbox-cli is installed beside thurbox and the interface is a directory of text files — so the two things you would otherwise script by hand are a sentence from inside any session.

Orchestrate sessions

The headless CLI creates, prompts, forks and tears down sessions, so an agent can spin up a whole fleet for you. Everything it does appears in your running TUI within a tick — both binaries share one SQLite database.

  • Using thurbox-cli , start refactor sessions for this repo — one per crate, each on its own worktree branch off main , and send each one a prompt to refactor its crate.
  • Spawn a reviewer session on this repo with no worktree, and send it standing instructions to review every file I change.
  • Create a nightly automation that sends “triage new issues” to the triage session on weekdays at 09:00.

Orchestration patterns →

Reshape the interface

Every pane is a Lua file, so changing the UI is also just a request. It works from any session, in whichever CLI you run, because thurbox ships a thurbox-ui skill that loads when the request is about the interface. Press F10 and the change is on your screen.

  • Add my project logs within a dedicated pane on the right in the thurbox UI.
  • Add a pane on the left with CPU and RAM usage. There is a top example plugin — install it and give it a slot in the layout.
  • Move the search strip to the bottom and make the session column 30% wide.
  • Install the info panel plugin.

What that actually does →

Feature walkthrough

See the TUI in action — recorded from the real app

Ctrl+, → Interface

The interface, editing itself

The Interface tab lists every file that makes up what you are looking at — bundled ones saying nothing, the two panes from examples/panes/ marked yours , and layout.lua marked edited because the demo replaced the shipped one. Here 85_top.lua — a pane thurbox never shipped — is switched off with space : the file stays on disk, intact, and its row is not reserved, so the tasks pane takes the whole column. The same machinery, for a file you wrote.

Learn more →
Interface

Ctrl+/

Search that reads screens

Find a session by its name, agent, branch or repo — or by the error printed on its screen. Matches highlight in the session list itself rather than being reprinted in the strip, and Esc puts back exactly what you were looking at.

Learn more →
Global search

Ctrl+N

Session creation

Spin up a session in a few keystrokes: pick one or more repos, name it, and choose an agent — optionally on a fresh git worktree for branch isolation.

Learn more →
Session creation

Ctrl+Y

Themes

Thirty-six built-in palettes — twenty-eight dark, eight light — switch live with a keystroke and persist across restarts. Bring your own in themes.toml : pick a base, override the colours you care about.

Learn more →
Themes

Prerequisites

What you need before running Thurbox — plus extra install options

Custom Directory

export INSTALL_DIR=/usr/local/bin

Pin a version

export VERSION=v2.5.4

$env:THURBOX_VERSION = 'v2.5.4'

Full Guide

docs/installation

Prerequisites

  • tmux >= 3.2 — session backend
  • A coding-agent CLI — e.g. claude , codex, antigravity, opencode, aider, copilot, vibe, pi, or omp
  • git — required for worktree features
  • Rust 1.75+ — only for building from source (optional)