# 通知

cmux 支援桌面通知，讓 AI 代理和腳本在需要注意時提醒你。

## 生命週期

1.  已接收：通知出現在面板中，桌面提醒觸發（如果未被抑制）
2.  未讀：在工作區分頁上顯示徽章
3.  已讀：當你查看該工作區時清除
4.  已清除：從面板中移除

### 抑制

在以下情況下，桌面提醒會被抑制：

-   cmux 視窗處於聚焦狀態
-   傳送通知的特定工作區處於活躍狀態
-   通知面板已開啟

### 通知面板

按 `⌘⇧I` 開啟通知面板。點擊通知以跳轉到該工作區。按 `⌘⇧U` 直接跳轉到有最新未讀通知的工作區。

## 自訂指令

每次排程通知時執行一個 shell 指令。在「設定 > 應用程式 > 通知指令」中設定。指令透過 /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
```

此指令獨立於系統聲音選擇器執行。將選擇器設為「無」以僅使用自訂指令，或兩者都保留以同時播放系統聲音和自訂動作。

## 通知 Hook

`cmux.json` 可以定義通知 Hook，讓每個通知政策以 JSON 形式從 stdin 傳入。每個 Hook 會透過 stdout 回傳 JSON。Hook 預設為關閉；只有在 `notifications.hooks` 至少包含一個已啟用的 Hook 時，cmux 才會執行它們。cmux 會套用回傳的通知文字與效果，因此 Hook 可以篩選橫幅、保留或略過側邊欄歷史、播放聲音，或停止後續 Hook。

cmux.json

```
{
  "notifications": {
    "hooks": [
      {
        "id": "quiet-docs",
        "command": "sed 's/"desktop":true/"desktop":false/'",
        "timeoutSeconds": 20
      }
    ]
  }
}
```

Hook 輸入與輸出

```
{
  "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
  },
  "agent": {
    "kind": "claude",
    "category": "turn-complete",
    "pending": false,
    "isSubagent": true
  },
  "effects": {
    "record": true,
    "markUnread": true,
    "reorderWorkspace": true,
    "desktop": true,
    "sound": true,
    "command": true,
    "paneFlash": true
  }
}
```

Hook 會從全域 `~/.config/cmux/cmux.json` 以及從父目錄到目前 workspace 的專案 `.cmux/cmux.json` 檔案繼承。專案 Hook 執行前會使用與其他專案 `cmux.json` 命令相同的信任提示。Feed 核准橫幅也會通過這些 Hook；停用 `desktop` 會抑制原生橫幅，同時讓 Feed 項目保留在 cmux 中。若要忽略繼承的 Hook，請在專案設定中將 `notifications.hooksMode` 設為 `replace`。如果 Hook 失敗、逾時或回傳無效 JSON，cmux 會使用預設通知行為並顯示 Hook 失敗提醒。

### Agent-event context

Notifications that originate from an agent completion signal carry a read-only `agent` object: `kind` is the stable lowercase agent slug (claude, codex, grok, …), `category` is turn-complete, needs-permission, or idle-reminder, `pending` is true when the turn ended with background work still running, and `isSubagent` is true for nested subagent sessions. The object is omitted for non-agent notifications, and the same values are exported to the hook process as CMUX\_NOTIFICATION\_AGENT\_KIND, CMUX\_NOTIFICATION\_AGENT\_CATEGORY, CMUX\_NOTIFICATION\_AGENT\_PENDING, and CMUX\_NOTIFICATION\_AGENT\_IS\_SUBAGENT. For example, this hook silences the banner, sound, and pane flash for subagent completions while leaving everything else untouched:

Silence subagent completions

```
{
  "notifications": {
    "hooks": [
      {
        "id": "mute-subagent-completions",
        "command": "if [ \"${CMUX_NOTIFICATION_AGENT_IS_SUBAGENT-0}\" = \"1\" ] && [ \"$CMUX_NOTIFICATION_AGENT_CATEGORY\" = \"turn-complete\" ]; then printf '{\"effects\":{\"desktop\":false,\"sound\":false,\"paneFlash\":false}}'; fi"
      }
    ]
  }
}
```

With no hooks configured, notification behavior is exactly cmux's built-in default — configuring a hook is the explicit opt-in that hands the delivery decision to your script. Note that cmux's built-in subagent suppression (`automation.suppressSubagentNotifications`, on by default) drops subagent completion events before they reach hooks; turn it off if your hook should see subagent events and make its own decision.

## 傳送通知

### 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 99 | OSC 777 |
| --- | --- | --- |
| 標題 + 內文 | 是 | 是 |
| 副標題 | 是 | 否 |
| 通知 ID | 是 | 否 |
| 複雜度 | 較高 | 較低 |

簡單通知使用 OSC 777。需要副標題或通知 ID 時使用 OSC 99。使用 CLI（cmux notify）是最簡單的整合方式。

## Claude Code hooks

cmux 透過 [Claude Code](https://docs.anthropic.com/en/docs/claude-code) 的 hooks 整合，在任務完成時通知你。

### 1\. 建立 hook 腳本

~/.claude/hooks/cmux-notify.sh

```
#!/bin/bash
# Outside cmux there is no socket to talk to, so do nothing.
[ -S /tmp/cmux.sock ] || exit 0

payload=$(cat)
field() { printf '%s' "$payload" | jq -r "$1 // empty"; }

event=$(field .hook_event_name)
if [ "$event" = "Stop" ]; then
    cmux notify --title "Claude Code" --body "Session complete"
elif [ "$event" = "SubagentStop" ]; then
    cmux notify --title "Claude Code" --body "Agent finished"
fi
```

```
chmod +x ~/.claude/hooks/cmux-notify.sh
```

### 2\. 設定 Claude Code

~/.claude/settings.json

```
{
  "hooks": {
    "SubagentStop": [
      {
        "hooks": [{ "type": "command", "command": "~/.claude/hooks/cmux-notify.sh" }]
      }
    ],
    "Stop": [
      {
        "hooks": [{ "type": "command", "command": "~/.claude/hooks/cmux-notify.sh" }]
      }
    ]
  }
}
```

重新啟動 Claude Code 以套用 hooks。

## GitHub Copilot CLI

Copilot CLI 支援[hooks](https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/use-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\\'
```

Canonical: https://cmux.com/zh-TW/docs/notifications

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