#通知

cmux 支持桌面通知,让 AI agent 和脚本在需要关注时提醒你。

#生命周期

  1. 已接收:通知出现在面板中,触发桌面提醒(如果未被抑制)
  2. 未读:工作区标签页上显示未读标记
  3. 已读:查看该工作区时清除
  4. 已清除:从面板中移除

#抑制

以下情况桌面提醒会被抑制:

  • cmux 窗口处于聚焦状态
  • 发送通知的工作区正处于活跃状态
  • 通知面板已打开

#通知面板

⌘⇧I 打开通知面板。点击通知跳转到对应工作区。按 ⌘⇧U 直接跳转到最近有未读通知的工作区。

#自定义命令

每次调度通知时运行一个 shell 命令。在 Settings > App > Notification Command 中设置。命令通过 /bin/sh -c 运行,可使用以下环境变量:

变量描述
CMUX_NOTIFICATION_TITLE通知标题(工作区名称或应用名称)
CMUX_NOTIFICATION_SUBTITLE通知副标题
CMUX_NOTIFICATION_BODY通知正文内容
Examples
# Text-to-speech
say "$CMUX_NOTIFICATION_TITLE"

# Custom sound file
afplay /path/to/sound.aiff

# Log to file
echo "$CMUX_NOTIFICATION_TITLE: $CMUX_NOTIFICATION_BODY" >> ~/notifications.log

该命令独立于系统声音选择器运行。将选择器设为「None」可仅使用自定义命令,或同时保留两者以获得系统声音加自定义操作。

#通知钩子

cmux.json 可以定义通知钩子,让每条通知策略以 JSON 形式从 stdin 传入。每个钩子通过 stdout 返回 JSON。钩子默认关闭;只有当 notifications.hooks 包含至少一个已启用的钩子时,cmux 才会运行它们。cmux 会应用返回的通知文本和效果,因此钩子可以过滤横幅、保留或跳过侧边栏历史、播放声音,或停止后续钩子。

cmux.json
{
  "notifications": {
    "hooks": [
      {
        "id": "quiet-docs",
        "command": "sed 's/"desktop":true/"desktop":false/'",
        "timeoutSeconds": 20
      }
    ]
  }
}
钩子输入和输出
{
  "version": 1,
  "notification": {
    "workspaceId": "3B3F0D83-...",
    "surfaceId": "7E9C1A02-...",
    "title": "Codex",
    "subtitle": "Waiting",
    "body": "Agent needs input"
  },
  "context": {
    "cwd": "/path/to/project",
    "configPath": "/path/to/project/.cmux/cmux.json",
    "hookId": "quiet-docs",
    "appFocused": false,
    "focusedPanel": false
  },
  "effects": {
    "record": true,
    "markUnread": true,
    "reorderWorkspace": true,
    "desktop": true,
    "sound": true,
    "command": true,
    "paneFlash": true
  }
}

钩子会从全局 ~/.config/cmux/cmux.json 以及从父目录到当前 workspace 的项目 .cmux/cmux.json 文件继承。项目钩子运行前会使用与其他项目 cmux.json 命令相同的信任提示。Feed 审批横幅也会经过这些钩子;禁用 desktop 会抑制原生横幅,同时在 cmux 中保留 Feed 项。若要忽略继承的钩子,请在项目配置中将 notifications.hooksMode 设为 replace。如果钩子失败、超时或返回无效 JSON,cmux 会使用默认通知行为并显示钩子失败提醒。

#发送通知

#CLI

cmux notify --title "Task Complete" --body "Your build finished"
cmux notify --title "Claude Code" --subtitle "Waiting" --body "Agent needs input"

#OSC 777(简单)

RXVT 协议使用固定格式,包含标题和正文:

printf '\e]777;notify;My Title;Message body here\a'
Shell function
notify_osc777() {
    local title="$1"
    local body="$2"
    printf '\e]777;notify;%s;%s\a' "$title" "$body"
}

notify_osc777 "Build Complete" "All tests passed"

#OSC 99(丰富)

Kitty 协议支持副标题和通知 ID:

# Format: ESC ] 99 ; <params> ; <payload> ESC \

# Simple notification
printf '\e]99;i=1;e=1;d=0:Hello World\e\\'

