重新定位 long-task 命令,重构整个长命令通知逻辑
你好,针对包 @ruan-cat/claude-notifier 内的长命令逻辑,其处理逻辑做的不合适。经过实际的使用,发现我们做的很乱,很差。
按照我的新设计,准备做大规模的重构,并允许做出巨大的破坏性变更。允许你在接下来的重构重写内,做出非兼容的改动。请认真阅读以下修改需求:
参考资料
请你认真的,全面的阅读 claude code hooks 的知识点,确保你不会写出不符合客观现实的代码和文档。
重新定位 long-task 命令
long-task 命令内部不要再处理,维护任何定时任务的东西了,和 timeout 、 task-complete 一样。作为一款简单的,普通的样式化通知指令。内部不要再做任何关于长命令处理的逻辑了。不负责长命令计时功能的任何部分。
长命令计时命令 check-and-notify 更改多任务区分的逻辑
不再使用 session_id 来区分 claude code 的任务了,这个 session_id 根本就无法实现独立任务的区分标识。所以我们的整个逻辑都要重写重做,推翻重做。
我们用读取 stdin 的 JSON 数据,重点使用 cwd 来实现多个 claude code 对话的区分。请你适当的开始改写其他相关代码逻辑,确保能够实现基于 cwd 的长任务标记管理。我们用调用 hook 时的当前工作目录,来定位当前任务。用不同的当前工作目录,作为区分多个独立长命令任务的核心标识。
虽然我知道,一个目录事实上允许多个独立 claude code 运行多个独立长任务的,但目前我真的不清楚一个完整长任务实现独立区分的方案。如果你有,请帮我找到,并设计落实。
定时器状态文件 .claude-notifier-timer.json 存储的数据
经过上一轮的迭代,我对 .claude-notifier-timer.json 要存储的数据格式和字段,有了更加清晰的认知,请按照以下要求做:
保留时间字段并修改格式
对于 addedTime、startTime、和 lastCheckTime 字段,请用语义化的时间来存储他们。请你用 dayjs 库来格式化时间数据,存储的时间格式为 年-月-日 分钟:秒:毫秒 。因为我会直接阅读 .claude-notifier-timer.json 文件。所以你存储的时间必须是语义化的,便于人去阅读的。
请你主动安装 node 库 dayjs,并实现日期格式化修改。
保留 triggeredIndexes 设计
在上一次开发中,你设计的 triggeredIndexes 防止多次提醒的机制,设计的很好,请你保留这个机制和字段。
删除的字段
- intervals
- sound
- icon
这些字段事实上和长任务计时没有任何关联。无法辅助长任务的计时。没有帮助,请默认删除掉,不要再存储这些和计时功能无关的字段了。
长命令计时命令 check-and-notify 要针对 hook_event_name 做出特定处理
我的实际用法会在几乎全部的 claude code 钩子事件内,大批量的,高频率的调用 check-and-notify 命令。你按照这些 hook_event_name 事件名称,来完成以下事情,至少包括:
- 开始记录长任务
- 检查长任务是否可以开始通知
- 删除长任务
- 删除过期的长任务
check-and-notify 应该根据 hook_event_name 所体现的不同的生命周期,做不同的事情:
hook_event_name = UserPromptSubmit 用户提示提交
现在的逻辑改成,你仅仅通过 UserPromptSubmit 事件名,来开始录入一款长任务。
根据 stdin 提供的 cwd 数据,对于同一个工作目录的来说,我们暂且认为一个 cwd 工作目录下,仅仅只有一个长任务。那么对你来说,你肯定要无条件的删除过往 cwd 下对应的任务记录。一律当做是过期的任务删除掉。
这个阶段下,你不可能去做任何长任务通知的。你应该删除掉历史脏数据,标记开始一个新的长任务计时。
hook_event_name = Stop || SubagentStop 停止和子代理停止时
在 hooks 事件 Stop 或 SubagentStop 时,请你务必根据 stop_hook_active=true 的情况,来开始删除长任务。
这个时候你只负责删除掉这个长任务,也不要考虑多此一举,去通知用户已完成。这不是该阶段该做的事情。
这个阶段只负责删除掉长任务,不要去做任何形式的通知。
其他事件
除开上面的事件场景以外,命令 check-and-notify 会高强度的调用。此时请检查长任务是否可以开始通知,根据 stdin 传递进来的时间,自主判断当前的长任务是否需要开始通知。
此时通知的样式和形式,毫无疑问是 long-task 样式的通知格式。其 icon 默认输出为 long-task 预设的图片。
通知文本: 基于时间差精准计算的时间值
通知文本我要求你做计算,因为我们现在能不能做检查和通知,完全是看 claude code 的其他事件触发的频率,假设有很极端的情况,一个 claude code 长任务很久没有机会触发 claude code 事件,那么当你有机会做通知时,请你做好任务时长的计算。
即使是你有高频触发的情况,你也要做好精准的时间差运算,我希望看到的通知文本,是有零有整的,很精确的,没有误导情况的时间差值。
比如以下情况:
- 任务开始时间: 2025-11-2 12:10:00
- hooks 触发时间: 2025-11-2 12:18:35
这是一个很低频的触发情况,那么你的通知文本应该是: claude code任务已运行8分35秒
与此同时,你标记已经完成了一次 6 分钟的提醒了。我希望你的通知文本能够精确到分钟和秒。只要分和秒。不需要太精确到毫秒。
- 请你在通知函数的 message 字段内,输出包含时间差通知文本。
- 请你在通知函数的 title 字段内,输出按阶段提醒的标题。比如该例子,通知框的 title 标题字段,应该设计成
长任务提醒: 6分钟阶段。
根据长任务通知间隔频率数组,控制通知频率,每个间隔频率仅做一次通知
对于我的 claude code hooks 配置情况,长任务通知时的频率有以下情况:
- 极端情况,低频事件触发
- 普遍情况,高频事件触发
请你阅读我给你的例子,来设计,标记,把控好长任务通知的节奏。避免出现长时间没有通知,或者是短时间内频繁通知的情况。
比如以下例子:
例子 1:时间间隔很短的情况:
- 任务开始时间: 2025-11-2 19:20:03
- hooks 触发时间: 2025-11-2 19:20:45
针对 2025-11-2 19:20:45 时间,时间差为 42 秒,还没有到最低的 6 分钟通知间隔,所以肯定不会做通知,即使有再多的 hooks 被触发,你也不做通知。
例子 2: 距离时间间隔最近的一次调用:
- 任务开始时间: 2025-11-2 19:20:03
- hooks 触发时间: 2025-11-2 19:28:45
- hooks 触发时间: 2025-11-2 19:29:05
对于时间 2025-11-2 19:28:45 ,时间间隔 8 分 42 秒。已经达到一次最低 6 分钟的通知间隔了,此刻你就通知 claude code任务已运行8分42秒。
对于时间 2025-11-2 19:29:05 ,时间间隔 9 分 02 秒。你已经做了一次 6 分钟间隔的通知了,距离 10 分钟间隔还到,不满 10 分钟,所以本次触发你不应该做出任何通知的。
主动询问咨询你不明确的细节
请你先思考一下,以 ultrathink 模式认真思考。有疑问疑惑的,请你立刻询问我。我与你共同完善设计一个合适的长任务计时的新增、删除、通知机制。
回答 AI 的疑问
- 长任务通知间隔频率数组的完整配置
- 完整的间隔配置数组,就是 packages\claude-notifier\src\core\timer.ts 的
DEFAULT_INTERVALS数组。 - 这是一个常量,允许用户在外部自己配置提醒间隔。默认的时间间隔数组就是 [6, 10, 18, 25, 45] 。
- 绝大多数情况下,我几乎不会去设置这个间隔数组。但是你仍然要保留这个接口设计。
- 完整的间隔配置数组,就是 packages\claude-notifier\src\core\timer.ts 的
- 时间格式的具体字符串
- 存储格式: YYYY-MM-DD HH:mm:ss
- 通知文本格式: 仅显示"X 分 Y 秒"
- 状态文件的存储位置和数据结构
- 状态文件存储在 os.tmpdir()/.claude-notifier-timer.json ,只是 key 值是基于 cwd,文件并不是存储在 hooks 钩子返回的 cwd 内。
- 文件内应该是 { [cwd: string]: TaskState } 的结构
- 你设计的数据结构 TaskState 和 TimerStateFile 满足我的需求。就这样设计。
- long-task 命令的新职责
- 只是输出一个样式化的通知消息。
- 不需要从任何地方读取 stdin 数据。
- 通知内容就是现在已经设计好的通知内容,icon 是预设的长时间 icon,即
iconPreset.ALICE_TIMEOUT。文本是动态计算的出来的通知文本。
- cwd 唯一性的确认
- 现阶段接受这个限制。
- 至于未来要怎么更改,我也说不准。按照你的方案来吧。
- 关于 hook_event_name 的其他事件处理
- SessionStart、SessionEnd 等事件也会触发 check-and-notify。
- 所有事件都应该检查并通知(除了 UserPromptSubmit 和 Stop/SubagentStop)
- triggeredIndexes 和间隔数组的对应关系
- 换一个标记方式。
- triggeredIndexes = [6] 表示已触发 6 分钟提醒
- triggeredIndexes = [6,10] 表示已触发 6 分钟和 10 分钟提醒
持续迭代通知逻辑
- 旧的基于 session_id 的 API(已废弃,保留用于兼容性)。把旧的 api 接口全部删掉,不做兼容处理了。
- 针对
packages\claude-notifier\src\commands\check-and-notify.ts的 createCheckAndNotifyCommand 函数。- 多增加对 SessionEnd 事件的处理,在 SessionEnd 事件,不做通知,删除任务。当 Claude Code 会话结束时运行。
- 增加对 UserPromptSubmit 和 SessionStart 事件的处理。在这个节点,不要做长任务提醒。刚刚打开界面时,不应该做提醒的。
- 在 UserPromptSubmit 事件运行时,就开始更新长任务了。无条件的删除掉本 cwd 对应的长任务,开始一轮新的长任务计时了。
- 每次运行 createCheckAndNotifyCommand 时,运行 check-and-notify 命令时,你应该及时的更新该 lastCheckTime 值,这个值更新不及时,导致了打开 claude code 对话后,出现了多次提醒。