@midaspenn/timelog
v1.0.0
Published
个人时间账本系统
Maintainers
Readme
timelog — 个人时间账本系统 (PDL)
一、项目简介
timelog 是一个本地命令行时间记录工具,用于追踪"你现在主要在做什么",并在一天结束时生成可读的时间分布报表。
主要用途:为 AI 提供时间上下文数据。 通过让 AI 助手调用 timelog 的命令,AI 可以随时查询你当前在做什么、今天的时间分布如何、哪些时间段未被记录——从而给出更贴合你实际状态的建议和回应。人工使用同样完全支持。
它不是什么:
- 不是番茄钟,不要求你每隔几分钟就点一次
- 不是全量流水账,不追踪每一秒的去向
- 不是任务管理器,不关心 TODO / Done 的状态
它是什么:
- 稀疏的重要状态记录器:只在状态切换时记录
- 分层时间区间模型:主状态(一心一意)+ 上下文(并行背景)分开记录
- 事后补记友好:支持用
--at指定历史时间点,或用primary add补录整段历史区间 - 报表驱动反馈:每天/每周自动推导覆盖率、间隙、专注分布
- AI 友好接口:输出为纯文本 + 非零退出码,非 TTY 环境自动关闭颜色,方便 AI 工具直接调用和解析
二、核心对象模型
Primary Block(主状态)
你在某段时间"主要在做的事"。任意时刻最多只有一个进行中的主状态,两条记录的时间区间绝对不能重叠。
每条主状态有:
- 名称(如"写架构文档")
- 分类:
工作/学习/休息/生活维护/杂事 - 任务属性:
目标任务/支撑任务/固定开销/休息恢复/漂移/分心 - 备注(可选)
- 开始/结束时间(UTC ISO-8601,结束为空表示进行中)
Context Block(上下文状态)
与主状态并行存在的背景状态,如"听音乐""在通勤"。允许多个并发,互相之间可以重叠。
每条上下文有:
- 名称
- 类型:
思考/移动/音频/背景事务
Routine Template(弹性开销模板)
静态配置规则,描述"某项固定活动每天应该花多少时间"。报表生成时用于对比实际时长并给出偏差反馈。
Derived Gap(推导间隙)
查询时实时推导,永不持久化。从查询窗口中减去所有主状态后剩余的空白区间,代表"未被记录的时间"。
三、安装与运行
依赖要求: Node.js 18+(推荐 22),npm
# 克隆/进入项目目录后:
npm install # 安装依赖,同时自动触发 prepare 脚本完成 TypeScript 构建npm install 会自动运行 npm run build(通过 prepare 钩子),首次安装后即可使用。
运行方式
项目有三种执行方式,适用场景不同:
1. 开发模式 — 改了源码立即生效,无需手动构建
npm run dev -- <命令> [参数]
npm run dev -- primary start "写代码" "工作" "目标任务"2. 本地构建后运行 — 开发验收推荐,本文档所有示例均使用此形式
node cli.js <命令> [参数]
node cli.js primary start "写代码" "工作" "目标任务"cli.js 是项目根目录的入口 shim,始终转发到 dist/cli.js。这是最稳妥、最不依赖环境的本地调用方式——只要在项目目录下执行,不需要任何额外的 PATH 配置。文档里统一写 node cli.js 就是这个原因。
3. 安装到 PATH 后使用 timelog 命令
package.json 中声明了 "bin": { "timelog": "./cli.js" },这只是注册了可执行入口的名称,并不意味着当前 shell 里直接输入 timelog 就能生效。需要先显式安装:
# 开发期:在当前 Node 全局 bin 目录创建符号链接
npm link
# 或永久全局安装
npm install -g .安装后可在任意目录使用:
timelog primary start "写代码" "工作" "目标任务"如果未做上述安装,在项目目录里直接输入
timelog会报"命令未找到",用node cli.js即可。
4. npx timelog — 不想全局安装、又想省略 node cli.js 前缀时的折中
在项目目录下,npm/npx 能解析到当前包自身的 bin 入口,因此无需 npm link 也可以用:
npx timelog primary start "写代码" "工作" "目标任务"适合偶尔使用或快速验证,不需要修改全局环境。对日常开发和验收,node cli.js 仍然更直接;若在项目目录之外运行且 timelog 不在 PATH 里,npx 会转而从 npm registry 拉取(可能与本地版本不符),此时应改用 node cli.js 或先执行 npm link。
shim 行为: 若 dist/ 不存在(未构建),cli.js 会打印明确提示而不是报神秘错误:
错误: dist/cli.js 不存在,请先构建项目:
npm run build
或直接以开发模式运行(无需构建):
npm run dev -- <命令>四、数据存储位置
所有数据保存在本地 SQLite 文件。
默认按平台使用各自的应用数据目录:
- Linux:
$XDG_DATA_HOME/timelog/timelog.sqlite- 若未设置
XDG_DATA_HOME,回落到~/.local/share/timelog/timelog.sqlite
- 若未设置
- macOS:
~/Library/Application Support/timelog/timelog.sqlite - Windows:
%LOCALAPPDATA%\timelog\timelog.sqlite- 若未设置
LOCALAPPDATA,回落到%USERPROFILE%\AppData\Local\timelog\timelog.sqlite
- 若未设置
也可以通过环境变量覆盖默认位置:
TIMELOG_DB=/your/path/timelog.sqlite node cli.js primary current这是纯本地持久化,不涉及任何云同步或网络请求。Linux 下重置数据库示例:
rm -f ~/.local/share/timelog/timelog.sqlite
# 下次运行时自动重建表结构五、命令总览
下面的树形图用 timelog 表示命令入口,实际执行时请按你的环境替换为 node cli.js(本地开发)、timelog(已安装到 PATH)或 npx timelog(项目目录内免安装调用)。
timelog
├── primary 管理主状态(互斥,最多一个进行中)
│ ├── start 开始新主状态(如有进行中则先关闭)
│ ├── switch 切换到新主状态(同 start,语义更清晰)
│ ├── stop 结束当前进行中的主状态
│ ├── current 查看当前状态
│ ├── add 补记历史区间
│ └── list 列出最近 20 条记录
│
├── context 管理上下文状态(可并发,互不干扰)
│ ├── start 开启新的上下文
│ ├── stop 结束上下文(按 ID 或 --name --latest)
│ └── list 列出上下文记录
│
├── routine 管理弹性开销模板(报表反馈用)
│ ├── add 新增模板
│ ├── list 列出所有模板
│ ├── enable 启用模板
│ ├── disable 停用模板
│ └── update 更新模板参数
│
└── report 生成报表
├── day 日报表
├── week 周报表(ISO 周,周一为起点)
└── range 自定义范围报表六、详细命令用法
所有时间参数格式:YYYY-MM-DD HH:mm,不指定时默认取当前时间。
primary start — 开始新主状态
node cli.js primary start <name> [category] [taskAttr] [remark] [--at <time>]如果当前有进行中的主状态,会原子性地关闭它再开启新的。--at 指定的时间点同时作为旧状态的结束时间和新状态的开始时间。
node cli.js primary start "写架构文档" "工作" "目标任务"
node cli.js primary start "开会" "工作" "支撑任务" "周例会" --at "2026-04-17 10:00"失败条件: --at 晚于当前时间;新区间与历史记录重叠。
primary switch — 切换主状态
与 primary start 完全相同,语义上强调"切换"。
node cli.js primary switch "代码审查" "工作" "支撑任务"primary stop — 结束当前主状态
node cli.js primary stop [--at <time>]node cli.js primary stop
node cli.js primary stop --at "2026-04-17 12:30"失败条件: 没有进行中的主状态;--at 晚于当前时间或早于开始时间。
primary current — 查看当前状态
显示当前进行中的主状态及所有进行中的上下文状态。
node cli.js primary current
# [进行中] 主状态: 写架构文档 (已持续 42 分钟)
# 分类: 工作 | 属性: 目标任务 | 备注:
# [进行中] 上下文状态:
# - [3] 听音乐 [音频] (已持续 20 分钟)primary add — 补记历史区间
node cli.js primary add <startTime> <endTime> <name> [category] [taskAttr]直接创建一条完整的历史记录(closed block),不影响当前进行中的状态。区间不能与任何已有记录重叠。
node cli.js primary add "2026-04-17 09:00" "2026-04-17 10:30" "晨间阅读" "学习" "目标任务"
node cli.js primary add "2026-04-16 23:00" "2026-04-17 01:00" "夜间工作" "工作" "目标任务"primary list — 列出近期主状态
列出最近 20 条主状态记录(按开始时间倒序)。
node cli.js primary list
# [5] 代码审查 (工作) | 04-17 14:00 -> 04-17 15:30
# [4] 写架构文档 (工作) | 04-17 10:30 -> 04-17 14:00
# ...context start — 开启上下文
node cli.js context start <name> [type] [--at <time>]type 可选值:思考 / 移动 / 音频 / 背景事务(默认:背景事务)
node cli.js context start "听音乐" "音频"
node cli.js context start "通勤" "移动" --at "2026-04-17 09:00"context stop — 结束上下文
按 ID:
node cli.js context stop 3按名称关闭最新的同名上下文:
node cli.js context stop --name "听音乐" --latest失败条件: 找不到对应 ID 或该 ID 已关闭;找不到名为该名称且进行中的上下文。
context list — 列出上下文
node cli.js context list # 列出所有(含历史)
node cli.js context list --open # 只列进行中的
# [1] 听音乐 (音频) | 04-17 10:30 -> 进行中
# [2] 听音乐 (音频) | 04-17 13:00 -> 进行中routine add — 新增弹性开销模板
node cli.js routine add <name> --category <cat> --min <分钟> --max <分钟> [--no-feedback]node cli.js routine add "睡眠" --category "生活维护" --min 390 --max 510
node cli.js routine add "运动" --category "生活维护" --min 30 --max 90 --no-feedback约束: name 不能重复;min ≥ 0;max ≥ min。
routine list / enable / disable / update
node cli.js routine list
node cli.js routine disable 1 # 停用,不再参与报表反馈
node cli.js routine enable 1 # 重新启用
node cli.js routine update 1 --min 400 --max 480 --no-feedback
node cli.js routine update 2 --name "冥想" --category "休息"update 只更新指定的字段,其余保持不变。
report day — 日报表
node cli.js report day # 今天
node cli.js report day --date 2026-04-17 # 指定日期report week — 周报表
以 ISO 周(周一为起点)为范围。
node cli.js report week # 本周
node cli.js report week --date 2026-04-14 # 该日期所在的周report range — 自定义范围报表
node cli.js report range --from "2026-04-17 00:00" --to "2026-04-17 18:00"
node cli.js report range --from "2026-04-01 00:00" --to "2026-04-30 23:59"七、报表说明
每次 report 输出 7 个部分:
=== 报表 [2026-04-17 00:00] 至 [2026-04-17 22:30] ===
生成时间: 2026-04-17 22:30:15
1. 记录覆盖摘要 — 总时长、已记录、未记录、覆盖率
2. 主要时间去向 — 按活动名称汇总,降序排列
3. 分类汇总 — 工作/学习/休息等大类分布
4. 任务属性汇总 — 目标任务/支撑任务等维度累计
5. 推导间隙 — 最大的几个未记录空白段
6. 专注反馈 — 基于目标任务、漂移占比的自动评价
7. 固定开销反馈 — 与 active 的 routine 模板对比实际时长跨天 Block 的裁剪(Clipping)
查询范围 [Q_start, Q_end] 与某条记录 [B_start, B_end] 的交集才计入报表:
实际开始 = max(Q_start, B_start)
实际结束 = min(Q_end, B_end)例:记录跨越 2026-04-16 23:00 → 2026-04-17 01:00,在 2026-04-17 日报中只计入 00:00 → 01:00 共 60 分钟。
Open Block 的处理
若报表范围覆盖当前时间,进行中的主状态会被当作虚拟闭合块处理:
虚拟结束时间 = min(当前时刻, Q_end)Derived Gap 的推导
从 [Q_start, Q_end] 出发,按时间顺序遍历所有裁剪后的主状态记录,找到块与块之间(以及首尾)的空白区间,即为 Derived Gap。报表显示其中最大的 3 个。
专注反馈规则
| 条件 | 提示 | |------|------| | 查询范围 ≥ 8 小时,目标任务 < 2 小时 | 红色提醒:目标任务偏少 | | 目标任务 ≥ 2 小时 | 绿色:投入良好 | | 漂移/分心 > 1 小时 | 红色提醒:漂移时间过多 | | 漂移/分心 ≤ 1 小时 | 绿色:控制合理 |
八、常见工作流示例
工作流 1:一天中不断切换主状态
# 早上开始工作
node cli.js primary start "晨间阅读" "学习" "目标任务"
# 切换到工作,自动关闭上一个
node cli.js primary switch "写代码" "工作" "目标任务"
# 有个会议,补记精确时间
node cli.js primary switch "开会" "工作" "支撑任务" "" --at "2026-04-17 10:00"
# 会议结束
node cli.js primary stop --at "2026-04-17 11:30"
# 继续写代码
node cli.js primary start "写代码" "工作" "目标任务"
# 下班,查看今天的日报
node cli.js report day工作流 2:同时记录上下文状态
# 开始工作
node cli.js primary start "写代码" "工作" "目标任务"
# 同时开启背景音乐
node cli.js context start "Lo-fi 音乐" "音频"
# 中途去咖啡馆,主状态不变,上下文改变
node cli.js context stop --name "Lo-fi 音乐" --latest
node cli.js context start "通勤" "移动"
# 到了咖啡馆,结束通勤
node cli.js context stop 2
# 查看当前状态
node cli.js primary current工作流 3:日终看报表 + 补录遗漏
# 发现中午有段时间忘记记录了,补录
node cli.js primary add "2026-04-17 12:00" "2026-04-17 13:00" "午饭休息" "休息" "休息恢复"
# 看今日报表
node cli.js report day --date 2026-04-17
# 看本周报表
node cli.js report week
# 看指定时段
node cli.js report range --from "2026-04-17 09:00" --to "2026-04-17 18:00"九、测试与验证
参见 TESTING.md,其中包含 5 组 smoke test:
- 主状态流转(start → switch → stop)
- 防重叠约束(overlapping add should fail)
- 上下文并发与条件关闭
- 跨天报表裁剪
- 弹性模板管理(routine lifecycle)
快速跑一遍(清库后执行):
rm -f ~/.local/share/timelog/timelog.sqlite
node cli.js primary start "写架构文档" "工作" "目标任务"
node cli.js primary switch "代码重构" "工作" "目标任务"
node cli.js primary stop
node cli.js primary list十、AI / 脚本调用注意事项
timelog 可被 AI 助手或 shell 脚本直接调用,以下行为已内置:
颜色输出
当 stdout 不是交互式终端(管道、重定向、AI 工具调用)时,ANSI 颜色码自动关闭,输出为纯文本。交互式终端下颜色保持不变。
# 纯文本输出(无 ANSI 码)
node cli.js primary list | cat
# 交互式终端下有颜色
node cli.js primary list退出码
| 退出码 | 含义 |
|--------|------|
| 0 | 命令成功 |
| 1 | 命令失败(错误信息输出到 stderr) |
失败场景包括:无效时间格式、时间在未来、区间重叠、找不到目标记录、无效参数等。
node cli.js primary add "2026-01-01 10:00" "2026-01-01 09:00" "测试"
echo $? # 输出 1时间格式
所有命令输出的时间均使用完整格式 YYYY-MM-DD HH:mm,不会出现缺少年份的 MM-DD HH:mm,方便跨天、跨年场景下的准确解析。
十一、当前边界 / 已知限制
| 项目 | 说明 |
|------|------|
| Routine 匹配策略 | 报表中 Routine 反馈按活动名称匹配(routine.name == block.name),这是临时策略,不够灵活 |
| 无多端同步 | 数据存储在本地平台默认应用数据目录(Linux: $XDG_DATA_HOME/timelog/timelog.sqlite;macOS: ~/Library/Application Support/timelog/timelog.sqlite;Windows: %LOCALAPPDATA%\\timelog\\timelog.sqlite),不涉及云同步或跨设备访问 |
| CLI 为唯一入口 | 目前只有命令行界面,无 Web UI 或 API |
| Context Block 无报表聚合 | 上下文状态目前不参与报表时长统计,仅作状态查询 |
| task_attr 不做枚举校验 | 命令行输入的任务属性值不会被校验是否合法,拼写错误会静默写入 |
| 时区 | 内部全程使用本地时间 + UTC ISO-8601 存储,跨时区场景未测试 |
