Introduction
Muster is a Rust library and CLI for terminal session group management built on tmux. It organizes terminal sessions into named, color-coded groups with saved profiles, runtime theming, and push-based state synchronization via tmux control mode.
What Muster Does
- Organizes terminals by project — group related tabs (shell, server, logs) into a single named session
- Saves profiles — define reusable templates for your project setups
- Applies color themes — each group gets a distinct color in the tmux status bar
- Syncs state via tmux — no polling, no stale state files; tmux is the single source of truth
- Provides a library API — the CLI is a thin consumer of the
musterRust library; the API is designed for GUI integration
Who It’s For
Developers who work across multiple projects and maintain numerous terminal sessions — development servers, test runners, build watchers — spread across many terminal tabs with no organizational structure. Muster turns that chaos into named, color-coded groups you can launch, switch between, and tear down with single commands.
How It Works
Muster is a tmux interface layer, not a tmux replacement. It creates and manages tmux sessions on your behalf, storing metadata (name, color, profile reference) as tmux user options on the sessions themselves. Profiles are saved templates; running state lives entirely in tmux.
The architecture is a Cargo workspace with three crates:
| Crate | Purpose |
|---|---|
muster | Library — tmux bindings, profiles, theming, control mode |
muster-cli | CLI binary |
muster-notify | macOS notification helper (optional) |
Documentation Structure
- Getting Started — installation and first steps
- User Guide — concepts, profiles, sessions, and features
- CLI Reference — complete command documentation (auto-generated)
- Architecture — internal design and tmux interface details
- Development — testing and contributing
- API Reference — rustdoc for the library crate
Installation
Prerequisites
- tmux — installed and available in PATH
- Rust — 2021 edition (for building from source)
Install from Source
cargo install --path crates/muster-cli
This installs the muster binary.
Optional: macOS Desktop Notifications
cargo install --path crates/muster-notify
muster notifications setup
This creates a minimal MusterNotify.app bundle at ~/.config/muster/MusterNotify.app/ containing the notification helper binary. The app bundle provides a CFBundleIdentifier (com.muster.notifier) that macOS requires for persistent Notification Center access. See Notifications for details.
Quick Start
Try It: System Dashboard
Here’s a ready-to-use profile you can launch right now. It opens three tabs — a shell, a live process monitor, and a live disk usage view:
muster profile save sysmon --color indigo \
--tab 'Shell:~' \
--tab 'Processes:~:top' \
--tab 'Disk:~:sh -c "while true; do clear; df -h; sleep 5; done"'
muster up sysmon
You’re now inside tmux with three tabs. Switch between them with Ctrl-b n (next) or Ctrl-b 0/1/2. Detach with Ctrl-b d to return to your shell — the session keeps running.
Upgrading to htop
htop is a better process monitor with color output, mouse support, and easier process management. Install it if you don’t have it:
# macOS
brew install htop
# Debian/Ubuntu
sudo apt install htop
# Fedora/RHEL
sudo dnf install htop
Then edit the sysmon profile to swap in htop. From outside tmux, run:
muster profile edit sysmon
This opens the profile in $EDITOR as TOML. Find the Processes tab and change the command:
[[tabs]]
name = "Processes"
cwd = "/Users/you"
command = "top" # change this to "htop"
Save and close the editor. Then bounce the session to pick up the change:
muster down sysmon
muster up sysmon
The Processes tab now runs htop.
Terminal Configuration
When you run muster up from inside an existing tmux session, muster opens a new terminal window rather than nesting sessions. It needs to know which terminal emulator to use.
Check the current setting:
muster settings
If it shows the wrong terminal, update it:
muster settings --terminal ghostty
muster settings --terminal kitty
muster settings --terminal alacritty
muster settings --terminal wezterm
muster settings --terminal iterm2
On macOS the default is Terminal.app (terminal). On Linux muster probes PATH for a known terminal. You only need to set this once — it persists in ~/.config/muster/settings.json.
See Configuration for all available settings.
Create Your Own Profile
Save a profile for a project:
muster profile save myproject --tab 'Shell:~/work/myproject' --color orange
Multi-tab example for a web project:
muster profile save webapp --color '#3b82f6' \
--tab 'Shell:~/work/app' \
--tab 'Server:~/work/app:npm start' \
--tab 'Logs:~/work/app/logs'
Start a Session
muster up myproject
This creates the tmux session and drops you in. If the session already exists, up reattaches instead of creating a duplicate.
Check What’s Running
From another terminal:
muster status
Reattach
muster up sysmon
Ad-hoc Sessions
Create a throwaway session without saving a profile:
muster new scratch
Typical Workflow
muster profile save— define a project (name, tabs, color)muster up <name>— start or reattach (execstmux attach, replacing your shell)- Work inside tmux. Use
Ctrl-b dto detach back to your regular shell. muster up <name>again to reattach latermuster statusfrom another terminal to see all sessionsmuster down <name>when done
For more detailed workflows — including adopting existing tmux sessions, saving a session as a profile, and releasing sessions back to plain tmux — see Workflows.
Concepts
Terminal Group
A named tmux session containing one or more windows (tabs), each with a working directory and optional startup command. Groups have a display name, color, and optional profile reference.
| App Concept | tmux Concept |
|---|---|
| Terminal Group | Session |
| Tab | Window |
| Terminal | Pane |
Profile
A saved template for creating a group. Stored in ~/.config/muster/profiles.json. Contains the group’s name, color, and tab definitions.
Profiles are not running state — they’re blueprints. You can have a profile without a running session, or a running session created ad-hoc without a profile.
Session
A running tmux session managed by muster. Session names are prefixed with muster_ to distinguish them from personal tmux sessions. Application metadata (name, color, profile ID) is stored as tmux user options (@muster_name, @muster_color, @muster_profile) on the session itself — no separate state file.
Sources of Truth
There are exactly two sources of truth:
| Source | Owns |
|---|---|
| tmux | All running state: windows, CWDs, active window, plus @muster_* metadata |
| Config directory | Saved profiles and settings — never runtime state |
There is no application-level cache. When you need session state, muster asks tmux. This eliminates state synchronization bugs entirely.
Session Naming
All managed sessions use the prefix muster_ followed by a slugified profile ID:
muster_myproject
muster_web-app
This lets muster distinguish its sessions from your personal tmux sessions.
Workflows
Muster is flexible about how you get to a managed session — you can start from a saved profile or from a running tmux session. This page makes the common workflows concrete.
The Core Concepts
- Profile — a saved template in
~/.config/muster/profiles.json. Defines name, color, and tabs. Persists across reboots. - Session — a live tmux session. May or may not have a backing profile.
- Pinned tab — a window tied to the profile. Shows the session color. Recreated when you
muster up. - Unpinned tab — a window with no profile backing. Shows a red
●. Lost when the session dies.
Profile First (The Happy Path)
You know what you want before you start.
muster profile save myproject \
--tab 'Shell:~/work/myproject' \
--tab 'Server:~/work/myproject:npm run dev' \
--color blue
muster up myproject # creates session and attaches
Next time:
muster up myproject # reattaches if running, creates if not
When done:
muster down myproject # kills the session (profile is kept)
Session First (Build Then Save)
You start working without a profile, then decide to keep it.
muster new scratch # ad-hoc session, attaches immediately
Inside the session, open more tabs however you like (Ctrl-b c). When you’re happy with the layout:
muster profile save myproject --from-session scratch
This snapshots the current tabs, saves a profile, and pins all windows in the live session (red dots clear immediately). The session is now fully managed:
muster down scratch
muster up myproject # recreates from profile
Adopting a Plain tmux Session
You have existing tmux sessions that predate muster.
tmux ls
# work: 3 windows (created ...)
# scratch: 1 window (created ...)
Bring one under muster management:
muster adopt work --name "Work" --color orange
This renames work → muster_work, applies the theme, and attaches. The session keeps running.
To also save a profile so the session can be recreated:
muster adopt work --name "Work" --color orange --save
--save snapshots the tabs into a profile and pins all windows in one step.
Formalizing an Ephemeral Muster Session
You have a muster-managed session (already has muster_ prefix) but no saved profile — tabs show red ●.
muster list
# Sessions:
# ● muster_232 — 232 (2 windows, 2 unpinned)
Save it:
muster profile save 232 --from-session 232
This saves the profile and pins the live windows. The red dots clear immediately. Now muster up 232 can recreate the session if it ever dies.
Editing a Profile and Bouncing
Make changes to a saved profile and apply them to a fresh session.
muster profile edit myproject # opens in $EDITOR as TOML
Or update inline:
muster profile update myproject --color teal
muster profile add-tab myproject --name Logs --cwd ~/work/myproject/logs
To pick up changes in a running session, bounce it:
muster down myproject
muster up myproject
Releasing a Session
Remove muster management while keeping the session alive. Useful if you want to hand off to plain tmux or clean up muster’s theming.
muster release myproject
# Released: muster_myproject → myproject
The session keeps running as plain tmux with no muster theme, hooks, or metadata. The profile is kept — you can muster up myproject later to create a fresh managed session.
Lifecycle Summary
plain tmux session ──── muster adopt ────► muster session
│
muster release
│
▼
plain tmux session
muster profile save ──────────────────► profile (file only)
│
muster up
│
▼
muster session ── muster down ──► dead
| Goal | Command |
|---|---|
| Create profile | muster profile save <name> --tab ... |
| Start / reattach | muster up <name> |
| Tear down | muster down <name> |
| Ad-hoc session | muster new <name> |
| Save running session as profile | muster profile save <name> --from-session <session> |
| Adopt plain tmux session | muster adopt <session> --name <name> |
| Release from muster | muster release <session> |
| Edit profile | muster profile edit <name> |
Profiles
Profiles are saved templates for creating terminal groups. They define the group’s name, color, and tab layout.
Creating Profiles
# Basic profile with one tab
muster profile save myproject --tab 'Shell:~/work/myproject' --color '#f97316'
# Multi-tab profile
muster profile save webapp --color '#3b82f6' \
--tab 'Shell:~/work/app' \
--tab 'Server:~/work/app:npm run dev' \
--tab 'Logs:~/work/app/logs'
The --tab flag uses colon-delimited format: name:cwd or name:cwd:command. It is repeatable for multiple tabs. If omitted, defaults to a single “Shell” tab at $HOME.
Listing Profiles
muster profile list
Viewing a Profile
muster profile show myproject
Editing Profiles
Interactive Editing
Open the profile in your $EDITOR as TOML:
muster profile edit myproject
Inline Updates
Update specific fields without opening an editor:
muster profile update myproject --name renamed --color '#22c55e'
Managing Tabs
# Add a tab
muster profile add-tab myproject --name Tests --cwd ~/work/myproject --command 'cargo test --watch'
# Remove a tab (by name or 0-based index)
muster profile remove-tab myproject Tests
muster profile remove-tab myproject 2
Snapshotting a Running Session
Capture the current tabs and working directories of a live tmux session as a new profile:
muster profile save myproject --from-session <session-name>
This queries the session’s windows, captures their names and CWDs, and saves them as a profile. Useful for formalizing a workflow you built interactively.
Environment Variables
Set per-session environment variables in a profile by editing the TOML:
muster profile edit myproject
[env]
NODE_ENV = "development"
DATABASE_URL = "postgres://localhost/myapp"
These are applied via set-environment -t <session> when the session is created.
Per-Session tmux Options
Override tmux settings for a session without touching your global ~/.tmux.conf:
[tmux_options]
mouse = "on"
status-position = "top"
These are applied via set-option -t <session> at session creation.
Deleting Profiles
muster profile delete myproject
This removes the profile from profiles.json. It does not affect any running sessions that were launched from this profile.
Storage
Profiles are stored in ~/.config/muster/profiles.json:
{
"profiles": {
"myproject": {
"id": "myproject",
"name": "myproject",
"color": "#f97316",
"tabs": [
{ "name": "Shell", "cwd": "/Users/you/work/myproject", "command": null }
]
}
}
}
The config directory can be overridden with --config-dir or the MUSTER_CONFIG_DIR environment variable.
Sessions
Sessions are running tmux sessions managed by muster.
Starting Sessions
# From a profile (creates or reattaches)
muster up webapp
# Attach and switch to a specific tab
muster up webapp --tab 2
# Create without attaching
muster up webapp --detach
up is idempotent — if the session already exists, it attaches. If not, it creates from the profile and attaches.
up replaces the current process with exec tmux attach. Use --detach to create the session in the background without attaching.
Ad-hoc Sessions
Create a session without a saved profile:
muster new scratch --tab 'Shell:~/work' --color '#808080'
muster new scratch --detach
If --tab is omitted, defaults to a single “Shell” tab at $HOME.
Status
# Show all sessions with tab details
muster status
# List profiles and running sessions
muster list
Inspecting Sessions
# Show processes running inside sessions
muster ps
muster ps webapp
# Show listening ports
muster ports
muster ports webapp
Changing Colors
muster color webapp orange
muster color webapp '#22c55e'
muster colour webapp teal-dark # colour is accepted as an alias
The tmux status bar updates instantly and the profile is updated, so the color persists on next muster up. If no session is running, updates the profile directly.
See Colors for the full list of named colors and shade variants.
Adopting Existing Sessions
Bring an existing (non-muster) tmux session under muster management:
muster adopt mysession --name webapp --color '#3b82f6'
This renames the session to muster_webapp and applies the muster status-bar theme. The --name flag defaults to the original session name. No profile is written — the session is managed live.
To also save a profile from the session’s current tabs:
muster adopt mysession --name webapp --color '#3b82f6' --save
Stopping Sessions
muster down webapp
Accepts a profile name, session ID, or full session name. Session metadata dies with the tmux session — no file cleanup needed.
JSON Output
All commands support --json for machine-readable output:
muster status --json
muster list --json
muster peek webapp --json
Colors
Muster sessions have a color applied to the tmux status bar. Colors can be set when creating a profile or changed live on a running session.
Usage
# Set by name
muster color webapp orange
# Set by hex
muster color webapp '#f97316'
# Set a shade variant
muster color webapp red-dark
# List available named colors
muster color --list
# colour is accepted as an alias
muster colour webapp teal
color accepts a profile name, session ID, or full session name. If a session is running, the status bar updates instantly and the profile is also updated. If no session is running, the profile is updated directly.
Named Colors
| Swatch | Name | Aliases | Hex |
|---|---|---|---|
| black | #000000 | ||
| red | #cc0000 | ||
| green | #4e9a06 | ||
| yellow | #c4a000 | ||
| blue | #3465a4 | ||
| magenta | #75507b | ||
| cyan | #06989a | ||
| white | #d3d7cf | ||
| orange | #f97316 | ||
| pink | #ec4899 | ||
| purple | violet | #a855f7 | |
| teal | #14b8a6 | ||
| lime | #84cc16 | ||
| amber | #f59e0b | ||
| rose | #f43f5e | ||
| indigo | #6366f1 | ||
| sky | #0ea5e9 | ||
| emerald | #10b981 | ||
| fuchsia | #d946ef | ||
| coral | #ff7f50 | ||
| tomato | #ff6347 | ||
| crimson | #dc143c | ||
| gold | #ffd700 | ||
| navy | #000080 | ||
| brown | chocolate | #8b4513 | |
| slate | #64748b | ||
| gray | grey | #808080 |
Shade Variants
Append -light or -dark to any color name for a lighter or darker variant:
muster color webapp orange-light
muster color webapp orange-dark
The following families have curated Tailwind CSS shade values. Other named colors compute light/dark by mixing toward white or scaling channels.
| Family | -light | -dark | ||
|---|---|---|---|---|
| slate | #cbd5e1 | #334155 | ||
| gray | #d1d5db | #374151 | ||
| red | #fca5a5 | #b91c1c | ||
| orange | #fdba74 | #c2410c | ||
| amber | #fcd34d | #b45309 | ||
| yellow | #fde047 | #a16207 | ||
| lime | #bef264 | #4d7c0f | ||
| green | #86efac | #15803d | ||
| emerald | #6ee7b7 | #047857 | ||
| teal | #5eead4 | #0f766e | ||
| cyan | #67e8f9 | #0e7490 | ||
| sky | #7dd3fc | #0369a1 | ||
| blue | #93c5fd | #1d4ed8 | ||
| indigo | #a5b4fc | #4338ca | ||
| violet | #c4b5fd | #6d28d9 | ||
| purple | #d8b4fe | #7e22ce | ||
| fuchsia | #f0abfc | #a21caf | ||
| pink | #f9a8d4 | #be185d | ||
| rose | #fda4af | #be123c |
Hex Colors
Any hex color is accepted directly:
muster color webapp '#a855f7'
muster color webapp '#10b981'
Peek
Check on a session’s terminal output without attaching:
muster peek myproject # all tabs, last 50 lines each
muster peek myproject Shell # specific tab only
muster peek myproject -n 10 # last 10 lines per tab
muster peek myproject --json # machine-readable output
Peek uses tmux capture-pane to grab scrollback from each tab. It’s a read-only operation that doesn’t affect the session.
Death Snapshots
When a tab’s process exits, muster captures the last 50 lines of output before cleaning up the dead tab. Snapshots are saved to ~/.config/muster/logs/<session_name>/<tab_name>.last.
Files are overwritten on each death event per tab name, keeping the directory small. The last few lines are included in the desktop notification body when notifications are enabled.
This preserves output that would otherwise be lost when tmux cleans up dead tabs — useful for seeing why a build or server crashed without having been attached at the time.
Pin & Unpin
Pin and unpin sync the current tab’s state back to the session’s profile.
Pin
muster pin
Run this from inside a muster-managed tmux session. It saves the current tab’s name, working directory, and command to the session’s profile. This is useful when you’ve customized a tab at runtime and want those changes persisted.
Unpin
muster unpin
Removes the current tab from the session’s profile. The tab continues to exist in the running session but won’t be recreated when the profile is run again.
Tab Rename Sync
Muster installs a tmux hook that automatically syncs tab renames to the profile when a tab is pinned. This means renaming a tab with Ctrl-b , updates the profile if the tab is pinned.
Notifications
Muster sends notifications on session events — pane exits (with last output lines) and terminal bell alerts.
Default Behavior
By default, notifications appear as tmux status bar messages. No setup required.
macOS Desktop Notifications
On macOS, you can enable native desktop notifications (Notification Center):
cargo install --path crates/muster-notify
muster notifications setup
This creates a minimal MusterNotify.app bundle at ~/.config/muster/MusterNotify.app/ containing the muster-notify helper binary. The app bundle provides a CFBundleIdentifier (com.muster.notifier) that macOS requires for persistent Notification Center access.
macOS may prompt you to allow notifications from Muster on first use.
When the helper is installed, notifications are delivered to Notification Center instead of the tmux status bar.
SSH Fallback
Over SSH (SSH_CONNECTION is set), muster falls back to tmux display-message automatically, regardless of whether the notification helper is installed.
Removing Notifications
muster notifications remove
This removes the MusterNotify.app bundle. Notifications revert to tmux status bar messages.
How It Works
Muster installs tmux hooks (pane-died and alert-bell) that invoke CLI subcommands to deliver notifications. The hooks are set per-session when a session is created.
Pane death notifications include the last few lines of terminal output in the notification body, so you can see at a glance why something crashed.
Configuration
Config Directory
Muster stores configuration in ~/.config/muster/ by default. Override with --config-dir or the MUSTER_CONFIG_DIR environment variable.
~/.config/muster/
├── profiles.json # Saved terminal group profiles
├── settings.json # Global settings
├── logs/ # Death snapshots
│ └── <session_name>/
│ └── <window_name>.last
└── Muster.app/ # macOS notification helper (optional)
Settings (settings.json)
{
"terminal": "ghostty",
"shell": "/usr/local/bin/fish",
"tmux_path": null
}
Settings can be viewed and updated with the muster settings command:
# Show current settings
muster settings
# Update a setting
muster settings --terminal ghostty
muster settings --shell /usr/local/bin/fish
muster settings --tmux-path /usr/local/bin/tmux
terminal
The terminal emulator to open when launching a session from inside tmux. If omitted, muster uses the platform default (Terminal.app on macOS; detected from PATH on Linux).
Supported values: ghostty, kitty, alacritty, wezterm, terminal (Terminal.app), iterm2.
shell
Overrides the default shell for new tmux panes. If omitted, muster uses $SHELL. Set this if your $SHELL differs from the shell you actually use (common on macOS where $SHELL defaults to /bin/zsh).
tmux_path
Overrides tmux discovery from $PATH. Set this if tmux is installed in a non-standard location.
Profiles (profiles.json)
See Profiles for the full profile schema and management commands.
Shell Integration
Muster can suggest launching profiles when you cd into a directory associated with one. Add the shell hook to your shell config:
Fish — add to ~/.config/fish/config.fish:
muster shell-init fish | source
Bash — add to ~/.bashrc or ~/.bash_profile:
eval "$(muster shell-init bash)"
Zsh — add to ~/.zshrc:
eval "$(muster shell-init zsh)"
After setup, when you cd into a directory that matches a profile tab’s CWD, muster prints a suggestion:
muster: profile 'webapp' matches this directory. Run: muster up webapp
Command-Line Help for muster
This document contains the help content for the muster command-line program.
Command Overview:
muster↴muster list↴muster up↴muster down↴muster new↴muster color↴muster ps↴muster ports↴muster top↴muster status↴muster peek↴muster pin↴muster unpin↴muster profile↴muster profile list↴muster profile delete↴muster profile save↴muster profile add-tab↴muster profile show↴muster profile edit↴muster profile update↴muster profile remove-tab↴muster notifications↴muster notifications setup↴muster notifications remove↴muster notifications test↴muster release↴muster adopt↴muster shell-init↴muster settings↴
muster
Terminal session group management built on tmux.
Muster organizes terminal sessions into named, color-coded groups with saved profiles, runtime theming, and push-based state synchronization via tmux control mode.
Usage: muster [OPTIONS] <COMMAND>
Subcommands:
list— List profiles and running sessionsup— Create or attach to a profile’s sessiondown— Destroy a sessionnew— Create an ad-hoc sessioncolor— Manage session colorsps— Show processes running inside sessionsports— Show listening ports inside sessionstop— Show resource usage (CPU, memory, GPU) for session processesstatus— Show all sessions with detailspeek— Peek at recent terminal outputpin— Pin the current tab to the session’s profileunpin— Unpin the current tab from the session’s profileprofile— Profile managementnotifications— Notification managementrelease— Release a muster-managed session back to plain tmuxadopt— Adopt an existing tmux session under muster managementshell-init— Output shell integration code for automatic profile suggestions on cdsettings— Show or update settings
Options:
--config-dir <CONFIG_DIR>— Path to the config directory--json— Output in JSON format
muster list
List profiles and running sessions
Usage: muster list
muster up
Create or attach to a profile’s session
Usage: muster up [OPTIONS] <PROFILE>
Arguments:
<PROFILE>— Profile name or ID
Options:
--tab <TAB>— Switch to this tab index on attach--detach— Create session but don’t attach
muster down
Destroy a session
Usage: muster down <SESSION>
Arguments:
<SESSION>— Profile name, ID, or session name
muster new
Create an ad-hoc session
Usage: muster new [OPTIONS] <NAME>
Arguments:
<NAME>— Display name
Options:
-
--tab <TAB>— Tab definition (name:cwd[:command]), repeatable -
--color <COLOR>— Color (hex)Default value:
#808080 -
--detach— Create session but don’t attach
muster color
Manage session colors
Usage: muster color [OPTIONS] [SESSION] [COLOR]
Arguments:
<SESSION>— Profile name, ID, or session name<COLOR>— New color (hex or named)
Options:
--list— List available named colors
muster ps
Show processes running inside sessions
Usage: muster ps [PROFILE]
Arguments:
<PROFILE>— Profile name or ID (shows all sessions if omitted)
muster ports
Show listening ports inside sessions
Usage: muster ports [PROFILE]
Arguments:
<PROFILE>— Profile name or ID (shows all sessions if omitted)
muster top
Show resource usage (CPU, memory, GPU) for session processes
Usage: muster top [PROFILE]
Arguments:
<PROFILE>— Profile name or ID (shows all sessions if omitted)
muster status
Show all sessions with details
Usage: muster status
muster peek
Peek at recent terminal output
Usage: muster peek [OPTIONS] <SESSION> [TABS]...
Arguments:
<SESSION>— Profile name, ID, or session name<TABS>— Tab names to show (all if omitted)
Options:
-
-n,--lines <LINES>— Lines of output per tabDefault value:
50
muster pin
Pin the current tab to the session’s profile
Usage: muster pin
muster unpin
Unpin the current tab from the session’s profile
Usage: muster unpin
muster profile
Profile management
Usage: muster profile <COMMAND>
Subcommands:
list— List all profilesdelete— Delete a profilesave— Save a new profile (or snapshot a running session with –from-session)add-tab— Add a tab to an existing profileshow— Show a profile’s full definitionedit— Edit a profile in $EDITORupdate— Update profile fields inlineremove-tab— Remove a tab from a profile
muster profile list
List all profiles
Usage: muster profile list
muster profile delete
Delete a profile
Usage: muster profile delete <ID>
Arguments:
<ID>— Profile name or ID
muster profile save
Save a new profile (or snapshot a running session with –from-session)
Usage: muster profile save [OPTIONS] <NAME>
Arguments:
<NAME>— Profile name
Options:
-
--tab <TAB>— Tab definition (name:cwd[:command]), repeatable -
--color <COLOR>— Color (hex or named)Default value:
#808080 -
--from-session <SESSION>— Snapshot tabs from a running tmux session
muster profile add-tab
Add a tab to an existing profile
Usage: muster profile add-tab [OPTIONS] --name <NAME> --cwd <CWD> <PROFILE>
Arguments:
<PROFILE>— Profile name or ID
Options:
--name <NAME>— Tab name--cwd <CWD>— Working directory--command <COMMAND>— Startup command
muster profile show
Show a profile’s full definition
Usage: muster profile show <ID>
Arguments:
<ID>— Profile name or ID
muster profile edit
Edit a profile in $EDITOR
Usage: muster profile edit <ID>
Arguments:
<ID>— Profile name or ID
muster profile update
Update profile fields inline
Usage: muster profile update [OPTIONS] <ID>
Arguments:
<ID>— Profile name or ID
Options:
--name <NAME>— New display name--color <COLOR>— New color (hex or named)
muster profile remove-tab
Remove a tab from a profile
Usage: muster profile remove-tab <PROFILE> <TAB>
Arguments:
<PROFILE>— Profile name or ID<TAB>— Tab name or 0-based index
muster notifications
Notification management
Usage: muster notifications <COMMAND>
Subcommands:
setup— Install macOS notification app bundleremove— Remove macOS notification app bundletest— Send a test notification to verify the notification system works
muster notifications setup
Install macOS notification app bundle
Usage: muster notifications setup
muster notifications remove
Remove macOS notification app bundle
Usage: muster notifications remove
muster notifications test
Send a test notification to verify the notification system works
Usage: muster notifications test
muster release
Release a muster-managed session back to plain tmux
Usage: muster release [OPTIONS] <SESSION>
Arguments:
<SESSION>— Profile name, ID, or session name
Options:
--name <NAME>— New name for the released session (defaults to session name without muster_ prefix)
muster adopt
Adopt an existing tmux session under muster management
Usage: muster adopt [OPTIONS] <SESSION>
Arguments:
<SESSION>— Existing tmux session name
Options:
-
--name <NAME>— Display name (defaults to session name) -
--color <COLOR>— Color (hex or named)Default value:
#808080 -
--save— Also save as a persistent profile -
--detach— Adopt without attaching
muster shell-init
Output shell integration code for automatic profile suggestions on cd
Usage: muster shell-init <SHELL>
Arguments:
<SHELL>— Shell type: fish, bash, or zsh
muster settings
Show or update settings
Usage: muster settings [OPTIONS]
Options:
--terminal <TERMINAL>— Set terminal emulator (e.g. ghostty, alacritty, kitty, wezterm, terminal, iterm2)--shell <SHELL>— Set default shell--tmux-path <TMUX_PATH>— Set tmux binary path
This document was generated automatically by
clap-markdown.
Architecture Overview
Muster is organized as a Cargo workspace with three crates:
crates/
├── muster/ # Library — tmux bindings, profiles, theming, control mode
├── muster-cli/ # CLI binary
└── muster-notify/ # macOS notification helper (minimal binary for Muster.app bundle)
Design Principles
- tmux is the runtime — muster is an organizational layer on top of tmux, not a replacement
- Library-first — the CLI is a thin consumer of the
musterlibrary crate; the API is designed to support GUI applications without modification - No application state — running session metadata lives in tmux user options, not in files
- Push-based sync — control mode provides structured notifications; no polling for state
Library Modules
| Module | Purpose |
|---|---|
tmux::client | Command execution, output parsing, session/window CRUD |
tmux::control | Control mode connection, event stream parsing (MusterEvent) |
tmux::types | TmuxSession, TmuxWindow, SessionInfo |
config::profile | Profile CRUD with atomic JSON persistence |
config::settings | Settings (tmux path, shell preference) |
session | Session lifecycle — create from profile, destroy |
session::theme | Hex color parsing, dimming, tmux status bar styling |
muster | Muster facade tying everything together |
Data Flow
Commands: CLI (or GUI) → library → tmux / config
State: Runtime state always from tmux, metadata from config files
Events: tmux control mode → library → subscribers
Control mode notifications cover window lifecycle, session lifecycle, and active tab changes. CWD tracking uses tmux’s native pane_current_path with on-demand queries.
Component Diagram
┌──────────────┐
│ │
│ CLI binary │
│ (muster) │
│ │
└──────┬───────┘
│
┌──────▼──────┐
│ │
│ muster │
│ (library) │
│ │
└──┬───────┬──┘
│ │
┌─────▼──┐ ┌──▼──────────────┐
│ Config │ │ tmux │
│ dir │ │ (sessions, │
│ (JSON) │ │ control mode) │
└────────┘ └─────────────────┘
tmux Interface
Sources of Truth
| Source | Owns |
|---|---|
| tmux | All running state: windows, CWDs, active window, plus @muster_* metadata |
| Config directory | Saved profiles and settings — never runtime state |
There is no application-level cache of tmux state. When a consumer needs to know what tabs a group has, it asks tmux.
Session Metadata
Running session metadata is stored as tmux user options:
| tmux User Option | Value | Example |
|---|---|---|
@muster_name | Display name | "Web App" |
@muster_color | Hex color | "#f97316" |
@muster_profile | Profile ID | "web-app" |
Set on creation, queryable at any time:
tmux show-option -t muster_web-app -v @muster_color
tmux list-sessions -F '#{session_name} #{@muster_name} #{@muster_color}'
Control Mode
For long-running consumers (GUI applications), muster establishes tmux control mode connections. Control mode is a persistent stdin/stdout pipe via tmux -CC attach -t <session> that provides push-based notifications:
Events Consumed
Window lifecycle:
| Notification | Library Action |
|---|---|
%window-add | Query window details, emit tab-added event |
%window-close | Emit tab-closed event |
%window-renamed | Emit tab-renamed event |
%session-window-changed | Emit active-tab-changed event |
Session lifecycle:
| Notification | Library Action |
|---|---|
%sessions-changed | Re-query session list |
%session-changed | Update active session tracking |
Output notifications (%output) are suppressed via refresh-client -f no-output.
Response Framing
Commands sent through control mode produce structured output blocks:
%begin <timestamp> <command_number> <flags>
<output lines>
%end <timestamp> <command_number> <flags>
tmux Hooks
Muster uses tmux hooks selectively for fire-and-forget actions:
pane-died— invokesmuster _pane-diedto capture death snapshots and send notificationsalert-bell— invokesmuster _bellto send bell notificationswindow-renamed— invokesmuster sync-renameto sync pinned window names to profiles
Hooks are appropriate here because they trigger one-shot external commands rather than feeding a stateful event stream.
Color Theming
Each session’s color is applied to the tmux status bar:
tmux set-option -t <session> status-style "bg=<color>,fg=#000000"
tmux set-option -t <session> status-left "#[bg=<darker>,fg=#ffffff,bold] <name> #[default]"
tmux set-option -t <session> window-status-current-format "#[fg=<color>,bg=#000000,bold] #I: #W #[default]"
Color changes are live — no session restart required.
Library Usage
The muster library crate provides a Rust API for terminal session group management.
Basic Usage
#![allow(unused)]
fn main() {
use muster::{Muster, Profile, TabProfile};
use std::path::Path;
let m = Muster::init(Path::new("~/.config/muster"))?;
// Create a profile
let profile = Profile {
id: "my-project".into(),
name: "My Project".into(),
color: "#f97316".into(),
tabs: vec![
TabProfile {
name: "Shell".into(),
cwd: "/home/user/project".into(),
command: None,
layout: None,
panes: vec![],
},
TabProfile {
name: "Server".into(),
cwd: "/home/user/project".into(),
command: Some("npm run dev".into()),
layout: None,
panes: vec![],
},
],
};
m.save_profile(profile.clone())?;
// Launch a session
let info = m.launch(&profile.id)?;
// List running sessions
let sessions = m.list_sessions()?;
// Subscribe to events (for GUI integration)
let rx = m.subscribe();
}
Event Subscription
The library provides push-based event notifications via tokio::broadcast:
#![allow(unused)]
fn main() {
let mut rx = muster.subscribe();
tokio::spawn(async move {
while let Ok(event) = rx.recv().await {
match event {
MusterEvent::TabAdded { session, window_index, name } => { /* ... */ },
MusterEvent::TabClosed { session, window_index } => { /* ... */ },
MusterEvent::SessionEnded { session } => { /* ... */ },
// ...
}
}
});
}
API Documentation
Full API documentation is available at the API Reference, generated from rustdoc comments on all public types, functions, and modules.
Testing
Unit Tests
Unit tests do not require tmux:
# With cargo-nextest
cargo nextest run
# With cargo test
cargo test
Integration Tests
Integration tests create real tmux sessions and require tmux to be installed:
# With cargo-nextest
cargo nextest run --run-ignored all
# With cargo test
cargo test -- --ignored
Integration tests create sessions with unique names and clean up after themselves. They do not interfere with your personal tmux sessions.
What’s Tested
Unit tests (no tmux required):
- Profile CRUD (reads/writes JSON files in a temp directory)
- Color computation (hex parsing, dimming, tmux style string generation)
- Session name convention (encoding/decoding profile IDs)
- Control mode stream parser (given raw control mode output, verify parsed events)
Integration tests (tmux required):
- Session lifecycle: create from profile, verify tabs exist, destroy
- Tab operations: add, close, rename, verify via tmux queries
- Theme application: set color, verify tmux options
- Control mode: connect, receive events on window add/close
Contributing
Development Commands
cargo t # alias for cargo nextest run
cargo clippy # lint
cargo fmt --check # format check
cargo doc --no-deps # build docs (check for warnings)
Project Structure
crates/
├── muster/ # Library crate (tmux bindings, profiles, theming, control mode)
├── muster-cli/ # CLI binary crate
│ └── src/
│ ├── main.rs # Entry point, CLI dispatch (~125 lines)
│ ├── cli.rs # Clap command definitions (library target)
│ ├── commands/ # One module per command (list, up/launch, down/kill, etc.)
│ ├── format.rs # Terminal formatting (color dots, memory display)
│ ├── tabs.rs # Tab definition parsing
│ ├── editing.rs # TOML profile editing types
│ ├── terminal.rs # tmux attach, notification helpers
│ ├── proctree.rs # Process tree building/rendering
│ ├── ports.rs # Listening port detection
│ └── resources.rs # CPU/memory/GPU resource collection
└── muster-notify/ # macOS notification helper
docs/ # mdBook documentation (this site)
Documentation
Building Docs Locally
# mdBook user guide
mdbook serve docs
# API reference (rustdoc)
cargo doc --no-deps --open
# Regenerate CLI reference
cargo run --example gen_cli_docs -p muster-cli > docs/src/cli-reference.md
Writing Doc Comments
All public types, functions, and modules should have rustdoc comments:
//!for module-level docs///for public types, functions, and methods
Code Quality
The workspace enforces:
unsafe_code = "deny"— no unsafe Rustclippy::allandclippy::pedantic— comprehensive linting- All public items documented