Files
agent-dashboard/docs/superpowers/specs/2026-05-26-agent-notify-design.md
laily 5fecb6d215 feat: implement agent-notify CLI with hooks and tmux passthrough
Add OSC 777 notification sender, Cursor/Claude hook adapters,
config/install commands, and README for Ghostty + tmux setup.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-26 11:19:34 +08:00

11 KiB
Raw Permalink Blame History

agent-notify 设计规格

日期: 2026-05-26
状态: 已批准(brainstorming
目标: 在本地/远程 tmux 中运行 Cursor CLI 与 Claude Code 时,通过 Agent hook 触发 OSC 777 桌面通知,经 tmux 透传至 Ghostty 终端。


1. 背景与目标

用户经常在本地 tmux 和远程 SSH tmux 中使用 AI Agent CLICursor CLI、Claude Code)。当 Agent 完成一轮回复、回到等待输入状态时,用户希望收到桌面通知,而无需一直盯着终端。

Ghostty 终端支持 OSC 777(带标题/正文的桌面通知)和 OSC 9。tmux 默认会吞掉 OSC 序列,需通过 DCS passthrough 或写入 client TTY 透传。大部分 Agent CLI 支持 hook,可在 hook 中触发通知。

成功标准

  • Agent 完成一轮回复、等待输入时,Ghostty 弹出桌面通知(默认开启)
  • 通知标题包含 Agent 名称(Cursor / Claude)和项目目录或 tmux 窗口名
  • 同一套 CLI 在以下场景均可工作:
    • 本地 Ghostty → 本地 tmux → Agent
    • 本地 Ghostty → SSH → 远程 tmux → Agent
    • 本地 Ghostty → 本地 tmux → SSH → 远程 tmux → Agent(嵌套 tmux
  • 提供 agent-notify install 一键写入 Cursor 与 Claude Code 的 hook 配置
  • 触发事件可配置:stop(默认开)、idle(默认关)、tool(默认关)

非目标(首版)

  • notify-send 等系统通知回退
  • macOS / Windows 支持
  • 不支持 Cursor IDE,仅支持 Cursor CLIcursor-agent
  • Cursor CLI 的 afterAgentResponse hookCLI 中不可靠)

2. 方案选择

在 brainstorming 中评估了三种方案:

方案 描述 结论
A 自研 agent-notify CLI + 安装脚本 选用
B 包装 soloterm/tnotify 外部依赖,Claude terminalSequence 适配不内聚
C 纯 Shell 脚本 嵌套 tmux 逻辑难维护

3. 整体架构

┌─────────────┐     hook 触发      ┌──────────────────┐
│ Cursor CLI  │ ────────────────► │                  │
│ Claude Code │ ────────────────► │  agent-notify    │
└─────────────┘   stdin/env/flag   │  (核心 CLI)       │
                                    └────────┬─────────┘
                                             │ OSC 777
                                             ▼
                              ┌──────────────────────────┐
                              │ tmux 透传层 (0~N 层)      │
                              └────────────┬─────────────┘
                                             │ SSH (远程场景)
                                             ▼
                                    ┌─────────────┐
                                    │   Ghostty   │
                                    └─────────────┘

组件

  1. agent-notify CLIGo 单二进制)

    • send:发送通知(供 hook 或直接调用)
    • hookAgent 专用入口,解析 stdin JSON
    • install:写入 hook 配置与默认 config
    • test:发送测试通知
    • doctor:检查 Ghostty/tmux/allow-passthrough 配置
  2. Hook 适配层

    • Cursor~/.cursor/hooks.json
    • Claude Code~/.claude/settings.json
  3. 配置文件

    • ~/.config/agent-notify/config.toml

4. 通知协议

OSC 格式

Ghostty 优先使用 OSC 777(支持标题 + 正文):

\033]777;notify;{title};{body}\007

OSC 9 作为备选(仅正文):

\033]9;{body}\007

首版默认使用 OSC 777。

tmux 透传策略

发送优先级:

  1. 写 client TTY(单层 tmux 最可靠)

    • tmux display-message -p '#{client_tty}'
    • 将 OSC 序列写入该 TTY
  2. DCS passthrough(嵌套 tmux 必需)

    • 每层 tmux 包裹:\033Ptmux;\033{inner}\033\\
    • 嵌套 N 层则包裹 N 次
  3. 直写 stdout(无 tmux 且 hook 允许时)

tmux 前置配置

用户需在涉及的每一层 tmux 中启用(安装脚本检测并提示):

set -g allow-passthrough on   # 需要 tmux 3.2+

远程 SSH 说明

  • 远程 tmux 中写入 client TTY 时,数据经 SSH pty 传回本地
  • 若本地还有 tmux,本地 tmux 也需 allow-passthrough on,否则 OSC 在本地被吞掉
  • 嵌套 tmux(本地 tmux → SSH → 远程 tmux)需双层 passthrough 或双层 DCS 包裹

5. Hook 接入

触发事件映射

事件 含义 Cursor CLI hook Claude Code hook 默认
stop Agent 完成回复,等待输入 stop Stop
idle 长时间无输入(约 60s 无等价 hook Notification
tool shell/工具执行结束 afterShellExecution PostToolUseshell 类)

通知内容

  • 标题: {agent} — {context}
    • {agent}CursorClaude
    • {context}tmux 窗口名(#{window_name});若无 tmux 则用 basename(cwd)
  • 正文: 按事件类型
    • stop等待输入
    • idle空闲 60s+,等待输入
    • tool工具执行完成

模板可在 config.toml 中覆盖。

Cursor CLI 集成

配置文件:~/.cursor/hooks.json(全局)或项目级 .cursor/hooks.json

{
  "version": 1,
  "hooks": {
    "stop": [
      { "command": "agent-notify hook cursor stop" }
    ],
    "afterShellExecution": [
      { "command": "agent-notify hook cursor tool" }
    ]
  }
}
  • stop hook 在 CLI 中可用
  • hook 进程的 stdout 可能被捕获,CLI 内部通过写 TTY / DCS passthrough 发送 OSC,不依赖 stdout
  • afterShellExecution 仅在 config 中 events.tool = true 时由 install 写入

Claude Code 集成

配置文件:~/.claude/settings.json

Claude Code v2.1.139+ 的 hook 进程无 controlling TTY不可直接写 /dev/tty。须通过 JSON 返回 terminalSequence,由 Claude Code 代为写入终端:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "agent-notify hook claude stop"
          }
        ]
      }
    ],
    "Notification": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "agent-notify hook claude idle"
          }
        ]
      }
    ]
  }
}

agent-notify hook claude * 输出:

{"terminalSequence": "\033]777;notify;Cursor — myproject;等待输入\007"}
  • Stop hook 必须检查 stop_hook_active:若为 true 则输出空 JSON 并 exit 0,避免无限循环
  • terminalSequence 中的 OSC 序列由 Claude Code 写入其终端路径,天然兼容 tmux

6. CLI 接口

命令

agent-notify send [--title T] [--body B] [--event stop|idle|tool]
agent-notify hook cursor  stop|tool
agent-notify hook claude  stop|idle
agent-notify install [--cursor] [--claude] [--all] [--force]
agent-notify test
agent-notify doctor

环境变量(可选覆盖)

变量 含义
AGENT_NOTIFY_AGENT Agent 名称
AGENT_NOTIFY_CWD 工作目录
AGENT_NOTIFY_EVENT 事件类型

配置文件

路径:~/.config/agent-notify/config.toml

[events]
stop = true
idle = false
tool = false

[notify]
protocol = "osc777"   # osc777 | osc9
title_template = "{agent} — {context}"
body_stop = "等待输入"
body_idle = "空闲 60s+,等待输入"
body_tool = "工具执行完成"

hook 子命令读取 config,若对应 event 为 false 则静默 exit 0。


7. 安装流程

agent-notify install --all 执行:

  1. 检测 agent-notify 是否在 PATH
  2. 运行 doctor:检查是否在 tmux、tmux 版本、allow-passthrough 状态
  3. 写入 ~/.config/agent-notify/config.toml(不存在时)
  4. 合并写入 Cursor ~/.cursor/hooks.json(不覆盖已有同 event hook,除非 --force
  5. 合并写入 Claude ~/.claude/settings.json
  6. 运行 agent-notify test 验证通知

8. 错误处理

场景 行为
不在 tmux 直接写 stdoutCursor)或返回 terminalSequenceClaude
tmux 无 client_tty 回退 DCS passthrough
config 中 event 关闭 hook 静默 exit 0
Claude stop_hook_active=true 不发送通知,输出 {}
doctor 发现 allow-passthrough 未开 打印修复提示,不阻断 install
OSC 发送失败 exit 1stderr 输出原因(hook 不应阻断 Agent)

Cursor/Claude hook 脚本始终以 exit 0 结束(Claude Stop 除外需遵循 stop_hook_active 规则),避免影响 Agent 正常运行。


9. 技术选型

  • 语言: Go 1.22+
  • 依赖: 标准库为主;TOML 解析可用 github.com/BurntSushi/toml
  • 分发: go install github.com/.../agent-notify@latest 或仓库内 make install
  • 平台: Linux + Ghostty(首版)

10. 测试计划

单元测试

  • OSC 777/9 序列生成
  • tmux 层数检测与 DCS 多层包裹
  • config 解析与 event 开关
  • Claude hook JSON 输出格式

集成测试(手动)

场景 命令 期望
无 tmux agent-notify test Ghostty 弹出通知
本地 tmux 在 tmux 内 agent-notify test Ghostty 弹出通知
远程 tmux SSH 到远程 tmux 内 test 本地 Ghostty 弹出通知
嵌套 tmux 本地 tmux → SSH → 远程 tmux test 本地 Ghostty 弹出通知
Cursor stop cursor-agent 完成一轮 通知标题含 Cursor + 项目名
Claude Stop claude 完成一轮 通知标题含 Claude + 上下文

11. 项目结构(预期)

agent-notify/
├── cmd/agent-notify/main.go
├── internal/
│   ├── notify/       # OSC 生成与发送
│   ├── tmux/         # 层数检测、passthrough、client_tty
│   ├── hook/         # cursor/claude stdin 解析
│   ├── config/       # TOML 配置
│   └── install/      # hook 配置合并写入
├── docs/superpowers/specs/
│   └── 2026-05-26-agent-notify-design.md
├── go.mod
├── Makefile
└── README.md

12. 参考资料