news 2026/9/17 12:38:00

Munder Difflin 的 /today 时间解析技能:用 when.mjs 把“今天“变成精确的日期窗口

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Munder Difflin 的 /today 时间解析技能:用 when.mjs 把“今天“变成精确的日期窗口

Munder Difflin 的 /today 时间解析技能:用 when.mjs 把"今天"变成精确的日期窗口

【免费下载链接】munder-difflinA local multi-agent harness that works with your existing Claude Code, Codex subscriptions, allows you to run an office of agents项目地址: https://gitcode.com/GitHub_Trending/mu/munder-difflin

/today是 Munder Difflin 这个本地多智能体(multi-agent)工具集中内置的一个只读时间技能(skill),它把口语化的"今天"解析成一组具体的 ISO 日期范围——既包含本地时区的自然日(civil date),也包含精确到毫秒的 UTC 时刻,供基于时间戳的查询直接使用。本文以 resources/skills/today/SKILL.md 为骨架,结合其底层解析器 resources/skills/temporal/when.mjs 的源码,讲清楚这个技能怎么用、输出字段是什么含义、底层是怎么算出来的,以及它如何在 Munder Difflin 的 hive worker(被派生的自主工作线程)中发挥作用。读完你不仅能熟练调用/today,还能理解整套时间窗口技能的设计哲学:解析器是唯一事实来源,Agent 绝不手工推算日期

技能是什么:一个只读的"今天"解析器

/today是一个以 Claude Code Skills 格式定义的技能,声明在resources/skills/today/SKILL.md的 YAML frontmatter 中:

