Skip to content

cmux: the claude <account> dispatcher is silently shadowed by cmux's own shell integration #161

Description

@grimmerk

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 only claude (and grok), so CodeV's per-account launcher functions survive intact. This is the workaround in cmux today. It works because env CLAUDE_CONFIG_DIR=… claude … resolves claude through PATH, so it goes through cmux's shim with the environment variable untouched.
  • CodeV's own launches — the account picker (⌥⌘+Enter), resume, and the cmux workspace create --command … path all emit CLAUDE_CONFIG_DIR='…' command claude …, which never relies on the dispatcher.
  • Other terminals — nothing replaces claude in 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)

  1. Document it and make claude-<name> the recommended form in cmux. Cheapest, and it already works.
  2. Warn once per shell: if claude is not the function accounts.sh defined, print one line pointing at claude-<name>. This is the mirror of Multi-account onboarding: detect existing config dirs, name-your-default UI, wrapper-collision warning #127 (which warns when accounts.sh overrides a hand-rolled claude()), so the two could share one check.
  3. Re-assert the dispatcher from a zsh precmd hook. This fights cmux's integration, which exists for cmux's own agent features, so it is probably the wrong trade.
  4. 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Priority 2 — real and silent, but has a workaroundbugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions