Run parallel coding agents in isolated git worktrees, from one terminal app.
maestro lets you run several coding agents at the same time without them getting in each other's way. It gives each one its own git worktree and branch, keeps them all in a sidebar, and shows the selected agent's terminal beside it. The terminal is a real one, so you type to the agent directly.
You can leave an agent working and come back to it. The agents run in detached tmux sessions, so they keep going after you quit maestro and are still there when you open it again.
cargo install --git https://github.com/ivancernja/maestroOr build it from a clone:
git clone https://github.com/ivancernja/maestro
cd maestro
cargo build --release
ln -s "$PWD/target/release/maestro" ~/.local/bin/maestro| Needs | For |
|---|---|
tmux, git |
required, a workspace is a tmux session in a worktree |
| an agent CLI | any of claude, codex, opencode, gemini, copilot, crush, grok, omp, pi |
ss (iproute2) |
the listening ports panel |
lazygit |
reviewing changes with g |
Repositories are found under ~/Work, ~/code, ~/src, ~/dev, ~/projects
and similar directories without configuration, three levels deep. If none of
those exist, maestro looks two levels deep in your home directory instead. If it
finds nothing either way, the picker says so and points at MAESTRO_REPO_ROOT,
which replaces the search and takes several paths separated by colons.
maestro runs on any Linux system that has tmux and git. It picks up a few extras on Omarchy, described below, and works without them everywhere else.
Run maestro. In normal mode the keys below drive the app, and the main pane
shows the agent you have selected.
| Key | Action |
|---|---|
j k |
move, and switch the main pane to that agent |
| click, scroll | select a workspace with the mouse |
⏎ i |
insert mode, every key goes to the agent |
n |
new workspace |
g |
lazygit over the worktree |
s e |
$SHELL or $EDITOR in the worktree |
p |
listening ports |
L |
merge into the base branch and retire the workspace |
R |
relaunch an agent that has exited |
d |
remove the workspace, worktree and branch |
r |
refresh now |
esc |
dismiss an error |
q |
quit, agents keep running |
While you are typing to an agent, three keys still belong to maestro.
| Key | Action |
|---|---|
alt+n |
new workspace |
alt+j alt+k |
switch agent and keep typing |
ctrl+q |
leave insert mode, or close an open tool |
n asks for the repository, the branch, the agent, and the task.
The branch field is pre-filled with a composer's name that is not already used
in that repository. ⏎ accepts it, tab picks a different one, and typing
replaces it. The worktree is created next to the repository as
../<repo>--<branch>.
The task is optional. If you give one, maestro passes it to the agent on startup with whatever prompt flag that agent takes, so it begins working right away. If you leave it empty, the agent opens at its prompt.
Status comes from whether an agent's screen changed since the last poll. A moving spinner is work, a still screen is not.
| Mark | Meaning |
|---|---|
● |
working |
◆ |
went quiet while you were looking elsewhere |
○ |
quiet, and you have seen it |
✗ |
the agent exited, R restarts it |
◆ is the only state that needs you, so it is the only one that sends a desktop
notification. Selecting the workspace clears the mark. If the agent was already
selected when it went quiet, it is not marked, because you were looking at it.
Neither state distinguishes "waiting for your approval" from "finished", since both leave a still screen. That would need per-agent transcript reading.
g opens lazygit over the worktree, for reading the diff and committing. L
merges the branch into the branch it was created from, then removes the
workspace.
Because the merge happens in the repository you work in, maestro checks the preconditions first and says which one failed. The worktree has to be clean, the branch has to be ahead of its base, and the main checkout has to be on that base and clean. If any check fails it does nothing.
p shows what is listening, read from ss and /proc.
Dev servers are listed first and marked. maestro reads the command line rather
than the process name, so a Next server shows as next and encore run as
encore rather than both showing as node.
Each row shows the git root the process was started in and how long it has been running. The pid and full command line of the selected row appear under the list.
x stops one. It asks first and names the port, then sends SIGTERM and escalates
to SIGKILL if that is ignored. Only your own sockets appear, because ss
withholds the pid for everyone else's.
| Variable | Default |
|---|---|
MAESTRO_REPO_ROOT |
discovered, colon separated for several roots |
MAESTRO_STATE |
~/.local/state/maestro |
MAESTRO_SESSION_PREFIX |
mst- |
MAESTRO_THEME_FILE |
the active theme's colors.toml |
- tmux hosts the agents and is never shown. Each workspace is a detached session, and maestro attaches to the selected one inside a pty sized to the main pane, rendered by tui-term. That is why agents survive quitting, and why keystrokes reach them unmodified.
- Panes are addressed by absolute tmux id, because a
base-indexof 1 makes numeric targets like:0.0fail to resolve. - Worktrees follow the
../<repo>--<branch>convention, the same layout Omarchy'sgashell helper uses, so worktrees made either way are interchangeable.
maestro does not require Omarchy, but it uses it when it is there.
Colors come from the theme you have active. maestro asks
omarchy-theme-color --all for the palette, which is the same resolver the
config templates, tmux and the terminal escape sequences use, so it ends up with
the theme's real accent, selection and status colors rather than an approximation
of them. It watches the theme file, so omarchy theme set shows up in a running
maestro within a second and there is nothing to restart.
Two smaller things: notifications go through omarchy-notification-send, so they
get the same styling as the rest of the desktop, and the agent picker opens on
whatever omarchy default agent is set to. Worktrees also use the same
../<repo>--<branch> layout as Omarchy's ga shell helper, so worktrees made
either way are interchangeable.
Off Omarchy, colors are ANSI names, which means they follow whatever palette your
terminal already uses. Notifications fall back to notify-send, and the agent
picker starts on the first agent it finds installed.
cargo uninstall maestro # or remove the symlink you made
rm -rf ~/.local/state/maestro # the workspace registryWorkspaces themselves outlive the binary. Remove them with d before
uninstalling, or clean up by hand:
tmux ls | grep '^mst-' | cut -d: -f1 | xargs -r -n1 tmux kill-session -t
git -C <repo> worktree list # then `worktree remove` and `branch -d`cargo build
cargo test # prompt composition and shell quoting
./test/run # 72 tests driving the real binary inside tmuxtest/run starts maestro inside tmux and checks what the panes actually render.
MAESTRO_AGENT_CMD replaces the agent with a stand-in process, so running the
suite does not use a real agent.
MIT, see LICENSE.



