Repository navigation
cmux control
You run Claude inside cmux. cmux has a notification centre, a status pill and a progress bar; Claude Code, running inside that terminal, uses none of them. So the only way to know a twenty-minute agent run finished is to keep looking at it.
This plugin closes that gap, and teaches Claude the cmux CLI properly on top.
/plugin marketplace add roypadina/padina-claude-code-plugins
/plugin install cmux-control@padina
Requires macOS and the cmux app with its CLI on PATH
(/Applications/cmux.app/Contents/Resources/bin/cmux). Everything else degrades rather than breaks:
jq |
Strongly recommended. Without it the notifications lose their body text and the subtitle falls back to $PWD instead of the payload's cwd. |
git |
Not required. Outside a repo the subtitle is just the directory's own name. |
| Python 3 |
/cmux-sessions only. Nothing else uses it. |
Both hooks are guarded on command -v cmux and on CMUX_WORKSPACE_ID being set. Outside cmux,
or without the CLI, they exit 0 without a sound — no errors, no output, nothing in your transcript.
| Feature | Mechanism |
|---|---|
| Notification when a turn finishes |
Stop hook |
| Notification when a subagent finishes |
SubagentStop hook |
| Sidebar progress + status pill | skill guidance — Claude's judgement, not a hook |
/cmux-sessions — session inventory and recovery |
slash command |
| The whole cmux CLI, for Claude | skill |
Stop and SubagentStop are the purpose-built events, not a PostToolUse matcher on Task. Their
payloads carry last_assistant_message, stop_hook_active, background_tasks, and for subagents
agent_type — so the notification says something worth reading:
Claude finished
myapp · feature/RD-12851 · 2 background tasks
Fixed the null-deref in the checkout handler. Added a regression test; both suites pass.
Subagent finished · Explore
myapp · feature/RD-12851
Found 3 call sites in src/api.ts
The body is the closing message, whitespace-collapsed and cut to 180 characters on a codepoint boundary, so a multi-byte character is never sliced in half.
The Stop hook respects stop_hook_active (so it never fires again on a continuation) and never
emits decision: "block". It notifies and exits 0. It cannot keep a session running.
Set CMUX_CONTROL_QUIET=1 in the environment to turn the notifications off.
cmux's own Claude wrapper sets
CMUX_SUPPRESS_SUBAGENT_NOTIFICATIONS=1for its own notifier. This plugin deliberately does not read that variable — its semantics are undocumented, and installing this plugin is itself the opt-in.CMUX_CONTROL_QUIETis the switch that belongs to you.
Derived from git, deterministically, not guessed:
| Situation | Subtitle |
|---|---|
~/Code/myapp on feature/RD-12851
|
myapp · feature/RD-12851 |
| A subdirectory of that repo | myapp · feature/RD-12851 |
A linked worktree at ~/wt/topic
|
myapp · wt/topic — the main repo's name |
| Detached HEAD | myapp · @a1b2c3d |
| Not a git repo | the directory's own name |
Branch names keep their slashes. A worktree reports the main repository via --git-common-dir,
because a worktree directory is usually named after its branch and you would otherwise read the
branch twice.
Versions up to 0.1.0 shipped a SessionStart hook that renamed the horizontal tab and the
sidebar entry to repo · branch. It is gone.
The reason is cmux's own opt-in automation.workspaceAutoNaming setting, whose settings copy says,
verbatim:
"When enabled, cmux summarizes supported agent sessions into short workspace and tab names using each agent's own binary, refreshed as the topic shifts. Manual renames always win and stop auto-naming for that workspace or tab. Uses your agent account for the short summarization calls."
A rename-tab / workspace-action rename counts as a manual rename. So the hook did not merely set
a name — it permanently disabled cmux's naming for that workspace, on every session start, without
asking. That decision belongs to cmux and to you.
Names are now yours. Leave them, set them by hand, or turn cmux's auto-naming on and let it summarise the session:
cmux rename-tab "release prep" # horizontal tab
cmux workspace-action --action rename --title "release prep" # sidebar entryThe skill tells Claude not to rename a tab or workspace on its own initiative — the same restraint
it applies to your cmux todo list.
This one is not hook-enforced. No hook knows how far through a task Claude is, so there is
nothing to wire it to. What the plugin ships is a set of rules in the skill: use set-progress /
set-status only for genuinely long multi-step work, at most one call per completed step, and
always clear both when the work ends — including when it fails.
Claude follows guidance well but not perfectly. If you want the pill gone:
cmux clear-progress && cmux clear-status cmux_controlWhen Claude does write a pill, the skill tells it to use the status key cmux_control and never
claude_code — cmux's own Claude wrapper owns that key and writing to it would stomp the app's own
state. No script in this plugin ever calls set-status or set-progress: every pill you see came
from Claude following the skill, which is why this is listed as guidance and not as a feature.
cmux has no detached server: when the app quits, every terminal child process dies, Claude Code included. On relaunch cmux replays each pane's saved resume binding and most sessions come back — most, not all. A workspace can be dropped from the restore, or a pane can come back as a bare shell.
/cmux-sessions inventory every live Claude pane, with its resume command
/cmux-sessions check MISSING workspaces, DEAD panes, transcript warnings
/cmux-sessions restore --dry-run
/cmux-sessions restore
It reads cmux's own state file. Multiple Claude Code profiles are discovered rather than assumed:
CLAUDE_CONFIG_DIR is honoured, ~/.claude* directories are scanned, and profiles are deduped by
resolved transcript path — so if you symlink projects/ between profiles nothing is counted
twice. One profile is the normal case and needs no setup.
One skill covering the whole cmux CLI: topology and handles, launching agents in panes, browser
automation, notifications and sidebar state, cmux.json layouts, and session recovery — plus four
reference files loaded on demand (cli-reference, agents, browser, custom-commands).
Verified against cmux 0.64.22, with the traps that cost real time written down: focus-surface
and list-surfaces do not exist; --surface resolves inside the caller's workspace, so
cross-workspace calls need --workspace (the failure is a misleading
invalid_params: Surface is not a terminal); close-surface reports the surface focused after
the close; list-panes prefixes the focused row with a literal *; and identify gives you both
.caller and .focused, which are frequently different.
It also carries cmux's own guardrail about cmux todo, verbatim: "this checklist belongs to the
user. Do not add, edit, complete, remove, or replace items on your own initiative."
| Hook-enforced — happens whether or not Claude cooperates | turn-finished notification, subagent-finished notification |
| Skill guidance — Claude's judgement, usually right, not guaranteed | sidebar progress and status pills, restraint about naming and about your todo list |
| On request |
/cmux-sessions and everything else in the skill |
scripts/selfcheck.shAsserts the repo · branch derivation against real throwaway repos — plain directory, slashed
branch, subdirectory, detached HEAD, linked worktree, bare repo — then runs the session script's own
assertions. No framework; it prints a line per check.
The idea of wiring cmux to Claude Code hooks comes from
hopchouinard/cmux-plugin (MIT, Patrick Chouinard).
This is an independent reimplementation — no code was copied — whose notifications say what finished,
and where, instead of "Agent finished — check results".