.--.
|o_o |
|:_/ |
// \ \
(| muxedo |)
'/'\_ _/`\
\___)=(___/
muxedo is a terminal multiplexer TUI that runs commands from a TOML profile in a live auto-grid layout.
It is built for the "run a few long-lived commands and keep them visible" workflow: app servers, logs, watchers, shells, and project startup tasks in one terminal window.
Recorded demo showing async startup progress in the Message Buffer and a focused panel in insert mode.
To refresh the local demo recording, edit docs/demo/session.toml and run:
scripts/record_demo.shThe script writes docs/media/demo.cast with ANSI color sequences preserved. If agg is installed, it also refreshes docs/media/demo.gif.
Requires Homebrew.
brew tap rikvanderkemp/muxedo
brew install muxedoUse brew upgrade muxedo to update later.
Install the latest tagged release:
go install github.com/rikvanderkemp/muxedo@latestInstall a specific release:
go install github.com/rikvanderkemp/muxedo@v0.1.0Install the latest Linux/macOS release to ~/.local/bin/muxedo:
curl -fsSL https://raw.githubusercontent.com/rikvanderkemp/muxedo/main/scripts/install.sh | shInstall a specific release or custom directory:
curl -fsSL https://raw.githubusercontent.com/rikvanderkemp/muxedo/main/scripts/install.sh | VERSION=v0.1.0 INSTALL_DIR="$HOME/bin" shThe installer supports linux and darwin on amd64 and arm64, verifies the published SHA-256 checksums, and prints a PATH hint if needed.
- Copy the example profile:
cp profile.toml.example .muxedo- Start muxedo from that directory:
muxedoOr point at a profile explicitly:
muxedo -profile profile.toml.exampleWhen -profile is omitted, muxedo looks for ./.muxedo in the current working directory. If no profile is found and the session is interactive, muxedo launches a first-run wizard that walks you through a title, a working directory, optional startup commands, and one or more panels, then writes a ready-to-use TOML profile (default path ./.muxedo). In non-interactive sessions muxedo prints the missing-profile error with the full command help instead.
The bundled profile.toml.example is a safe cross-platform demo. A profile defines optional startup tasks plus one or more panels:
workingdir = "."
[[startup]]
shell = "printf 'bootstrapping demo...\\n'; sleep 1"
mode = "sync"
[[teardown]]
shell = "sleep 15"
mode = "sync"
[panel.clock]
order = 0
shell = "while true; do date; sleep 1; done"
[panel.system]
shell = "while true; do uname -a; sleep 5; done"
[panel.pong]
shell = "i=0 d=1; while true; do printf '\\r[%*s><%*s]' \"$i\" '' \"$((20-i))\" ''; sleep 0.08; [ \"$i\" -eq 20 ] && d=-1; [ \"$i\" -eq 0 ] && d=1; i=$((i+d)); done"
[panel.echo]
shell = "printf 'Insert mode demo: type something and press Enter.\\n\\n'; while IFS= read -r line; do printf 'you typed: %s\\n' \"$line\"; done"Profile rules:
- Use
[[startup]]for one-off setup commands that run before or alongside the UI. - Use
[[teardown]]for one-off cleanup commands that run after all panels have stopped on global quit. - Use
[panel.<name>]for long-lived commands you want visible in the grid. - Set exactly one of
programorshellfor each startup/teardown item and panel. workingdirat the top level is default, override per startup/teardown item or panel when needed.- Use
orderto pin a panel earlier in the grid without rearranging the file.
Shell fields execute via sh, so treat profile files as trusted local automation.
The shortest path to using muxedo:
- Click a panel to focus it.
- Press
ito enter insert mode and send keys to the running process. - Press
Esconce to return to normal mode, thenEscagain to unfocus the panel. - Press
hjklin normal mode to move between panels. - Press
1to9in normal mode to jump to the first nine panels. - Press
sin normal mode to open the focused panel's scrollback. - Use the mouse wheel,
PgUp/PgDn, arrows,j/k, org/Gin scrollback to navigate. - Press
Escin scrollback to return to the live panel. - Drag inside a live panel or scrollback to select text, then press
yorEnterto copy it. - Press
rin normal mode to restart the focused panel. - Press
xin normal mode to stop the focused panel. - Press
min normal mode to maximize or restore the focused panel. - Press
?(not in insert mode) to open a help dialog with shortcuts. - Press
Ctrl+Bto toggle the Message Buffer. - Press
qorCtrl+Cto quit when no panel is focused (gracefully stops panels, then runs[[teardown]]). - While quitting, press
Ctrl+Cagain (orq) to force quit.
Migration note (breaking change):
- Per-panel kill fields (
shell_kill,kill_program,kill_args) removed. Move those commands into global[[teardown]](order them as desired).
Muxedo captures each panel's output to a scrollback file for the current run. Scrollback starts empty on launch, grows while the panel runs, and is cleared when that panel is restarted with R.
Scrollback is best-effort for full-screen TUIs and works best with shells, logs, and line-oriented output.
Panel selection copies text to the system clipboard when one of these tools is available: pbcopy, wl-copy, xclip, or xsel. If none are available, muxedo falls back to OSC52 terminal clipboard copy when supported.
Optional scrollback settings:
[scrollback]
dir = "~/.cache/muxedo/scrollback"
max_bytes = 1048576dir defaults to the OS cache directory. max_bytes defaults to 0 (unlimited), and you can set it to a value like 1048576 to cap scrollback per panel.
Restarting a panel with R clears that panel's scrollback file for the current run.
Muxedo also looks for an optional app-level config at ~/.config/muxedo/config.toml.
That file is for muxedo behavior, not for your panel layout. To generate a complete config file with defaults, run:
muxedo -dump-configUseful app-level options:
[ui]
show_exit_message = true
check_updates_on_start = trueshow_exit_messagecontrols the support message printed after muxedo exits.check_updates_on_startcontrols the default-on startup update check. In non-interactive sessions, muxedo skips the prompt and starts normally.
You can also override UI colors with a [theme] section:
[theme]
inactive_border = "#5f87af"
inactive_title_fg = "#d0d0d0"
inactive_title_bg = "#5f5f87"
active_normal_border = "#ff8700"
active_normal_title_fg = "#ffffd7"
active_normal_title_bg = "#ff8700"
active_insert_border = "#00ff00"
active_insert_title_fg = "#ffffd7"
active_insert_title_bg = "#00af00"
stopped_border = "#585858"
stopped_title_fg = "#8a8a8a"
stopped_title_bg = "#444444"
empty_border = "#303030"
overlay_fg = "#ffffd7"
overlay_bg = "#444444"
status_bar_fg = "#d0d0d0"
status_bar_bg = "#262626"
status_time_fg = "#ffffd7"
status_time_bg = "#5f5f87"
status_active_panel_fg = "#ffffd7"
status_active_panel_bg = "#5f5fd7"
status_mode_none_fg = "#ffffd7"
status_mode_none_bg = "#585858"
status_mode_normal_fg = "#ffffd7"
status_mode_normal_bg = "#ff8700"
status_mode_insert_fg = "#ffffd7"
status_mode_insert_bg = "#00af00"
status_hint_fg = "#d0d0d0"
status_hint_bg = "#444444"Official release builds can check for newer GitHub releases and replace themselves in place:
muxedo update check
muxedo update applyupdate checkprints the current and latest release versions.update applydownloads the matching release tarball, verifieschecksums.txt, replaces the current executable, then exits.- Startup checks for updates by default in interactive terminals and prompts before continuing.
- Self-update is unavailable for
devbuilds. - Package-manager installs may not be writable or may be managed externally.
Pull request titles and commit messages must use Conventional Commit format:
feat(ui): add panel maximize togglefix(process): stabilize scrollback IDsdocs(readme): improve first-run guide
This repository uses squash merges on main for release automation.
MIT - see LICENSE for details.
If muxedo saves you time, you can support development here:
