Custom Commands
Define actions, custom commands, and workspace layouts by adding .cmux/cmux.json to your project or ~/.config/cmux/cmux.json globally. Actions can appear in the surface tab bar and Command Palette.
File locations
cmux looks for configuration in two places:
- Per-project:
./.cmux/cmux.json- lives in your project directory, takes precedence - Fallback local:
./cmux.json- still supported for existing repos - Global:
~/.config/cmux/cmux.json- applies to all projects, fills in commands not defined locally
actions, shortcut, or ui.surfaceTabBar.buttons.Edit cmux.json, then press Cmd+Shift+, or run cmux reload-config to apply changes.
Schema
commands still define reusable shell commands and workspace layouts. Nightly builds add an actions registry. Actions are the public IDs shared by the surface tab bar, the Command Palette, and action-level shortcuts.
{
"actions": {
"cmux.newTerminal": {
"type": "command",
"title": "Codex",
"subtitle": "Open Codex in a new terminal tab",
"command": "codex --dangerously-bypass-approvals-and-sandbox",
"target": "newTabInCurrentPane",
"shortcut": "cmd+t",
"icon": { "type": "image", "path": "./icons/codex.svg" }
},
"claude": {
"type": "command",
"title": "Claude Code",
"command": "claude --dangerously-skip-permissions",
"target": "newTabInCurrentPane",
"shortcut": "cmd+shift+c",
"icon": { "type": "image", "path": "./icons/claude.svg" }
},
"opencode": {
"type": "command",
"title": "OpenCode",
"command": "opencode",
"target": "newTabInCurrentPane",
"palette": false,
"icon": { "type": "emoji", "value": "๐งช", "scale": 0.9 }
},
"web-dev": {
"type": "workspaceCommand",
"title": "Web Dev",
"commandName": "Web Dev"
}
},
"ui": {
"surfaceTabBar": {
"buttons": [
"cmux.newTerminal",
"cmux.newBrowser",
"cmux.splitRight",
"cmux.splitDown",
"claude"
]
}
},
"commands": [
{
"name": "Web Dev",
"keywords": ["dev", "start"],
"workspace": { ... }
}
]
}Nightly action registry
actions maps stable IDs to runnable behavior. Use the built-in IDs cmux.newTerminal, cmux.newBrowser, cmux.splitRight, and cmux.splitDown to override the defaults. Use your own IDs for project-specific tools.
palette defaults to true. Set it to false to keep an action out of Command Palette while still making it available to the surface tab bar or a shortcut. shortcut uses the same syntax as settings shortcuts, for example cmd+shift+c or ["cmd+k", "cmd+c"].
ui.surfaceTabBar.buttons replaces the default button list when present. Leave out a built-in ID to hide it. Icons always use an object shape: { "type": "symbol", "name": "play.circle" }, { "type": "emoji", "value": "๐งช", "scale": 0.9 }, or { "type": "image", "path": "./icons/codex.svg" }. Image paths are relative to the config file. Emoji scale is optional and defaults to 1. SVG, PDF, PNG, JPEG, GIF, TIFF, BMP, HEIC, HEIF, WebP, AVIF, ICO, and ICNS are supported.
Each button entry can be either an action ID string or a button object. Use a button object when you want the same action with a different surface label, icon, or tooltip. The resolved button title is also used as the trust prompt title.
Put any approval or permission flags directly in the command string you actually want to run. The default action target is newTabInCurrentPane, so the common pattern is to open a new terminal tab in the current pane and start Codex, Claude Code, or OpenCode there.
Custom actions and Command Palette
An actions entry is the reusable thing cmux runs. Use actions when the same behavior should be available from the Command Palette, surface tab bar, shortcuts, or the plus-button menu. Keep commands for reusable shell commands and workspace layouts. Set palette to false when an action should stay out of Command Palette.
Action types
"builtin": Alias a built-in cmux action such as cmux.newTerminal, cmux.newBrowser, cmux.splitRight, or cmux.splitDown."command": Run shell text in a terminal. Use target to choose the current terminal or a new tab in the current pane."agent": Start a supported coding agent. Today this supports codex and claude, with optional args."workspaceCommand": Run a named workspace definition from commands. Use this for multi-pane layouts, custom working directories, and startup commands.
Action fields
title: Command Palette row title, surface button label, menu item title, and trust prompt title unless an entry overrides it.subtitle/description: Command Palette secondary text. description is accepted as an alias for subtitle.keywords: Extra search terms for Command Palette.palette: Defaults to true for custom actions. Set false to hide the action from Command Palette while keeping it callable elsewhere.shortcut: Optional action shortcut, using the same single-stroke or two-stroke chord syntax as settings shortcuts.target: For command and agent actions only. Use currentTerminal or newTabInCurrentPane.confirm: Ask before running the action.
Command Palette behavior
Command Palette reads the resolved action registry. Custom action IDs are added as rows when palette is not false. Legacy commands are added automatically as custom rows unless an action with the same generated ID already exists. Built-in commands keep their normal palette labels, but overriding a built-in ID such as cmux.newTerminal changes the behavior behind that shared entrypoint.
Custom plus button actions
Use ui.newWorkspace.action to override what the plus button does. Use ui.newWorkspace.contextMenu (or the rightClick alias) to define the ordered right-click menu. Menu entries can be action IDs, action objects, or { "type": "separator" }.
{
"actions": {
"worktree-agents": {
"type": "workspaceCommand",
"title": "Worktree Agents",
"commandName": "Worktree Agents",
"icon": { "type": "symbol", "name": "folder.badge.plus" }
}
},
"ui": {
"newWorkspace": {
"action": "worktree-agents",
"contextMenu": [
{ "action": "worktree-agents", "title": "Worktree Agents" },
{ "type": "separator" },
{ "action": "cmux.newTerminal", "title": "New Terminal" },
{ "action": "cmux.newBrowser", "title": "New Browser" }
]
}
},
"commands": [
{
"name": "Worktree Agents",
"description": "Create a fresh Git worktree and start Codex and Claude inside it",
"workspace": {
"name": "Worktree Agents",
"cwd": ".",
"layout": {
"direction": "horizontal",
"split": 0.38,
"children": [
{
"pane": {
"surfaces": [
{
"type": "terminal",
"name": "Worktree",
"command": "set -euo pipefail; state=\"${TMPDIR:-/tmp}/cmux-worktree-${CMUX_WORKSPACE_ID:-manual}.dir\"; rm -f \"$state\"; repo=$(git rev-parse --show-toplevel); mkdir -p \"$repo/../worktrees\"; slug=agents-$(date +%Y%m%d-%H%M%S); dir=\"$repo/../worktrees/$slug\"; git -C \"$repo\" worktree add -b \"$slug\" \"$dir\"; printf \"%s\\n\" \"$dir\" > \"$state\"; cd \"$dir\"; exec \"${SHELL:-/bin/zsh}\" -l",
"focus": true
}
]
}
},
{
"direction": "vertical",
"split": 0.5,
"children": [
{
"pane": {
"surfaces": [
{
"type": "terminal",
"name": "Codex",
"command": "state=\"${TMPDIR:-/tmp}/cmux-worktree-${CMUX_WORKSPACE_ID:-manual}.dir\"; echo \"Waiting for worktree...\"; while [ ! -s \"$state\" ]; do sleep 0.2; done; dir=$(cat \"$state\"); cd \"$dir\"; exec codex --dangerously-bypass-approvals-and-sandbox"
}
]
}
},
{
"pane": {
"surfaces": [
{
"type": "terminal",
"name": "Claude",
"command": "state=\"${TMPDIR:-/tmp}/cmux-worktree-${CMUX_WORKSPACE_ID:-manual}.dir\"; echo \"Waiting for worktree...\"; while [ ! -s \"$state\" ]; do sleep 0.2; done; dir=$(cat \"$state\"); cd \"$dir\"; exec claude --dangerously-skip-permissions"
}
]
}
}
]
}
]
}
}
}
]
}This example makes the normal plus click run the worktree-agents action. The workspace command from commands uses a visible setup terminal to create the Git worktree first. Codex and Claude start at the same time, wait for the workspace-specific state file, then cd into the created directory before exec.
Simple commands
A simple command runs a shell command in the currently focused terminal:
{
"commands": [
{
"name": "Run Tests",
"keywords": ["test", "check"],
"command": "npm test",
"confirm": true
}
]
}Fields
name: Displayed in the command palette (required)description: Optional descriptionkeywords: Extra search terms for the command palettecommand: Shell command to run in the focused terminalconfirm: Show a confirmation dialog before running
Simple commands run in the focused terminal's current working directory. If your command relies on project-relative paths, prefix it with cd "$(git rev-parse --show-toplevel)" && to run from the repo root, or cd /your/path && for any specific directory.
Workspace commands
A workspace command creates a new workspace with a custom layout of splits, terminals, and browser panes:
{
"commands": [
{
"name": "Dev Environment",
"keywords": ["dev", "fullstack"],
"workspace": {
"name": "Dev",
"cwd": ".",
"layout": {
"direction": "horizontal",
"split": 0.5,
"children": [
{
"pane": {
"surfaces": [
{
"type": "terminal",
"name": "Frontend",
"command": "npm run dev",
"focus": true
}
]
}
},
{
"pane": {
"surfaces": [
{
"type": "terminal",
"name": "Backend",
"command": "cargo watch -x run",
"cwd": "./server",
"env": { "RUST_LOG": "debug" }
}
]
}
}
]
}
}
}
]
}Workspace fields
name: Workspace tab name (defaults to command name)cwd: Working directory for the workspacecolor: Workspace tab colorlayout: Layout tree defining splits and panes
Restart behavior
Controls what happens when a workspace with the same name already exists:
"new": Create a new workspace (default)"ignore": Switch to the existing workspace"recreate": Close and recreate without asking"confirm": Ask the user before recreating
Layout tree
The layout tree defines how panes are arranged using recursive split nodes:
Split node
Divides space into two children:
direction:"horizontal"or"vertical"split: Divider position from 0.1 to 0.9 (default 0.5)children: Exactly two child nodes (split or pane)
Pane node
A leaf node containing one or more surfaces (tabs within the pane).
Surface definition
Each surface in a pane can be a terminal or a browser:
type:"terminal"or"browser"name: Custom tab titlecommand: Shell command to auto-run on creation (terminal only)cwd: Working directory for this surfaceenv: Environment variables as key-value pairsurl: URL to open (browser only)focus: Focus this surface after creation
Working directory resolution
.or omitted: workspace working directory./subdir: relative to workspace working directory~/path: expanded to home directory- Absolute path: used as-is
Full example
{
"actions": {
"web-dev": { "type": "workspaceCommand", "commandName": "Web Dev" },
"cmux.newTerminal": {
"type": "command",
"title": "Codex",
"command": "codex --dangerously-bypass-approvals-and-sandbox",
"target": "newTabInCurrentPane",
"shortcut": "cmd+t",
"icon": { "type": "image", "path": "./icons/codex.svg" }
},
"claude": {
"type": "command",
"title": "Claude Code",
"command": "claude --dangerously-skip-permissions",
"target": "newTabInCurrentPane",
"shortcut": "cmd+shift+c",
"icon": { "type": "image", "path": "./icons/claude.svg" }
},
"start-dev": {
"type": "command",
"command": "npm run dev",
"target": "newTabInCurrentPane",
"icon": { "type": "symbol", "name": "play.circle" }
}
},
"ui": {
"surfaceTabBar": {
"buttons": [
"cmux.newTerminal",
"cmux.newBrowser",
"cmux.splitRight",
"cmux.splitDown",
{
"action": "claude",
"title": "Claude Here"
},
"start-dev"
]
}
},
"commands": [
{
"name": "Web Dev",
"description": "Docs site with live preview",
"keywords": ["web", "docs", "next", "frontend"],
"workspace": {
"name": "Web Dev",
"cwd": "./web",
"color": "#3b82f6",
"layout": {
"direction": "horizontal",
"split": 0.5,
"children": [
{
"pane": {
"surfaces": [
{
"type": "terminal",
"name": "Next.js",
"command": "npm run dev",
"focus": true
}
]
}
},
{
"direction": "vertical",
"split": 0.6,
"children": [
{
"pane": {
"surfaces": [
{
"type": "browser",
"name": "Preview",
"url": "http://localhost:3777"
}
]
}
},
{
"pane": {
"surfaces": [
{
"type": "terminal",
"name": "Shell",
"env": { "NODE_ENV": "development" }
}
]
}
}
]
}
]
}
}
},
{
"name": "Debug Log",
"description": "Tail the debug event log from the running dev app",
"keywords": ["log", "debug", "tail", "events"],
"workspace": {
"name": "Debug Log",
"layout": {
"direction": "horizontal",
"split": 0.5,
"children": [
{
"pane": {
"surfaces": [
{
"type": "terminal",
"name": "Events",
"command": "tail -f /tmp/cmux-debug.log",
"focus": true
}
]
}
},
{
"pane": {
"surfaces": [
{
"type": "terminal",
"name": "Shell"
}
]
}
}
]
}
}
},
{
"name": "Setup",
"description": "Initialize submodules and build dependencies",
"keywords": ["setup", "init", "install"],
"command": "./scripts/setup.sh",
"confirm": true
},
{
"name": "Reload",
"description": "Build and launch the debug app tagged to the current branch",
"keywords": ["reload", "build", "run", "launch"],
"command": "./scripts/reload.sh --tag $(git branch --show-current)"
},
{
"name": "Run Unit Tests",
"keywords": ["test", "unit"],
"command": "./scripts/test-unit.sh",
"confirm": true
}
]
}