用 Hooks 把 Claude Code 通知发到手机

Claude Code 需要授权或任务完成时,手机立刻收到提醒。基于 Notification 和 Stop hook 加 Webhook,配置可直接复制。

更新于

目录

Claude Code 有两种值得关注的停下方式:一是卡住了在等你(权限确认、提问),二是已经做完了。如果你没盯着终端,这两种情况看起来完全一样——什么都不会发生,会话就一直闲置,直到你回来。解决办法是用 hook:在 settings.json 里加几行配置,任何一种情况发生的瞬间,就把 Webhook 发到你的手机。

这篇指南给出可以直接复制的配置,说明不同场景该用哪个 hook 事件,并讲清让它稳定工作的细节:怎么读取 hook 的输入、shell 引号怎么处理、以及如何避免被通知淹没。

什么场景用哪个 hook

Claude Code 会在生命周期事件上运行 hook。其中两个正好覆盖这个需求(hooks 参考文档):

你想知道……Hook 事件Matcher / 过滤
Claude 在等你做权限决定Notificationpermission_prompt
Claude 已经闲置、在等你输入Notificationidle_prompt
Claude 回复完毕Stop无需设置

Notification hook 的 matcher 按通知类型过滤,所以你可以精确选择哪些通知会送到手机。输入以 JSON 形式从 stdin 传入,包含 notification_type、message、cwd 和 session_id。Notification hook 无法阻止或修改任何行为——它们只是观察者,而这正是这里需要的。

Stop 的输入包含 last_assistant_message,也就是 Claude 产出的最后一段文字,正好可以白送一行通知摘要。

你需要什么

  • 手机上装好 Echobell(App Store / Google Play)
  • 运行 Claude Code 的机器上有 curl 和 jq
  • 两分钟

第 1 步 —— 建两个频道

别把所有通知都塞进同一个频道。在 Echobell 里创建:

  • Claude Blocked,类型选 时效性通知(Time Sensitive) 或 来电(Calling) —— 用于权限确认:智能体已经停摆,每过一分钟都是在白白浪费算力和时间
  • Claude Done,类型选 普通(Normal) —— 一条普通推送,什么时候看都行

复制每个频道的 Webhook URL,形如 https://hook.echobell.one/t/<channel-token>。把它当密码对待:任何拿到它的人都能让你的手机响铃。

把两个 URL 存成环境变量,这样它们就不会进到提交的配置文件里:

export ECHOBELL_CLAUDE_BLOCKED_URL="https://hook.echobell.one/t/<blocked-token>"
export ECHOBELL_CLAUDE_DONE_URL="https://hook.echobell.one/t/<done-token>"

把这两行放进 shell 配置文件,Claude Code 才能继承到它们。

第 2 步 —— 添加 hooks

要对所有项目生效,加到 ~/.claude/settings.json;只对某个仓库生效,加到 .claude/settings.local.json:

{
  "hooks": {
    "Notification": [
      {
        "matcher": "permission_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "jq -c '{title: \"Claude 需要授权\", body: .message, project: (.cwd | split(\"/\") | last)}' | curl -sS -m 10 -X POST -H 'Content-Type: application/json' -d @- \"$ECHOBELL_CLAUDE_BLOCKED_URL\""
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "jq -c '{title: \"Claude 已完成\", body: (.last_assistant_message // \"完成\" | .[0:200]), project: (.cwd | split(\"/\") | last)}' | curl -sS -m 10 -X POST -H 'Content-Type: application/json' -d @- \"$ECHOBELL_CLAUDE_DONE_URL\""
          }
        ]
      }
    ]
  }
}

各部分的作用:

  • jq -c '{...}' 从 stdin 读取 hook 的 JSON 并构造一个小载荷,这样 Echobell 收到的正是你想要的字段,而不是整份 hook 文档。
  • .cwd | split("/") | last 把 /home/me/code/api 变成 api,通知里就能看出是哪个项目在请求你。
  • .[0:200] 截断最后一条消息。Echobell worker 把通知正文限制在 1500 字节以内,而锁屏上能显示的本来就少得多。
  • -m 10 给 curl 设了 10 秒超时,网络出问题时 Claude Code 也不会一直等你的 hook。

Echobell 通过频道模板渲染你发送的键,而新建的频道没有读取这些键的模板——请在频道设置里把标题模板设为 {{title}}、正文模板设为 {{body}}。想显示项目名的话,在其中一个里加上 {{project}},例如 {{title}} ({{project}})。

