关于 time-lens
time-lens 是面向开发者的项目工时追踪技能,可汇总多个代码库和编码工具的活动以估算工时。它整合 Git、WakaTime、Claude Code、Codex CLI 和 Cursor 数据,并指导生成 HTML 与 Markdown 报告。
主要功能
- 01收集 Git 会话数据
- 02获取 WakaTime 项目活动
- 03读取 Claude Code、Codex CLI 和 Cursor 活动
- 04整合数据并估算工时
- 05区分监督与无人值守时间
- 06生成 HTML 和 Markdown 报告
适用场景
- 01估算多代码库项目的工时
- 02对照编码 Agent 活动、提交记录和 IDE 使用
- 03查看每日会话与监督时长
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ vladmdgolam/ agent-skills --skill time-lens安装后在 Claude Code 中输入/time-lens确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/data-sources.mdfolder-move-detection.mdreconciliation.mdscripts/claude_messages.pycodex_messages.pycursor_messages.pygit_sessions.pyreconcile.pywakatime_fetch.py详细介绍
项目时间跟踪器
整合五种数据源 → 对账 → 生成 HTML 仪表板和 Markdown 报告。
脚本
所有脚本都位于与此 SKILL.md 同级的 scripts/ 目录中。使用 python3 运行:
| 脚本 | 用途 | 主要参数 |
|---|---|---|
git_sessions.py | 解析 Git 历史记录 → 会话 → 小时数 | <repo> --since YYYY-MM-DD --until YYYY-MM-DD [--raw] |
wakatime_fetch.py | WakaTime API → 每日小时数,筛选指定项目 | --start YYYY-MM-DD --end YYYY-MM-DD --project name |
claude_messages.py | 按天统计 Claude Code 用户提示词和活动时间戳 | --project-path /abs/path 或 --filter name |
codex_messages.py | 按天统计 Codex CLI 用户提示词和活动时间戳 | --project-path /abs/path 或 --filter name |
cursor_messages.py | 按天统计 Cursor IDE 用户提示词和活动时间戳 | --project-path /abs/path 或 --filter name |
reconcile.py | 将上述脚本输出的 JSON 合并为单一工时估算,并可选择区分受监督与无人值守的工作时长 | --git f.json --claude f.json --codex f.json --since Y-M-D --until Y-M-D [--gap-hours 0.5] [--presence-window-min 10] [--tz Area/City] [--daily] |
claude_messages.py、codex_messages.py 和 cursor_messages.py 各自输出两个时间戳数组:timestamps(仅包含用户提示词,用于提示词计数指标)和 activity_timestamps(包含每个带时间戳的事件,包括工具调用/工具结果循环和助手回复;对账时请使用此数组)。有关这一区别为何重要,请参阅 references/reconciliation.md 中的“使用 activity_timestamps,而非 timestamps”:使用仅包含提示词的时间戳对账工时时,会悄无声息地漏掉实际工作时间。
请使用 reconcile.py,不要在 Python 中手动编写合并逻辑。 它是对下方算法的确定性实现——请在其他脚本已生成的 JSON 文件上运行它,而不是每次都重新推导合并/会话化/求交逻辑。临时手动编写这段逻辑,正是之前出现过一次真实错误的原因:将两次使用不同间隔阈值(15 分钟和 30 分钟“引擎时间”)的运行结果当作同一基准进行比较,得出了毫无意义的数字。reconcile.py 将间隔阈值和在场窗口设为明确的参数,确保每次运行都可复现、可比较。
工作流
1. 确定范围
询问或推断:
- 项目目录(可多个,均为 Git 仓库)
- 日期范围(首次提交 → 最后一次提交,或用户指定)
- HTML 和 Markdown 文件的输出位置
自动发现子仓库: 默认情况下,扫描项目目录,在子目录中查找 .git 文件夹(不只扫描根目录)。每个 .git 目录的上级目录都是待分析的子仓库。
# Find all git repos under the project directory
find /path/to/project -name ".git" -type d 2>/dev/null | sort
输出结果如下所示:
/path/to/project/frontend/.git
/path/to/project/backend/.git
/path/to/project/libs/shared/.git
对其中每个目录(去掉 /.git 后),运行 git_sessions.py、claude_messages.py 和 codex_messages.py。此外,也要对项目根目录本身运行这些脚本(用于统计从根目录发送的 Claude/Codex 消息;使用单体仓库式工作流时,这很常见)。
2. 提取数据
对每个发现的仓库运行全部五个脚本。对于 git、Claude、Codex 和 Cursor,按子仓库分别运行。对于 WakaTime,使用下文所述的多项目发现方法。
# Git sessions — run per sub-repo
python3 git_sessions.py /path/to/project/frontend --since 2026-01-15 --until 2026-02-02
python3 git_sessions.py /path/to/project/backend --since 2026-01-15 --until 2026-02-02
# Claude Code — run per sub-repo AND the root directory
python3 claude_messages.py --project-path /path/to/project
python3 claude_messages.py --project-path /path/to/project/frontend
python3 claude_messages.py --project-path /path/to/project/backend
# Codex CLI — same as Claude
python3 codex_messages.py --project-path /path/to/project
python3 codex_messages.py --project-path /path/to/project/frontend
python3 codex_messages.py --project-path /path/to/project/backend
# Cursor IDE — same as Claude/Codex
python3 cursor_messages.py --project-path /path/to/project
python3 cursor_messages.py --project-path /path/to/project/frontend
python3 cursor_messages.py --project-path /path/to/project/backend
WakaTime 多项目发现: WakaTime 通常会将子目录作为独立项目进行跟踪(例如,位于 my-project/ 的 monorepo 可能包含名为 my-project、frontend、backend 和 shared 的 WakaTime 项目)。单次 --project 查询会遗漏其他项目。
-
首先运行
wakatime_fetch.py,不带--project,获取该日期范围的完整项目列表:python3 wakatime_fetch.py --start 2026-01-15 --end 2026-02-02 # Returns: { "projects": [{"project": "my-project", "hours": 9.2}, {"project": "frontend", "hours": 5.1}, ...] } -
筛选返回的
projects列表,保留名称符合以下任一条件的项目:- 根项目目录的基名(例如
my-project) - 任一子仓库目录的基名(例如
frontend、backend) - 包含子仓库的任一中间目录的基名(例如
libs)
- 根项目目录的基名(例如
-
获取每个匹配项目的时间段:
python3 wakatime_fetch.py --start 2026-01-15 --end 2026-02-02 --project my-project python3 wakatime_fetch.py --start 2026-01-15 --end 2026-02-02 --project frontend python3 wakatime_fetch.py --start 2026-01-15 --end 2026-02-02 --project backend -
将所有匹配的 WakaTime 项目的时间段合并到一个列表中,以供对账。
这样做很重要: 在一个包含 4 个子仓库的项目中,单次 --project 查询只获取了实际 26.5 小时 WakaTime 数据中的 9 小时。其余 17.5 小时记录在子目录项目名称下。
文件夹移动检测: 如果 claude_messages.py、codex_messages.py 或 cursor_messages.py 返回 0 条结果,请检查输出中是否有 alternate_paths。如果有,请询问用户:
“在
/current/path未找到 Claude/Codex 历史记录,但发现project-name在/old/path有会话记录。这个项目是否已移动?是否应该一并纳入这些历史记录?”
如果用户确认,请使用 --project-path /old/path 重新运行,并合并两个路径中的时间戳。
完整检测逻辑和边缘情况请参阅 references/folder-move-detection.md。
3. 核算时长
合并总时长 = 最佳估算值(git ∪ Claude ∪ Codex ∪ Cursor ∪ WakaTime 区间,不重复计数)。请对第 2 步中的 JSON 文件运行 reconcile.py,不要手动合并:
python3 reconcile.py \
--git git_backend.json --git git_frontend.json \
--claude claude_backend.json --claude claude_frontend.json \
--codex codex_backend.json --codex codex_frontend.json \
--cursor cursor_backend.json \
--waka waka.json \
--since 2026-06-29 --until 2026-08-19 \
--tz Europe/Berlin --daily
(--git/--claude/--codex/--cursor/--waka 都可以重复传入——对于多仓库项目,每个子仓库传入一个;reconcile.py 会将它们合并。)
该脚本会确定性地执行以下操作:
- 将 git 的原始提交
timestamps,以及 Claude/Codex/Cursor 的activity_timestamps(不是timestamps——见下文)作为时间点事件。 - 根据间隔阈值分别对每个来源进行会话划分(
--gap-hours,默认 0.5h;如需更严格、偏保守的估算,可使用 0.25)。 - 将 WakaTime 已生成的
[start, end]区间与划分好的时间点来源合并。 - 将所有重叠或间隔不超过阈值的内容合并为最终会话。
- 累加原始时长——不加
+0.5h缓冲,也不设0.5h下限(这是旧版行为;如需使用,例如针对没有活动级粒度、仅有timestamps的来源,请参阅 reconciliation.md)。 - 如果
--presence-window-min > 0(默认 10),还会计算有人监督与无人值守时长的拆分——合并后的引擎运行时长中,有多少落在实际用户提示词或 git 提交时间前后的这个时间窗口内,又有多少是 Agent 在附近没有人工确认的情况下运行。请参阅下文“有人监督与无人值守时长”。 - 如果指定
--daily,则使用当地时间 08:00 作为界限(--day-boundary-hour,默认 8)按天归类,避免跨夜会话在表格中被拆分到两天。
核算引擎运行时长时,务必使用 activity_timestamps,而非 timestamps(reconcile.py 会自动执行此操作)。timestamps 仅记录提示词;如果 Agent 在最后一条已记录提示词之后仍持续运行,就会让仍在活跃工作的会话看起来像一个时长为 0 的单点事件。activity_timestamps 包含工具调用/工具结果周期和助手回复,因此能反映 Agent 实际仍在工作的时间。具体示例(一个看似“时长为 0”的会话,实际连续工作了约 50 分钟)以及间隔阈值为何从旧默认值 1.5h 降至 0.5h,请参阅 references/reconciliation.md。
不要把不同 --gap-hours 运行结果中的 engine_hours 当作同一种度量进行比较——阈值为 15 分钟的总时长与阈值为 30 分钟的总时长是两种不同的量。如需同时获得两者,请运行两次 reconcile.py,并在每个结果中标明所用阈值。
有监督与无人值守时间
engine_hours 衡量的是 Agent 处于活动状态的时间,不一定代表有人类在旁监督。如果任务自主运行了 20 分钟以上,期间没有收到新提示词,这段时间仍会计入引擎时间,但未必能反映实际的亲自操作时间。reconcile.py 的有监督时间拆分功能会处理这个问题:它会围绕每条真实的用户提示词或 git 提交,构建一个前后各为 --presence-window-min 分钟的“在线窗口”,并报告 engine_hours 中有多少时间在窗口内、多少在窗口外。
窗口大小是一个会影响结果的参数,并非已确定的固定值:在一个实际项目中,±5 分钟时有监督时间占 72%,±30 分钟时占 99%。推荐的默认值是 ±10 分钟:时间足够长,可以涵盖正常的“阅读输出、思考、回复”间隔;也足够短,能让真正离开键盘的时段显示为无人值守。请始终在报告数字的同时注明所用的窗口大小。
为何合并很重要:AI Agent 提示词(Claude/Codex/Cursor)常会在同一工作时段的 git 提交前后几分钟出现。WakaTime 捕获的 IDE 击键可能发生在两次提交之间。用户可能先用 Claude 调研,再使用 Cursor 的 AI,然后编写代码(WakaTime),最后提交(git)——这些都属于同一时段。合并所有五种来源,可以捕获真实的时段边界,同时避免重复计数。
- 合并后的总时长取代“仅 git”总时长,作为主要估算值
- 显示 WakaTime 小时数供参考(仅统计活动击键,始终更低)
完整的底层算法说明(由 reconcile.py 实现)、按天分桶的依据,以及旧版缓冲区/下限公式,请参阅 references/reconciliation.md。
4. 生成 HTML 仪表板
生成单文件 HTML,内嵌 Chart.js(CDN)。使用深色主题(背景为 #0a0a0a,卡片为 #1a1a1a)。
必需部分:
- 统计卡片 — 合并总时长(git∪claude∪codex∪cursor∪waka)、Git 估算值、WakaTime、时段数、提交数、Claude 提示词数、Codex 提示词数、Cursor 提示词数
- 每日活动图表 — 重叠柱形图(git + WakaTime + 合并时长)+ 次坐标轴上的 AI 提示词折线
- 甘特时间线 — UTC 水平条形图;git、Claude、Codex 和 Cursor 分别使用不同颜色的数据集,显示在同一图表上(同一天内有重叠时,使用不同的泳道行)
- 数据表 — 时段 | 时间(UTC) | 活动时长 | 估算时长 | WakaTime | Claude | Codex | Cursor | 提交数
Chart.js 要点:
Chart.defaults.color = '#888';
Chart.defaults.borderColor = '#2a2a2a';
// Overlapping bars: same barPercentage/categoryPercentage on both datasets, different opacity
// Mixed chart: type:'bar' on container, each dataset has its own type + yAxisID
// Gantt floating bars: data: [[startH, endH]], indexAxis: 'y'
// Claude as line on secondary axis:
{
type: 'line',
yAxisID: 'yClaude',
// right-side axis, max ~100, different color
}
// All charts: responsive: true, maintainAspectRatio: false
保存为 <project-dir>/work-hours-analysis.html。
5. 生成 Markdown 报告
# [Project] - Work Hours Analysis
## Summary
**Estimated Total Working Hours: Xh** (based on commit timing analysis)
**Supervised: Xh (Y%) · Unattended: Xh** (±10min presence window — see Methodology)
| Date | Sessions | Time Range | Git Est. | WakaTime | Commits | Project |
...
## Timeline
- Start: [date], End: [date], Duration: N days
## Per-Project Breakdown
[sub-project sections with commit/session counts]
## Charts
### Daily Activity (ASCII)
Jan 15 ██░░░░░░░░░░░░░░░░░░ 0.5h
...
### Project Distribution
project-a ████████████████████ 45% (~18h)
...
## Methodology
- Session Detection: events within 0.5h gap = same session (git commits + Claude/Codex/Cursor activity_timestamps, not just prompts)
- Hour Estimate: Σ(session_duration), no buffer, no floor — activity_timestamps already captures real start/end
- Supervised vs. Unattended: hours within ±10min of a real user prompt/commit count as supervised; the rest is the agent running with no recent human check-in
- Why Git > WakaTime: WakaTime only tracks active IDE typing; git includes thinking/research/AI prompting
ASCII 条形图:blocks = round(hours / max_hours * 20),█ 表示填充,░ 表示空白,宽度为 20 列。
保存为 <project-dir>/total_hours.md。
验证清单
运行前
- WakaTime API 密钥 —
~/.wakatime.cfg存在,且[settings]下包含api_key = waka_...。运行cat ~/.wakatime.cfg进行验证。如果缺少此密钥,WakaTime 脚本会静默失败或报出身份验证错误。 - 可访问 Git 仓库 — 项目目录是一个包含提交记录的 git 仓库(运行
git log --oneline -5 /path/to/repo会返回结果)。如果不是,git_sessions.py将返回 0 个时段。 - 存在 Claude 历史记录 — 存在且非空的
~/.claude/history.jsonl,或者~/.claude/projects/包含该项目的会话文件。如果两者都缺失,Claude 小时数将为 0。 - 可访问 Cursor 数据 — Cursor 状态数据库存在于平台对应的路径下(macOS:
~/Library/Application Support/Cursor/User/globalStorage/state.vscdb,Windows:%APPDATA%\Cursor\User\globalStorage\state.vscdb,Linux:~/.config/Cursor/User/globalStorage/state.vscdb)。如果缺失,Cursor 小时数将为 0。脚本会自动检测平台,并以只读模式读取 SQLite 数据库。 - 日期范围有效 —
--since早于--until;日期范围涵盖了实际发生工作的日期。
生成后
- HTML 无错误加载 — 在浏览器中打开
work-hours-analysis.html;所有图表均已渲染;JS 控制台中没有错误。 - 总时长一致 — HTML 中的“合并总时长”统计卡片与
total_hours.md中的“预计总工作时长”一致(允许因四舍五入产生 ±0.01h 的误差)。 - 日期范围与输入一致 — 数据表和时间线部分中的首尾日期与请求的
--since/--until值一致。 - 会话数不为零 — 至少有一个来源提供了会话数据。如果所有来源都返回 0,则说明有问题(路径错误、项目名称错误,或日期范围不在项目历史记录内)。
示例
示例 1:单个代码仓库,标准报告
触发语句:“我这个月在 api-server 项目上花了多少小时?生成完整报告。”
操作:
-
确定范围:项目位于
/Users/alice/code/api-server,日期范围推断为 2026-02-01 → 2026-02-23(本月至今)。 -
提取数据:
python3 git_sessions.py /Users/alice/code/api-server --since 2026-02-01 --until 2026-02-23 python3 wakatime_fetch.py --start 2026-02-01 --end 2026-02-23 --project api-server python3 claude_messages.py --project-path /Users/alice/code/api-server python3 codex_messages.py --project-path /Users/alice/code/api-server python3 cursor_messages.py --project-path /Users/alice/code/api-server -
对账:
git_sessions.py返回 14 个会话(22.5h),WakaTime 返回 11.2h,Claude 返回 9 天内的 47 条提示词,Codex 返回 0 条,Cursor 返回 3 天内的 12 条提示词。合并并去重、合并间隔后,总时长为:27.1h。 -
在
/Users/alice/code/api-server/中生成work-hours-analysis.html和total_hours.md。
结果:“你在 2026 年 2 月于 api-server 项目上花费了约 27.1 小时(14 个 git 会话、47 条 Claude 提示词、12 条 Cursor 提示词;WakaTime 参考值:11.2h 的活跃打字时间)。报告已保存至 /Users/alice/code/api-server/work-hours-analysis.html。”
示例 2:包含文件夹迁移的多代码仓库项目
触发语句:“计算 marketplace 项目的开发总时长——它有一个前端代码仓库和一个后端代码仓库。另外我记得我曾经重命名过文件夹。”
操作:
-
确定范围:两个代码仓库分别位于
/Users/bob/marketplace-backend和/Users/bob/marketplace-frontend,用户指定日期范围为 2025-11-01 → 2026-01-31。 -
提取数据:
python3 git_sessions.py /Users/bob/marketplace-backend --since 2025-11-01 --until 2026-01-31 python3 git_sessions.py /Users/bob/marketplace-frontend --since 2025-11-01 --until 2026-01-31 python3 wakatime_fetch.py --start 2025-11-01 --end 2026-01-31 --project marketplace python3 claude_messages.py --project-path /Users/bob/marketplace-backend python3 codex_messages.py --project-path /Users/bob/marketplace-backend python3 cursor_messages.py --project-path /Users/bob/marketplace-backend -
claude_messages.py返回 0 条结果,并附带alternate_paths: ["/Users/bob/old-market/backend"]。 -
询问用户:“在
/Users/bob/marketplace-backend中没有找到 Claude 历史记录,但在/Users/bob/old-market/backend中找到了backend的会话。这是之前的位置吗?要包含那里的历史记录吗?” -
用户确认。重新运行:
python3 claude_messages.py --project-path /Users/bob/old-market/backend。合并两次运行的时间戳。 -
合并两个代码仓库(后端 + 前端)的 git 提交时间戳与 Claude 的
activity_timestamps,排序后,在 0.5h 间隔阈值内重新合并。与所有来源的数据进行对账。
结果:“预计总时长:84.7h,跨越 3 个月(后端 + 前端合计,包括旧路径 /Users/bob/old-market/backend 中的 Claude 历史记录)。”
故障排除
WakaTime API 密钥缺失或无效
症状:wakatime_fetch.py 因身份验证错误、HTTP 401 或 KeyError: 'api_key' 而退出。
解决方法:
- 检查配置文件是否存在:
cat ~/.wakatime.cfg - 如果不存在,请创建:
[settings] api_key = waka_xxxx... - 从 wakatime.com/settings/api-key 获取密钥。
- 如果密钥存在但返回 401,可能已过期或被撤销——请生成新密钥。
另请检查:--project 标志与 WakaTime 记录的项目名称完全一致(区分大小写)。你可以在 WakaTime 控制面板的 Projects 页面中确认项目名称。
Git 返回 0 个会话
症状:git_sessions.py 返回 "sessions": [] 或 "total_hours": 0。
可能原因及解决方法:
| 原因 | 解决方法 |
|---|---|
| 日期范围不在项目历史记录内 | 使用 git log --oneline 检查实际日期范围;调整 --since/--until |
| 路径不是 git 代码仓库 | 使用 git -C /path/to/repo log --oneline -1 验证 |
| 当前用户在该日期范围内没有提交 | 如果按作者筛选,请传入 --author 标志;否则移除该标志 |
| 浅克隆 | 运行 git fetch --unshallow 以恢复完整历史记录 |
Claude、Codex 或 Cursor 返回 0 个会话(没有 alternate_paths)
症状:timestamps: [] 和 alternate_paths: [] 均为空。
可能原因及解决方法:
| 原因 | 解决方法 |
|---|---|
| 未在此项目中使用该工具 | 这是预期情况——在报告中注明 |
--project-path 错误(拼写错误、符号链接、末尾斜杠) | 使用 realpath /path/to/repo 获取规范的绝对路径,并传入该路径 |
| 历史记录文件不存在 | 检查 ~/.claude/history.jsonl 和 ~/.claude/projects/ 是否存在;检查 ~/.codex/sessions/ 是否存在;检查 Cursor 的 state.vscdb 是否存在于平台对应的路径中(参见 Validation Checklist) |
| 项目路径使用的符号链接解析结果不同 | 使用解析后的路径:python3 -c "import os; print(os.path.realpath('/your/path'))" |
| 运行中的 Cursor 实例锁定了数据库 | 脚本以只读模式打开数据库——通常不应发生这种情况,但如果发生,请尝试暂时关闭 Cursor |
找到了 alternate_paths,但用户表示路径不正确
**症状:**脚本报告了备用路径,但用户表示其中没有一个是旧项目位置。
**解决方法:**改用 --filter <project-name>,它会匹配目录名称中的子字符串,而不是精确匹配路径。请注意,这可能会找到名称相似但无关的项目——合并前与用户一起检查会话列表。
python3 claude_messages.py --filter marketplace
HTML 图表显示空白或 NaN
**症状:**HTML 文件可以打开,但图表为空或显示“NaN”值。
可能原因:
- 对账产生了
null或None值,这些值被序列化到 JS 数据数组中。 - 写入 HTML 前未将时间戳转换为 UTC(本地 epoch 与 UTC epoch 不匹配)。
- Chart.js CDN 加载失败(离线环境)。
解决方法:
- 打开浏览器 DevTools 控制台——具体的 JS 错误会指出问题所在。
- 确认所有 epoch 时间戳都是 UTC 浮点数,而不是字符串。
- 对于离线环境,下载 Chart.js 并将其内嵌:
<script>/* chart.js source */</script>。
HTML 与 Markdown 中的总时长不一致
**症状:**HTML 统计卡片显示的合并总时长与 total_hours.md 不同。
**原因:**分别独立运行了两次对账,结果略有不同(例如,由于浮点数舍入,或其中一个文件使用了过期数据)。
**解决方法:**只运行一次对账,将结果存储在变量中,并将相同的计算值写入两个输出文件。不要为每个输出文件分别重新计算。
备注
**WakaTime 身份验证和配置:**脚本会自动读取 ~/.wakatime.cfg。对于特定项目的数据,始终传入 --project。WakaTime 只跟踪活跃击键——时长始终低于 git 估算值。大量使用 Claude Code 会导致 WakaTime 数据与实际投入之间出现较大差距。有关完整的身份验证设置、配置格式和限制,请参阅 references/data-sources.md。
Codex CLI 数据源:codex_messages.py 会读取 ~/.codex/sessions/YYYY/MM/DD/rollup-*.jsonl。每个文件包含 session_meta(其中有 payload.cwd 和 payload.id——对于长期运行或恢复的会话,这些信息可能在文件中途再次出现,因此会持续跟踪 cwd,而不是假定它固定不变)、用于表示实际提示词的 event_msg 条目(payload.type == "user_message"),以及许多其他带时间戳的 response_item/event_msg 条目(工具调用、补丁、推理),用于标记 Agent 在提示词之间的执行活动。输出包括 timestamps(仅提示词)、activity_timestamps(每个带时间戳的事件——用于对账)和用于检测文件夹移动的 alternate_paths。有关完整的文件结构,请参阅 references/data-sources.md。
Claude Code 数据源: claude_messages.py 使用两个数据源:~/.claude/history.jsonl(主要数据源;project 字段 = 绝对路径,timestamp 以毫秒为单位,仅包含提示词)和 ~/.claude/projects/<encoded>/*.jsonl(会话文件;包含 cwd 字段、type=="user" 和 type=="assistant" 条目,以及 ISO 时间戳)。编码后的目录名格式:/Users/foo/bar → -Users-foo-bar。始终优先使用 --project-path,而非 --filter。输出包含 timestamps(仅真实用户提示词)和 activity_timestamps(用户提示词 + tool_result 条目 + assistant 条目——使用此字段进行对账)。完整字段参考请见 references/data-sources.md。
Cursor IDE 数据源: cursor_messages.py 从 Cursor 的 SQLite 数据库(.vscdb 文件)读取数据。主要数据源是平台专属的 state.vscdb(macOS:~/Library/Application Support/Cursor/User/globalStorage/,Windows:%APPDATA%\Cursor\User\globalStorage\,Linux:~/.config/Cursor/User/globalStorage/)——cursorDiskKV 表包含 composerData:{sessionId} 条目(带有用于项目匹配的 workspaceUri)和 bubbleId:{sessionId}:{messageId} 条目(包含每条消息的时间戳,type=1 = 用户)。备用数据源是 workspaceStorage/*/ 下工作区级别的 state.vscdb 文件,其中 workspace.json 将每个工作区映射到其项目文件夹。数据库以只读方式打开。脚本会自动检测平台。输出格式与 Claude/Codex 脚本一致:timestamps(仅用户气泡)+ activity_timestamps(所有气泡/消息,无论角色——使用此字段进行对账)+ 用于检测文件夹移动的 alternate_paths。完整字段参考请见 references/data-sources.md。
对账算法(2026-08-19 更新): 使用 reconcile.py 运行,不要手动编写。间隔阈值默认为 0.5h(更严格的估算使用 0.25h),适用于 Claude/Codex/Cursor 的 activity_timestamps(而非 timestamps),以及 git 的原始提交 timestamps。所有数据源都会转换为 UTC epoch 浮点时间间隔。若间隔 ≤ 阈值,则合并时间间隔。每个时间间隔的估算值为原始时长(end - start),不加缓冲,也不设下限。旧的 1.5h 阈值 + max(duration + 0.5h, 0.5h) 公式已弃用——它用于补偿基于仅包含提示词的时间戳进行对账所造成的低估;只要 Agent 在最后一条记录的提示词之后仍继续执行,就会低估实际工作量(已在真实项目中证实:一个看似孤立的提示词在接下来 50 分钟内触发了 1,343 个 Agent 执行事件,仅凭 timestamps 无法看到这些事件)。对于缺少活动级粒度的数据源,该功能仍可通过 git_sessions.py 的默认(非 --raw)模式使用。reconcile.py 默认还会计算受监督与无人值守的时间划分(--presence-window-min,默认值为 10)——请参见上方工作流部分中的“受监督与无人值守时间”。完整底层伪代码、会话检测辅助函数,以及用于每日明细的按日边界分组辅助函数,请见 references/reconciliation.md。
文件夹移动检测: 当提供的路径下未找到结果时,claude_messages.py、codex_messages.py 和 cursor_messages.py 都会扫描已知历史记录,查找名称匹配的项目。返回 alternate_paths 列表。如果列表非空,请询问用户确认,然后使用旧路径重新运行并合并时间戳。检测逻辑和边缘情况请见 references/folder-move-detection.md。
多仓库项目: 默认情况下,扫描 .git 子目录以自动发现所有子仓库。在每个子仓库和根目录上运行 git_sessions.py、claude_messages.py、codex_messages.py 和 cursor_messages.py。使用 WakaTime 多项目发现功能查找所有匹配的 WakaTime 项目名称。合并所有会话数组,按日期重新排序,并重新计算每日总计和总计数。