dux dux · getdux.app
dux: a duck conductor with a baton v0.5 · macOS + Linux + WSL

Your AI agents deserve a proper office.

dux (pronounced "dooks") is a terminal UI for orchestrating multiple AI coding agents in parallel, each in its own git worktree. No protocol layers. No adapters. Just real CLIs in real terminals. And it's all yours to tune: every setting, down to every keybinding dux controls, is configurable and documented inline in the config file.

  • Claude, Codex, Gemini, OpenCode
  • Any CLI as a provider
  • Git worktree per agent
  • AI commit messages
~ install dux
Shell any unix
$ curl -sSfL https://getdux.app/install.sh | bash

Sniffs your OS + arch, drops dux in ~/.local/bin (or /usr/local/bin).

239
Downloads
GitHub + Homebrew
902
npm
all time
1.1k
Total
downloads + npm
▶ demo

See it before you trust it.

open on asciinema.org
dux in action live recording
▸ the office tour

Three panes. Unlimited agents.

Most AI coding tools give you one agent in one directory. dux gives you unlimited agents across unlimited worktrees, all visible at once. Spawn five agents on five branches and let them work in parallel.

1 Left

Your projects and agent sessions, plus the companion terminals list. Click. Switch. Sorted.

2 Center

The agent's live terminal: full PTY, full output, the same CLI you'd run by hand.

3 Right

Staged files, syntax-highlighted diffs, AI commit messages. The full git dance, toggleable away when you want room.

dux running: a projects pane with a tree of agent sessions on the left, a Claude agent's live terminal in the center, and the dux header bar showing project, branch, agent, and provider.
actual dux · not a mockup · Tab between panes · resize with keys or mouse · go fullscreen with interactive mode
▸ no middleman

Real CLIs. Your setup. No protocol tax.

dux spawns the actual CLI (claude, codex, gemini, opencode) in a real pseudo-terminal, exactly as it would run in your shell. No ACP. No JSON-RPC. No adapter binary cosplaying as your agent.

Skip the protocol tax. Agent protocols like ACP are increasingly metered and billed on their own. Generous to start, sure, but a separate meter all the same. Running the real CLI keeps you on the subscription and auth you already pay for.

Keep everything that makes your agent yours. Your skills, MCP servers, hooks, slash commands, and permission prompts all work, because dux doesn't touch your setup. It just runs the tool you already configured.

no protocol layers · no adapters · no JSON-RPC · just PTYs

dux · welcome

Lost? Ctrl-P opens the command palette.

Every action lives there, even the ones you forgot existed.

▸ what's in the box

The stuff that makes you wonder how you tolerated one agent at a time.

Every dux feature exists because someone (read: me) got tired of doing it the dumb way. Hover over any tile for a closer look.

[providers.my-agent]
command = "my-cool-agent"
args = ["--some-flag"]
resume_args = ["--continue"]

Bring any CLI

Claude, Codex, Gemini, OpenCode ship as defaults. Anything else is a TOML block away. No adapters, no JSON-RPC, no protocol layer.

dux_dark
catppuccin
nord
dracula

Themes that actually theme

Opaline TOML format. Bundled dux_dark, catppuccin, nord, dracula, gruvbox, tokyo night, and more, or define your own with eight colors. change-theme live-previews before you commit.

Watch every theme in action
[macros]
"Review" = "review for bugs"
"Build" = "cargo build --release"
"Ship it" = "test, fix, commit"

Macros, but make them fun

Stop typing the same prompt over and over. Save it as a macro, fuzzy-find it from a quick-select bar, fire it into the agent or the companion terminal. Multi-line prompts land as one piece, not confetti.

change-th
change-theme
change-default-provider
change-agent-provider

A palette that knows your tricks

Fuzzy-searchable access to every action in dux, including ones with no keybinding. Forget a shortcut? Just open the palette.

$ cargo test --lib pty
running 4 tests
test pty::tests::shutdown ... ok
test pty::tests::write_race ... ok
test result: ok. 4 passed

Companion terminals

Every agent gets its own shell session in the same worktree. Build, test, grep, poke around, all without leaving dux. Spawn as many as you want.

startup_command = """
npm install
ln -sfn $DUX_WORKTREE_PATH/.env .env
"""

Startup ceremonies, automated

Some repos demand a ritual before they're useful: install deps, warm caches, link env files, whatever goblin dance your project needs. dux runs your startup script in the new worktree before the agent even boots.

new-agent-from-pr
PR: #246
✓ fetched origin/match-palette-cmds
✓ worktree + agent ready

Spin up an agent from a PR

Paste a PR number or URL. dux resolves it, fetches the head branch, and drops you into a fresh worktree with an agent ready to review or push back. GitHub Enterprise remotes are invited too. No sitting outside with the weird remotes.

refactor-pty● open #243
add-themes✦ merged #238
bench-startup✕ closed #210

Befriend the gh CLI

Authenticate gh and dux tracks your PRs with open / merged / closed pills right in the pane, opens them in your browser, and lets your agents cut commits and pull requests without leaving the worktree.

resource-monitorCPU · RSS
dux0.2% · 41 MB
├ claude · refactor-pty3.1% · 214 MB
└ codex · add-themes1.8% · 180 MB

Featherweight footprint

It's Rust. Idle, dux sits under 50 MB of RAM, leaving the memory for the agents that actually need it. The resource-monitor command shows live CPU and memory for dux and every agent it runs.

click pane → focus it
drag border → resize panes
wheel → scroll back
drag in terminal → select text

Yes, the mouse works

A TUI that doesn't punish you for reaching for the mouse. Click a pane to focus it, click a file to open its diff, drag the borders to resize, wheel through scrollback, and drag across the terminal to select text. Keyboard-first, mouse-friendly.

$ dux
↺ reopening 2 agents…
✓ claude · refactor-pty (resumed)
✓ codex · add-themes (resumed)

Crash recovery, opt-in

Closed your laptop mid-thought? Flip on auto-reopen and agents still running when dux exited come back on next launch, resumed where they left off. Clean exits stay closed. Not every zombie deserves resurrection.

reload-config
✓ config.toml valid
✓ applied, no restart needed
invalid? keeps the last good config

Hot-reload your config

Edit config.toml, run reload-config, keep working. dux validates first and applies changes live; fat-finger the TOML and it keeps the last good config and shows you the error. No restart pilgrimage.

change-agent-provider
claudecodex
✓ resuming previous session

Swap brains mid-session

Change the CLI on a worktree on the fly, Claude to Codex and back. Used that provider here before? dux passes its resume args so you pick up the prior conversation instead of starting cold.

new-agent-from-worktree
~/code/feature-wip (external)
✓ copied into a managed worktree
dirty + untracked preserved

Adopt an existing worktree

Already have a worktree lying around? dux adopts it. Managed ones reconnect as continuable sessions; external ones get copied in (dirty and untracked files intact), leaving your original checkout alone. Bring that "temporary" worktree in without admitting how long it's been temporary.

press o
↗ cursor ~/dux/worktrees/refactor-pty
detected: cursor · code · zed · antigravity

Jump straight to your editor

One key and the agent's worktree opens in your editor (Cursor, VS Code, Zed, Antigravity), auto-detected on your PATH. open-worktree-with lets you pick which one, per open.

Changes (3)2 staged
Msrc/pty.rs+18
- write_buf.extend(chunk);
+ self.write_buf.lock()…

A real git staging area

The right pane is the full git dance: stage and unstage files, read syntax-highlighted diffs (computed in-process with similar + syntect, not shelled out), write a commit, push, and pull, all without leaving dux.

$ refresh checkout
git pull --ff-only
✓ fast-forwarded, no surprise merges
new agents branch from: main

Conservative git, on purpose

Your worktrees are user data, so dux handles git with care: source refresh is --ff-only (never a surprise merge or rebase), new agents branch from the project's leading branch, and destructive actions always ask first.

[env] # every project
API_KEY = "${PROD_KEY}"
[[projects]].env # overrides
API_KEY = "${STAGING_KEY}"

Environment, per project

Inject env vars into agent terminals, companion shells, and startup commands, globally or per-project, with ${VAR} expansion so secrets come from the parent environment, not the config file. Project values win over global.

▸ configuration

The config file is the documentation.

Every setting is configurable. Every setting is commented inline. You should never have to leave the file to figure out what an option does. dux writes a fully annotated config.toml the first time it launches: themes preselected, keybindings ready to remap, providers wired in.

linux
~/.config/dux/config.toml
macOS
~/.dux/config.toml

dux config path prints where it lives.

dux config diff shows what you've changed from defaults.

dux config regenerate previews the latest default config.

Safe to commit to git. Project paths take $HOME, ${HOME}, or ~, and env values expand ${VAR} from your shell, so secrets stay as references, never hardcoded. The file holds portable intent (projects, providers, themes, keybindings), not runtime state. Drop it in your dotfiles and it travels between machines without leaking your username.

config.toml syntax: ok
# Where projects live. Supports $HOME, ${HOME}, and ~ so you can
# share this file between machines without leaking your username.
[[projects]]
id   = "a4f3..."
path = "$HOME/projects/web-app"
name = "web-app"
env  = { EDITOR = "true", API_KEY = "${FOO_KEY}" }

# Some projects need a little ceremony before an agent is useful.
# Suffering builds character, but so does automating the ritual.
startup_command = """
npm install
ln -sfn "$DUX_WORKTREE_PATH/.env.local" .env
"""

[providers.claude]
command     = "claude"
resume_args = ["--continue"]

[macros]
"Review"  = { text = "review this for bugs", surface = "agent" }
"Build"   = { text = "cargo build --release", surface = "terminal" }
"Ship it" = { text = "run tests, fix, commit", surface = "agent" }

[ui]
theme              = "dux_dark"   # or catppuccin_mocha, nord, dracula...
auto_reopen_agents = true         # pick up where you left off

[keys]
quit          = ["q", "ctrl-c"]
open_palette  = ["ctrl-p"]
▸ install

Get dux on your PATH, your way.

Pick whichever one makes you feel the least guilty. They all end with you running dux in a terminal.

The only hard requirement is git (dux is built on worktrees). Beyond that the CLI needs no dependencies. The gh CLI is optional: install it for PR tracking and agent-opened PRs; skip it and dux quietly disables anything GitHub.

~ install dux
Shell any unix
$ curl -sSfL https://getdux.app/install.sh | bash

Sniffs your OS + arch, drops dux in ~/.local/bin (or /usr/local/bin).

Prefer to read it first? Of course you do. view install.sh or grab it straight from the latest release.

Power-user knobs

The shell installer takes a couple of env vars when you want to override its defaults.

DUX_INSTALL_DIR
Override the target directory. Defaults to ~/.local/bin when it exists and is on PATH, else /usr/local/bin.
$ curl -sSfL https://getdux.app/install.sh | DUX_INSTALL_DIR=~/.bin bash
DUX_VERSION
Pin to a specific tag instead of the latest release.
$ curl -sSfL https://getdux.app/install.sh | DUX_VERSION=v0.1.0 bash