work-hours
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWork hours
工作时长
Reconstructs working time after the fact from timestamps the person already produces. The answer is
only as good as two things: telling the person's own actions from automation, and deciding what a
silence means. Everything below is about getting those two right.
Read before trusting any number — it lists the traps that each moved the answer
by hours. has the per-source how-to.
references/methodology.mdreferences/sources.md本技能可根据用户已产生的时间戳事后重建工作时长。统计结果的准确性取决于两个核心因素:区分用户本人操作与自动化行为,以及判断无活动时段的含义。以下所有内容都围绕如何优化这两点展开。
在采信任何统计数据前,请先阅读 ——其中列出了各类可能导致统计结果偏差数小时的陷阱。 包含各数据源的使用说明。
references/methodology.mdreferences/sources.mdGround rules
基本规则
- Personal data, temporary by default. Everything goes in the run dir from the config
(, default
run_dir). Never write extracted events into a repo. When the person says they are done, delete the run dir and every upload (browser histories, chat exports, screen-time databases); confirm the paths are gone.~/.cache/work-hours/run - Read-only everywhere. Slack, GitHub, trackers, mail: search and read, never post or react.
- Throttle. Transcript folders run to gigabytes: , stream line by line, one or two readers,
nice -n 19 ionice -c3.python3 -I - Ask for data, don't guess it. The person can give you things you cannot reach (chat exports, laptop files, a chess username, their meeting schedule). Ask early; see "What to ask for".
- 个人数据默认临时存储。 所有数据都存入配置中指定的运行目录(,默认为
run_dir)。严禁将提取的事件写入代码仓库。当用户表示使用完毕后,删除运行目录及所有上传文件(浏览历史、聊天导出文件、屏幕时间数据库),并确认路径已被清除。~/.cache/work-hours/run - 所有数据源均为只读访问。 对于 Slack、GitHub、追踪工具、邮件:仅进行搜索和读取操作,绝不发布内容或作出反应。
- 资源限流。 对话记录文件夹可能达到数GB大小:使用 命令、逐行流式读取、仅启用一到两个读取进程、使用
nice -n 19 ionice -c3模式运行。python3 -I - 主动索要数据,而非猜测。 用户可以提供你无法获取的内容(聊天导出文件、笔记本文件、象棋账号用户名、会议日程)。请尽早询问;详见「需要索要的内容」。
Workflow
工作流程
bash
S=<this skill>/scriptsbash
S=<this skill>/scripts0. config: copy references/config.example.json to ~/.config/work-hours/config.json and fill it in
0. config: copy references/config.example.json to ~/.config/work-hours/config.json and fill it in
(timezone, window, customer rules, Slack user id, chess usernames, meeting schedule, categories).
(timezone, window, customer rules, Slack user id, chess usernames, meeting schedule, categories).
The person's real config never lives inside the skill.
The person's real config never lives inside the skill.
python3 -I $S/extract_claude.py # human prompts + answers/interrupts from transcripts
python3 -I $S/extract_claude.py # human prompts + answers/interrupts from transcripts
Slack: run the MCP search loop (references/sources.md), then:
Slack: run the MCP search loop (references/sources.md), then:
python3 -I $S/slack_parse.py <saved result files...>
python3 -I $S/extract_whatsapp.py _chat.txt --me "<name>" --customer <c> --tag <who>
python3 -I $S/extract_chess.py # chess.com / lichess → leisure intervals
python3 -I $S/extract_meetings.py # recordings + recurring calls (run after the point extractors)
python3 -I $S/extract_mac.py <upload-dir> # laptop presence, foreground apps, browsing
python3 -I $S/slack_parse.py <saved result files...>
python3 -I $S/extract_whatsapp.py _chat.txt --me "<name>" --customer <c> --tag <who>
python3 -I $S/extract_chess.py # chess.com / lichess → leisure intervals
python3 -I $S/extract_meetings.py # recordings + recurring calls (run after the point extractors)
python3 -I $S/extract_mac.py <upload-dir> # laptop presence, foreground apps, browsing
any other human-only source (PR approvals, tracker moves made in the UI): write events.<name>.ndjson
any other human-only source (PR approvals, tracker moves made in the UI): write events.<name>.ndjson
python3 -I $S/calibrate.py # silence survival, bout criterion, ground-truth check
python3 -I $S/model.py --idle 25 --tail 5 # → report.md
Point events are `events.<source>.ndjson` (`ts`, `src`, `customer`, `kind`); intervals are
`intervals.<source>.ndjson` (`start`, `end`, `category`). `model.py` picks up every file matching those names,
so a new source is just a new file.python3 -I $S/calibrate.py # silence survival, bout criterion, ground-truth check
python3 -I $S/model.py --idle 25 --tail 5 # → report.md
点事件存储为 `events.<source>.ndjson`(包含 `ts`、`src`、`customer`、`kind` 字段);区间事件存储为 `intervals.<source>.ndjson`(包含 `start`、`end`、`category` 字段)。`model.py` 会读取所有匹配上述命名规则的文件,因此新增数据源只需添加对应文件即可。The model
统计模型
Two independent methods. Report both; their agreement is the accuracy claim.
- B. Messages: one merged timeline of every human action across every source. A silence of up to
minutes is work; a longer one is a break that still credits
idleminutes after the last action. Leisure intervals (chess games, leisure browsing) are subtracted.tail - A. Presence: laptop present (unlocked, display-sleep tail removed), minus leisure, plus off-laptop work
only where there is evidence: phone messages while the laptop was away, bridged when under minutes apart, and calls. Do not let the message model's gap-filling run while the laptop says the person left — that double-counts about 1.5 h/day.
idle
Measure across all customers combined first; the per-customer split comes from attributing each worked
minute to the nearest action's customer. Cut workdays at a fixed local hour (default 05:00), so a 02:00
session belongs to the day before.
采用两种独立的统计方法。会同时报告两种方法的结果;二者的一致性代表了结果的准确程度。
- B. 消息法: 整合所有数据源中的所有用户人工操作,形成统一时间线。空闲时长不超过 (空闲阈值)分钟的时段计为工作;超过该阈值的时段计为休息,但最后一次操作后的
idle(尾差)分钟仍计入工作时长。休闲区间(象棋游戏、休闲浏览)会被扣除。tail - A. 在线状态法: 基于笔记本电脑在线状态(已解锁,扣除显示器休眠后的尾差时间),减去休闲时间,仅在有证据的情况下 计入非电脑端工作:比如电脑不在身边时的手机消息(若间隔小于 分钟则与前后工作时段衔接),以及通话时间。当电脑显示用户已离开时,不得使用消息模型的间隙填补逻辑——否则每天会多计约1.5小时。
idle
首先统计所有客户的总工作时长;再将每分钟工作时长归因于距离最近的操作所属的客户,从而得到各客户的时长拆分。工作日以固定的当地时间点进行切割(默认为05:00),因此凌晨02:00的工作时段属于前一天。
Choosing the idle threshold (the number that matters most)
空闲阈值的选择(最重要的参数)
On one real dataset the answer swung from 6.0 to 13.9 h/day between a 5- and 60-minute threshold, and
the gap histogram had no natural break. Calibrate it; don't pick it.
- gives the bout criterion: a lower bound, usually around 10 minutes.
calibrate.py - Ground truth from the person's own words. Read their messages for "lunch", "brb", "back",
"going to sleep", "on the train, working from my phone", in every language they use. Read each hit
yourself: a word list must not decide meaning. Write with
labels.ndjsonandawaystretches. The right threshold separates the two. In the calibration run, stated breaks always left a silence of 27 minutes or more and phone work never exceeded about 26, so the answer was 25 minutes.phone_work - Attention check. In agent-driven work, see whether the person replies seconds after an agent finishes (they were watching) or long after (they were away). If replies come about 16 minutes after the agent finished and almost never within 2 minutes, a silence is not attentive waiting, so do not stretch the threshold to cover it.
在一个真实数据集中,当阈值设为5分钟和60分钟时,统计结果从每天6.0小时波动到13.9小时,且间隔直方图上没有明显的自然分界点。必须校准阈值,而非随意选取。
- 会给出连续工作时段的判定标准:即阈值下限,通常约为10分钟。
calibrate.py - 基于用户本人表述的真实数据。 查阅用户所有语言的消息,查找「午饭」「马上回来」「回来了」「要睡觉了」「在火车上,用手机工作」这类内容。请人工核对每一条匹配结果:不能仅靠词表判断语义。将 (离开)和
away(手机工作)的时段写入phone_work。合适的阈值能够区分这两种情况。在某次校准运行中,用户自述的休息时间对应的无活动时长均为27分钟及以上,而手机工作的无活动时长从未超过约26分钟,因此最终阈值设为25分钟。labels.ndjson - 注意力检查。 在 Agent 驱动的工作场景中,观察用户是在 Agent 完成任务后几秒内回复(说明用户一直在关注),还是过了很久才回复(说明用户当时不在)。如果回复通常出现在 Agent 完成任务后约16分钟,且几乎从未在2分钟内回复,说明无活动时段并非专注等待,因此不应拉长阈值来覆盖这类时段。
What to ask for
需要索要的内容
Ranked by value:
- Laptop activity. Send to an agent on their Mac. It uploads browser histories,
references/mac-upload-prompt.md(screen on, foreground app) and the lock/power logs. This is the strongest signal.knowledgeC.db - Chat exports with the people they work with (WhatsApp "Export chat").
- Usernames for leisure that has an API (chess.com, lichess).
- Recurring meetings: day, time, length, and when they changed.
- Slow exports, if they want more precision: Google Takeout (YouTube and Maps), Apple Health (sleep). iPhone Screen Time only reaches the Mac if "Share Across Devices" is on, and even then it sits in system stores that need Full Disk Access.
- Vacations and other periods to exclude, as config .
exclude
按价值排序:
- 笔记本电脑活动数据。 将 发送给用户 Mac 上的 Agent。它会上传浏览历史、
references/mac-upload-prompt.md(屏幕开启状态、前台应用)以及锁屏/电源日志。这是最可靠的信号。knowledgeC.db - 与工作对象的聊天导出文件(WhatsApp 的「导出聊天」功能)。
- 有开放API的休闲平台用户名(chess.com、lichess)。
- 周期性会议: 日期、时间、时长,以及变更时间。
- 如果需要更高精度,可索要导出较慢的数据: Google Takeout(YouTube 和地图数据)、Apple 健康(睡眠数据)。只有开启「跨设备共享」时,iPhone 屏幕时间数据才会同步到 Mac,即便如此,这些数据仍存储在需要「完全磁盘访问权限」的系统存储中。
- 需要排除的假期及其他时段,配置在 字段中。
exclude
Reporting
结果报告
Lead with the combined hours per weekday and weekend day, then the range across methods and thresholds.
Then cover the per-customer split, the clock-time routine (start, meal dips, second shift, end) and how much
leisure was found inside "work". Name what is still invisible: phone screen time, unrecorded calls,
thinking away from the keyboard.
报告首先展示工作日和周末的总工作时长,再说明不同方法和阈值下的结果波动范围。随后涵盖各客户的时长拆分、每日作息规律(开始时间、用餐时段时长下降、第二班次、结束时间),以及「工作时间」中发现的休闲时长占比。同时需说明仍无法统计的内容:手机屏幕时间、未录制的通话、离开键盘时的思考时间。