Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ ignore = [
# Revisit when zellij-tile moves off clap 3.
"RUSTSEC-2024-0370", # proc-macro-error
"RUSTSEC-2024-0375", # atty
# Transitive through notify-rust -> tauri-winrt-notification -> quick-xml.
# tauri-winrt-notification is a Windows-only (WinRT toast) dependency, so
# quick-xml is not compiled into muxa on the Unix targets we ship. Both are
# DoS-on-untrusted-XML issues; muxa never feeds attacker-controlled XML to
# this crate (Windows notification templates only), and tauri-winrt-notification
# 0.7.2 pins quick-xml ^0.37 so the fixed 0.41 can't be selected yet.
# Revisit when notify-rust ships a tauri-winrt-notification that allows quick-xml >=0.41.
"RUSTSEC-2026-0194", # quick-xml: quadratic runtime on duplicate attribute names
"RUSTSEC-2026-0195", # quick-xml: unbounded namespace-declaration allocation (NsReader)
]

[licenses]
Expand Down
23 changes: 20 additions & 3 deletions docs/demo-README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,24 @@ to re-record it.
| ---------------------- | ------------------------------------------------------------------------------------- |
| `docs/demo.tape` | [VHS](https://github.com/charmbracelet/vhs) script — the recording itself. |
| `docs/demo-setup.sh` | Bootstraps an isolated `tmux -L muxa-demo` server with a few seeded panes + windows. |
| `docs/demo-paint.sh` | Paints believable static agent frames (claude/codex/gemini) into the demo panes so `muxa attend` / attach land on real-looking content instead of an empty `cat`. |
| `docs/demo-seed.sh` | Older pane-seed script (kept for reference; the live setup script does this inline). |
| `docs/demo.gif` | The output. 1200 × 720; includes status, stats, activity, and the session watch view. |
| `docs/demo.gif` | The output. 1200 × 720; the status → stats → watch → attend story arc. |

## Story arc

The recording is a four-beat narrative, not a feature tour — the point is
to make "which of my agents needs me?" felt, then answered:

1. **Overwhelm** — `muxa status`: claude working, codex blocked on an
approval, gemini waiting.
2. **The accountant** — `muxa stats`: where the day actually went
(WORK / WAIT / TMUX / ACT / THINK).
3. **The god view** — `prefix + s` → `muxa watch`, every agent on one
screen, with the real prompt + response in the preview popup.
4. **The finale** — `muxa attend` teleports the tmux client straight to
the longest-blocked agent (codex, mid shell-approval). Strongest note
to end on, so it comes last: attend moves us off the shell pane.

The tape's prelude (`Hide` block) does the boring setup so the visible
recording stays focused on the muxa UI:
Expand Down Expand Up @@ -74,9 +90,10 @@ docker cp ~/.cargo/bin/muxa "$CID":/usr/local/bin/muxa
docker cp ~/.cargo/bin/muxad "$CID":/usr/local/bin/muxad
docker cp docs/demo.tape "$CID":/work/docs/demo.tape
docker cp docs/demo-setup.sh "$CID":/work/docs/demo-setup.sh
docker cp docs/demo-paint.sh "$CID":/work/docs/demo-paint.sh
docker cp docs/demo-seed.sh "$CID":/work/docs/demo-seed.sh

# 4) Record. The visible flow shows status, stats, activity, and watch.
# 4) Record. The visible flow shows status, stats, watch, and attend.
docker exec -w /work "$CID" vhs docs/demo.tape

# 5) Pull the resulting GIF out and tear the container down.
Expand Down Expand Up @@ -105,7 +122,7 @@ docker exec -w /src "$CID" sh -c \

A few patterns that come up:

* **Pacing**: the visible flow targets ~17 s. `Sleep` durations are in
* **Pacing**: the visible flow targets ~19 s. `Sleep` durations are in
milliseconds; ~1500 ms after a key gives the viewer time to read,
~500 ms is right between consecutive keystrokes.
* **New key in muxa watch**: add the keystroke after `Type "p"` /
Expand Down
66 changes: 66 additions & 0 deletions docs/demo-paint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#!/usr/bin/env bash
# Paint a believable, static agent frame into a demo pane, then hold on
# `cat` so the pane stays put (and keeps clean scrollback) for the
# recording. Replaces the bare `cat` panes in docs/demo-setup.sh so that
# `muxa attend` / Enter actually land the viewer on something that looks
# like a real agent session instead of an empty shell.
#
# Usage (from a tmux pane command):
# bash docs/demo-paint.sh claude
# bash docs/demo-paint.sh codex
# bash docs/demo-paint.sh gemini
#
# Colors are raw ANSI so this has no dependency on the agent binaries —
# the frames are fixtures, matched closely enough to each CLI's look to
# read as genuine at GIF resolution.

set -euo pipefail

C_RESET=$'\033[0m'
C_DIM=$'\033[2m'
C_BOLD=$'\033[1m'
C_GREEN=$'\033[1;32m'
C_YELLOW=$'\033[1;33m'
C_BLUE=$'\033[1;34m'
C_CYAN=$'\033[1;36m'
C_MAG=$'\033[1;35m'

clear

case "${1:-}" in
claude)
printf '%s● claude%s %s· muxa%s\n' "$C_MAG" "$C_RESET" "$C_DIM" "$C_RESET"
printf '%s~/src/muxa on ipc-frames%s\n\n' "$C_DIM" "$C_RESET"
printf '%s›%s refactor the ipc module to use length-prefixed frames\n\n' "$C_GREEN" "$C_RESET"
printf ' %s⚙%s editing crates/muxa/src/ipc.rs\n' "$C_DIM" "$C_RESET"
printf ' %s⚙%s running cargo test --lib ipc\n\n' "$C_DIM" "$C_RESET"
printf ' %s▶ working…%s\n' "$C_BLUE" "$C_RESET"
;;
codex)
printf '%s● codex%s %s· legacy-auth%s\n' "$C_CYAN" "$C_RESET" "$C_DIM" "$C_RESET"
printf '%s~/src/legacy on main%s\n\n' "$C_DIM" "$C_RESET"
printf '%s›%s audit the legacy auth middleware for raw bearer tokens\n\n' "$C_GREEN" "$C_RESET"
printf ' %s⚙%s ran rg -n "bearer" src/ %s18 matches%s\n' "$C_DIM" "$C_RESET" "$C_DIM" "$C_RESET"
printf ' %s⚙%s ran cat src/auth/middleware.rs\n\n' "$C_DIM" "$C_RESET"
printf ' %s⏸ Approval required%s\n' "$C_YELLOW" "$C_RESET"
printf ' %s$ rg -n "token" --type rust -g '\''!tests/'\''%s\n\n' "$C_BOLD" "$C_RESET"
printf ' %s[y]%s yes %s[n]%s no %s[a]%s yes, and don'\''t ask again\n' \
"$C_GREEN" "$C_RESET" "$C_YELLOW" "$C_RESET" "$C_DIM" "$C_RESET"
;;
gemini)
printf '%s● gemini%s %s· review%s\n' "$C_BLUE" "$C_RESET" "$C_DIM" "$C_RESET"
printf '%s~/src/review on main%s\n\n' "$C_DIM" "$C_RESET"
printf '%s›%s review PR #482 — new sorting knob in muxa watch\n\n' "$C_GREEN" "$C_RESET"
printf ' %s?%s Which default ordering should I evaluate against —\n' "$C_YELLOW" "$C_RESET"
printf ' [Session, Activity] or [Activity, Session]?\n\n'
printf ' %s▶ waiting for your reply%s\n' "$C_BLUE" "$C_RESET"
;;
*)
printf 'usage: demo-paint.sh {claude|codex|gemini}\n' >&2
exit 2
;;
esac

