自定义命令

通过在项目根目录或 ~/.config/cmux/ 中添加 cmux.json 文件来定义自定义命令和工作区布局。命令将显示在命令面板中。

文件位置

cmux 在两个地方查找配置:

  • 按项目: ./.cmux/cmux.json - 位于项目目录中,优先级更高
  • 本地回退: ./cmux.json - 仍支持现有仓库
  • 全局: ~/.config/cmux/cmux.json - 适用于所有项目,补充本地未定义的命令
本地操作和命令会覆盖具有相同 ID 或名称的全局条目。
动作注册表是 nightly 功能。使用 actionsshortcutui.surfaceTabBar.buttons 前,请安装最新 nightly 构建。
项目本地动作会立即显示在 surface 标签栏和 Command Palette 中。首次运行仍会请求信任。信任按精确的动作 fingerprint 计算,而不是按仓库计算。项目本地图片图标会保持锁定,直到该动作被信任。
如果项目配置或全局配置存在 schema 错误,cmux 会回退到下一个有效配置,并在 Command Palette 中显示 cmux.json Schema Error 行。选择它即可打开配置文件。

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

配置架构

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

cmux.json
{
  "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 动作注册表

actions 将稳定 ID 映射到可运行的行为。使用内置 ID cmux.newTerminalcmux.newBrowsercmux.splitRightcmux.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。

操作类型

  • "builtin": 为内置 cmux 操作创建别名,例如 cmux.newTerminal、cmux.newBrowser、cmux.splitRight 或 cmux.splitDown。
  • "command": 在终端中运行 shell 文本。使用 target 选择当前终端或当前面板中的新标签。
  • "agent": 启动受支持的编码代理。目前支持 codex 和 claude,并可传入可选参数。
  • "workspaceCommand": 运行 commands 中的命名工作区定义。用于多面板布局、自定义工作目录和启动命令。

操作字段

  • title: Command Palette 行标题、表面按钮标签、菜单项标题以及信任提示标题,除非条目自行覆盖。
  • subtitle / description: Command Palette 中的副标题文本。description 可作为 subtitle 的别名。
  • keywords: Command Palette 的额外搜索词。
  • palette: 自定义操作默认为 true。设为 false 可将操作从 Command Palette 隐藏,同时仍可从其他位置调用。
  • shortcut: 可选的操作快捷键,使用与设置快捷键相同的单键或两段式按键序列语法。
  • target: 仅用于 command 和 agent 操作。使用 currentTerminal 或 newTabInCurrentPane。
  • confirm: 运行操作前请求确认。

Command Palette 行为

Command Palette 读取解析后的操作注册表。当 palette 不是 false 时,自定义操作 ID 会作为行加入。旧的 commands 会自动作为自定义行加入,除非已有相同生成 ID 的操作。内置命令保留正常的面板标签,但覆盖 cmux.newTerminal 等内置 ID 会改变该共享入口背后的行为。

加号按钮的自定义动作

使用 ui.newWorkspace.action 覆盖加号按钮的行为。使用 ui.newWorkspace.contextMenu(或别名 rightClick)定义有序的右键菜单。菜单项可以是动作 ID、动作对象,或 { "type": "separator" }

cmux.json
{
  "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"
                      }
                    ]
                  }
                }
              ]
            }
          ]
        }
      }
    }
  ]
}

此示例让普通加号点击运行 worktree-agents 操作。来自 commands 的工作区命令会先使用可见的设置终端创建 Git worktree。Codex 和 Claude 同时启动,等待该工作区专用的状态文件,然后在 exec 前进入创建的目录。

简单命令

简单命令在当前聚焦的终端中运行 shell 命令:

cmux.json
{
  "commands": [
    {
      "name": "Run Tests",
      "keywords": ["test", "check"],
      "command": "npm test",
      "confirm": true
    }
  ]
}

字段

  • name: 在命令面板中显示(必填)
  • description: 可选描述
  • keywords: 命令面板的额外搜索词
  • command: 在聚焦终端中运行的 shell 命令
  • confirm: 运行前显示确认对话框

简单命令在当前聚焦终端的工作目录中运行。如果命令依赖于项目相对路径,请在前面加上 cd "$(git rev-parse --show-toplevel)" && 以从仓库根目录运行,或使用 cd /your/path && 指定任意目录。

工作区命令

工作区命令会创建一个新工作区,具有自定义的分屏、终端和浏览器面板布局:

cmux.json
{
  "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" }
                  }
                ]
              }
            }
          ]
        }
      }
    }
  ]
}

工作区字段

  • name: 工作区标签页名称(默认为命令名称)
  • cwd: 工作区的工作目录
  • color: 工作区标签页颜色
  • layout: 定义分屏和面板的布局树

重启行为

控制当同名工作区已存在时的行为:

  • "new": 创建新的 workspace(默认)
  • "ignore": 切换到现有 workspace
  • "recreate": 不询问,直接关闭并重新创建
  • "confirm": 重新创建前询问用户

布局树

布局树使用递归分割节点定义面板的排列方式:

分割节点

将空间分割为两个子节点:

  • direction: "horizontal" "vertical"
  • split: 分割线位置,从 0.1 到 0.9(默认 0.5)
  • children: 恰好两个子节点(分割或面板)

面板节点

包含一个或多个 surface(面板内标签页)的叶节点。

Surface 定义

面板中的每个 surface 可以是终端或浏览器:

  • type: "terminal" "browser"
  • name: 自定义标签页标题
  • command: 创建时自动运行的 shell 命令(仅限终端)
  • cwd: 此 surface 的工作目录
  • env: 以键值对形式表示的环境变量
  • url: 要打开的 URL(仅限浏览器)
  • focus: 创建后聚焦此 surface

工作目录解析

  • . 省略: 工作区工作目录
  • ./subdir: 相对于工作区工作目录
  • ~/path: 展开到主目录
  • 绝对路径: 按原样使用

完整示例

cmux.json
{
  "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
    }
  ]
}