Skip to content

Repository files navigation

cmux-herdr

A cmux plugin for Herdr

Herdr pane viewers and agent status inside cmux. External attach through the plugin today; native Herdr UI is a roadmap.

CI Release MIT License Rust binary cmux plugin

English · Deutsch · cmux · Herdr · changelog

cmux-herdr is a plugin for Herdr running inside cmux. It projects agent status and creates external pane viewers using cmux-herdr attach-pane. It does not transfer Herdr TTY ownership into Ghostty or implement cmux's builtin native tmux integration. Making cmux the native UI of Herdr is a roadmap, not a capability shipped by this plugin.

The current source version is v0.7.0. This is a plugin for cmux.app, not a patch to it. The plugin manager downloads a checksum-verified Rust binary; users need neither Python nor a Rust toolchain.

Install

Official install is the cmux plugin manager plus the cmux-herdr CLI. Native Herdr chrome is roadmap work associated with upstream proposals #8736 and #10045, not present in the audited cmux commit 829c6af45478ef5c2196801824c35f5cb4dc5d69. Native Sidebar issue #75 remains blocked, not fixed by v0.7.0. This plugin does not copy a custom herdr sidebar into ~/.config/cmux/sidebars/.

cmux sidebar plugin install https://github.com/RaviTharuma/cmux-herdr.git
cmux sidebar plugin use cmux-herdr
cmux sidebar plugin update cmux-herdr
cmux sidebar plugin remove cmux-herdr

That clones into $XDG_DATA_HOME/cmux/mux-plugins/cmux-herdr (or ~/.local/share/cmux/mux-plugins/cmux-herdr). The plugin-manager build step uses bin/cmux-herdr-fetch to select one of four release targets, download the matching binary and SHA256SUMS over HTTPS, verify the checksum, and install it atomically. bin/cmux-herdr and bin/cmux-herdr-sidebar are thin POSIX-sh launchers for that binary. A source build (cargo build --release) is only a fallback for unusual architectures or offline development.

Development

The canonical checks are:

./scripts/test.sh          # cargo fmt --check, cargo clippy -- -D warnings, cargo test
./bin/cmux-herdr --version
./bin/cmux-herdr --help
./bin/cmux-herdr doctor
./bin/cmux-herdr-sidebar --help

