Repository navigation
cmux: the claude <account> dispatcher is silently shadowed by cmux's own shell integration #161
Description
Symptom
In a cmux pane, claude work does not switch accounts. Instead work is handed to Claude Code as the initial prompt, and the session starts under whatever account the environment already names — normally the anchor. Nothing errors, so the only signs are the assistant answering a one-word prompt and the session carrying the wrong account badge in CodeV's Sessions tab.
This is pre-existing, not a regression: it reproduces on a CodeV build from before #160.
Cause
cmux ships its own shell integration that installs a claude wrapper, and it loads after ~/.zshrc, so it replaces the dispatcher accounts.sh defines. From cmux.app/Contents/Resources/shell-integration/cmux-zsh-integration.zsh:
builtin unalias "$command_name" >/dev/null 2>&1 || true
if [[ "$command_name" == "claude" ]]; then
eval "$command_name() { _cmux_claude_wrapper_command \"\$@\"; }"
...
_cmux_install_cli_wrapper claude _CMUX_CLAUDE_WRAPPER cmux-claude-wrapper_cmux_claude_wrapper_command runs a generated shim — whose directory is also prepended to PATH — which execs cmux's own cmux-claude-wrapper, which execs the real claude with the arguments unchanged. So the account name is simply argv[1], which Claude Code reads as the initial prompt.
Measured in a live pane (the account label is replaced with the README's work example):
--- type claude ---
claude is a shell function # cmux's, defined by eval — no file attribution
--- type claude-work ---
claude-work is a shell function from ~/.config/codev/accounts.sh
--- PATH head ---
/var/folders/…/cmux-cli-shims/<uuid>
/Applications/cmux.app/Contents/Resources/bin
What still works
claude-<name>— cmux wraps onlyclaude(andgrok), so CodeV's per-account launcher functions survive intact. This is the workaround in cmux today. It works becauseenv CLAUDE_CONFIG_DIR=… claude …resolvesclaudethroughPATH, so it goes through cmux's shim with the environment variable untouched.- CodeV's own launches — the account picker (
⌥⌘+Enter), resume, and thecmux workspace create --command …path all emitCLAUDE_CONFIG_DIR='…' command claude …, which never relies on the dispatcher. - Other terminals — nothing replaces
claudein iTerm2, Terminal.app or Ghostty.
Also worth knowing
cmux carries a competing account notion of its own: _cmux_normalize_claude_config_dir rewrites CLAUDE_CONFIG_DIR when it points under ~/.subrouter/codex/claude, remapping it to ~/.codex-accounts/claude. CodeV's account folders (~/.claude-<name>) sit outside that prefix, so they are left alone today — but two account models are live in the same shell, and that remap is worth watching.
Possible fixes (none decided)
- Document it and make
claude-<name>the recommended form in cmux. Cheapest, and it already works. - Warn once per shell: if
claudeis not the functionaccounts.shdefined, print one line pointing atclaude-<name>. This is the mirror of Multi-account onboarding: detect existing config dirs, name-your-default UI, wrapper-collision warning #127 (which warns whenaccounts.shoverrides a hand-rolledclaude()), so the two could share one check. - Re-assert the dispatcher from a zsh
precmdhook. This fights cmux's integration, which exists for cmux's own agent features, so it is probably the wrong trade. - Chain instead of replace — have the dispatcher call through to a previously defined
claude. Ordering is wrong here (cmux loads last), so this alone does not fix it.
The silent part is what makes this worth more than a documentation line: a session meant for one account starts under another, and the only trace is a stray one-word prompt.
Environment
zsh, cmux bundled at /Applications/cmux.app, CodeV 1.0.92.
🤖 On behalf of @grimmerk — generated with Claude Code
Activity
docs(readme): both launcher forms carry the memory redirect