第 3 步 —— 不等真实提示,直接测试

用一份伪造的 hook 载荷,手动跑一遍同样的管道:

echo '{"message":"Claude needs your permission to use Bash","cwd":"/home/me/code/api","notification_type":"permission_prompt"}' \
  | jq -c '{title: "Claude 需要授权", body: .message, project: (.cwd | split("/") | last)}' \
  | curl -sS -m 10 -X POST -H 'Content-Type: application/json' -d @- "$ECHOBELL_CLAUDE_BLOCKED_URL"

如果手机亮了,说明 Echobell 这一侧没问题,剩下的问题都出在 Claude Code 读取配置的方式上。在 Claude Code 里运行 /hooks 确认 hook 已加载;会话运行期间修改的设置,可能需要通过 hooks 菜单或重启才能生效。

第 4 步 —— 让「被阻塞」频道真正响铃

推送通知打不过勿扰模式。来电(Calling) 频道的表现和来电一样,能穿透 iOS 专注模式——当你启动一个长任务、晚上出门时,这一点最重要。如果你只想在部分提示出现时被打扰,不要去改 hook,而是在 Echobell 频道里对你发送的变量设置条件,例如 project == "api"。完整的变量列表见条件参考。

避免通知过载

一个繁忙的会话可能触发大量 Stop 事件——每次回复结束都会有一个。保住有效信号有三个办法:

  • 不要把 Stop 发到会响铃的频道。 任务完成并不紧急,保持普通推送即可。
  • 用 idle_prompt 代替 Stop,如果你只想在 Claude 停下来等你时才收到通知,而不是每次回复之后都收到。
  • 按项目收窄,用条件过滤,这样临时的试验仓库就完全不会通知你。

在远程环境或 CI 中运行 Claude Code

同样的方法在云端或远程环境里也能用,因为 hook 只是一个对外的 HTTPS 请求,不需要任何东西能被公网访问。把这两个环境变量配置到环境的 secrets 里,而不是 shell 配置文件里,无论会话跑在哪里,通知都能送到你的手机。

故障排查

什么都没收到,手动测试也失败。 检查启动 Claude Code 的那个 shell 里是否设置了 URL 变量:echo "$ECHOBELL_CLAUDE_BLOCKED_URL"。变量为空时 curl 会静默失败。

手动测试能通,但 hook 不触发。 确认 matcher:permission_prompt 和 idle_prompt 是不同的,不匹配的 matcher 只是永远不会运行。运行 /hooks 并检查 JSON 是否合法——多一个尾随逗号,就会让整个 settings 文件里的 hook 全部失效。

通知正文是空的。 jq 过滤器引用了该事件中不存在的字段。message 存在于 Notification 的输入中;last_assistant_message 存在于 Stop 的输入中。像上面的配置那样,用 // 提供默认值。

触发太频繁。 参见上一节——把 Stop 换成 idle_prompt,或者加一个条件。

常见问题

Claude Code 需要授权时,怎么收到通知?

添加一个 matcher 为 permission_prompt 的 Notification hook,把 Webhook 发到某个频道。Echobell 会把这个请求变成手机上的推送或来电式提醒。

怎么知道 Claude Code 什么时候完成了任务?

添加一个 Stop hook。它在 Claude 回复完毕时运行,输入里包含 last_assistant_message,可以直接用作通知正文。

通知能突破勿扰模式吗?

可以,前提是接收的频道是来电(Calling)频道。它的表现就像来电,iOS 专注模式会放行;而任何 App 的普通推送都不行。

在网页版 Claude Code 或远程会话里能用吗?

能。hook 只发起一个对外的 HTTPS 请求,所以只要会话能访问互联网就能用。通过环境的 secrets 提供频道 URL 即可。

把 Webhook URL 写进 settings.json 安全吗?

建议像上面那样用环境变量,这样令牌就不会被提交进仓库。如果泄露了,新建一个频道,停用旧的。

小结

两个频道、两个 hook,加一条 jq | curl 管道:permission_prompt 走**来电(Calling)**或时效性频道,Stop 走普通频道。之后你就可以启动一个长时间的智能体任务,离开房间,只在智能体真正需要你的时候被叫回来。

下载 iPhone 版 Echobell,或前往 Google Play 获取,然后先跑一遍上面的手动测试,再放心地把长任务交给它。

相关阅读