# 自定义命令和操作

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` (由所有项目共享；补充项目文件中未定义的内容)

本地操作和命令会覆盖具有相同 ID 或名称的全局条目。

编辑 cmux.json 后，按 Cmd+Shift+, 或运行 cmux reload-config 来应用更改。

如果项目配置或全局配置存在 schema 错误，cmux 会回退到下一个有效配置，并在 Command Palette 中显示 **cmux.json Schema Error** 行。选择它即可打开配置文件。

## shell 命令

最简单的条目只有一个名称和一个命令字符串。在 Command Palette 中选择它，就会把该字符串输入到当前聚焦的终端并运行。

cmux.json

```
{
  "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 对象来代替命令字符串，它就会改为打开一个新的工作区，排列成一棵由分屏窗格组成的树，其中容纳终端和浏览器。每个条目必须且只能包含两者之一。

cmux.json

```
{
  "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 workspace
-   `setup`: 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`: 拼接在工作区目录之后
-   `.` 或 未设置: 沿用工作区目录

## 文件结构

动作注册表是 nightly 功能。使用 `actions`、`shortcut` 或 `ui.surfaceTabBar.buttons` 前，请安装最新 nightly 构建。

`commands` 仍用于定义可复用的 shell 命令和 workspace 布局。Nightly 构建会加入 `actions` 注册表。动作是 surface 标签栏、Command Palette 和动作级快捷键共享的公开 ID。

cmux.json

```
{
  "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。

项目本地动作会立即显示在 surface 标签栏和 Command Palette 中。首次运行仍会请求信任。信任按精确的动作 fingerprint 计算，而不是按仓库计算。项目本地图片图标会保持锁定，直到该动作被信任。

## 自定义操作和 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 及其引用的包中运行，项目配置不能声明它们。

~/.config/cmux/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。

cmux.json

```
{
  "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:

cmux.json

```
{
  "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`.

## 完整示例

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"
    }
  ]
}
```

Canonical: https://cmux.com/zh-CN/docs/custom-commands

Documentation index: https://cmux.com/llms.txt
