Make cmux tabs show each Claude Code
session's conversation name instead of the default Claude Code [N] stamp.
A tiny watcher that runs inside a cmux terminal and clears cmux's numbered tab stamp the moment it appears — the same effect as right-clicking a tab and choosing Remove Custom Tab Name, but automatic and for every tab.
┌ before ──────────────┐ ┌ after ───────────────┐
│ Claude Code [37] │ │ hook cmux tab name │
│ Claude Code [38] │ → │ cc update problem │
│ Claude Code [41] │ │ yo! │
└──────────────────────┘ └──────────────────────┘
When Claude Code runs inside cmux, the cmux Claude wrapper injects a
SessionStart hook (cmux hooks claude session-start) that stamps the tab with
a custom name Claude Code [N] (N = the surface ref number). It re-applies on
every SessionStart — i.e. on startup, /clear, auto-compact, and resume —
so manually clearing it doesn't stick.
Interestingly, Codex tabs don't have this problem: cmux shows the Codex session name as the tab title natively. Only Claude Code gets the numbered stamp, so this is really an inconsistency between the two integrations.
There is no cmux setting to disable just the stamp (only
automation.claudeCodeIntegration: false, which also kills session-restore,
Feed approvals, and notifications — too heavy). So this watcher clears the stamp
after the fact.
Upstream feature request: manaflow-ai/cmux#5705 ("Rename tab to Claude's current conversation name").
~/Dev/cmux-claude-tabname/install.shThis symlinks bin/cmux-claude-tabname into ~/.local/bin (already on PATH).
Run it inside a cmux terminal and keep that tab open (e.g. a dedicated tab next to your other long-running processes):
cmux-claude-tabname- Polls every 5s and clears any tab whose title ends exactly with
[N] Claude Code, across all workspaces. Codex tabs and already-named tabs are never touched. - Prints
HH:MM:SS cleared surface:N (workspace:M)when it clears something, so you can see it's alive. - Change the interval:
CMUX_TABWATCH_INTERVAL=3 cmux-claude-tabname. - Stop with
Ctrl-C. - Running it a second time just exits ("already running") — a single-instance lock prevents duplicate watchers. Nothing breaks either way.
Restart it after a reboot or a cmux update (those restart cmux, which ends the tab the watcher lived in).
cmux's control socket defaults to access mode cmuxOnly — only processes that
cmux itself spawned may use it. A terminal you open inside cmux is a cmux
descendant, so the watcher launched there is allowed. The same script run from
a launchd LaunchAgent is rejected (Failed to write to socket (Broken pipe)) because launchd is not a cmux descendant.
Running it inside cmux therefore needs no security change. (If you ever did
want a real background daemon, you'd have to switch Settings ▸ Automation to
automation or password mode — not recommended just for this.)
# every INTERVAL seconds:
cmux workspace list # all workspaces
→ cmux list-pane-surfaces --workspace W # tabs in each
→ keep titles ending in "[N] Claude Code"
→ cmux tab-action --action clear-name --workspace W --tab surface:N--workspace is required: clear-name --tab surface:N alone only resolves
surfaces in the focused workspace.
- Claude Code hooks are too racy for this. They do fire under cmux
(
--settingsmerges with~/.claude/settings.json), but cmux applies the stamp asynchronously, after the hook returns; aSessionStartclear runs too early, and a backgrounded(sleep N; …) &gets killed when the hook returns. Hooks also can't fix "resume then sit idle" (no hook fires). A polling watcher wins because it reacts after the stamp settles. - launchd /
nohup/disown/ closing the host tab all detach the process from cmux's process tree → socket access is refused. Keep it inside cmux.
bin/cmux-claude-tabname the watcher (zsh)
install.sh symlink into ~/.local/bin
README.md this file