muster

module
v0.7.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 22, 2026 License: MIT

README ΒΆ

muster

A local coordination bus for coding agents.

muster lets coding-agent sessions in separate terminals send messages and hand tasks to each other. Any agent that can register an MCP server can join the bus β€” Claude Code and OpenAI Codex are the two it's tested with. Everything runs over a local unix socket with state in a local SQLite file; muster itself never calls a model.

  • Messages and tasks between sessions. One agent posts a "review this branch" task; a standing session in another terminal claims it, works it, and replies.
  • One static Go binary, three modes: a lazy-started daemon, a stdio MCP server each agent registers, and a CLI for you.
  • tmux-native wake β€” mail sets a mailbox flag (πŸ“¬<count>) on the recipient's tmux session; muster nudge is the only thing that types into a pane.

Landing page: muster.tools

Status

v0.6.0 β€” muster station, the full-screen operator TUI, as a pure drill-down (projects β†’ agents β†’ threads β†’ messages) with its own mailbox page; messages and tasks carry an intent (fyi, reply-requested, action-requested) that renders as a plain word everywhere, not a code; deregistration now tombstones an agent instead of deleting it, so a departed agent's thread history stays visible and re-registering the same alias revives it cleanly; and target resolution is canonical β€” an unknown send/task_create target (CLI or MCP) fails loudly instead of silently creating a thread addressed to nobody. Builds on the v0.5.x bus journal and muster watch live tail, and the v0.2.2 identity/addressing core (project-scoped agents, addressable labels, tmux-verified liveness) on top of the v0.1.0 base (SQLite store, lazy daemon, MCP server, human CLI, notify/nudge wake). See releases for the changelog.

Setup

# 1. install the binary (macOS or Linux; on Windows use WSL2)
curl -fsSL https://muster.tools/install.sh | sh
#    (or build from source with Go 1.22+: go install github.com/schuettc/muster/cmd/muster@latest)

# 2. register the MCP server with each agent
claude mcp add muster -s user -- muster mcp     # Claude Code
codex mcp add muster -- muster mcp              # Codex
# (any other MCP client: point it at `muster mcp` over stdio)

# 3. in each session, have the agent call register_agent once
#    (or add that instruction to your project's CLAUDE.md / AGENTS.md)

That's a working bus. Two optional layers, both in contrib/:

  • See the mailbox β€” two lines of tmux config render πŸ“¬<count> on tabs with unread mail (contrib/tmux-mailbox.conf).
  • Automate the lifecycle β€” session hooks (muster hook <event> <model>) auto-register agents on start and have them drain their own inbox at turn end (config for both harnesses in contrib/).

MCP mode

muster mcp runs muster as an MCP server over stdio, exposing the bus as tools any MCP client (Claude Code, Codex) can call. Register it once per tool:

# Claude Code
claude mcp add muster -s user -- muster mcp
# Codex
codex mcp add muster -- muster mcp

The tools, by what they do:

Group Tools Notes
Identity register_agent, list_agents join the bus once per session; see who's on it
Conversation send_message, reply, get_inbox, get_thread a message is a plain thread β€” no state, just an exchange
Work task_create, task_claim, task_transition a task is a thread with a lifecycle: open β†’ claimed β†’ needs_info | blocked β†’ completed | declined | cancelled. Claiming is atomic β€” two agents can't take the same task
Shared state kv_set, kv_get a key/value scratchpad both sides can read (an API contract, a port, a decision)

The MCP server talks to the local daemon (auto-started on first use).

Note: stdout is the MCP channel in this mode; muster writes all diagnostics to stderr.

CLI

Agents coordinate through the MCP tools above β€” the CLI is for you (and for hooks): commands you run from any shell to watch the bus and step in when you want to (they auto-start the daemon):

muster agents                              # who's registered
muster inbox <alias>                       # an agent's threads β€” addressed to it or started by it
muster tasks <alias>                       # just the tasks for an agent
muster events                              # the bus event log: every mailbox notify and inbox read
muster watch                               # follow the bus live β€” every message, task, wake and read as it happens
muster station                             # the full-screen operator TUI β€” projects, agents, threads, compose
muster send <alias> "message"  --from me   # send a directed message
muster send <alias> "message"  --from me --intent action-requested  # mark it as needing a reply
muster send --role reviewer "please look"  --from me   # to a role
muster send --broadcast "heads up"         --from me   # to everyone
Registering & liveness

Agents can self-register (so a shell hook can do it at session start):

muster register [alias] --role <r> --model <name>
muster deregister [alias]
muster gc                 # tombstone agents whose tmux session is gone
muster gc --purge-agents  # hard-delete departed/dead agent rows (irreversible)

deregister (and gc's default reap) don't delete an agent's row anymore β€” they tombstone it (departed=1): identity, project, label, and read-state all survive, so a departed agent's history stays visible (muster station lists them dimmed, below the live roster) and re-registering the same alias revives it cleanly. muster gc --purge-agents is the old hard-delete behavior, now explicit and opt-in: it removes every departed or currently-dead agent row for good.

muster gc also prunes the event log: rows older than --events-keep are deleted (default 720h, i.e. 30 days), so the journal doesn't grow without bound on a long-running daemon.

register captures the tmux pane automatically. Alias precedence: explicit arg β†’ $MUSTER_ALIAS β†’ tmux session name. --model is stored on the agent and tunes muster nudge's submit keystroke (claude and codex auto-submit; other values are typed without submitting).

muster agents shows each agent's project and live label:

  • project is derived from the tmux socket name when it follows a proj-<name> convention (one tmux server per project). On the default tmux server there's no project β€” everything shares one namespace, and the rest of muster works the same.
  • label is a name you give a session: run muster label backend inside it (or muster label --clear to remove it). Only deliberately-set labels are addressable; auto-generated values are shown parenthesized and are not. (Stored in a tmux session option β€” default @claude_task, override with $MUSTER_LABEL_OPTION.)
Addressing

Any command that takes a target β€” send, nudge, inbox, tasks β€” accepts a target of the form <alias|label|proj:label>:

  • an alias (the tmux session name, globally unique): muster nudge muster-2
  • a label, resolved within your current project: muster send frontend "…"
  • a qualified label to cross projects: muster send timewalk:frontend "…"

A bare label never silently crosses projects; if it's ambiguous or only exists elsewhere, muster errors and lists the proj:label candidates. Resolution is canonical on the daemon side, not just the CLI: an unresolvable target fails with an error and never creates a thread, whether the call came from muster send/muster nudge or an MCP tool like send_message/task_create.

muster station

muster station is the operator's station β€” the full-screen TUI where everyone reports in. It's a pure drill-down chain: projects β†’ agents β†’ threads β†’ messages. Projects and their agents browse two columns wide (a list on the left, a preview of the selected row on the right); open an agent and its threads open into a full-width table; open a thread and it reads full-width.

Keys: Enter opens the selected item Β· Esc goes back one level Β· g jumps home from anywhere Β· m toggles the mailbox page β€” station's own mail, unread and read history; the header always shows a πŸ“¬ badge with the current unread count, on every screen Β· s opens the composer to send (with a target picker and an intent cycle) Β· r replies on the open thread Β· n nudges the selected agent (with a confirmation prompt) Β· / filters the current list Β· a toggles aliases vs. labels Β· q quits.

Intents render as plain words, not the CLI's bracket shorthand β€” "needs action", "wants reply", "fyi". An agent that exits cleanly doesn't vanish from its project: it stays listed below a divider, dimmed, with its thread history intact (a tombstone).

Station registers on the bus itself, as agent station β€” muster send station "…" and muster nudge station reach it like any other agent. If an alias station is already live (a second station on the same machine), it fails over to station-2, station-3, and so on. It deregisters on quit, provided nothing else has since taken over its alias.

Notifications & nudging

When a thread that concerns an agent gets new activity β€” a message addressed to it, or a reply on a thread it started β€” muster sets @muster_inbox on its tmux session to that agent's unread count. It never types into a pane, and unlike a transient bell the flag persists until the agent reads its inbox (get_inbox), which clears it. An agent's own writes never flag its own mailbox. tmux doesn't display the option by default β€” add the two render lines from contrib/tmux-mailbox.conf to see πŸ“¬<count> on the tab title and status bar.

The badge is a session-level count, not a per-alias one: if a session registers under more than one alias (a session name plus a chosen label, say), the count is that session's distinct unread threads, deduplicated across its aliases β€” a thread addressed to both aliases is counted once, and draining one alias's inbox brings the badge down to whatever the session's other aliases still have unread, never to zero prematurely and never double-counted.

A message or task carries an optional intent β€” fyi, reply-requested, or action-requested (set with muster send --intent, or by the MCP tools; a task defaults to action-requested since it's inherently a request). muster events and muster watch tag rows with it ([fyi], [reply?], [action]), and the Stop hook's drain instruction (below) splits its count accordingly: "You have N unread muster thread(s), M needing action."

Every notify outcome (lit, cleared, skipped, errored) and every inbox read is recorded in an event log β€” muster events [--agent <alias>] [--limit <n>] β€” so "whose mailbox was lit when, and when was it cleared" is answerable after the fact. Rows now carry the thread's subject alongside its target, so you can tell which conversation an event belongs to without a separate lookup. muster watch is the live view of the same journal: it prints new rows as they land instead of a fixed page, and Ctrl-C exits immediately.

To actively poke an agent to act now:

muster nudge <alias>              # types the full drain-and-act instruction into the agent's pane and submits
muster nudge <alias> --no-submit  # type only; don't press Enter

Nudge submits for both Claude Code (immediate Enter) and Codex (a short delayed Enter β€” Codex treats an Enter bundled with pasted text as part of the paste). Other model types are typed without submitting.

Hooks (optional)

Registration and inbox-draining can be driven by session lifecycle hooks instead of typed by hand:

  • SessionStart β†’ muster register β€” the session joins the bus on start. Claude Code fires this at launch; Codex fires it on the session's first turn, so say anything to a fresh Codex session ("hi" is enough) before addressing mail to it.
  • Stop (turn end) β†’ if the session has unread muster mail, the hook tells the agent to drain its inbox and reply, autonomously.
  • SessionEnd (Claude Code) β†’ muster deregister; muster gc covers the rest.

The muster binary is its own hook β€” point your harness at muster hook <event> <model> (e.g. muster hook Stop claude). Copy-paste config for both Claude Code and Codex is in contrib/.

Pane ownership. Only the session's primary agent pane acts on these hooks β€” a second Claude in the same tmux session (e.g. a spawned subagent) won't register, drain mail, or deregister. To move the identity deliberately, run muster register from the pane that should own it β€” the explicit commands always override.

License

MIT Β© Court Schuett

Directories ΒΆ

Path Synopsis
cmd
muster command
Command muster is the entrypoint for the muster coordination bus.
Command muster is the entrypoint for the muster coordination bus.
internal
client
Package client talks to the muster daemon, spawning it if needed.
Package client talks to the muster daemon, spawning it if needed.
clock
Package clock provides a test-overridable millisecond clock.
Package clock provides a test-overridable millisecond clock.
daemon
Package daemon serves the muster store over a unix socket.
Package daemon serves the muster store over a unix socket.
display
Package display is the one canonical sanitizer for anything a hostile or careless bus payload (subject, body, reply preview) could put in front of an operator's terminal: the renderer, journal preview columns, and the station TUI all funnel through Sanitize rather than each rolling their own escaping.
Package display is the one canonical sanitizer for anything a hostile or careless bus payload (subject, body, reply preview) could put in front of an operator's terminal: the renderer, journal preview columns, and the station TUI all funnel through Sanitize rather than each rolling their own escaping.
humancli
Package humancli implements muster's operator subcommands (agents, inbox, send, tasks) that read/drive the bus from a plain shell.
Package humancli implements muster's operator subcommands (agents, inbox, send, tasks) that read/drive the bus from a plain shell.
mcpserver
Package mcpserver exposes muster's daemon operations as MCP tools over stdio.
Package mcpserver exposes muster's daemon operations as MCP tools over stdio.
mustertest
Package mustertest provides shared test helpers for muster.
Package mustertest provides shared test helpers for muster.
nudge
Package nudge delivers an operator-triggered "check your inbox" prompt into an agent's tmux pane via send-keys.
Package nudge delivers an operator-triggered "check your inbox" prompt into an agent's tmux pane via send-keys.
paths
Package paths resolves muster's on-disk locations.
Package paths resolves muster's on-disk locations.
proto
Package proto defines the daemon wire protocol: newline-delimited JSON.
Package proto defines the daemon wire protocol: newline-delimited JSON.
render
Package render holds the shared bus-journal rendering machinery: decoding list_events pages, resolving the alias→label map, and rendering rows into the WHO/WHAT vocabulary the humancli `events`/`watch` commands and the station TUI's feed pane both use.
Package render holds the shared bus-journal rendering machinery: decoding list_events pages, resolving the alias→label map, and rendering rows into the WHO/WHAT vocabulary the humancli `events`/`watch` commands and the station TUI's feed pane both use.
resolve
Package resolve is muster's single canonical target-resolution module (CLAUDE.md's "one canonical module per concern" rule): the precedence a bare user- or agent-supplied target string is matched against the roster by β€” exact alias, then a project-qualified "proj:label", then a bare label scoped to the caller's own project.
Package resolve is muster's single canonical target-resolution module (CLAUDE.md's "one canonical module per concern" rule): the precedence a bare user- or agent-supplied target string is matched against the roster by β€” exact alias, then a project-qualified "proj:label", then a bare label scoped to the caller's own project.
station
Package station implements `muster station`, the operator TUI.
Package station implements `muster station`, the operator TUI.
store
Package store is muster's SQLite persistence layer.
Package store is muster's SQLite persistence layer.
tmuxenv
Package tmuxenv is muster's single point of contact with tmux from outside the daemon: capturing the current pane's identity, deriving the project from the per-project socket, checking session liveness, and reading the session label.
Package tmuxenv is muster's single point of contact with tmux from outside the daemon: capturing the current pane's identity, deriving the project from the per-project socket, checking session liveness, and reading the session label.
version
Package version holds muster's build-time version stamp.
Package version holds muster's build-time version stamp.
wake
Package wake delivers best-effort "notify" signals to agents' tmux sessions by setting/clearing a per-session tmux option (which the operator's status bar surfaces).
Package wake delivers best-effort "notify" signals to agents' tmux sessions by setting/clearing a per-session tmux option (which the operator's status bar surfaces).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL