muster

module
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Sep 10, 2026 License: MIT

README

Muster

A session manager for concurrent Claude Code sessions: a Go daemon (musterd) plus a web dashboard served in its own window, with interactive terminal panes backed by tmux.

Personal tool, macOS only, single user. Not a product.

Why "Muster": to muster is to assemble a group and review it. The name is deliberately agent-CLI-agnostic — nothing in it ties to Claude, so supporting other agent CLIs later costs no rename. internal/claudecode/ is the only concession to the current one.

Status

M0–M4 shipped. The daemon manages real sessions end to end: hook/status-line ingest, the state machine, the SQLite store, tmux-backed terminal panes, reconcile and shutdown policy, and a dashboard with Focus and Tiles views. Remaining work is the Pre-v1 Cleanup and the triaged pre-v1 issues in TODO.md.

musterd ships as a single self-contained binary — the dashboard is compiled in via internal/webui (//go:embed), so a copy of the binary needs nothing beside it.

Document What it is
SPEC.md Authoritative spec. Features, tech stack, milestones, risks
TODO.md Milestone backlog (SPEC §10) and open questions
next-steps.md Session-by-session plan
spikes/FINDINGS.md Validated interface facts, measured against Claude Code 2.1.233 (addenda through 2.1.246)
spikes/canary-fields.md Field inventory the canary asserts before any version bump
interview-notes.md Rationale and rejected options
docs/claude-code-versions.md The verified version range and the upgrade rituals

claude-session-manager-handoff.md is prior research, treated as input only — SPEC.md supersedes it wherever they disagree.

Issues and contributions

Issues are welcome; pull requests are not accepted at this time. Muster is built through a fixed multi-agent pipeline (CLAUDE.md § Workflow) with one maintainer, and outside changes don't fit that flow yet — a PR will be closed without review. Fork freely instead; the MIT licence permits it. If that policy changes, this section will say so.

Two ways to file an issue:

  • The dashboard's Issue button (masthead). It attaches a snapshot of muster's own state — session states, timings, context percentages — built from a strict allowlist that never includes prompt text, hook payloads, pane contents, directories, branches or repo names. The dialog shows the exact payload before you post, and what you see is byte-for-byte what gets sent.
  • GitHub directly, if you'd rather not attach anything.

Things to know about the button:

  • The issue is public, filed on this repo under your GitHub account, so read the preview as you would any public post.
  • It needs the gh CLI, logged in. Muster takes the token from gh auth token at the moment you click, stores nothing, and has no login flow of its own. If filing fails with an auth error, run gh auth login and try again.

Layout

cmd/musterd/          daemon entrypoint
internal/claudecode/  adapter for everything Claude-Code-specific (hooks, status line, CLI)
web/                  dashboard (Vite + TypeScript, no framework) + Playwright E2E
test/canary/          canary suite: run before adopting a new Claude Code version
docs/                 operational docs
spikes/               step-1 validation findings (historical record, not built code)

Packages arrive when they have contents — M0 adds the HTTP/WS server, the state machine and the SQLite store.

Prerequisites

  • tmux 3.2 or newer. Muster runs every session in tmux, on its own dedicated socket, never your default server. musterd checks this at startup and refuses to start if it's missing or too old, naming the remedy:
    brew install tmux    # not installed
    brew upgrade tmux    # older than 3.2
    
  • macOS. Muster is single-user, macOS-only tooling — see SPEC.md.
  • Claude Code. Auto-update stays on; see Claude Code versions below.

Install

Releases are published to GitHub whenever a feat or fix lands on main (see docs/conventions.md § Commits). Each release publishes two darwin archives — musterd_*_darwin_amd64.tar.gz (Intel) and musterd_*_darwin_arm64.tar.gz (Apple Silicon); take the one matching your Mac (uname -m: x86_64 → amd64, arm64 → arm64). The repo is private, so downloads go through gh:

make install     # latest release -> ~/.local/bin/musterd

Or by hand, if you want a different location or a specific version. Both blocks below take the latest release — gh release download defaults to it when given no tag, which is why --pattern is mandatory there. Apple Silicon (uname -m → arm64):

tmp=$(mktemp -d) &&
gh release download --repo Zalaras/muster \
  --pattern 'musterd_*_darwin_arm64.tar.gz' --dir "$tmp" &&
mkdir -p ~/.local/bin &&
tar -xzf "$tmp"/musterd_*.tar.gz -C ~/.local/bin musterd &&
rm -rf "$tmp"

Intel (uname -m → x86_64) — identical but for the pattern:

tmp=$(mktemp -d) &&
gh release download --repo Zalaras/muster \
  --pattern 'musterd_*_darwin_amd64.tar.gz' --dir "$tmp" &&
mkdir -p ~/.local/bin &&
tar -xzf "$tmp"/musterd_*.tar.gz -C ~/.local/bin musterd &&
rm -rf "$tmp"

Both download into a fresh temp dir on purpose, and that is not cosmetic (it is what make install does too). Downloading into the current directory needs --clobber on every re-run, and leaves last version's archive sitting there — so musterd_*.tar.gz then matches several files and tar is handed a list of archives plus a member name and fails (tar: musterd_0.3.0_darwin_arm64.tar.gz: Not found in archive). An empty dir per run makes the glob single by construction. Substitute a different -C target for a location other than ~/.local/bin; anything already on your $PATH works.

To pin a version instead of taking the latest, pass the tag as the first argument — the rest of the command is unchanged:

gh release download v0.3.0 --repo Zalaras/muster \
  --pattern 'musterd_*_darwin_arm64.tar.gz' --dir "$tmp"

The binary is unsigned, but gh doesn't set the com.apple.quarantine xattr, so it runs without a Gatekeeper prompt. If you download one through a browser instead, clear it with xattr -d com.apple.quarantine musterd.

Confirm it worked — both lines, not just the second:

command -v musterd   # must print the path you just installed to
musterd -version     # prints the musterd version and the Claude Code verified range

If command -v prints some other path, an older copy earlier in your $PATH (/usr/local/bin/musterd is the usual one) is shadowing the install and -version is reporting that stale binary, not the one you just fetched. Delete the old copy, or re-run the install with -C pointing at the directory it lives in.

musterd -version succeeds even without tmux installed — the tmux preflight above only runs when musterd actually starts.

Requirements

Tool Version Note
Go 1.26.6
Node 24.19.0 pinned in .nvmrc; nvm use in the repo root
tmux 3.7b Dev machine's version; musterd preflights 3.2+ at startup (see Prerequisites)
Claude Code 2.1.246–2.1.267 The verified range. Auto-update is deliberately left on — see below

Frontend toolchain: Vite 8, TypeScript 7, Playwright 1.62.

Claude Code versions

Muster does not disable Claude Code's auto-updater — that would freeze your everyday install. It detects and classifies instead: musterd compares the installed version against the canary-verified range (2.1.246–2.1.267 as of this build) and logs a warning on either side of it — older than the floor means "update Claude Code", newer than the ceiling means Muster hasn't been tested against it yet — without ever refusing to start. A green, non-offline make canary run extends the range automatically. The full rituals are in docs/claude-code-versions.md.

Development

Build from source (Go and Node versions in the table above; nvm use picks the pinned Node):

git clone https://github.com/Zalaras/muster && cd muster
nvm use && make build        # ./bin/musterd, dashboard embedded
make help      # list targets
make build     # build ./bin/musterd
make check     # lint + unit tests
make web       # frontend dev server
make e2e       # Playwright suite (needs: npx playwright install chromium)
make canary    # assert the installed Claude Code still emits what Muster needs

Directories

Path Synopsis
cmd
musterd command
Command musterd is the Muster daemon: it owns the tmux sessions, ingests Claude Code hooks and status-line posts, derives session state, and serves the dashboard.
Command musterd is the Muster daemon: it owns the tmux sessions, ingests Claude Code hooks and status-line posts, derives session state, and serves the dashboard.
internal
claudecode
Package claudecode is the adapter boundary for everything Claude-Code-specific: hook payloads, the status-line payload, CLI invocation and version pinning.
Package claudecode is the adapter boundary for everything Claude-Code-specific: hook payloads, the status-line payload, CLI invocation and version pinning.
claudecode/claudecodetest
Package claudecodetest builds Claude-Code-wire-format request bodies for tests in other packages.
Package claudecodetest builds Claude-Code-wire-format request bodies for tests in other packages.
ghissue
Package ghissue is a minimal GitHub Issues client: obtaining a bearer token and filing one issue.
Package ghissue is a minimal GitHub Issues client: obtaining a bearer token and filing one issue.
gitutil
Package gitutil shells out to the `git` CLI (docs/conventions.md: "Git/GitHub: never go-git") to answer the small set of questions m1-sessions needs about a launch directory: is it a git checkout, what branch is it on, and is it a linked worktree.
Package gitutil shells out to the `git` CLI (docs/conventions.md: "Git/GitHub: never go-git") to answer the small set of questions m1-sessions needs about a launch directory: is it a git checkout, what branch is it on, and is it a linked worktree.
locate
Package locate resolves a dropped file's original on-disk path from its bytes alone (plan file-drop-fix): a browser hands a page a dropped file's name and bytes, never its path, so the daemon locates the original by asking a sequence of Finders for basename+size candidates and verifying each one byte-for-byte before it counts.
Package locate resolves a dropped file's original on-disk path from its bytes alone (plan file-drop-fix): a browser hands a page a dropped file's name and bytes, never its path, so the daemon locates the original by asking a sequence of Finders for basename+size candidates and verifying each one byte-for-byte before it counts.
server
Package server wires up musterd's HTTP+WS surface: auth, static serving, the ingest endpoints and the state stream.
Package server wires up musterd's HTTP+WS surface: auth, static serving, the ingest endpoints and the state stream.
session
Package session holds the §7 state machine (docs/protocol.md) and the in-memory session registry that feeds it.
Package session holds the §7 state machine (docs/protocol.md) and the in-memory session registry that feeds it.
store
Package store owns Muster's SQLite database: opening it in WAL mode, applying migrations, and the hand-written SQL for the kv and event tables.
Package store owns Muster's SQLite database: opening it in WAL mode, applying migrations, and the hand-written SQL for the kv and event tables.
termbridge
Package termbridge owns the daemon-side PTY lifecycle for one terminal socket (docs/protocol.md §6): spawning `tmux attach-session` under a `creack/pty`-managed pseudo-terminal, streaming raw bytes both directions, and resizing per FINDINGS §7(d) (pty.Setsize then tmux resize-window, never the pane-level primitive).
Package termbridge owns the daemon-side PTY lifecycle for one terminal socket (docs/protocol.md §6): spawning `tmux attach-session` under a `creack/pty`-managed pseudo-terminal, streaming raw bytes both directions, and resizing per FINDINGS §7(d) (pty.Setsize then tmux resize-window, never the pane-level primitive).
tmux
Package tmux wraps the `tmux` CLI operations Muster needs, always against a dedicated socket (CLAUDE.md hard rule: never the user's default server).
Package tmux wraps the `tmux` CLI operations Muster needs, always against a dedicated socket (CLAUDE.md hard rule: never the user's default server).
tmux/tmuxtest
Package tmuxtest is the one shared per-test tmux socket helper (plan v1-cleanup REQ-4): internal/tmux, internal/termbridge, internal/server and cmd/musterd's tests all built their own copy of the same os.MkdirTemp + t.Cleanup(kill-server) idiom, so this replaces all of them.
Package tmuxtest is the one shared per-test tmux socket helper (plan v1-cleanup REQ-4): internal/tmux, internal/termbridge, internal/server and cmd/musterd's tests all built their own copy of the same os.MkdirTemp + t.Cleanup(kill-server) idiom, so this replaces all of them.
usage
Package usage holds the account-level usage aggregator (m3-gauges): a neutral Sample shape plus one in-memory Aggregator, the SPEC §9.6 seam for a second usage source down the line.
Package usage holds the account-level usage aggregator (m3-gauges): a neutral Sample shape plus one in-memory Aggregator, the SPEC §9.6 seam for a second usage source down the line.
webui
Package webui embeds the built dashboard (web/vite.config.ts's outDir) into the musterd binary, so a copy of the binary moved away from the checkout still serves its own frontend (plan embed-dashboard REQ-1).
Package webui embeds the built dashboard (web/vite.config.ts's outDir) into the musterd binary, so a copy of the binary moved away from the checkout still serves its own frontend (plan embed-dashboard REQ-1).
test
rig/capture command
Command capture is the interface-probe capture server.
Command capture is the interface-probe capture server.
rig/failproxy command
Command failproxy is a fault-injection stand-in for the Anthropic API, used by interface probes to induce API errors inside a Claude Code turn (point ANTHROPIC_BASE_URL at it — honoured under subscription OAuth, spikes/FINDINGS.md §3).
Command failproxy is a fault-injection stand-in for the Anthropic API, used by interface probes to induce API errors inside a Claude Code turn (point ANTHROPIC_BASE_URL at it — honoured under subscription OAuth, spikes/FINDINGS.md §3).
tools
versions command
Command versions keeps internal/claudecode/observed_versions.txt and the generated fragments in README.md, spikes/canary-fields.md and docs/claude-code-versions.md in sync (docs/claude-code-versions.md "The green ritual").
Command versions keeps internal/claudecode/observed_versions.txt and the generated fragments in README.md, spikes/canary-fields.md and docs/claude-code-versions.md in sync (docs/claude-code-versions.md "The green ritual").

Jump to

Keyboard shortcuts

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