编程 cmux 深度实战:基于 Ghostty 的原生 macOS 终端如何用通知系统+内置浏览器+可编程 API 重新定义 AI 编码工作流

2026-05-05 10:06:19

cmux 深度实战:基于 Ghostty 的原生 macOS 终端如何用通知系统+内置浏览器+可编程 API 重新定义 AI 编码工作流

引言:当终端遇上 AI Agent,工具链的缺口在哪里

2025 年是 AI Coding Agent 爆发的一年。Claude Code、Codex、OpenCode、Cursor Agent……每个工具都在试图让 AI 替你写代码。但一个尴尬的现实是:工具本身准备好了,终端没有。

你同时开着 5 个 Claude Code 会话,它们各自在不同的目录下跑,时不时需要你审批权限、回答问题。macOS 原生通知弹出来的永远是同一句话:"Claude is waiting for your input"——你根本不知道是哪个会话、在哪个目录、需要你做什么。等你一个个切过去找,已经浪费了好几分钟。

更麻烦的是,你想让 AI 帮你看一下你本地跑的 dev server 页面效果,它做不到——终端里只有命令行,没有浏览器。你需要在 AI 和浏览器之间反复切换,复制粘贴 URL,手动截图再喂给 AI。

这就是 cmux 要解决的问题。

cmux 是一个由 Manaflow 团队开发的原生 macOS 终端,基于 Ghostty 渲染引擎,用 Swift/AppKit 构建。它不是一个 Electron 包装器,也不是一个 tmux 的 GUI 前端——它是一个从头设计的、为 AI 编码工作流优化的终端应用。

2026 年 4 月,cmux 在 GitHub 上已积累超过 2374 个 commit,最新版本 v0.63.2,活跃的开发节奏和社区参与度表明这不是一个玩具项目。本文将从架构设计、核心功能、可编程 API 到实际工作流,对 cmux 进行一次全面深度拆解。


一、架构分析:为什么是 Swift + AppKit + libghostty

1.1 技术栈选型的深层逻辑

cmux 选择 Swift + AppKit 而非 Electron 或 Tauri,这个决定背后的思考值得深挖:

性能维度:终端是 I/O 密集型应用。每秒可能需要渲染数万行输出(比如编译日志、测试输出),Electron 的 V8 引擎在这里是负担——JavaScript 的垃圾回收机制会导致帧率抖动,Chromium 的渲染管线对于纯文本场景太重了。AppKit 直接调用 Core Animation 和 Metal,从系统层面获得硬件加速,渲染延迟可以控制在亚毫秒级。

内存占用:一个典型的 Electron 终端(如 Hyper)空闲状态下占用 300-500MB 内存。cmux 基于 Ghostty 的 Zig 实现渲染核心,Swift 层只负责窗口管理和 UI 逻辑,空闲内存通常在 30-80MB 之间。当你同时跑 10 个 Claude Code 会话时,这个差距会被放大到 GB 级别。

系统集成:macOS 的很多能力需要直接调用系统 API——通知中心、Dock 菜单、Sparkle 自动更新、文件拖放、IME 输入法集成、VoiceOver 无障碍。Electron 需要通过 Node.js bridge 间接调用,每一层都有延迟和兼容性问题。Swift 直接调用,零中间层。

GPU 加速:cmux 使用 Ghostty 的 libghostty 渲染引擎,这是一个用 Zig 编写的终端渲染库,直接调用 GPU 着色器来绘制文本网格。每一帧只需要上传字形图集和位置数据,不需要逐字符绘制。这意味着即使终端在疯狂滚动输出,UI 线程也不会卡顿。

1.2 libghostty 的集成方式

cmux 不是简单地把 Ghostty 当作外部进程嵌入——它直接链接了 Ghostty 的核心库 libghostty:

cmux App (Swift/AppKit)
  ├── GhosttyKit (Zig → C ABI → Swift bridge)
  │     ├── 终端解析 (VT100/XTerm 转义序列)
  │     ├── GPU 渲染 (Metal shaders)
  │     ├── 字形栅格化
  │     └── Shell 集成 (prompt 检测, cwd 跟踪)
  ├── AppKit UI 层
  │     ├── 窗口管理
  │     ├── 侧边栏 (垂直标签)
  │     ├── 通知面板
  │     └── 浏览器面板 (WKWebView)
  └── CLI + Socket API
        ├── cmux 命令行工具
        └── Unix Domain Socket 控制接口

这种集成方式意味着 cmux 可以完全读取 Ghostty 的配置文件(~/.config/ghostty/config),包括主题、字体、颜色方案——你之前在 Ghostty 里的所有配置都会自动继承,零迁移成本。

同时,cmux 会在 Ghostty 的核心渲染层之上注入自己的钩子:

  • OSC 序列拦截:捕获 OSC 9/99/777 通知序列,触发自己的通知系统
  • Shell 集成扩展:在 Ghostty 原有的 prompt 检测基础上,增加了 cwd 跟踪和 git 分支状态读取
  • 终端状态同步:实时将终端的焦点状态、选择状态同步到 AppKit 层,用于侧边栏高亮和通知路由

1.3 进程模型

cmux 的进程模型非常清晰:

  1. 主进程:Swift AppKit 应用,负责所有 UI 渲染和用户交互
  2. Ghostty 渲染线程:每个终端面板一个,负责 VT 解析和 GPU 渲染
  3. Shell 进程:每个终端面板一个 PTY(伪终端),运行用户的 shell
  4. CLI Socket Server:Unix Domain Socket,监听 cmux CLI 命令
  5. SSH 代理进程:当使用 cmux ssh 时,在远程机器上运行一个轻量代理

这个模型的优势是故障隔离——一个终端面板的 shell 崩溃不会影响其他面板,Ghostty 渲染线程的异常不会拖垮主 UI 线程。


二、核心功能深度拆解

2.1 通知系统:AI Agent 工作流的关键基础设施

这是 cmux 最核心的创新。在传统终端中,你只能靠 macOS 的通知中心来知道 AI Agent 需要你——但通知中心提供的信息粒度太粗,而且多个 Agent 的通知会混在一起。

cmux 的通知系统设计了一个完整的状态机:

通知来源:

  1. OSC 序列:终端输出的 OSC 9/99/777 转义序列被捕获
  2. Shell 集成钩子:Claude Code 的 hook 系统可以调用 cmux notify CLI
  3. CLI 直接调用:任何脚本都可以通过 cmux notify --title "Build" --body "Done" 发送通知

通知路由:

通知不是广播式的——它被精确路由到产生通知的终端面板:

Agent 在 Panel A 输出 OSC 9 序列
  → cmux 捕获 OSC 9
  → 识别来源面板 Panel A
  → Panel A 的边框变为蓝色环
  → 侧边栏对应标签高亮
  → 通知面板显示新通知
  → Cmd+Shift+U 跳转到最近未读

通知面板:

按 ⌘ I 打开通知面板,你会看到所有待处理通知的列表,每条通知包含:

  • 来源工作区和面板
  • 通知标题和正文
  • 时间戳

点击任何一条通知,cmux 会自动切换到对应的工作区、面板,并将焦点定位到该终端。这意味着你有 10 个 Claude Code 在跑,哪个需要你,一目了然。

cmux notify 命令:

这是可编程的通知接口。你可以把它集成到任何 Agent 的 hook 中:

# Claude Code hooks 配置示例
# ~/.claude/settings.json
{
  "hooks": {
    "post_prompt": [{
      "command": "cmux notify --title 'Claude Code' --body 'Waiting for input on $PWD'"
    }]
  }
}
# 在脚本中使用
cmux notify --title "Deploy" --body "Production deploy complete ✅"

# 在 CI/CD pipeline 中
./run_tests.sh && cmux notify --title "Tests" --body "All 847 tests passed" \
  || cmux notify --title "Tests" --body "3 tests failed ❌"

通知持久化:

通知不会消失——它们会一直留在侧边栏标签上,直到你显式清除。最新的一条通知文本会显示在侧边栏的标签描述中,让你无需切换就能看到每个 Agent 的最新状态。

2.2 垂直标签与侧边栏:信息密度最大化

传统终端的水平标签栏在标签数量超过 5-6 个时就基本不可用了——标题被截断,你只能看到前几个字符。cmux 用垂直侧边栏解决了这个问题:

侧边栏显示的元数据:

每个工作区标签在侧边栏中显示:

  • 工作区标题:可自定义,或自动设置为 cwd 的目录名
  • Git 分支:当前工作区的 git 分支名
  • 关联的 PR 状态和编号:如果分支有关联的 GitHub PR,显示 PR 号和状态(open/merged/closed)
  • 工作目录:完整的 cwd 路径
  • 监听端口:该工作区中正在监听的端口号(如 localhost:3000)
  • 最新通知文本:该工作区收到的最后一条通知

这些信息是实时更新的——当你 checkout 到另一个 git 分支,侧边栏会立即更新;当你启动一个 dev server,监听端口会自动出现。

工作区描述:

你可以用 ⌥ ⌘ E 给任何工作区添加描述文本。这对于标记"这个工作区是用来做 X 的"非常有用——当你开了 10 个工作区时,描述能帮你快速找到目标。

工作区颜色:

每个工作区可以设置自定义颜色,用于在侧边栏中视觉区分。你可以通过 CLI 或配置文件设置:

cmux set-color --color "#3b82f6"  # 当前工作区设置为蓝色

固定与排序:

工作区可以被"固定"到侧边栏顶部,不受普通排序影响。你可以把最常用的 2-3 个工作区固定在顶部,其他的按使用频率排列。

最小化模式:

侧边栏可以折叠为只有图标的最小化模式(⌘ B 切换),在需要最大终端面积时非常有用。

2.3 内置浏览器:让 AI Agent 能"看到"你的应用

这是 cmux 最独特的设计之一。终端里内嵌了一个浏览器,而且这个浏览器是可编程的。

基本用法:

  • ⌘ ⇧ L:在当前面板旁边分割一个浏览器面板
  • ⌘ L:聚焦浏览器地址栏
  • 浏览器面板支持标签、前进后退、刷新、开发者工具

浏览器数据导入:

首次使用时,cmux 可以从 Chrome、Firefox、Arc、Safari 等 20 多个浏览器导入 cookies、历史记录和会话数据。这意味着你在浏览器里登录的 GitHub、Vercel、AWS Console,在 cmux 的浏览器里也是登录状态——不需要重新登录。

与 AI Agent 的协作:

这是关键——cmux 的浏览器 API 是从 Vercel 的 agent-browser 项目移植过来的,它让 AI Agent 可以直接操作浏览器:

# 让 AI 打开你的 dev server
cmux browser open http://localhost:3000

# 截取页面快照(AI 可读的 accessibility tree)
cmux browser snapshot --interactive --compact

# 截图保存为文件
cmux browser screenshot --out /tmp/page.png

# 点击页面元素
cmux browser click "button[type='submit']"

# 填写表单
cmux browser fill "#email" --text "test@example.com"

# 执行 JavaScript
cmux browser eval "document.title"

这意味着 Claude Code 可以在你的 dev server 上直接操作浏览器——查看页面效果、点击按钮、填写表单、读取 DOM 状态。不再需要你手动截图喂给 AI 了。

SSH 场景下的浏览器:

当使用 cmux ssh 连接远程服务器时,浏览器面板的网络请求会通过 SSH 隧道路由——所以 localhost 直接指向远程机器的 localhost。这在调试远程服务时非常有用,不需要手动设置端口转发。

2.4 SSH 远程工作区

cmux ssh user@remote 会创建一个完整的远程工作区,不仅仅是打开一个 SSH shell:

  • 自动在远程机器上启动 cmux 代理
  • 侧边栏显示远程工作区的 git 分支、工作目录等信息
  • 浏览器面板自动路由到远程网络
  • 拖拽文件到远程面板自动通过 scp 上传
  • 远程工作区的监听端口自动检测和显示
# 连接远程服务器
cmux ssh deploy@prod-server

# 在远程工作区中,localhost 指向远程机器
cmux browser open http://localhost:8080  # 打开远程的 8080 端口

# 上传文件:直接拖拽图片到终端面板

2.5 Claude Code Teams:一键启动多 Agent 协作

cmux claude-teams 是 cmux 对 Claude Code 的 teammate 模式的封装——一条命令启动多个 Claude Code 实例,它们各自在独立的面板中运行,侧边栏显示元数据和通知,不需要 tmux。

# 一键启动 Claude Code Teams
cmux claude-teams

cmux 会自动:

  1. 创建新的工作区
  2. 根据项目结构分割面板
  3. 在每个面板中启动 Claude Code
  4. 配置通知钩子
  5. 在侧边栏显示每个 Agent 的状态

2.6 会话恢复

cmux 在退出时会保存完整的会话状态:

  • 窗口、工作区、面板布局
  • 工作目录
  • 终端回滚缓冲区(尽力恢复)
  • 浏览器 URL 和导航历史
  • 已保存的 Claude Code / Codex 会话(如果有 resume token)

重新打开 cmux 时,一切恢复原样。⌘ ⇧ O 可以手动恢复上一次保存的快照。

注意:cmux 不恢复任意终端进程的运行状态——已关闭的 tmux、vim 等需要重新打开。


三、可编程 API:用 JSON 配置你的整个开发环境

cmux 的可编程性是它区别于其他终端的核心优势。通过 cmux.json 配置文件,你可以定义自定义命令、工作区布局、甚至覆盖默认的 UI 行为。

3.1 配置文件位置与优先级

cmux 按以下顺序查找配置:

  1. 项目级:./.cmux/cmux.json(优先级最高)
  2. 备选位置:./cmux.json(向后兼容)
  3. 全局:~/.config/cmux/cmux.json(兜底)

项目级配置会覆盖全局配置中的同 ID 条目。编辑后用 ⌘ ⇧ , 或 cmux reload-config 热重载。

3.2 Actions 注册表:统一的行为抽象

cmux v0.63 引入了 Actions 注册表——一个将 UI 按钮、命令面板、快捷键统一到一个 ID 空间的机制:

{
  "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 }
    }
  }
}

这里有几个关键点:

  • cmux.newTerminal 是内置 ID,覆盖它意味着把"新建终端"的默认行为改成启动 Codex
  • target 控制命令在哪里执行:currentTerminal 在当前终端执行,newTabInCurrentPane 在新标签中执行
  • palette 默认为 true,设为 false 可以让 action 只在标签栏按钮中使用,不出现在命令面板中
  • shortcut 支持单键和双键组合(如 ["cmd+k", "cmd+c"])
  • icon 支持三种类型:symbol(SF Symbols)、emoji、image(SVG/PNG 等)

3.3 工作区命令:定义多面板布局

工作区命令允许你用一个 JSON 树定义复杂的分屏布局:

{
  "commands": [
    {
      "name": "Web Dev",
      "keywords": ["dev", "fullstack"],
      "workspace": {
        "name": "Dev",
        "cwd": ".",
        "color": "#3b82f6",
        "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" }
                  }
                ]
              }
            }
          ]
        }
      }
    }
  ]
}

这个配置定义了一个"Web Dev"工作区:

  • 左边 50% 是前端面板,自动运行 npm run dev
  • 右边 50% 是后端面板,自动运行 cargo watch
  • 后端面板设置了 RUST_LOG=debug 环境变量

布局树的数据结构:

布局树是递归的:

  • Split 节点:direction(水平/垂直)+ split(0.1-0.9 的分割位置)+ 两个子节点
  • Pane 节点:包含一个或多个 Surface
  • Surface:terminal 或 browser,可指定命令、工作目录、环境变量、URL

工作区启动行为:

当工作区已存在时:

  • "new":总是创建新的(默认)
  • "ignore":切换到已有的
  • "recreate":关闭并重建
  • "confirm":询问用户

3.4 高级示例:Git Worktree + 多 Agent 并行

这是一个实战配置——一键创建 Git Worktree 并启动 Codex 和 Claude 两个 Agent:

{
  "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\""
                      }
                    ]
                  }
                }
              ]
            }
          ]
        }
      }
    }
  ]
}

这个配置的执行流程:

  1. 点击"+"按钮,触发"Worktree Agents"工作区命令
  2. 左侧面板创建新的 Git Worktree(带时间戳的分支名),将目录路径写入临时文件
  3. 右上 Codex 面板和右下 Claude 面板同时启动,各自等待临时文件出现
  4. 临时文件就绪后,两个 Agent 自动 cd 到新 Worktree 目录并启动

这个设计巧妙地用文件系统作为进程间同步机制——简单、可靠、不需要额外的进程管理工具。

3.5 自定义标签栏按钮

你可以完全控制标签栏上显示哪些按钮:

{
  "ui": {
    "surfaceTabBar": {
      "buttons": [
        "cmux.newTerminal",
        "cmux.newBrowser",
        "cmux.splitRight",
        "cmux.splitDown",
        "claude"
      ]
    }
  }
}

不列出的内置按钮会被隐藏。你可以把最常用的 Agent 启动按钮放在标签栏上,一键触达。

3.6 简单命令

对于不需要分屏的简单命令:

{
  "commands": [
    {
      "name": "Run Tests",
      "keywords": ["test", "check"],
      "command": "npm test",
      "confirm": true
    },
    {
      "name": "Deploy",
      "keywords": ["deploy", "ship"],
      "command": "cd \"$(git rev-parse --show-toplevel)\" && ./scripts/deploy.sh",
      "confirm": true
    }
  ]
}

confirm: true 会在执行前弹出确认对话框——适合破坏性命令。


四、浏览器自动化 API:从脚本到 Agent 的桥梁

cmux 的浏览器自动化 API 是从 Vercel 的 agent-browser 项目移植并扩展的,提供了一套完整的浏览器控制能力。这部分 API 是 cmux 最有想象力的功能——它把终端从一个被动的输出显示器变成了一个主动的应用控制台。

4.1 完整命令索引

类别子命令
导航identify, open, open-split, navigate, back, forward, reload, url
等待wait
DOM 交互click, dblclick, hover, focus, check, uncheck, type, fill, press, select, scroll
检查snapshot, screenshot, get, is, find, highlight
JavaScripteval, addinitscript, addscript, addstyle
状态cookies, storage, state
标签/日志tab, console, errors
高级frame, dialog, download

4.2 自动化测试实战

假设你有一个 Next.js 应用,你想用 cmux 的浏览器 API 写一个端到端测试脚本:

#!/bin/bash
# e2e-test.sh - 用 cmux 浏览器 API 做端到端测试

# 1. 打开浏览器到登录页
cmux browser open http://localhost:3000/login

# 2. 等待页面加载完成
cmux browser surface:2 wait --load-state complete --timeout-ms 15000

# 3. 填写登录表单
cmux browser surface:2 fill "#email" --text "test@example.com"
cmux browser surface:2 fill "#password" --text "testpassword123"
cmux browser surface:2 click "button[type='submit']" --snapshot-after

# 4. 等待登录成功
cmux browser surface:2 wait --text "Welcome" --timeout-ms 10000

# 5. 验证 dashboard 可见
cmux browser surface:2 is visible "#dashboard"
echo "✅ Login test passed"

# 6. 截图作为测试证据
cmux browser surface:2 screenshot --out /tmp/e2e-dashboard.png

# 7. 检查控制台错误
errors=$(cmux browser surface:2 errors list)
if [ -n "$errors" ]; then
  echo "❌ Console errors found: $errors"
  exit 1
fi

echo "✅ All E2E tests passed"
cmux notify --title "E2E Tests" --body "All tests passed ✅"

4.3 状态持久化

浏览器状态可以保存和恢复,这对于需要登录的测试场景非常有用:

# 首次登录后保存状态
cmux browser surface:2 state save /tmp/session.json

# 后续测试恢复状态,不需要重新登录
cmux browser surface:2 state load /tmp/session.json
cmux browser surface:2 reload
# 读取特定 cookie
cmux browser surface:2 cookies get --name session_id

# 设置 cookie
cmux browser surface:2 cookies set session_id abc123 --domain example.com --path /

# 操作 localStorage
cmux browser surface:2 storage local set theme dark
cmux browser surface:2 storage local get theme

4.5 JavaScript 注入

# 执行 JavaScript
cmux browser surface:2 eval "document.title"
cmux browser surface:2 eval "window.performance.timing.loadEventEnd - window.performance.timing.navigationStart"

# 注入初始化脚本(在每个页面加载前执行)
cmux browser surface:2 addinitscript "window.__cmuxReady = true;"

# 注入自定义样式
cmux browser surface:2 addstyle "#debug-banner { display: none !important; }"

五、CLI 与 Socket API:自动化一切

cmux 不仅仅是终端——它是一个可编程的开发环境控制器。

5.1 CLI 命令总览

# 工作区管理
cmux list-workspaces          # 列出所有工作区
cmux new-workspace            # 创建新工作区
cmux set-color --color "#f00" # 设置当前工作区颜色

# 通知
cmux notify --title "Build" --body "Done"  # 发送通知
cmux notify --title "Deploy" --body "Failed ❌" --sound default

# 终端控制
cmux split-right              # 右分屏
cmux split-down               # 下分屏
cmux send-keys "hello"        # 向终端发送按键

# 浏览器
cmux browser open https://example.com
cmux browser snapshot --interactive --compact

# SSH
cmux ssh user@remote          # 创建远程工作区

# Claude Code Teams
cmux claude-teams             # 启动多 Agent 协作

# 配置
cmux reload-config            # 热重载 cmux.json
cmux tree                     # 显示当前工作区布局树

# 会话
cmux restore-session          # 恢复上次保存的会话

5.2 Socket API

CLI 命令底层通过 Unix Domain Socket 与 cmux 主进程通信。这意味着你可以从任何脚本或程序控制 cmux——Python、Node.js、甚至另一个 cmux 实例。

5.3 环境变量

cmux 在终端面板中注入了以下环境变量:

  • CMUX_WORKSPACE_ID:当前工作区的唯一标识
  • CMUX:设置为 1,用于检测是否在 cmux 中运行
  • TERM_PROGRAM:设置为 cmux

你可以在脚本中利用这些变量:

if [ -n "$CMUX" ]; then
  # 在 cmux 中运行,可以使用 cmux CLI
  cmux notify --title "Script" --body "Completed in $CMUX_WORKSPACE_ID"
fi

六、性能优化:从渲染到内存的全链路调优

6.1 渲染性能

cmux 的渲染管线经过精心优化:

  1. 字形图集缓存:Ghostty 将所有使用到的字形预渲染到 GPU 纹理图集中,渲染时只需要上传位置坐标和字形索引
  2. 脏区域检测:只有变化的单元格才会被重新渲染,避免全屏重绘
  3. Portal 同步节流:侧边栏和终端面板之间的状态同步经过几何合并(coalesce),避免频繁的布局计算
  4. GPU 着色器:文本渲染直接在 GPU 着色器中完成,CPU 只负责计算网格坐标

6.2 内存优化

  • 终端回滚缓冲区:使用环形缓冲区,避免无限增长
  • 侧边栏数据:Git 分支、PR 状态等元数据的查询是事件驱动的,不轮询
  • 会话自动保存:只在状态变化时写入磁盘,且跳过未变化的快照
  • 浏览器面板:WKWebView 的内存由系统管理,cmux 在面板不可见时降低其优先级

6.3 主线程保护

cmux 非常注意不在主线程做阻塞操作:

  • 文件 IO 操作(如配置读取、会话保存)都在后台队列
  • Git 元数据查询使用事件驱动,不轮询
  • Socket 命令处理不阻塞 UI 线程
  • Sparkle 更新检查在后台进行

从 v0.63.2 的 changelog 可以看到大量的"Fix...hang"、"Fix...deadlock"修复——这说明团队对性能问题的态度是零容忍。


七、与竞品对比:cmux 的定位

7.1 cmux vs Warp

Warp 是另一个 Rust 实现的现代终端,但它走的是完全不同的路线:

维度cmuxWarp
架构Swift/AppKit + libghosttyRust + 自研渲染
AI 集成开放式,支持任意 Agent内置 Warp AI
通知系统原生,面板级路由无面板级通知
内置浏览器有无
可编程性CLI + Socket + JSON 配置有限
开源GPL-3.0部分开源
平台macOSmacOS/Linux/Windows

Warp 更像一个"AI 原生终端"——它内置了 AI 能力,但这也意味着你被锁定在 Warp 的 AI 生态中。cmux 走的是"原语"路线——它不替你决定怎么用 AI,而是提供终端、浏览器、通知、布局等可组合的原语,让你自己搭建工作流。

7.2 cmux vs tmux

tmux 是终端复用器的经典选择,但它是为 SSH 会话持久化设计的,不是为 AI Agent 设计的:

维度cmuxtmux
通知面板级蓝色环 + 侧边栏无
浏览器内置无
配置JSON,支持工作区布局声明式配置,有限布局
学习曲线GUI 为主纯键盘,高学习成本
GPU 渲染Metal终端渲染
会话恢复自动手动 attach

tmux 在 SSH 场景下仍然不可替代——cmux 目前不支持 Linux 服务器上的无头运行。但在本地 macOS 开发场景中,cmux 提供了更好的体验。

7.3 cmux 的哲学:原语而非方案

cmux 官方博客"The Zen of cmux"阐述了一个核心哲学:

cmux is a primitive, not a solution. It gives you a terminal, a browser, notifications, workspaces, splits, tabs, and a CLI to control all of it. cmux doesn't force you into an opinionated way to use coding agents. What you build with the primitives is yours.

翻译过来就是:cmux 给你原语,不给你方案。它相信最接近代码库的开发者自己会找到最高效的工作流,而不是由产品团队自上而下设计。

这个哲学在实践中意味着:

  • 没有内置的 AI 对话界面——你在终端里直接用 Claude Code / Codex
  • 没有预设的"最佳实践"工作流——你用 JSON 配置定义自己的
  • 没有闭源的 Agent 编排——你用 Shell 脚本 + CLI 组合

八、实战:从零搭建一个完整的 AI 编码工作流

让我们把前面学到的所有东西组合起来,搭建一个真实可用的 AI 编码工作流。

8.1 项目配置

假设你有一个全栈项目,前端 Next.js + 后端 Go,你想让 Claude Code 和 Codex 同时帮你开发:

// .cmux/cmux.json
{
  "actions": {
    "cmux.newTerminal": {
      "type": "command",
      "title": "Codex",
      "command": "codex --dangerously-bypass-approvals-and-sandbox",
      "target": "newTabInCurrentPane",
      "shortcut": "cmd+t",
      "icon": { "type": "emoji", "value": "🤖", "scale": 0.9 }
    },
    "claude": {
      "type": "command",
      "title": "Claude Code",
      "command": "claude --dangerously-skip-permissions",
      "target": "newTabInCurrentPane",
      "shortcut": "cmd+shift+c",
      "icon": { "type": "emoji", "value": "🧠", "scale": 0.9 }
    },
    "dev": {
      "type": "workspaceCommand",
      "commandName": "Fullstack Dev"
    },
    "review": {
      "type": "workspaceCommand",
      "commandName": "Code Review"
    }
  },
  "ui": {
    "surfaceTabBar": {
      "buttons": [
        "cmux.newTerminal",
        "cmux.newBrowser",
        "cmux.splitRight",
        "cmux.splitDown",
        "claude"
      ]
    },
    "newWorkspace": {
      "contextMenu": [
        { "action": "dev", "title": "Fullstack Dev" },
        { "action": "review", "title": "Code Review" },
        { "type": "separator" },
        { "action": "cmux.newTerminal", "title": "New Terminal" }
      ]
    }
  },
  "commands": [
    {
      "name": "Fullstack Dev",
      "keywords": ["dev", "fullstack"],
      "workspace": {
        "name": "Dev",
        "cwd": ".",
        "color": "#3b82f6",
        "layout": {
          "direction": "horizontal",
          "split": 0.5,
          "children": [
            {
              "direction": "vertical",
              "split": 0.6,
              "children": [
                {
                  "pane": {
                    "surfaces": [
                      {
                        "type": "terminal",
                        "name": "Frontend",
                        "command": "cd web && npm run dev",
                        "focus": true
                      }
                    ]
                  }
                },
                {
                  "pane": {
                    "surfaces": [
                      {
                        "type": "browser",
                        "name": "Preview",
                        "url": "http://localhost:3000"
                      }
                    ]
                  }
                }
              ]
            },
            {
              "direction": "vertical",
              "split": 0.5,
              "children": [
                {
                  "pane": {
                    "surfaces": [
                      {
                        "type": "terminal",
                        "name": "Backend",
                        "command": "cd api && go run ./cmd/server"
                      }
                    ]
                  }
                },
                {
                  "pane": {
                    "surfaces": [
                      {
                        "type": "terminal",
                        "name": "Shell"
                      }
                    ]
                  }
                }
              ]
            }
          ]
        }
      }
    },
    {
      "name": "Code Review",
      "keywords": ["review", "pr"],
      "workspace": {
        "name": "Review",
        "cwd": ".",
        "color": "#a855f7",
        "layout": {
          "direction": "horizontal",
          "split": 0.5,
          "children": [
            {
              "pane": {
                "surfaces": [
                  {
                    "type": "terminal",
                    "name": "Claude",
                    "command": "claude --dangerously-skip-permissions",
                    "focus": true
                  }
                ]
              }
            },
            {
              "pane": {
                "surfaces": [
                  {
                    "type": "terminal",
                    "name": "Codex",
                    "command": "codex --dangerously-bypass-approvals-and-sandbox"
                  }
                ]
              }
            }
          ]
        }
      }
    }
  ]
}

8.2 通知 Hook 集成

在 Claude Code 的配置中添加 cmux 通知钩子:

// ~/.claude/settings.json
{
  "hooks": {
    "post_prompt": [{
      "command": "cmux notify --title 'Claude Code' --body 'Waiting for input in ${PWD##*/}'"
    }],
    "post_completion": [{
      "command": "cmux notify --title 'Claude Code' --body 'Task completed in ${PWD##*/}'"
    }]
  }
}

8.3 日常使用流程

  1. 打开 cmux,右键点击"+"→ "Fullstack Dev"
  2. 自动创建四面板布局:前端终端 + 浏览器预览 + 后端终端 + Shell
  3. 前端和后端服务自动启动,浏览器自动打开 localhost:3000
  4. 在 Shell 面板启动 Claude Code(⌘ ⇧ C)
  5. Claude Code 需要你时,面板边框变蓝,侧边栏标签高亮
  6. ⌘ ⇧ U 跳转到最近的未读通知
  7. Claude Code 可以通过 cmux browser API 直接操作预览浏览器

8.4 性能监控脚本

创建一个简单的脚本,当 dev server 内存超过阈值时通知你:

#!/bin/bash
# watch-memory.sh
while true; do
  mem=$(ps aux | grep "next-server" | grep -v grep | awk '{print $6}')
  if [ -n "$mem" ]; then
    mb=$((mem / 1024))
    if [ "$mb" -gt 1024 ]; then
      cmux notify --title "⚠️ Memory" --body "Next.js using ${mb}MB"
    fi
  fi
  sleep 60
done

九、生态系统与路线图

9.1 当前生态

  • 插件/扩展:目前 cmux 没有插件系统,所有扩展通过 CLI + JSON 配置实现
  • 主题:直接复用 Ghostty 的主题生态(100+ 主题)
  • 社区:Discord 活跃,GitHub Issues 响应及时

9.2 创始人版路线图

cmux 的 Founder's Edition 提供了未来功能的预览:

  • cmux AI:为每个工作区、标签和面板提供上下文感知的 AI 助手
  • iOS 应用:终端在桌面和手机之间同步
  • Cloud VMs:一键启动云虚拟机
  • 语音模式:语音控制终端和 Agent

9.3 许可证

cmux 使用 GPL-3.0-or-later 许可证。如果组织无法满足 GPL 要求,可以联系 founders@manaflow.com 获取商业许可证。


十、局限性与改进建议

10.1 当前局限

  1. 仅支持 macOS:目前只有 macOS 版本,Linux 和 Windows 用户无法使用
  2. 无插件系统:扩展只能通过 CLI 和配置文件,无法修改 UI 组件
  3. 终端进程恢复不完整:关闭后重新打开无法恢复运行中的 tmux/vim
  4. 浏览器面板功能有限:基于 WKWebView,某些网站兼容性不如 Chrome
  5. 内存占用:浏览器面板会增加内存消耗(WKWebView 本身开销)
  6. 学习曲线:JSON 配置对非程序员不太友好

10.2 改进建议

  1. 加入 Linux 支持:这是扩大用户群的关键一步
  2. Web UI 配置编辑器:可视化编辑 cmux.json,降低配置门槛
  3. 模板市场:社区共享工作区模板和命令配置
  4. 更完善的 Agent 集成:与更多 Agent(如 Aider、Cline)的深度集成
  5. 性能仪表板:显示每个面板的 CPU/内存占用

总结

cmux 代表了终端工具的一种新思路:不做方案,做原语。在 AI Agent 时代,没有人已经想清楚最佳工作流是什么——与其造一个 opinionated 的工具,不如给开发者可组合的积木。

它的通知系统解决了多 Agent 并行的信息过载问题;内置浏览器让 Agent 和 Web 应用之间的鸿沟得以跨越;JSON 配置 + CLI + Socket API 提供了无限的自动化可能;Swift + AppKit + libghostty 的技术栈保证了原生级的性能和系统集成度。

如果你是一个同时在跑多个 AI 编码 Agent 的开发者,如果你厌倦了在 10 个终端窗口之间迷失,如果你想让 AI Agent 能真正"看到"你的应用——cmux 值得一试。

安装只需要一行命令:

brew tap manaflow-ai/cmux && brew install --cask cmux

你的 Ghostty 配置会自动继承,你的 Claude Code 可以立刻接入通知系统,你的 dev server 可以直接在浏览器面板中预览。

cmux 不告诉你怎么工作。它给你工具,让你自己决定。

推荐文章

程序员茄子在线接单