# 自訂命令與動作

cmux.json 是讓 cmux 學會你自己工作流程的地方：經常執行的 shell 命令、開啟時就依預設排好終端機和瀏覽器的工作區，以及可以綁定到快速鍵、分頁列按鈕和 Command Palette 的動作。可以放在專案內的 .cmux/cmux.json、適用於所有專案的 ~/.config/cmux/cmux.json，或兩處都放。

## cmux 從哪裡讀取 cmux.json

項目可以來自專案檔案、使用者層級檔案，或兩者同時提供。對於專案檔案，cmux 會從目前目錄開始尋找，並逐一檢查其上方的每一層上層目錄。

-   **專案層級：** `./.cmux/cmux.json` (從工作目錄往上尋找而得；優先於使用者層級檔案)
-   **本機回退:** `./cmux.json` (仍支援現有 repo)
-   **使用者層級：** `~/.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 計算，而不是依 repo 計算。專案本機圖片圖示會保持鎖定，直到該動作被信任。

## 自訂動作與 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-TW/docs/custom-commands

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