# Hold the pane. `cat` keeps the frame on screen and the pane alive with a
# quiet stdin, exactly like the old bare-`cat` panes.
exec cat
17 changes: 12 additions & 5 deletions docs/demo-setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ set -euo pipefail
: "${MUXA_SOCKET:=/tmp/muxa-demo.sock}"
TMUX_LBL=muxa-demo
SHIM_DIR=/tmp/muxa-demo-shim
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PAINT="$SCRIPT_DIR/demo-paint.sh" # believable agent frames instead of bare `cat`

rfc3339_ago() {
local seconds="$1"
Expand All @@ -32,15 +34,18 @@ rfc3339_ago() {

# 1) PATH shim — every later `tmux` call (ours and muxa's children) routes
# through `tmux -L muxa-demo`.
# Real tmux binary, resolved absolutely so it bypasses the PATH shim below.
# Docker/CI has it at /usr/bin/tmux; a Homebrew mac has it elsewhere, so the
# tape passes MUXA_DEMO_TMUX after resolving `command -v tmux` pre-shim.
TM="${MUXA_DEMO_TMUX:-/usr/bin/tmux}"

mkdir -p "$SHIM_DIR"
cat > "$SHIM_DIR/tmux" <<EOF
#!/bin/sh
exec /usr/bin/tmux -u -L $TMUX_LBL "\$@"
exec $TM -u -L $TMUX_LBL "\$@"
EOF
chmod +x "$SHIM_DIR/tmux"

TM=/usr/bin/tmux # use absolute path here so we don't depend on PATH ordering

# 2) Isolated demo server.
# - main:0 runs an interactive bash with a dead-simple rcfile so we
# can type `muxa status-line` inside the recording without dragging
Expand All @@ -58,9 +63,11 @@ BASHRC
# `-d` keeps the current window as main:0 — without it new-window switches
# focus to itself, so attach would land on `vim` (cat-stdin) instead of
# the bash pane.
"$TM" -u -L "$TMUX_LBL" new-window -d -t main: -n review cat
# `review` hosts codex mid-approval — this is where `muxa attend` lands, so it
# paints a believable approval prompt instead of an empty shell.
"$TM" -u -L "$TMUX_LBL" new-window -d -t main: -n review "bash '$PAINT' codex"
"$TM" -u -L "$TMUX_LBL" new-window -d -t main: -n vim cat
"$TM" -u -L "$TMUX_LBL" new-session -d -s ops -x 200 -y 40 cat
"$TM" -u -L "$TMUX_LBL" new-session -d -s ops -x 200 -y 40 "bash '$PAINT' gemini"
"$TM" -u -L "$TMUX_LBL" new-window -d -t ops: -n logs cat
"$TM" -u -L "$TMUX_LBL" new-session -d -s lab -x 200 -y 40 cat
# Belt-and-suspenders: be explicit about which window the recording
Expand Down
Binary file modified docs/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
140 changes: 49 additions & 91 deletions docs/demo.tape
Original file line number Diff line number Diff line change
@@ -1,30 +1,18 @@
# muxa demo script — regenerate `docs/demo.gif` via:
# muxa demo — "which of your agents needs you right now?"
#
# vhs docs/demo.tape
# vhs docs/demo.tape → docs/demo.gif
#
# Requires charmbracelet/vhs, ttyd, ffmpeg, and a Chromium-based browser.
# docs/demo-setup.sh handles the muxad + tmux bootstrap so this file
# stays focused on the recording.
# Story arc (see docs/demo-README.md):
# 0–3s overwhelm — muxa status: claude working, codex blocked, gemini waiting
# 3–7s the accountant — muxa stats: where the day actually went
# 7–12s the god view — prefix+s → muxa watch, all agents one screen, with the
# real prompt/response in the preview
# 12–17s the finale — `muxa attend` teleports to the longest-blocked agent
# (codex, mid shell-approval) — the strongest note to end on
#
# Visible flow (the recording starts already attached to tmux):
# 1. muxa status — table, run from inside the tmux pane
# 2. muxa stats — duration summary with WORK/WAIT/HUMAN/THINK
# 3. muxa activity — raw human interaction intervals
# 4. prefix + s — muxa watch session dashboard
# Cursor lands on the current session
# thanks to `set_initial_pane`, so the
# preview acts on a row with real prompt
# + response data.
# 5. p — open the preview popup over the table
# 6. j j — scroll the assistant response inside
# the popup to show the multi-line body
# 7. f — toggle full-screen for long content
# 8. f — back to popup
# 9. q — close preview, back to picker
# 10. j / k — walk the session list, watch the detail
# line update for each row
# 11. Enter — switch to the selected pane
# 12. prefix + d — detach
# Depends on docs/demo-setup.sh painting the codex/gemini panes with
# docs/demo-paint.sh instead of bare `cat`, so attend/attach land on a
# believable agent frame rather than an empty shell.

Output docs/demo.gif

Expand All @@ -37,9 +25,12 @@ Set Theme "GitHub Dark"
Set TypingSpeed 45ms
Set PlaybackSpeed 1

# Prelude — invisible setup. We attach to the demo tmux server inside
# this same shell so the recording starts already inside tmux.
# ── Prelude — invisible setup (same isolation as the old tape) ───────────
Hide
# Resolve the real tmux BEFORE the shim shadows it on PATH, so attach/detach
# work whether tmux lives at /usr/bin (Docker/CI) or Homebrew (mac).
Type "export MUXA_DEMO_TMUX=$(command -v tmux)"
Enter
Type "export PATH=$HOME/.cargo/bin:/tmp/muxa-demo-shim:$PATH"
Enter
Type "export LANG=C.UTF-8"
Expand All @@ -64,7 +55,7 @@ Type "theme = 'high-contrast'"
Enter
Type "view = 'session'"
Enter
Type "sort = ['session']"
Type "sort = ['state']"
Enter
Type "[watch.preview]"
Enter
Expand All @@ -86,97 +77,64 @@ Enter
Sleep 300ms
Type "clear"
Enter
# Attach in-place so the recording opens already inside the tmux session.
# `exec` replaces the bash, so vhs records the tmux UI directly.
Type "exec /usr/bin/tmux -u -L muxa-demo attach -t main:0"
Type "exec $MUXA_DEMO_TMUX -u -L muxa-demo attach -t main:0"
Enter
Sleep 1500ms
Show

# 1. muxa status — read the data, from inside the tmux pane. The
# status-right at the top already reflects this pane's agent state,
# so the viewer sees both surfaces at once.
# All shell commands run FIRST, from the main:0 bash pane, so nothing is
# ever typed into a non-shell (painted) pane. `muxa attend` teleports us
# away for the finale, so it must come last.

# ── 1. Overwhelm (0–3s) ──────────────────────────────────────────────────
# status-right at the top already shows this pane's agent glyph. The table
# makes the fleet legible: one working, one blocked on approval, one waiting.
Type "muxa status"
Enter
Sleep 2200ms
Sleep 3000ms

# 2. muxa stats — show that muxa is more than live status. The demo seed
# writes activity.ndjson intervals, so WORK / WAIT / HUMAN / THINK are
# already populated instead of waiting for a long recording.
# ── 2. The accountant (3–7s) ─────────────────────────────────────────────
# muxa also quantifies the day: how long agents worked, how long they sat
# blocked on you, how long you actually spent driving.
Type "clear"
Enter
Type "muxa stats --since today --limit 4"
Enter
Sleep 3200ms

# 3. muxa activity — raw ledger view for debugging stats/report inputs.
Type "clear"
Enter
Type "muxa activity --since today --type human --limit 4"
Enter
Sleep 2600ms

# ── 3. The god view (7–12s) ──────────────────────────────────────────────
# prefix + s pops muxa watch. sort=['state'] floats blocked/error agents to
# the top; the status gutter shows multi/single/bare sessions at a glance.
Type "clear"
Enter

# 4. prefix + s — pop the muxa watch session dashboard. This is the
# "switch" experience: same gesture as tmux's stock prefix + s, but
# with agent-aware session rows. Cursor lands on the current session
# via `set_initial_pane`, and the status gutter shows multi-agent,
# single-agent, and bare sessions without moving the session names.
Ctrl+B
Type "s"
Sleep 2200ms

# 5. p — open the preview popup. The selected claude row has a real
# prompt + a multi-paragraph assistant response (seeded via fake
# transcript), so the popup body is full of content rather than
# `—` placeholders.
# p — preview the highlighted agent's real prompt + response body
# (default_content=prompt_response), then q q back out to the shell.
Type "p"
Sleep 2400ms

# 6. j j — scroll the assistant response inside the popup so the
# viewer sees the body wraps and scrolls as expected.
Type "j"
Sleep 600ms
Type "j"
Sleep 1200ms

# 7. f — toggle full-screen for long content where the popup wraps too
# aggressively. Footer hint flips to "f popup" so the next press is
# self-documenting.
Type "f"
Sleep 1700ms

# 8. f — back to popup.
Type "f"
Sleep 1000ms

# 9. q — close the preview, return to the picker.
Sleep 2600ms
Type "q"
Sleep 700ms

# 10. Walk a row or two so the detail line updates for each highlight —
# showcases the `{last_response|last_prompt}` fallback while keeping
# the compact session status gutter aligned.
Type "j"
Sleep 1000ms
Type "k"
Sleep 800ms
Sleep 400ms
Type "q"
Sleep 600ms

# 11. Enter — switch the client to the selected pane. The popup closes
# and tmux pulls us into the chosen window.
# ── 4. The finale — teleport to whoever needs you (12–17s) ───────────────
# Don't hunt across windows. One command jumps the tmux client to whichever
# agent has been blocked the longest — here, codex on the shell approval.
# demo-paint.sh has painted a real-looking approval prompt, so the teleport
# lands somewhere that reads as genuine. Strongest note to end on.
Type "muxa attend"
Enter
Sleep 1500ms

# 12. Detach — clean exit out of the demo server.
Sleep 700ms
Sleep 3200ms
Ctrl+B
Type "d"
Sleep 600ms

# Postlude — tear down everything.
# ── Postlude — tear everything down ──────────────────────────────────────
Hide
Type "/usr/bin/tmux -u -L muxa-demo kill-server 2>/dev/null"
Type "$MUXA_DEMO_TMUX -u -L muxa-demo kill-server 2>/dev/null"
Enter
Type "kill %1 2>/dev/null"
Enter
Expand Down