# With title, subtitle, and body
printf '\e]99;i=1;e=1;d=0;p=title:Build Complete\e\\'
printf '\e]99;i=1;e=1;d=0;p=subtitle:Project X\e\\'
printf '\e]99;i=1;e=1;d=1;p=body:All tests passed\e\\'
功能OSC 99OSC 777
标题 + 正文
副标题
通知 ID
复杂度较高较低
简单通知用 OSC 777。需要副标题或通知 ID 时用 OSC 99。最简单的集成方式是用 CLI(cmux notify)。

#Claude Code hooks

cmux 通过 Claude Code 的 hooks 集成,在任务完成时通知你。

#1. 创建 hook 脚本

~/.claude/hooks/cmux-notify.sh
#!/bin/bash
# Skip if not in cmux
[ -S /tmp/cmux.sock ] || exit 0

EVENT=$(cat)
EVENT_TYPE=$(echo "$EVENT" | jq -r '.hook_event_name // "unknown"')
TOOL=$(echo "$EVENT" | jq -r '.tool_name // ""')

case "$EVENT_TYPE" in
    "Stop")
        cmux notify --title "Claude Code" --body "Session complete"
        ;;
    "PostToolUse")
        [ "$TOOL" = "Task" ] && cmux notify --title "Claude Code" --body "Agent finished"
        ;;
esac
chmod +x ~/.claude/hooks/cmux-notify.sh

#2. 配置 Claude Code

~/.claude/settings.json
{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/cmux-notify.sh"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Task",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/cmux-notify.sh"
          }
        ]
      }
    ]
  }
}

重启 Claude Code 以应用 hooks。

#GitHub Copilot CLI

Copilot CLI 支持hooks,可在提示提交、代理停止和错误等生命周期事件中运行 shell 命令。

~/.copilot/config.json
{
  "hooks": {
    "userPromptSubmitted": [
      {
        "type": "command",
        "bash": "if command -v cmux &>/dev/null; then cmux set-status copilot_cli Running; fi",
        "timeoutSec": 3
      }
    ],
    "agentStop": [
      {
        "type": "command",
        "bash": "if command -v cmux &>/dev/null; then cmux notify --title 'Copilot CLI' --body 'Done'; cmux set-status copilot_cli Idle; fi",
        "timeoutSec": 5
      }
    ],
    "errorOccurred": [
      {
        "type": "command",
        "bash": "if command -v cmux &>/dev/null; then cmux notify --title 'Copilot CLI' --subtitle 'Error' --body 'An error occurred'; cmux set-status copilot_cli Error; fi",
        "timeoutSec": 5
      }
    ],
    "sessionEnd": [
      {
        "type": "command",
        "bash": "if command -v cmux &>/dev/null; then cmux clear-status copilot_cli; fi",
        "timeoutSec": 3
      }
    ]
  }
}

对于仓库级别的 hooks,使用相同结构创建 .github/hooks/notify.json 文件:

.github/hooks/notify.json
{
  "version": 1,
  "hooks": {
    "userPromptSubmitted": [ ... ],
    "agentStop": [ ... ]
  }
}

#集成示例

#长时间命令后发送通知

~/.zshrc
# Add to your shell config
notify-after() {
  "$@"
  local exit_code=$?
  if [ $exit_code -eq 0 ]; then
    cmux notify --title "✓ Command Complete" --body "$1"
  else
    cmux notify --title "✗ Command Failed" --body "$1 (exit $exit_code)"
  fi
  return $exit_code
}

# Usage: notify-after npm run build

#Python

python
import sys

def notify(title: str, body: str):
    """Send OSC 777 notification."""
    sys.stdout.write(f'\x1b]777;notify;{title};{body}\x07')
    sys.stdout.flush()

notify("Script Complete", "Processing finished")

#Node.js

node
function notify(title, body) {
  process.stdout.write(`\x1b]777;notify;${title};${body}\x07`);
}

notify('Build Done', 'webpack finished');

#tmux 透传

如果在 cmux 内使用 tmux,启用透传:

.tmux.conf
set -g allow-passthrough on
printf '\ePtmux;\e\e]777;notify;Title;Body\a\e\\'