自定义命令和操作
cmux.json 用来让 cmux 学会你自己的工作流程:经常运行的 shell 命令、打开时就按预设排好终端和浏览器的工作区,以及可以绑定到快捷键、标签栏按钮和 Command Palette 的操作。可以放在项目内的 .cmux/cmux.json、对所有项目生效的 ~/.config/cmux/cmux.json,或两处都放。
cmux 从哪里读取 cmux.json
条目可以来自项目文件、用户级文件,或两者同时提供。对于项目文件,cmux 从当前目录开始查找,并依次检查其上方的每一级父目录。
- 项目级:
./.cmux/cmux.json(从工作目录向上查找得到;优先于用户级文件) - 本地回退:
./cmux.json(仍支持现有仓库) - 用户级:
~/.config/cmux/cmux.json(由所有项目共享;补充项目文件中未定义的内容)
编辑 cmux.json 后,按 Cmd+Shift+, 或运行 cmux reload-config 来应用更改。
shell 命令
最简单的条目只有一个名称和一个命令字符串。在 Command Palette 中选择它,就会把该字符串输入到当前聚焦的终端并运行。
{
"commands": [
{
"name": "Format Changed Files",
"description": "Run the formatter on files that differ from main",
"keywords": [
"fmt",
"prettier",
"style"
],
"command": "git diff --name-only main -- '*.ts' '*.tsx' | xargs npx prettier --write"
},
{
"name": "Reset Local Database",
"keywords": [
"db",
"wipe"
],
"command": "cd \"$(git rev-parse --show-toplevel)\" && ./scripts/db-reset.sh",
"confirm": true
}
]
}shell 命令字段
name: 必填且不能为空的标签,用于在 Command Palette 中选择command: cmux 输入到当前聚焦终端并执行的 shell 文本description: 可选的次要文本,显示在条目旁边keywords: 在 Command Palette 中搜索时也应匹配此条目的额外词语confirm: 设为 true 后,每次运行前都会弹出确认提示
shell 命令会在当前聚焦终端所在的位置执行。如果命令需要一个确定的起始位置,可以在开头加上 cd "$(git rev-parse --show-toplevel)" && 来切换到 Git 仓库的顶层,或者加上 cd /some/folder && 固定到某个文件夹。
打开工作区的命令
给命令提供一个 workspace 对象来代替命令字符串,它就会改为打开一个新的工作区,排列成一棵由分屏窗格组成的树,其中容纳终端和浏览器。每个条目必须且只能包含两者之一。
{
"commands": [
{
"name": "API Stack",
"keywords": [
"api",
"server",
"logs"
],
"restart": "confirm",
"workspace": {
"name": "API",
"cwd": "./services/api",
"color": "#10b981",
"env": {
"PORT": "8080"
},
"layout": {
"direction": "vertical",
"split": 0.65,
"children": [
{
"direction": "horizontal",
"children": [
{
"pane": {
"surfaces": [
{
"type": "terminal",
"name": "Server",
"command": "go run ./cmd/api",
"focus": true
}
]
}
},
{
"pane": {
"surfaces": [
{
"type": "terminal",
"name": "Worker",
"command": "go run ./cmd/worker",
"env": {
"QUEUE": "default"
}
}
]
}
}
]
},
{
"pane": {
"surfaces": [
{
"type": "terminal",
"name": "Logs",
"cwd": "/var/log",
"command": "tail -F system.log"
},
{
"type": "browser",
"name": "Health",
"url": "http://localhost:8080/healthz"
}
]
}
}
]
}
}
}
]
}workspace 对象的字段
name: 工作区标题;未设置时使用命令名称cwd: 工作区的基准目录;每个标签页的 cwd 都相对于它解析layout: 下文所述布局树的根节点color: 侧边栏颜色,使用 #RRGGBB 十六进制值或工作区颜色名称env: Environment variables inherited by every shell in the workspacesetup: Bootstrap command sent to the workspace's first terminal before that terminal's own command
当工作区已经打开时
在命令上设置 restart,决定已存在同名工作区时的行为:
"new": 创建新的 workspace(默认)"confirm": 先询问,你同意后再替换"recreate": 不询问,立即替换"ignore": 切换到现有 workspace
描述布局
布局是一棵树。每个内部节点把自己的区域一分为二,每个叶子节点都是一个包含标签页的窗格。
分屏节点
分屏把自己的区域分成两部分:
direction:"horizontal"或"vertical"children: 恰好包含两个节点的数组,每个节点是另一个分屏或一个窗格split: 分隔线的位置,以区域的比例表示。默认为 0.5;超出 0.1 到 0.9 范围的值会被限制在该范围内
窗格节点
窗格是树的叶子节点。它的 surfaces 数组列出打开时包含的标签页,且至少要有一个条目。
Surface
窗格 surfaces 数组中的每个条目都会打开一个标签页,可以是终端或浏览器:
type:"terminal"或"browser"name: 替代默认标题显示的标签页标题focus: 设为 true 后,布局构建完成时键盘焦点会落在这个标签页上command: 仅限终端:终端打开后立即运行的 shell 文本url: 仅限浏览器:要加载的页面cwd: 此标签页的起始目录,按下方规则解析env: 包含额外环境变量名称和值的对象
cwd 值的解析方式
~/path: 在你的主文件夹内解析- 以 / 开头的路径: 按原样使用
./subdir: 拼接在工作区目录之后.或 未设置: 沿用工作区目录
文件结构
actions、shortcut 或 ui.surfaceTabBar.buttons 前,请安装最新 nightly 构建。commands 仍用于定义可复用的 shell 命令和 workspace 布局。Nightly 构建会加入 actions 注册表。动作是 surface 标签栏、Command Palette 和动作级快捷键共享的公开 ID。
{
"actions": {
"cmux.newTerminal": {
"type": "agent",
"agent": "claude",
"title": "Claude Code",
"subtitle": "Start Claude Code in a new tab"
},
"lint": {
"type": "command",
"title": "Lint",
"subtitle": "Run the linter in a fresh tab",
"keywords": [
"eslint",
"check"
],
"command": "npm run lint",
"shortcut": [
"cmd+k",
"cmd+l"
],
"icon": {
"type": "symbol",
"name": "checkmark.seal"
}
},
"scratch-dir": {
"type": "command",
"title": "Scratch Directory",
"command": "cd \"$(mktemp -d)\"",
"target": "currentTerminal",
"palette": false,
"icon": {
"type": "emoji",
"value": "📝",
"scale": 0.85
}
},
"api-stack": {
"type": "workspaceCommand",
"title": "API Stack",
"commandName": "API Stack"
}
},
"ui": {
"surfaceTabBar": {
"buttons": [
"cmux.newTerminal",
"cmux.splitRight",
"lint",
{
"action": "scratch-dir",
"tooltip": "cd into a new temp folder"
}
]
}
},
"commands": [
{
"name": "API Stack",
"keywords": [
"api"
],
"workspace": {
"cwd": "./services/api",
"layout": {
"pane": {
"surfaces": [
{
"type": "terminal",
"name": "Server",
"command": "go run ./cmd/api"
}
]
}
}
}
}
]
}Nightly 动作注册表
actions 将稳定 ID 映射到可运行的行为。使用内置 ID cmux.newTerminal、cmux.newBrowser、cmux.splitRight 和 cmux.splitDown 覆盖默认值。项目专用工具可以使用你自己的 ID。
palette 默认是 true。将其设为 false 可以让动作不显示在 Command Palette 中,但仍可从 surface 标签栏或快捷键调用。shortcut 使用与设置快捷键相同的语法,例如 cmd+shift+c 或 ["cmd+k", "cmd+c"]。
存在 ui.surfaceTabBar.buttons 时,它会替换默认按钮列表。省略某个内置 ID 即可隐藏它。图标始终使用对象形式:{ "type": "symbol", "name": "play.circle" }、{ "type": "emoji", "value": "🧪", "scale": 0.9 } 或 { "type": "image", "path": "./icons/codex.svg" }。图片路径相对于配置文件。Emoji scale 可选,默认值为 1。支持 SVG、PDF、PNG、JPEG、GIF、TIFF、BMP、HEIC、HEIF、WebP、AVIF、ICO 和 ICNS。
每个按钮条目可以是动作 ID 字符串,也可以是按钮对象。当你想让同一个动作使用不同的 surface 标签、图标或 tooltip 时,请使用按钮对象。解析后的按钮标题也会用作信任提示标题。
请把任何 approval 或 permission flags 直接放进你真正想运行的命令字符串里。默认动作目标是 newTabInCurrentPane,所以常见模式是在当前 pane 中打开新的终端标签,并在那里启动 Codex、Claude Code 或 OpenCode。
自定义操作和 Command Palette
actions 条目是 cmux 执行的可复用行为。当同一行为需要从 Command Palette、表面标签栏、快捷键或加号按钮菜单触发时,请使用操作。将 commands 保留给可复用的 shell 命令和工作区布局。如果某个操作不应出现在 Command Palette 中,请将 palette 设为 false。
操作类型
"command": 在终端中运行 shell 文本。使用 target 选择当前终端或当前面板中的新标签。"agent": 启动受支持的编码代理。目前支持 codex 和 claude,并可传入可选参数。"builtin": 为内置 cmux 操作创建别名,例如 cmux.newTerminal、cmux.newBrowser、cmux.splitRight 或 cmux.splitDown。 复制类内置操作:cmux.copyWorkingDirectory 复制终端的工作目录,cmux.copyProjectRoot 复制其 Git 仓库的根目录(不在仓库中时复制工作目录),cmux.copyScreen 复制当前可见的屏幕内容。 在 SSH、云端或远程 tmux 工作区中,cmux 无法查找另一台主机上的 Git 仓库,因此 cmux.copyProjectRoot 会复制远程工作目录。"workspaceCommand": 运行 commands 中的命名工作区定义。用于多面板布局、自定义工作目录和启动命令。"workspace": Create a workspace from an inline workspace definition, with optional restart behavior. Offered in the new-workspace plus-button menu automatically."setting": 修改全局 cmux.json 中的一项设置,例如切换 true/false 设置,或在一组值之间循环。请参阅下方的设置操作。"settingPreset": 一次性应用 settingPresets 中的一组命名设置。
操作字段
title: Command Palette 行标题、表面按钮标签、菜单项标题以及信任提示标题,除非条目自行覆盖。subtitle/description: Command Palette 中的副标题文本。description 可作为 subtitle 的别名。keywords: Command Palette 的额外搜索词。palette: 自定义操作默认为 true。设为 false 可将操作从 Command Palette 隐藏,同时仍可从其他位置调用。shortcut: 可选的操作快捷键,使用与设置快捷键相同的单键或两段式按键序列语法。target: 仅用于 command 和 agent 操作。使用 currentTerminal 或 newTabInCurrentPane。confirm: 运行操作前请求确认。newWorkspaceMenu: Set true to offer any action in the new-workspace plus-button menu, or false to hide a workspace action from it.
Command Palette 行为
Command Palette 读取解析后的操作注册表。当 palette 不是 false 时,自定义操作 ID 会作为行加入。旧的 commands 会自动作为自定义行加入,除非已有相同生成 ID 的操作。内置命令保留正常的面板标签,但覆盖 cmux.newTerminal 等内置 ID 会改变该共享入口背后的行为。
设置操作
设置操作需指定设置的 path,并且只能指定以下其中一项:set(写入一个值)、toggle: true(切换 true/false 设置)、cycle(切换到列表中的下一个值,到末尾后回到开头)或 unset: true(从 cmux.json 中删除该键,改用设置窗口中的值或默认值)。toggle 和 cycle 从 cmux 当前使用的值开始,包括你在设置窗口中设定的值。settingPreset 操作会应用 settingPresets 中的一个条目,即一份部分 cmux.json,其中的嵌套对象按键逐一合并。cmux 在保存前会按 schema 检查每项更改,保留你的注释和其他键,并立即应用更改。设置操作和预设只能从全局 cmux.json 及其引用的包中运行,项目配置不能声明它们。
{
"actions": {
"scroll.cycle": {
"type": "setting",
"title": "Cycle Scroll Speed",
"path": "terminal.scrollSpeed",
"cycle": [1.0, 1.4, 1.8]
},
"editor.wrap": {
"type": "setting",
"title": "Toggle Editor Word Wrap",
"path": "fileEditor.wordWrap",
"toggle": true
},
"sidebar.quiet": {
"type": "settingPreset",
"title": "Quiet Sidebar",
"preset": "sidebar.quiet"
}
},
"settingPresets": {
"sidebar.quiet": {
"sidebar": { "showPorts": false, "showPullRequests": false, "showLog": false }
}
}
}同样的更改也可以在终端中完成:cmux config set terminal.scrollSpeed 1.4、cmux config toggle fileEditor.wordWrap 和 cmux config preset sidebar.quiet。
设置 confirm: true 后,cmux 会在保存前询问,并显示对应的 cmux config 命令。路径会在每个 "." 处拆分,因此本身包含 "." 的键(例如 ~/src/app.web 对应的 workspaceGroups.byCwd 条目)无法用这种方式修改。请直接在 cmux.json 中编辑。
加号按钮的自定义动作
使用 ui.newWorkspace.action 覆盖加号按钮的行为。使用 ui.newWorkspace.contextMenu(或别名 rightClick)定义有序的右键菜单。菜单项可以是动作 ID、动作对象,或 { "type": "separator" }。 未设置时,菜单会列出 cmux.newWorkspace (⌘N), cmux.newCloudWorkspace (⌘Y), cmux.newCloudMachine (⇧⌘Y), cmux.newTerminal (⌘T), cmux.newBrowser (⇧⌘L),并显示各自当前的快捷键。仅在启用 Cloud Machines 时显示云菜单项。 New Cloud Workspace 使用最近选中的 Cloud 工作区所属机器,然后使用右侧边栏中第一台机器;如果没有可用机器,则打开 New Cloud Machine。
{
"actions": {
"worktree-agents": {
"type": "workspaceCommand",
"title": "Agents in New Worktree",
"commandName": "Agents in New Worktree",
"icon": {
"type": "symbol",
"name": "arrow.triangle.branch"
}
}
},
"ui": {
"newWorkspace": {
"action": "worktree-agents",
"contextMenu": [
"worktree-agents",
{
"type": "separator"
},
{
"action": "cmux.newWorkspace",
"title": "Blank Workspace"
},
{
"action": "cmux.newBrowser",
"title": "Browser"
}
]
}
},
"commands": [
{
"name": "Agents in New Worktree",
"description": "Branch off a new Git worktree, then run Codex and Claude Code in it side by side",
"workspace": {
"name": "Worktree",
"layout": {
"direction": "vertical",
"split": 0.7,
"children": [
{
"direction": "horizontal",
"children": [
{
"pane": {
"surfaces": [
{
"type": "terminal",
"name": "Codex",
"command": "marker=\"${TMPDIR:-/tmp}/cmux-wt-${CMUX_WORKSPACE_ID:-local}\"; echo \"Waiting for the worktree...\"; until [ -s \"$marker\" ]; do sleep 0.25; done; cd \"$(cat \"$marker\")\" && exec codex --yolo"
}
]
}
},
{
"pane": {
"surfaces": [
{
"type": "terminal",
"name": "Claude",
"command": "marker=\"${TMPDIR:-/tmp}/cmux-wt-${CMUX_WORKSPACE_ID:-local}\"; echo \"Waiting for the worktree...\"; until [ -s \"$marker\" ]; do sleep 0.25; done; cd \"$(cat \"$marker\")\" && exec claude --dangerously-skip-permissions"
}
]
}
}
]
},
{
"pane": {
"surfaces": [
{
"type": "terminal",
"name": "Setup",
"command": "marker=\"${TMPDIR:-/tmp}/cmux-wt-${CMUX_WORKSPACE_ID:-local}\"; rm -f \"$marker\"; root=$(git rev-parse --show-toplevel) || exit 1; branch=\"wt-$(date +%m%d-%H%M%S)\"; wt=\"$root/../$(basename \"$root\")-$branch\"; git -C \"$root\" worktree add -b \"$branch\" \"$wt\" && printf \"%s\\n\" \"$wt\" > \"$marker\" && cd \"$wt\" && exec \"${SHELL:-/bin/zsh}\" -l",
"focus": true
}
]
}
}
]
}
}
}
]
}此示例让普通加号点击运行 worktree-agents 操作。来自 commands 的工作区命令会先使用可见的设置终端创建 Git worktree。Codex 和 Claude 同时启动,等待该工作区专用的状态文件,然后在 exec 前进入创建的目录。
Workspace layouts
A workspace layout is stored as a workspace action, embedding a full workspace definition directly in the action so it needs no separate commands entry. It supports the same workspace fields plus setup, a bootstrap command sent to the first terminal before that terminal's own command:
{
"actions": {
"pair-review": {
"type": "workspace",
"title": "Pair Review",
"icon": {
"type": "symbol",
"name": "person.2.wave.2"
},
"restart": "ignore",
"newWorkspaceMenu": true,
"workspace": {
"name": "Review",
"cwd": "~/src/app",
"setup": "git pull --ff-only",
"layout": {
"direction": "horizontal",
"split": 0.55,
"children": [
{
"pane": {
"surfaces": [
{
"type": "terminal",
"name": "Diff",
"command": "git log -p -n 5",
"focus": true
}
]
}
},
{
"pane": {
"surfaces": [
{
"type": "terminal",
"name": "Codex",
"command": "codex"
},
{
"type": "terminal",
"name": "Shell"
}
]
}
}
]
}
}
}
}
}Workspace layouts appear in the plus-button right-click menu automatically. Set newWorkspaceMenu to false to hide one, or to true on any other action to add it to the menu.
You can also create these without writing JSON: right-click the plus button and choose Save Workspace as Layout to capture the current workspace's splits, directories, running agents, and browser tabs into ~/.config/cmux/cmux.json. They are stored as entries in the actions block. Customize Workspace Layouts opens that file in your editor.
Default for new workspaces
Use the Default for New Workspace submenu to pick which saved workspace layout plain New Workspace should run, or choose None to return to a blank terminal. The save dialog also has a Use as default for new workspaces checkbox. The setting is ui.newWorkspace.action; a project-local .cmux/cmux.json value wins over ~/.config/cmux/cmux.json.
完整示例
{
"actions": {
"cmux.newTerminal": {
"type": "command",
"title": "Codex",
"command": "codex --yolo",
"shortcut": "cmd+t",
"icon": {
"type": "image",
"path": "./icons/codex.svg"
}
},
"review": {
"type": "agent",
"agent": "claude",
"args": "--permission-mode plan",
"title": "Plan with Claude",
"shortcut": "cmd+shift+p",
"icon": {
"type": "symbol",
"name": "list.bullet.clipboard"
}
},
"storybook": {
"type": "workspaceCommand",
"commandName": "Storybook",
"newWorkspaceMenu": true
},
"typecheck": {
"type": "command",
"title": "Typecheck",
"command": "npx tsc --noEmit",
"target": "newTabInCurrentPane",
"confirm": false,
"icon": {
"type": "emoji",
"value": "🔎"
}
}
},
"ui": {
"surfaceTabBar": {
"buttons": [
"cmux.newTerminal",
"review",
{
"action": "typecheck",
"title": "tsc"
},
"cmux.newBrowser",
"cmux.splitDown"
]
},
"newWorkspace": {
"contextMenu": [
"storybook",
{
"type": "separator"
},
"cmux.newTerminal",
"cmux.newBrowser"
]
}
},
"commands": [
{
"name": "Storybook",
"description": "Component explorer with a live preview beside it",
"keywords": [
"ui",
"components",
"stories"
],
"workspace": {
"name": "Storybook",
"cwd": "./packages/ui",
"color": "#a855f7",
"layout": {
"direction": "horizontal",
"split": 0.4,
"children": [
{
"direction": "vertical",
"split": 0.5,
"children": [
{
"pane": {
"surfaces": [
{
"type": "terminal",
"name": "Storybook",
"command": "npx storybook dev -p 6006 --no-open",
"focus": true
}
]
}
},
{
"pane": {
"surfaces": [
{
"type": "terminal",
"name": "Tests",
"command": "npx vitest --watch",
"env": {
"CI": "false"
}
}
]
}
}
]
},
{
"pane": {
"surfaces": [
{
"type": "browser",
"name": "Preview",
"url": "http://localhost:6006"
}
]
}
}
]
}
}
},
{
"name": "Install Dependencies",
"keywords": [
"install",
"deps"
],
"command": "npm ci",
"confirm": true
},
{
"name": "Open Changelog",
"keywords": [
"changes",
"release"
],
"command": "${EDITOR:-vi} CHANGELOG.md"
}
]
}