Make claude-cli:// run cmux agent-teams instead ;-)
#4147
braden-reap
started this conversation in
Show and tell
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Claude's URL handler only has hard coded list of terminals allowed on Mac OS (linux you can just set TERMINAL env var and windows is hard coded)... but i asked claude and claude literally read its own soruce code and found this :
So I had claude write instruction prompt that will help set your claude up to use cmux ... read it over first!! Totally overkill for this but it is quite nice for runbooks and such!
I am sure there is some stupid simple method to do it :-P but I wanted to make sure it just reuses my
ccommand that i use so i can edit the flags and command that i use when i run claude myself or when using the cli.claude-cli://open?repo=manaflow-ai/cmux&q=hey(if you had cmux repo cloned somewhere on your system)cmuxAND/OR control the flags to send when opening from url...The Prompt Content for Claude
Paste this whole file into Claude Code (
/copyfirst, then paste). The agent will walk you through the setup with questions for the parts that are personal preference.Goal (for the agent)
The user wants
claude-cli://deep links (clicked anywhere on macOS — from a browser, Slack, etc.) to open a new cmux workspace running an interactive claude session, instead of opening Ghostty or Terminal.app.You'll set up four pieces:
~/.local/bin/<name>shim that launches cmux's claude integration with the user's preferred flags. They can edit this file later to change flags — the deep-link path will pick those changes up automatically./Applications/<HijackedName>.appthat hijacks a slot in claude's hardcoded terminal-name list and re-routes incoming launches into cmux.~/.claude.json: an env override (mandatory) and thedeepLinkTerminalvalue./tmp/claude-handler-out.txtthat the wrapper writes every time it's invoked — useful if the wiring later breaks.Read this before touching anything
Claude's URL handler reads
~/.claude.json#deepLinkTerminaland looks the value up in a hardcoded list of six names:iTerm, Ghostty, kitty, Alacritty, WezTerm, Terminal. cmux is not in that list. SettingdeepLinkTerminal: "cmux"does not work — claude falls back to a probe, and a background routine calledQrK()readsTERM_PROGRAMon every interactive startup and overwrites the config back to whatever's installed.Two consequences:
CFBundleName. The wrapper is just an Info.plist + an executable script.TERM_PROGRAMwould constantly clobberdeepLinkTerminal. We disable that by settingTERM_PROGRAMto a value the lookup table doesn't recognize, in claude's own env block.Also:
cmux claude-teamsinvoked from outside an existing cmux daemon context just exec's claude inline. Under launchd (where the URL handler runs) there's no TTY, so claude auto-enables--printand errors with"Input must be provided either through stdin or as a prompt argument". The wrapper must usecmux new-workspace --command "..."so the new workspace owns a pty.Confirm with the user via AskUserQuestion
Before writing any files, ask these four questions. Don't proceed with defaults — these are personal-preference values.
Q1: Which terminal slot should we hijack?
The hijacked slot must be in claude's hardcoded list AND not actually installed (otherwise launches go to the real app instead of the wrapper). Before showing the question, run:
Recommend the first one that's not installed. Options to present:
(
GhosttyandiTermandTerminalare likely installed; avoid them unless the user explicitly wants to give them up.)Q2: What should we name the shim command?
This is the script at
~/.local/bin/<name>that the user types in a shell to start a claude session. Default:c. Ask for a 1–3 character name; warn that something likec,cc, or initials is conventional. The wrapper will exec this same script, so any flag changes the user makes later flow through to the deep-link path too.Q3: How should sessions launch?
Options:
cmux claude-teams(recommended — opens in a cmux workspace with claude's teams features)"claude(simpler, no cmux integration)"If they pick bare
claude, the wrapper still routes through cmux (cmux new-workspace --command "claude ..."); we just skip theclaude-teamssubcommand.Q4: Which flags should the shim use?
Options:
--exclude-dynamic-system-prompt-sections)"If they pick the preset, also ask Q4a: "Want to add
--remote-control-session-name-prefix <prefix>? Pick a short prefix unique to this machine (e.g. hostname or initials). Skip if unsure." This is optional — the prefix shows up in cmux's session-list UI when remote-controlling sessions.Prerequisites the agent should verify (don't install for them)
/Applications/cmux.appexists (its CLI is at/Applications/cmux.app/Contents/Resources/bin/cmux)~/Applications/Claude Code URL Handler.appexists — this is what registers theclaude-cli://scheme. If it's missing, Claude Code isn't installed or hasn't been launched yet.~/.local/binis onPATH(checkecho $PATH). If not, the user needs to add it before the shim works from outside the wrapper.Mandatory mechanics (do not change)
CFBundleNameof the wrapper MUST exactly match one ofiTerm,Ghostty,kitty,Alacritty,WezTerm,Terminal. Mismatch breaksopen -na <name>lookup.~/.claude.json#env.TERM_PROGRAMMUST be a string not in{iterm, iterm.app, ghostty, kitty, alacritty, wezterm, apple_terminal}. Use"cmux".COLORTERM=truecolorso 24-bit color isn't lost when the spoofedTERM_PROGRAMskips the color-detection switch./Applications/cmux.app/Contents/Resources/bintoPATH. cmux's bin dir is added only by cmux's own zsh integration, not by.pathrc.cmux new-workspace --command "...". Do NOT callcmux claude-teamsdirectly from the wrapper — it will exec claude inline and fail without a TTY.-e <bin>pair from incoming argv when building the forwarded command. cmux runs its own claude.Step-by-step
For the placeholders below:
<HijackedName>= Q1 answer,<shimName>= Q2 answer,<launcher>= eithercmux claude-teamsorclaudeper Q3,<flags>= Q4 answer (may be empty).1.
~/.local/bin/<shimName>Then
chmod +x ~/.local/bin/<shimName>.If
<launcher>iscmux claude-teams, leave it as-is —cmuxresolves through the user's shell PATH. If it's bareclaude, the user must haveclaudeon PATH (Claude Code installs it there by default).2.
/Applications/<HijackedName>.app/Contents/Info.plist3.
/Applications/<HijackedName>.app/Contents/MacOS/<HijackedName>Then
chmod +x /Applications/<HijackedName>.app/Contents/MacOS/<HijackedName>.Argv-parse note: the snippet above assumes the Alacritty flag shape. If the user picked a different slot in Q1, adjust the parser. For reference, claude's launcher invokes:
4. Register the bundle with Launch Services
Verify ours is the only registered app with that
CFBundleName:You want exactly one line,
local.cmux-shim.<lowercased-hijackedname>. If another entry appears, the real app is installed somewhere — abort and have the user pick a different slot.5. Patch
~/.claude.jsonTwo edits — preserve every other key. Don't run a destructive rewrite; use a JSON patch tool or read-modify-write.
envobject, add (merge with whatever's already there):deepLinkTerminalto the hijacked name:6. Verify end-to-end
Ask the user to click any
claude-cli://link (e.g. a session deep-link from the Claude Code web UI), then run:Expected: an
===== invocation <timestamp> =====block, withcmux: /Applications/cmux.app/Contents/Resources/bin/cmuxresolved, and-- new-workspace exited with 0 --at the end. A new cmux workspace should have appeared with claude running and the deep-link's prefill text loaded.Then check that the rewrite gotcha is dead:
After starting any interactive claude session, those values should still match what you wrote. If
deepLinkTerminalflipped back to something else, the env override didn't take — re-verify theenvblock in~/.claude.json.Troubleshooting cheatsheet
lsregister -f /Applications/<HijackedName>.app.cmux: MISSING"Input must be provided ... when using --print"cmux claude-teamsdirectly instead ofcmux new-workspace --command "...".deepLinkTerminalkeeps flipping to"Ghostty"~/.claude.json#env.TERM_PROGRAMisn't set, or is set to a value the lookup table recognizes.cmux claude-teamsinternally and the wrapper is also invoking viacmux claude-teamssomewhere. Make sure the wrapper usescmux new-workspace --command "<shim> ...".--deep-link-*and--prefillare all reaching the forwardedcmdline.open -a /Applications/cmux.appstep is missing from the wrapper, or System Settings ("Displays have separate Spaces" / a window pinned to a specific Space) is overriding the activation. Escalation: replaceopen -awithosascript -e 'tell application "cmux" to activate'.Beta Was this translation helpful? Give feedback.
All reactions