Coding agents
One control surface over the agent CLIs you already use, with live status, isolated logins, and a queue that works through tasks for you.
Supported agent CLIs
23 branded adapters ship, plus a generic shell adapter.
Login is who holds the credential. Account means you bind an AI account
in Muster and Muster puts its key into the agent's environment when the task starts.
CLI signs itself in means the CLI runs its own login flow
(copilot login, auggie login, Droid's browser flow, Kiro's device
code, acli's auth) and writes the credential into the profile's isolated home —
Muster holds no key for it, and the adapter's screen-detection rules surface the "not signed
in" state instead. Those CLIs still need their own subscription or account.
Session resume is what the app's Resume action actually launches. For Claude Code and Codex, Muster discovers the CLI's own session id — the newest session the CLI recorded for the task's directory, read from the profile's isolated config dir, never your real HOME — and resumes that exact conversation by id. If the saved session has since been deleted, Resume falls back to the id-less form and says so rather than erroring. The other five rows naming a form resume id-less: a session picker the CLI presents, or its most recent session for that directory. Only the seven rows naming one of those can be resumed; on the rest, Resume is not offered.
| Agent | Command | Login | Session resume |
|---|---|---|---|
| Claude Code | claude | Account | By session id |
| Codex | codex | Account | By session id |
| Cursor Agent | cursor-agent | Account | — |
| Gemini CLI | gemini | Account | — |
| OpenCode | opencode | Account | — |
| Amp | amp | Account | — |
| Aider | aider | Account | — |
| Kimi CLI | kimi | Account | Most recent session |
| Qwen Code | qwen | Account | Session picker |
| GitHub Copilot CLI | copilot | CLI signs itself in | — |
| Cline | cline | Account | — |
| Continue CLI | cn | Account | Most recent session |
| Goose | goose session | Account | Session picker |
| Grok CLI | grok | Account | — |
| Kilo Code | kilo | Account | — |
| Mistral Vibe | vibe | Account | Session picker |
| GLM CLI (z.ai) | zai | Account | — |
| Pi | pi | Account | — |
| oh-my-pi | omp | Account | — |
| Auggie | auggie | CLI signs itself in | — |
| Droid | droid | CLI signs itself in | — |
| Kiro CLI | kiro-cli | CLI signs itself in | — |
| Rovo Dev | acli rovodev run | CLI signs itself in | — |
| Shell Agent | sh | — | — |
Installed state is probed against your PATH inside each profile's environment,
so a CLI installed under only one profile shows correctly.
Adapters are configuration
Each adapter is a small TOML file describing how to launch the CLI, how to detect its status, and how to resume a session. Nothing about an agent is compiled in, so:
- A CLI that is not built in can be added as a file — no release required.
- An adapter can be corrected locally when a vendor changes a flag.
- The generic Shell Agent runs any command as a task's agent, with heuristic status detection.
- Settings can override an agent's model, extra arguments and environment per agent. The model reaches the CLI for the 16 agents that accept one at launch, each wired from that vendor's own documentation; for the rest — where the model is chosen in a config file or an in-session command instead — the card says so rather than accepting a value it would drop.
Adding your own
Drop a .toml file in the adapters directory beside your
config.toml — ~/Library/Application Support/dev.muster.muster/adapters
on macOS, ~/.config/muster/adapters on Linux,
%APPDATA%\muster\muster\config\adapters on Windows. Provider descriptors work the
same way, in a sibling providers directory.
- Your file is read everywhere an agent is resolved: the app, the
musterCLI, the MCP server, and the spawn path itself — not just the picker. - Giving your adapter the same
idas a built-in replaces it, which is how you correct a shipped flag without waiting for a release. - One bad file cannot break the others: each is parsed and validated on its own, the rest still load, and Settings → Agents lists the ones that were rejected and why. A malformed adapter is never silently missing.
- If you add or change a file by hand while the app is running,
muster reload-configmakes the running app re-read its adapters (it re-reads the config too).
Importing a shared adapter
Adapters are meant to be shared: they are plain TOML, carry no credentials, and anyone's file works in your app. Settings → Agents → Import an adapter brings one in without opening the config directory:
- Import from file… picks a
.tomlfrom your disk. - Import from URL fetches one over https only, with a short timeout and a 64 KiB size cap.
- The descriptor is validated before anything is written — a malformed file is refused with the same message Settings would show for a hand-dropped one, and the same size cap applies to a picked file as to a fetched one.
- If the adapter's
idalready exists — built-in or one of yours — nothing is written until you explicitly choose Replace. Replacing a built-in is the documented override path; replacing one of your own updates it in place. - The import lands in the
adaptersdirectory and is live immediately: the agent picker, the launch path and themusterCLI see it with no restart.
The descriptor format
An adapter is one TOML document. The four required keys are id, label,
vendor and program, plus the [status] table; everything else
is optional and defaults to the safe/absent value.
| Key | Type | Meaning |
|---|---|---|
id | string, required | Stable identifier — the registry key.
A user file whose id matches a built-in replaces it. Stick to
letters, digits, -, _ and .: imports are written as
<id>.toml. |
label | string, required | The display name shown in the picker and on the Settings card. |
vendor | string, required | Vendor key. When
providers is empty, the provider this agent binds to is derived from it. |
program | string, required | The CLI binary, resolved on
PATH inside the profile's environment. Drives the installed/not-installed
badge. |
args | string list | Arguments passed to program on
every launch (default: none). |
requires_account | bool | When true, a task with
this agent is refused until the profile has a bound account (default:
false). |
config_dir_env | string | Env var Muster sets to point the CLI's config/auth directory at the profile — what keeps two subscriptions from mixing. |
providers | string list | Provider ids this runner can drive.
["*"] means provider-agnostic (the account is chosen at bind time). Empty derives
the provider from vendor. |
kind | string | pty (default; a TUI in a pane),
api_oneshot or chat_repl (headless runners). |
model_arg | table | How a chosen model is passed:
{ flag = "--model" } appends --model <model>;
{ env = "SOME_VAR" } sets an env var. Absent ⇒ no model selector, and the
Settings card says so. |
| Table | Keys | Meaning |
|---|---|---|
[caps] | agentic, interactive_pty,
needs_workdir, tool_use | Capability flags (all default
false): edits files autonomously, runs as a TUI, operates on the task worktree,
does function/tool calling. |
[status] | mechanism (required),
idle_states, busy_states, input_states | How
status arrives: hook (the CLI POSTs its state to Muster) or
heuristic (inferred from output activity). The state lists map the CLI's raw
state strings to Muster's vocabulary — a raw state may appear in exactly one
list, and validation rejects an overlap. |
[hooks] | install | Named hook-installer routine
that wires the CLI to emit status events. Only meaningful with
mechanism = "hook"; the routine itself is built into Muster, so user adapters
normally use heuristic. |
[install] | cmd, args,
docs_url, verified | The one-click CLI installer on the
Settings card. Only a verified = true command is ever run — anything else makes
the card open docs_url instead. Multi-step installs go through
cmd = "sh", args = ["-c", "…"] as a single spawn. |
[resume] | args, session_dir | How to
resume the CLI's own prior session: args with the literal
{session} token replaced by the session id
(["--resume", "{session}"]), and session_dir, the subdirectory of
the config dir where the CLI writes its sessions (where Muster discovers the id). Absent ⇒
Resume is not offered. |
[[detection.rules]] | substring or
regex, state, detail,
case_insensitive | Ordered, first-match-wins rules that read the pane's
screen output into a rich state: idle, working,
blocked or done, with an optional human-readable
detail. Exactly one of substring/regex per rule;
regexes are compiled once at load and an invalid one rejects the adapter with a named
error. |
A complete example
A fictional CLI, fixit, exercising every part of the schema — launch, model flag,
verified install, by-id resume, and screen detection:
# adapters/fixit.toml — an adapter for the fictional Fixit CLI.
id = "fixit"
label = "Fixit"
vendor = "fixit"
program = "fixit" # resolved on PATH inside the profile env
args = ["--tui"]
requires_account = true
config_dir_env = "FIXIT_HOME" # profile isolation for its credentials
providers = ["fixit"] # or ["*"] for a provider-agnostic CLI
kind = "pty"
model_arg = { flag = "--model" }
[caps]
agentic = true
interactive_pty = true
needs_workdir = true
tool_use = true
[status]
mechanism = "heuristic" # no native status hook — infer from the screen
idle_states = ["ready"]
busy_states = ["working"]
input_states = ["awaiting_confirmation"]
[install]
cmd = "npm"
args = ["install", "-g", "@fixit/cli"]
docs_url = "https://fixit.example.com/docs/install"
verified = true # only a verified command is ever one-click run
[resume]
args = ["--resume", "{session}"] # {session} is replaced by the discovered id
session_dir = "sessions" # under FIXIT_HOME, where the id is discovered
# Screen detection, first match wins — put specific prompts before generic ones.
[[detection.rules]]
substring = "Apply these changes?"
state = "blocked"
detail = "waiting for your confirmation"
[[detection.rules]]
regex = "error|panic|traceback"
state = "blocked"
case_insensitive = true
[[detection.rules]]
substring = "All tasks complete"
state = "done"
detail = "reported finished"
Share the file itself — in a repo, a gist, a message. The recipient drops it in their
adapters directory or imports it straight from the URL in Settings → Agents.
Profiles & isolation
A profile is an identity, and the reason work and personal accounts can share one machine without mixing. Each carries:
- Its own agent config directories, so subscription logins never collide.
- Its own git identity — name, email, signing key.
- Its own default SSH key.
- Its own environment overlay for spawned processes. For an agent bound to a single vendor (Claude Code, Codex, Cursor Agent, Gemini CLI, Amp and the rest), the overlay is the last word: an entry here outranks everything Muster sets itself, including the bound account's API key — so a profile can point that agent at a proxy or a scoped key without unbinding its account.
- Optional pinned Node and Python versions, resolved against your nvm/pyenv installs.
tool_isolation decides which tools get a private config directory per profile
rather than sharing the host's. Account tokens live in your chosen
secret backend, never in a config file.
Queue & templates
Rather than starting agents one at a time, enqueue the work:
- Each entry has its own prompt, agent and profile — a mixed stack in one queue is fine.
- Muster works entries in order: open the worktree, start the agent, feed the prompt, move the task to review when it finishes.
- A failed entry can be retried with nothing retyped — it keeps the project, agent, profile, prompt, worktree flag and, deliberately, its permission policy.
- Concurrency is bounded, so a queue cannot spawn twenty agents at once.
- Templates turn a repeated setup into one click.
What the queue is, exactly
- There is one queue, not one per project. A single position counter and a single concurrency cap cover everything, so a long-running task in another project can hold the slot your entry is waiting for.
- Retry re-runs the work, not the row. The retry is appended at the tail with a new id and a fresh task; the failed row is then dropped. It does not resume the failed task — that task may have a dirty worktree — and it does not keep its old position.
doneis a scheduler state, not a verdict. An entry retires when its task has been idle for a few seconds, or reached review or error. It says the slot is free, not that the agent did what you wanted. A task waiting on input is never retired, so a blocked agent holds its slot until you answer it.- “Clear finished” also clears failed. One delete removes done, failed and canceled rows — and a failed row's saved spec is the only thing Retry can re-run, so clearing throws away every retry. That is why it asks first.
The queue also runs headlessly —
muster queue run works the same queue from a terminal with the app closed.
Something must be working it: with neither the app open nor that command running, entries just
sit at their position.
New here? Start the 7-day trial — every feature, card up front, $0 today, key on screen. Or buy outright: $39 once per device, no subscription.