Runtime implementation is under src/*.rs; bin/* contains only thin POSIX-sh launchers. Layout of the repo: docs/ARCHITECTURE.md. Index: docs/README.md.

After install

cmux-herdr doctor
cmux-herdr watch

watch is the live GUI path (tmux-parity on by default). Contributor symlink of the CLI (not a custom-sidebar copy): CONTRIBUTING.md. Release notes: RELEASE.md. sidebars/herdr.js and herdr.swift remain in the repo as experimental leftovers, not the default install.

Capability matrix

This is the release capability summary. Native design documents describe targets, not additional features shipped by the plugin.

Capability Status Scope
External pane viewers Shipped plugin path cmux terminal surfaces run attach-pane; Herdr retains the TTYs.
Live watch and mirror Shipped plugin path Userspace tab/split, layout, focus, order and prune projection; not builtin ssh-tmux output streaming or native TTY takeover.
Status pills Shipped plugin path Agent status chips and session progress on the containing cmux workspace.
CLI and agent skill Shipped plugin path Topology, control, external viewers and the allowlisted Herdr socket API; never server.stop.
Writer coordination Shipped protocol Instance-owned leases and recognition of native ownership records; a record does not prove a native controller exists.
Native Herdr attach / window mirror Planned, not shipped Requires upstream AppKit/Bonsplit/Ghostty integration; plugin lifecycle models are not native attachment.
Integrated multi-workspace native sidebars Planned, blocked Native Sidebar issue #75 remains blocked. The plugin sidebar TUI and experimental custom sidebars are not this integration.

Source evidence and command-level constraints: stabilization audit.

Quick start

Run these inside a Herdr pane nested in cmux so both sockets are in the environment:

cmux-herdr doctor
cmux-herdr watch

watch is enough. You live in cmux chrome. --pills-only writes status chips without projecting tabs and panes.

Sidebar philosophy (cmux-native)

  • Left sidebar stays workspaces/machines navigation. Do not invent Herdr-only chrome there. The intended nesting is: Herdr workspaces appear as ordinary sub-workspaces under the relevant machine connection — the same way cmux already nests workspaces under machines.
  • Right sidebar is the home for richer Herdr UI/actions/status beyond navigation (ssh-tmux fashion): Agents/sessions, Feeds, and Dock — not a foreign Herdr panel. Project with cmux-herdr rail / agents --rail; see docs/RIGHT_RAIL.md.
  • Until native nested workspace sync lands, use the CLI (sessions, doctor, watch, rail, attach) and existing status/mirror surfaces. Do not duplicate the same agent in pills, sessions, and a custom list.

Commands

Command What it does
doctor Diagnose plugin install, host fingerprint, LaunchAgent
status Show nested cmux + Herdr context
tree / agents Inner topology, compact agent list (agents --rail → right-rail JSON)
rail Project deduped agents into cmux right-rail JSON + rail-<fp>.json
watch Live pills + real cmux tabs/panes (--pills-only skips projection)
sync One-shot status pills
mirror Project Herdr tabs/panes into cmux tabs/splits (--all, --prune, --dry-run)
attach-pane Follow one Herdr pane in this terminal
attach / detach / restore Live apply host; detach leaves Herdr running; restore never replays a stale tree
associations Read the pane → status-key cache
lock-title / unlock-title Pin a pill display name
lease Inspect the plugin ↔ native writer lease
clear Remove herdr:* pills; leave other cmux status alone
focus-workspace / focus-tab / focus-pane / focus-agent Jump in the inner mux
read-pane / read-agent Read terminal output
send / send-key / agent-prompt Type, key chords, wait-until-done prompts
new-tab / close-pane / split / zoom-pane / resize-pane Inner layout
layout / set-ratio / move-pane / focus-dir / move-tab / rename-pane Tab geometry
start-agent / agent-explain / agent-view / process-info Agent extras
worktree / manifests / notify / window-title Herdr-only surface
api Allowlisted Herdr RPC (--list; never server.stop)
sessions List Herdr sessions (remote.tmux.sessions JSON shape)
observe Subscribe to a Herdr method (for example pane_surfaces / sessions)
json-dump Full snapshot for debugging (redact personal paths before sharing)
update-service Opt-in Herdr auto-update (LaunchAgent / systemd user timer); off by default

cmux-herdr --help lists flags for every subcommand. Herdr-only verbs with no tmux analogue: docs/upstream/HERDR_BEYOND_TMUX.md.

Optional Herdr auto-update

update-service is opt-in. It is not installed by the plugin manager. When enabled, it registers a LaunchAgent (macOS) or systemd user timer (Linux), writes only a marker-owned block under [update] in Herdr's config, checks about every six hours, runs herdr update --handoff, and restores the previous binary if the update fails after replacing it. You pass the release manifest yourself — the plugin does not pin a third-party Herdr fork.

cmux-herdr update-service install \
  --manifest-url https://example.com/herdr-preview.json \
  --channel preview
# plist  → ~/Library/LaunchAgents/com.cmux-herdr.herdr-auto-update.plist
# logs   → ~/Library/Logs/cmux-herdr-herdr-auto-update.{out,err}.log
# timer  → ~/.config/systemd/user/com.cmux-herdr.herdr-auto-update.timer
# logs   → journalctl --user -u com.cmux-herdr.herdr-auto-update.service

cmux-herdr update-service status
cmux-herdr update-service run       # one-shot check (same path the timer uses)
cmux-herdr update-service uninstall

Install refuses to overwrite a different active channel or manifest URL. Uninstall removes only the managed marker block and the service units this command registered. Backups of prior Herdr binaries are kept under the plugin state dir (bounded retention).

Requirements

  • macOS with cmux and herdr on PATH; a cmux build that includes cmux sidebar plugin
  • A working Herdr socket (usual when HERDR_ENV=1)
  • Herdr 0.8+ (agent name may live under agent_session.agent)
  • sync / watch / mirror from a nested pane so both contexts exist; tree and agents still work without cmux The plugin manager supplies the checksum-verified runtime binary. Contributors building from source additionally need Rust/Cargo; users do not.

How it works

cmux.app  (outer terminal host; plugin creates external Herdr viewers)
   └── Herdr engine
          └── tabs / panes / agents
                 └── cmux-herdr
                       herdr CLI + Unix socket  →  snapshot
                       cmux CLI                 →  pills, tabs, splits

sync and watch keep a user-owned cache under $XDG_STATE_HOME/cmux-herdr/ (default ~/.local/state/cmux-herdr/):

File Role
parent-<fingerprint>.json Locked outer cmux workspace for this host
associations-<fingerprint>.json Live pane_id → status_key / agent_session / status, plus mirrors and title locks
writer-<fingerprint>.json Single-writer lease (owner, pid, heartbeat_ms)
restore-<endpointHash>.json Last attach (mode: reattach only)

Host fingerprint (selects which files to read/write):

Piece Source Required for auto-resolve
Outer surface CMUX_SURFACE_ID yes
Herdr socket HERDR_SOCKET_PATH yes
Herdr server pid HERDR_SERVER_PID or a pid file beside the socket optional
Inner workspace HERDR_WORKSPACE_ID scopes associations (defaults to default)

Missing fingerprint pieces fail closed with a clear error — the plugin will not guess a host and write pills onto a random workspace. --workspace still overrides. This cache is not authoritative restore state for cmux.

Single writer. Plugin sync / watch / mirror / attach / observe / restore yield to a fresh foreign writer. A dead pid or expired heartbeat is stale. Native records are a compatibility protocol for future integration, not evidence of shipped native topology. CMUX_HERDR_NATIVE_LIVE=1 explicitly asserts native ownership; CMUX_HERDR_FORCE_PLUGIN=1 forces the plugin. CMUX_HERDR_LOCK_TITLES=1 locks each display name after the first successful write. This is a handoff, not Ghostty PTY theft.

Full design: docs/PLUGIN_DESIGN.md · docs/ARCHITECTURE.md · mapping/concept-map.md.

Status mapping

Herdr status Native icon Priority
working hammer 80
idle pause.circle 40
done checkmark.circle 30
blocked exclamationmark.triangle 90
unknown questionmark.circle 10

Every sync removes stale herdr:* keys and leaves unrelated cmux status alone. Progress is the fraction of agents still working. The sidebar shows the status label (working, idle, done), not the raw key.

Native metadata receives text, SF Symbols and priority, without --color. cmux owns theme and selected-row contrast. Its audited generic status API interprets explicit hex colors, not adaptive semantic color tokens; omission also clears a previous explicit tint on successful replacement. Legacy cached colors remain retryable until that write succeeds, then unchanged syncs deduplicate. Status meaning remains visible in text and icons, not plugin-owned state colors. cmux deliberately substitutes selected foregrounds for contrast; this is not an upstream defect established by #75, and the full colored-selected request is not closed by this change.

The plugin-manager sidebar entrypoint is a terminal workspace fallback, not a native metadata component. It retains socket navigation, > selection and * active-workspace markers, using terminal-default foreground/background without a reverse-video selection palette. Agent status uses native metadata where exposed; no separate custom sidebar/theme or invented native plugin API is introduced. See the source-pinned capability audit.

Deep mirror

cmux-herdr watch is the product path. It turns on the full reconcile contract (all tabs, prune, layout tree, ratios, tab order, focus) so inner Herdr sessions appear as real cmux tabs and panes. mirror remains the one-shot / scoped tool. Shipped versus planned status is defined by the capability matrix; tmux design targets are not release guarantees.

Herdr cmux projection
Tab cmux tab (first pane is the tab root); order follows Herdr tab numbers
Extra panes cmux splits from the layout tree (horizontal → right, vertical → down)
Split ratios cmux set-ratio from layout cell rects
Focused pane matching cmux surface
Pane contents cmux-herdr attach-pane follower (herdr pane read + pane send-text)

Reconcile is idempotent: each pane is keyed herdr-mirror:<pane_id>. A second watch keeps existing surfaces and only creates, renames, or prunes diffs.

cmux-herdr watch                  # product path: live tabs/splits + pills
cmux-herdr watch --pills-only     # pills, no projection
cmux-herdr mirror                 # current $HERDR_TAB_ID only (safe one-shot)
cmux-herdr mirror --all           # full Herdr session
cmux-herdr mirror --tmux-parity
cmux-herdr mirror --dry-run       # plan only
cmux-herdr mirror --prune         # close cmux surfaces whose Herdr panes are gone

This cannot steal Herdr PTYs into Ghostty. It creates extra cmux viewers of the live Herdr session — the same idea as attaching a second tmux client.

Limitations

  • Extra viewers, not TTY takeover. RemoteHerdrWindowMirror is roadmap work, absent from the audited cmux source. Native Sidebar issue #75 remains blocked.
  • Nested shells can carry stale outer cmux IDs. The plugin re-resolves the live containing workspace before writing status.
  • Multi-parent hosts need a complete fingerprint (CMUX_SURFACE_ID + HERDR_SOCKET_PATH).
  • The plugin does not inject a fake tmux binary; see shims/README.md.
  • Titles and renames are owned by Herdr title tracks, not this plugin.

Inventory and open checklist: OPEN.md.

FAQ

Is this shipped inside cmux.app? No. It is a user-installed cmux plugin. You keep it when you upgrade cmux.

How is this different from herdr-plugin-cmux? lachieh/herdr-plugin-cmux is a Herdr plugin (herdr plugin install …) that adds sidebar rows from the Herdr side. cmux-herdr is the cmux plugin: official cmux sidebar plugin install, cmux-herdr CLI, watch as the live GUI path, and agent skill. You can use one or both; they share the idea, not the install.

Will native cmux nested topology replace this? That roadmap is tracked by cmux#8737 and related proposals. This plugin provides external viewers today. Its lease protocol anticipates native cooperation but does not ship a native controller.

Does it need a cmux PR to work? No. Install it with the plugin manager and run it.

Planned native Herdr GUI

Integrated multi-workspace native sidebars and native pane attachment are planned capabilities, not shipped features. They require upstream cmux integration; installing this plugin does not enable them. Native Sidebar issue #75 is blocked; plugin stabilization does not close it. The capability matrix above defines current release scope; the stabilization audit provides source evidence. Design notes live in docs/upstream/.

Track Link
Community poll Discussion #10106
Compat dispatcher PR #8736
Nested topology sidebar PR #10045
Window-mirror engine RaviTharuma/cmux#8
Full design issue Issue #8737

Development

The canonical checks are:

./scripts/test.sh          # cargo fmt --check, cargo clippy -- -D warnings, cargo test
./bin/cmux-herdr --version
./bin/cmux-herdr --help
./bin/cmux-herdr doctor
./bin/cmux-herdr-sidebar --help

Runtime implementation is under src/*.rs; bin/* contains only thin POSIX-sh launchers. Layout of the repo: docs/ARCHITECTURE.md. Index: docs/README.md.

Contributing

Bug reports and PRs are welcome. Please read CONTRIBUTING.md and the Code of Conduct. Security issues go through SECURITY.md (private advisory), not a public issue.

Maintainer notes: docs/MAINTAINING.md (English) and docs/de/GITHUB.md (Deutsch).

License

MIT © 2026 Ravi Tharuma.

About

cmux plugin for Herdr: live sidebar status pills, tab/pane mirroring, nested agents CLI

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

8 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages