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

2026-05-05 10:06:19 +0800 CST views 6

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)、emojiimage(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
  • Surfaceterminalbrowser,可指定命令、工作目录、环境变量、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 不告诉你怎么工作。它给你工具,让你自己决定。

推荐文章

初学者的 Rust Web 开发指南
2024-11-18 10:51:35 +0800 CST
MySQL 优化利剑 EXPLAIN
2024-11-19 00:43:21 +0800 CST
如何配置获取微信支付参数
2024-11-19 08:10:41 +0800 CST
pip安装到指定目录上
2024-11-17 16:17:25 +0800 CST
MyLib5,一个Python中非常有用的库
2024-11-18 12:50:13 +0800 CST
Golang Select 的使用及基本实现
2024-11-18 13:48:21 +0800 CST
Vue3中哪些API被废弃了?
2024-11-17 04:17:22 +0800 CST
Vue3中如何处理权限控制?
2024-11-18 05:36:30 +0800 CST
纯CSS实现3D云动画效果
2024-11-18 18:48:05 +0800 CST
Elasticsearch 监控和警报
2024-11-19 10:02:29 +0800 CST
程序员茄子在线接单