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 的进程模型非常清晰:
- 主进程:Swift AppKit 应用,负责所有 UI 渲染和用户交互
- Ghostty 渲染线程:每个终端面板一个,负责 VT 解析和 GPU 渲染
- Shell 进程:每个终端面板一个 PTY(伪终端),运行用户的 shell
- CLI Socket Server:Unix Domain Socket,监听
cmuxCLI 命令 - SSH 代理进程:当使用
cmux ssh时,在远程机器上运行一个轻量代理
这个模型的优势是故障隔离——一个终端面板的 shell 崩溃不会影响其他面板,Ghostty 渲染线程的异常不会拖垮主 UI 线程。
二、核心功能深度拆解
2.1 通知系统:AI Agent 工作流的关键基础设施
这是 cmux 最核心的创新。在传统终端中,你只能靠 macOS 的通知中心来知道 AI Agent 需要你——但通知中心提供的信息粒度太粗,而且多个 Agent 的通知会混在一起。
cmux 的通知系统设计了一个完整的状态机:
通知来源:
- OSC 序列:终端输出的 OSC 9/99/777 转义序列被捕获
- Shell 集成钩子:Claude Code 的 hook 系统可以调用
cmux notifyCLI - 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 会自动:
- 创建新的工作区
- 根据项目结构分割面板
- 在每个面板中启动 Claude Code
- 配置通知钩子
- 在侧边栏显示每个 Agent 的状态
2.6 会话恢复
cmux 在退出时会保存完整的会话状态:
- 窗口、工作区、面板布局
- 工作目录
- 终端回滚缓冲区(尽力恢复)
- 浏览器 URL 和导航历史
- 已保存的 Claude Code / Codex 会话(如果有 resume token)
重新打开 cmux 时,一切恢复原样。⌘ ⇧ O 可以手动恢复上一次保存的快照。
注意:cmux 不恢复任意终端进程的运行状态——已关闭的 tmux、vim 等需要重新打开。
三、可编程 API:用 JSON 配置你的整个开发环境
cmux 的可编程性是它区别于其他终端的核心优势。通过 cmux.json 配置文件,你可以定义自定义命令、工作区布局、甚至覆盖默认的 UI 行为。
3.1 配置文件位置与优先级
cmux 按以下顺序查找配置:
- 项目级:
./.cmux/cmux.json(优先级最高) - 备选位置:
./cmux.json(向后兼容) - 全局:
~/.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,覆盖它意味着把"新建终端"的默认行为改成启动 Codextarget控制命令在哪里执行: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\""
}
]
}
}
]
}
]
}
}
}
]
}
这个配置的执行流程:
- 点击"+"按钮,触发"Worktree Agents"工作区命令
- 左侧面板创建新的 Git Worktree(带时间戳的分支名),将目录路径写入临时文件
- 右上 Codex 面板和右下 Claude 面板同时启动,各自等待临时文件出现
- 临时文件就绪后,两个 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 |
| JavaScript | eval, 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
4.4 Cookie 和存储操作
# 读取特定 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 的渲染管线经过精心优化:
- 字形图集缓存:Ghostty 将所有使用到的字形预渲染到 GPU 纹理图集中,渲染时只需要上传位置坐标和字形索引
- 脏区域检测:只有变化的单元格才会被重新渲染,避免全屏重绘
- Portal 同步节流:侧边栏和终端面板之间的状态同步经过几何合并(coalesce),避免频繁的布局计算
- 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 实现的现代终端,但它走的是完全不同的路线:
| 维度 | cmux | Warp |
|---|---|---|
| 架构 | Swift/AppKit + libghostty | Rust + 自研渲染 |
| AI 集成 | 开放式,支持任意 Agent | 内置 Warp AI |
| 通知系统 | 原生,面板级路由 | 无面板级通知 |
| 内置浏览器 | 有 | 无 |
| 可编程性 | CLI + Socket + JSON 配置 | 有限 |
| 开源 | GPL-3.0 | 部分开源 |
| 平台 | macOS | macOS/Linux/Windows |
Warp 更像一个"AI 原生终端"——它内置了 AI 能力,但这也意味着你被锁定在 Warp 的 AI 生态中。cmux 走的是"原语"路线——它不替你决定怎么用 AI,而是提供终端、浏览器、通知、布局等可组合的原语,让你自己搭建工作流。
7.2 cmux vs tmux
tmux 是终端复用器的经典选择,但它是为 SSH 会话持久化设计的,不是为 AI Agent 设计的:
| 维度 | cmux | tmux |
|---|---|---|
| 通知 | 面板级蓝色环 + 侧边栏 | 无 |
| 浏览器 | 内置 | 无 |
| 配置 | 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 日常使用流程
- 打开 cmux,右键点击"+"→ "Fullstack Dev"
- 自动创建四面板布局:前端终端 + 浏览器预览 + 后端终端 + Shell
- 前端和后端服务自动启动,浏览器自动打开 localhost:3000
- 在 Shell 面板启动 Claude Code(
⌘ ⇧ C) - Claude Code 需要你时,面板边框变蓝,侧边栏标签高亮
⌘ ⇧ U跳转到最近的未读通知- Claude Code 可以通过
cmux browserAPI 直接操作预览浏览器
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 当前局限
- 仅支持 macOS:目前只有 macOS 版本,Linux 和 Windows 用户无法使用
- 无插件系统:扩展只能通过 CLI 和配置文件,无法修改 UI 组件
- 终端进程恢复不完整:关闭后重新打开无法恢复运行中的 tmux/vim
- 浏览器面板功能有限:基于 WKWebView,某些网站兼容性不如 Chrome
- 内存占用:浏览器面板会增加内存消耗(WKWebView 本身开销)
- 学习曲线:JSON 配置对非程序员不太友好
10.2 改进建议
- 加入 Linux 支持:这是扩大用户群的关键一步
- Web UI 配置编辑器:可视化编辑 cmux.json,降低配置门槛
- 模板市场:社区共享工作区模板和命令配置
- 更完善的 Agent 集成:与更多 Agent(如 Aider、Cline)的深度集成
- 性能仪表板:显示每个面板的 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 不告诉你怎么工作。它给你工具,让你自己决定。