骗了我的那条 claude code 通知
骗了我的那条通知
我当时脚踩三个项目。项目 A 在一个 VS Code 窗口里开着一个 review。项目 B 跑在 Claude Code 上,用的是 superpowers 技能集,也就是 TDD 模式:红、绿、重构,循环往复——这种模式写出来的代码扎实,但会把一个任务从几分钟拉长到一小时甚至更久。就在那一个小时里的某个时刻,Claude 走到了需要我签字确认才能继续的地方。我没注意到。我人在项目 A。等我切回项目 B 时,它已经在那儿卡了小二十分钟——什么都没做,就在等我。
这是第一个问题:长时间、TDD 驱动的任务会制造出真正的决策点,如果你没盯着,时间就在等一个没人接收的信号中流走了。
第二个问题本质上是同一种失败,换了张脸。我会并行跑好几个项目,每个都开在自己的 VS Code 窗口里,各自挂着一个 Claude Code 会话。“有没有跑完的?有没有卡住的?”——这个问题如果不一个一个窗口、一个一个会话点进去看,是回答不了的。乘上一个普通下午的量,光是"检查"这件事就吃掉了相当一部分时间。
这两个问题其实是同一个形状:Claude Code 自己知道它是卡住了还是跑完了。我不知道,因为我没在盯着。所以我做了 claude-code-notify——一个在真正关键的两个时刻通过 Telegram 提醒我的工具:一轮任务真正结束时,以及它需要我的时候。
那个天真的第一版,以及它为什么在骗我
最直觉的做法是用一个 Stop hook:Claude Code 本身就暴露了这个钩子,接上一个脚本,脚本发一条 Telegram 消息。我一开始就是这么做的。一开始能用——直到它不能用了。
这个 bug 表现为通知来得太早。我会收到"完成了",切过去一看,终端其实还在跑。追下去之后,原因几乎有点尴尬:Claude Code 的 Bash 工具在 run_in_background=true 模式下运行时,会立刻返回一条确认——类似 "Command running in background with ID: …"——这条确认是在命令被派发的那一刻返回的,不是在它执行完的时候。一个天真的 Stop hook 如果把任何匹配到的工具结果都当成"已解决",就会把这条确认误读成完成。其实并没有,命令还在跑。后台 Agent 子代理还有另一种角度的同样问题:它们根本不会发出确认,所以只追踪明确"完成"信号的钩子压根看不到它们。
所以这个天真的钩子,恰恰在我最在意的那类场景——长的、多步骤的、大量依赖后台任务的那种回合——上,比什么都没有还糟。
"完成"到底应该是什么意思
修法可以归结为一条规则:一个后台任务——不管是子代理还是一个丢进后台的 Bash 命令——只有当会话记录里出现一条携带该任务原始 tool_use_id 的 <task-notification> 时,才算真正解决。一条即时确认永远不算数。到 Stop 触发的那一刻,钩子会算一遍 pending = launched − resolved;只要还有没解决的,就保持沉默。只有等每一个后台任务都真正报回来了,才会去检查一下速率限制,然后发送通知。
我原本以为可以直接用 Claude Code 原生的 SubagentStop 钩子来做这件事,完全跳过会话记录解析。结果发现那个钩子根本干不了这活:后台代理(run_in_background=true)会完全绕过 Stop/SubagentStop,子代理完成与否也不能通过它可靠地上报,就算它真触发了,携带的还是父会话共用的 session_id,没法分辨到底是哪个子代理跑完了。这三点都是 Claude Code 本身已知的问题,不是我这边配置的毛病。基于会话记录、按 tool_use_id 匹配,是目前唯一靠得住的信号。
我没为它设计过的那种情况
我几乎是顺手加上错误钩子的——一轮任务可能因为任何原因死掉,一个悄无声息死掉的回合,跟一条过早的"完成"是同一种谎言。结果某天下午,它反倒成了三个钩子里最有用的那个。
当时我同时跑着三个会话:两个长时间的自主回合,加一个 review。跑着跑着,我的账号撞上了用量限制。用量限制不会只停掉一个回合——它会同时停掉全部,卡在任务中间,卡在我看不见的地方。要是按旧的工作方式,我会像往常一样发现:一个个点进每个窗口,撞上一堵原本期待是进度、结果是一片红的墙。
而这次,我的手机响了三次:
Claude Code stopped with error | Refactor the transcript parser | ~/work/foo | 11/07/2026 10:29:28
——还有另外两条类似的。每条提醒都带着它来自哪个项目、这一回合自己的标题,因为错误通知携带的字段跟其他每一种通知一样:Claude Code 给这一回合起的标题、工作目录、还有时间。
这恰恰是限制会从你脑子里抹掉的信息。等它几小时后重置,问题已经不是"是不是出了什么问题"——全都出问题了——而是"我在每个项目里,出问题的时候人在哪儿"。躺在我 Telegram 记录里的那三条消息,不用我打开任何一个窗口就回答了这个问题:哪些项目、哪些任务——足够让我直接走回每一个项目接着干,而不是从头拼凑我之前停在哪儿。这篇开头提到的"并行检查"问题,在它最糟的形态下——好几个会话同时卡住——被压缩成了一段我可以在手机上边等限制解除边读的滚动记录。
现在它长什么样
我起一个长任务,切到另一个项目,然后不再去想它。会有一条消息传来——发在 Telegram 上,所以不管哪个窗口在前台都能收到——而且只会因为三种原因之一:这一回合真的完成了,包括所有后台任务;它需要我输入;或者它出错终止了。不会有东西还在跑的时候提前收到"完成",也不用手动挨个检查五个会话找哪个卡住了。
我最初的两个问题,说到底是同一个问题,而这也是同一个修法:别再指望自己会注意到,让通知本身变得可信。
试试看
curl -fsSL https://raw.githubusercontent.com/Jeromefromcn/claude-code-notify/main/install.sh | bash
只有一个依赖(python3),配置存在 settings.json 之外,所以你的 token 不会被不小心提交进去。改主意了?同一行命令加上 --uninstall 就能把钩子和代码都撤出来(config.env 会被保留,所以以后重装不用重新输入一次 token):
curl -fsSL https://raw.githubusercontent.com/Jeromefromcn/claude-code-notify/main/install.sh | bash -s -- --uninstall
MIT 协议开源:github.com/Jeromefromcn/claude-code-notify。
如果你也是并行跑多个 Claude Code 会话,或者依赖长时间的自主回合,我是真的想知道这套东西对你是不是管用——或者你碰到了它处理不了的情况。
更多推荐


所有评论(0)