--- name: today description: | Resolve "today" to a concrete ISO date range relative to your run time — today. Returns inclusive civil dates plus exact UTC instants so you have temporal context without computing dates by hand. Read-only: no writes, no network. Use before any task scoped to today (today's logs, metrics, "what happened today"). allowed-tools: - Bash ---

从 frontmatter 可以读出三个关键设计意图:

  • 用途定位:任何以"今天"为时间边界的任务(今天的日志、指标、"今天发生了什么")在执行前都应先调用它;
  • 能力约束allowed-tools只有Bash,且技能本身明确声明Read-only(只读):不写文件、不访问网络
  • 单一事实来源:description 强调"don't computing dates by hand"——日期由解析器统一给出,Agent 不得自己推算。

/today其实不是一个独立的实现,而是调用同一套解析器的一个命名快捷方式。技能正文给出的调用方式如下:

node "$AGENT_DIR/.claude/skills/temporal/when.mjs" today

其中$AGENT_DIR是 Munder Difflin 的 hive 注入给每个 worker 的环境变量,指向 worker 的私有工作区(包含identity.mdmemory.mdinbox/outbox/以及.claude/skills/),这一点在 resources/skills/capabilities/SKILL.md 的环境说明部分有完整定义。也就是说,每个被派生的 worker 都自带这份时间技能,随时可以解析日期。

输出字段:一份可直接用于查询的 JSON 记录

执行上面的命令后,解析器会打印一行人类可读的摘要,随后跟一条 JSON 记录。JSON 记录的字段如下(以/today为例):

字段含义示例
start/end本地时区下的包含式自然日日期(YYYY-MM-DD2026-09-16/2026-09-16
startUtc/endExclusiveUtc同一窗口表示为半开区间[start, end)的精确 UTC 时刻2026-09-16T00:00:00.000Z/2026-09-17T00:00:00.000Z
days窗口跨越的自然日天数1
timezone/tzOffsetMinutes解析时所在时区及相对 UTC 的偏移分钟数Asia/Shanghai/480
asOf本次解析发生的时刻(UTC ISO 字符串)2026-09-16T07:57:39.123Z
window/label/inclusive请求的窗口关键字、人类可读标签、是否为包含式today/Today/true

关键点在于两组日期各有用途:

  • start/end是给人类和自然语言任务看的:直接作为"今天的起止日期"使用;
  • startUtc/endExclusiveUtc是给机器查询用的:对于createdAt、时间戳这类字段的过滤,使用半开区间[startUtc, endExclusiveUtc)可以避免"边界时刻到底算不算"的歧义——这正是技能文档强调[start, end)语义的原因。

源码级解析:when.mjs 是怎么算出"今天"的

/today背后真正的执行者是 resources/skills/temporal/when.mjs,一个纯 Node.js 标准库实现、无任何第三方依赖的脚本。理解它就能理解整个时间技能家族的实现原理。

本地自然日(civil day)的计算

脚本的核心是civ()fmt()两个辅助函数:

const now = new Date(); const pad = (n) => String(n).padStart(2, '0'); const civ = (y, m, d) => new Date(y, m, d); const fmt = (dt) => `${dt.getFullYear()}-${pad(dt.getMonth() + 1)}-${pad(dt.getDate())}`;

civ(y, m, d)构造的是本地时区当天的午夜零点,而不是某个 UTC 时刻。这样"今天"就严格等于本地日历上的一天,跨时区运行时会各自解析出符合本时区习惯的日期。fmt()则用padStart把月份和日期补成两位,保证输出恒为YYYY-MM-DD格式。

today窗口的定义极其简单:

today: () => ({ label: 'Today', start: civ(Y, M, D), end: civ(Y, M, D) }),

其中YMD取自TODAYciv(now.getFullYear(), now.getMonth(), now.getDate()))的三个字段。start 与 end 相同,都指向今天零点——因为"今天"只包含一个自然日。

半开区间与天数

resolve()函数负责把窗口加工成最终记录,其中最值得注意的是endExclusiveUtcdays的推导:

const endExclusive = civ(w.end.getFullYear(), w.end.getMonth(), w.end.getDate() + 1); const days = Math.round((endExclusive - w.start) / 86400000); return { window: key, label: w.label, start: fmt(w.start), end: fmt(w.end), inclusive: true, startUtc: w.start.toISOString(), endExclusiveUtc: endExclusive.toISOString(), days, asOf: now.toISOString(), timezone: TZ, tzOffsetMinutes: TZ_OFFSET_MIN };
  • 结束边界通过end.getDate() + 1得到次日零点,因此无论是否跨月(比如 9 月 30 日加一天自动变成 10 月 1 日),都能正确得到半开区间的右端点;
  • days(endExclusive - start) / 86400000计算,本质上是两个本地零点之间的毫秒差换算成天数,再配合Math.round消除夏令时(DST)等导致的毫秒级偏移;
  • 时区信息通过Intl.DateTimeFormat().resolvedOptions().timeZone获取,偏移分钟数使用-now.getTimezoneOffset()得到"东八区为 +480、UTC-7 为 -420"的惯用符号。

参数解析与错误处理

脚本的main()支持从命令行参数直接指定窗口关键字:

const keys = argv.filter((a) => !a.startsWith('--')); const requested = keys.length ? keys : ORDER; // 不带参数时输出全部窗口

也就是说node when.mjs today只解析一个窗口;不带参数则把 14 个命名窗口全部打印出来(作为一个"时间上下文快照");如果传入了未知关键字,脚本会向 stderr 输出提示并设置退出码2,方便调用方感知错误。另有两个开关:--json只输出 JSON(机器可读),--list列出所有支持的窗口关键字。

为什么 Agent 不该手工推算日期

技能文档反复强调"Do not derive dates by hand — this resolver is the source of truth"(不要手工推算日期——解析器才是事实来源),这不是教条,而是有实际工程原因的:

  1. 时区歧义:Agent 运行环境所在时区决定了"今天"的边界。同一个时刻,在 UTC+8 和 UTC-8 可能分属两个不同的自然日。手工推算极易把"本地今天"和"UTC 今天"混淆。
  2. 边界语义:查询时间戳时,用"今天 23:59:59"作为右边界会漏掉最后一毫秒,用"明天 00:00:00"作为开区间右边界才是无歧义的做法。startUtc/endExclusiveUtc直接把这个陷阱消解掉了。
  3. 月份/季度/年份的边界算术civ()依赖 JSDate对越界字段的自动归一化(如day 0自动回退到上个月最后一天、month 12自动进到下一年一月),这让lastMonthlastQuarter这类窗口的起止计算既简短又正确,手工推算则很容易在 2 月、闰年、季度切换处出错。

在 Munder Difflin 的实际运行中,这套约定已经写进了 worker 的启动引导语:在 src/main/index.ts 中,hive 派生 worker 时会注入一段提示,明确要求"任何时间范围限定的工作,先用这些技能解析日期,而不是手工计算",并把/today/last30Days/lastQuarter等列为 worker 的时序技能。这说明时间解析是 worker 执行时间敏感任务(今日报告、周报、季度回顾)的前置标准动作

从 /today 扩展到整套时间窗口

/today只是 resources/skills/temporal/SKILL.md 中定义窗口列表里的一个。temporal 技能是"所有窗口的单一事实来源",支持以下命名窗口:

关键字含义
today/yesterday单个自然日
thisWeek/lastWeekISO 周(周一为起始);this= 周一到今天,last= 上一完整周(周一至周日)
last7Days/last30Days/last90Days截至今天的滚动 N 天窗口(含今天)
thisMonth/lastMonththis= 1 号到今天;last= 上一完整自然月
thisQuarter/lastQuarterthis= 季度首日到今天;last= 上一完整季度
thisYear/lastYearthis= 1 月 1 日到今天(YTD);last= 上一完整年
last12Months截至今天的滚动 12 个月

统一约定this*窗口是"周期起点 → 今天"(to-date);last*命名周期是"上一个完整周期"。此外还支持任意滚动窗口lastNdays/lastNweeks/lastNmonths(例如last45dayslast2weekslast6months),以及一组别名:ytdqtdmtdwtd7d30d90d12m。这些别名和lastN通配形式的实现都集中在when.mjsbuild()函数里,通过正则^last(\d+)days?$^last(\d+)weeks?$^last(\d+)months?$匹配并换算成对应的起始日期。

对应地,仓库里为每个常用窗口都单独放置了技能快捷方式,内容和/today完全同构——例如 resources/skills/last30Days/SKILL.md(滚动 30 天)、resources/skills/thisWeek/SKILL.md(周一到今天)、resources/skills/yesterday/SKILL.md(昨天)——它们内部都指向同一个when.mjs,只是参数不同。这也是为什么需要/temporal作为完整入口:当任务窗口不在快捷技能之列(如last90Dayslast12Months或任意lastNdays)时,直接调用:

# 全部窗口一次输出(快速的时间上下文快照) node "$AGENT_DIR/.claude/skills/temporal/when.mjs" # 一次请求一个或多个窗口 node "$AGENT_DIR/.claude/skills/temporal/when.mjs" last30Days thisQuarter # 只输出 JSON(机器可读),或列出支持的关键字 node "$AGENT_DIR/.claude/skills/temporal/when.mjs" --json last7Days node "$AGENT_DIR/.claude/skills/temporal/when.mjs" --list

如果$AGENT_DIR意外未设置,脚本在~/.claude/skills/temporal/when.mjs也有一份副本可调用——when.mjs的文件头注释里明确说明了这一点,作为兜底路径。

在 hive worker 中的典型用法

结合 resources/skills/capabilities/SKILL.md 的能力目录,一个典型的时间敏感任务流程是这样的:

  1. worker 启动时由 hive 注入AGENT_IDAGENT_NAMEAGENT_DIRHIVE_ROOT等环境变量;
  2. 接到任务后,若任务带时间范围("汇总今天的日志"、"报告上周指标"、"梳理本季度发布"),先执行node "$AGENT_DIR/.claude/skills/temporal/when.mjs" <窗口>拿到start/endstartUtc/endExclusiveUtc
  3. 把解析出的具体 ISO 边界作为查询条件传给后续的数据源或集成调用——能力目录明确建议"先解析日期窗口,再把具体的 ISO 边界传给集成查询";
  4. 任务完成后,向 god 发送一条"act":"done"的 outbox 消息汇报结果(worker 是临时进程,不直接推送远端,god 是唯一集成者)。

整个过程遵守严格的边界:时间技能只读(仅读取系统时钟并打印到 stdout,不写文件、不联网),外部集成调用则统一经由 hive 的 loopback broker 代理,worker 侧不持有任何凭据。这套"解析器单一事实来源 + 半开 UTC 区间 + 只读边界"的设计,让所有派生 worker 在时间语义上保持一致,从根上消除了多智能体协作中最常见的日期歧义问题。

小结

  • /today是一个只读、无网络依赖的 Claude Code 技能,通过node "$AGENT_DIR/.claude/skills/temporal/when.mjs" today把"今天"解析成包含式自然日 + 半开 UTC 区间的完整日期记录;
  • 输出同时覆盖人类可读的YYYY-MM-DD与机器查询友好的startUtc/endExclusiveUtc,并附带daystimezoneasOf等上下文;
  • 底层实现 resources/skills/temporal/when.mjs 用本地午夜 + JS Date 越界归一化完成全部日期算术,无任何第三方依赖,随处可用;
  • 需要更广窗口(周、月、季度、lastNdays)时,升级到/temporal技能或直接调用同一个解析器,整套窗口列表与别名都以它为单一事实来源。

【免费下载链接】munder-difflinA local multi-agent harness that works with your existing Claude Code, Codex subscriptions, allows you to run an office of agents项目地址: https://gitcode.com/GitHub_Trending/mu/munder-difflin

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 12:37:54

大模型API聚合平台三大维度测评:硅基流动、OpenRouter、OneAPI、七牛云AI与词元之河(TokenRiver.ai)横评(2026最新)

用一个 Key 接入多家大模型、统一计费与访问管理、把供应商切换成本压到最低——这是大模型 API 聚合平台的核心价值&#xff0c;也是 2026 年 AI 团队普遍的接入方式。市面主流方案可以归成三类&#xff1a;国际商业聚合&#xff08;OpenRouter&#xff09;、国内商业聚合&…

作者头像 李华
网站建设 2026/9/17 12:37:26

Fiddler下载安装汉化避坑指南:从抓包原理到常见问题全解析

第一次听说Fiddler的人&#xff0c;多半是被“抓包”这两个字吸引来的。前后端联调的时候想看请求头带了什么、弱网环境下页面为什么卡、某个接口返回的数据到底是不是预期的那份&#xff0c;这些场景都用得上Fiddler。但现实是&#xff0c;很多人卡在了第一步——下载和汉化。…

作者头像 李华
网站建设 2026/9/17 12:37:24

LaTeX安装实战指南:TeX Live+VSCode配置与避坑全解析

一个被问过无数遍的问题&#xff1a;LaTeX到底好不好装&#xff1f;我的回答始终是——真不难。难点从来不在安装本身&#xff0c;而在安装之前那一堆让你无从下手的“选择题”&#xff1a;该装TeX Live还是MiKTeX&#xff1f;要不要为了个编辑器去折腾环境变量&#xff1f;VSC…

作者头像 李华
网站建设 2026/9/17 12:35:57

VLC HTTP/RC接口远程控制与手机遥控实战

家里那台接了电视和音响的旧笔记本&#xff0c;硬盘里塞满了片源&#xff0c;人一旦躺到沙发上就再也不想起来——想切下一集、想调音量、想跳到片尾&#xff0c;都得爬起来摸键盘。这个场景下最省事的做法并不是装一整套远程桌面软件&#xff0c;而是把 VLC 播放器自带的 HTTP…

作者头像 李华
网站建设 2026/9/17 12:35:30

操作系统课后习题验证:PV操作、银行家算法与页面置换复算

简介&#xff1a;这份文档是《计算机操作系统教程》左万利、王英第四版课后习题的配套答案资料&#xff0c;面向高校计算机专业学生及考研、期末备考人群&#xff0c;用于核对章节作业、梳理考点与巩固解题思路。压缩包共1个文件&#xff0c;为doc格式&#xff0c;大小约4.21MB…

作者头像 李华