在终端中高效管理纯文本待办事项,支持任务增删改查、标签、优先级、截止日期、重复任务、番茄钟和批量操作。实现 xit! 格式规范 v1.1,零第三方依赖,仅用 Rust 标准库,产物为单个可执行文件,支持 JSON 输出和 Agent Skill。【此简介由AI生成】
| Files | Last commit | Last update |
|---|---|---|
| 27 days ago | ||
| 27 days ago | ||
| 27 days ago | ||
| 27 days ago | ||
| 27 days ago | ||
| 27 days ago | ||
| 27 days ago | ||
| 27 days ago | ||
| 27 days ago | ||
| 27 days ago | ||
| 27 days ago | ||
| 27 days ago | ||
| 27 days ago | ||
| 27 days ago |
xtodo —— Rust 版 [x]it! 待办管理工具
一个用 Rust 编写的命令行待办(todo)管理工具,完整实现 [x]it! 纯文本待办格式规范 v1.1。
- 格式规范:jotaen/xit(本机克隆在仓库同级目录
../xit,其Specification.md是唯一权威格式定义) - 功能参考实现:hoechstleistungshaartrockner/xitkit(Python 版;本文档
docs/xitkit-reference.md是其行为分析笔记)
零第三方依赖:只用 Rust 标准库实现解析、日期运算、颜色输出、JSON 序列化与参数解析,因此 cargo build --offline 也能构建,产物是单个可执行文件 xtodo。
1. 构建、分发与安装
最简单的方式是用仓库自带的 justfile(需要 just;不装也能直接用下面的 cargo 命令):
cd xtodo
just # 列出所有配方
just check # cargo fmt --check + clippy -D warnings + 全部测试(提交前跑一遍)
just release # release 构建 -> target/release/xtodo
just dist # 分发:dist/xtodo-<版本>-<平台>.tar.gz + .sha256
just dist-verify # 校验压缩包、解包并运行其中的二进制
just dist-src # 源码包(cargo package 的 .crate + .sha256)
just dist-all # 二进制包 + 源码包
just install # cargo install 到 ~/.cargo/bin
PREFIX=/usr/local just install-to # 不走 cargo,直接把二进制拷到 <前缀>/bin
just uninstall # 删除已安装的 xtodo
just info # 版本 / 目标平台 / 产物路径 / 安装前缀
just run show --active # 直接用 cargo run 跑
just demo # 用 examples/todo.xit 演示(固定“今天”,输出可复现)
just skill-install # 安装 agent skill 到 ~/.agents/skills/xtodo
just skill-check # 校验 skill 文档与 CLI 是否漂移
just clean # 清理 target/ 与 dist/
不用 just 时等价的命令:
cargo build --release # 生成 target/release/xtodo
cargo test # 94 个测试(56 单元 + 38 端到端)
cargo clippy --all-targets -- -D warnings # 静态检查
install -m 755 target/release/xtodo ~/.cargo/bin/xtodo # 手动安装
just dist 产出的压缩包结构(同时提供 SHA-256 校验和):
xtodo-<版本>-x86_64-apple-darwin/
├── bin/xtodo # 可直接运行的二进制
├── BUILD-INFO # 版本、目标平台、构建时间、rustc 版本、规范版本
├── README.md LICENSE
├── examples/todo.xit
└── docs/xitkit-reference.md
需要 Rust 1.70 或更新版本(开发环境为 1.98)。
版本号与构建时间
二进制会把编译时刻打进版本号(做法与 ../key-manager 一致):
$ xtodo --version
xtodo 1.0.1+20261012141424 (x86_64-apple-darwin, [x]it! specification 1.1)
- 版本号取自
Cargo.toml,时间戳由build.rs在编译时注入(XTODO_BUILD_TIME,本地时间、秒级),拼在 semver 的 build metadata(+)位置:semver 规定它不参与版本比较,所以1.0.1+2026…与1.0.1等价,cargo 与 crates.io 都接受(写成1.0.1.2026…会被 cargo 直接拒绝:patch 号后面不能加点)。 - 只有本 crate 的代码或
Cargo.toml变化时才会重新打戳(build.rs声明了rerun-if-changed):什么都不改时 cargo 连重编都不发生,戳自然不变;改了源码才更新——所以它的语义是"这份代码是什么时候编的"。 - 需要可复现构建时设
SOURCE_DATE_EPOCH(秒),戳以它为准:
SOURCE_DATE_EPOCH=0 cargo build --release # 时间戳固定为 19700101080000(按本地时区换算)
--version还会带上目标平台(同样由build.rs注入),方便分辨手上这个二进制是哪个平台的产物。just info除了显示Cargo.toml里的版本,还会直接运行已构建的二进制,打印它自我报告的版本(含时间戳)。
2. 快速开始
cd examples
xtodo show # 列出任务(默认命令,可省略 show)
xtodo add "写文档 -> tomorrow #docs" -p 2
xtodo mark 1 --done
xtodo reschedule 1 -n 3d
xtodo tag 1 -t urgent
xtodo stats
xtodo validate
默认位置是 todos/:不指定任何路径时,xtodo 先看 ./todos/(有任务文件就用它),没有才回退到当前目录;xtodo add 与 xtodo new 在什么都没有时会创建 todos/todo.xit。
xtodo show # 自动找 ./todos/*.xit(没有则看当前目录)
xtodo add "写周报 -> tomorrow" # 什么都没有时创建 ./todos/todo.xit
xtodo -f other.xit show # 显式指定文件(最高优先级)
xtodo -d docs show # 显式指定目录(不做 todos/ 回退)
扫描规则:目录下所有 .xit 文件 + 确实含有任务的 .md 文件(不递归)。
3. 命令一览
| 命令 | 说明 |
|---|---|
xtodo show [编号] [过滤] |
列出任务(默认命令,别名 list / ls) |
xtodo stats [过滤] |
统计信息 |
xtodo tags [--values] |
列出所有标签及出现次数 |
xtodo add "文本" [-p N] [-d 日期] [-t 标签] [-g 小节] [-s 状态] |
新增任务 |
xtodo mark 编号... --done |
修改状态:--open / --done / --ongoing / --obsolete / --inquestion |
xtodo reschedule 编号... -n 日期 |
设置/修改截止日期(-n none 清除) |
xtodo prio 编号... -p N |
设置优先级(-p 0 清除) |
xtodo tag 编号... -t 名称[=值] |
添加或覆盖标签 |
xtodo untag 编号... -t 名称 |
删除标签(按名称) |
xtodo edit 编号 "新描述" |
替换整条描述 |
xtodo rm 编号... [--force] |
默认只是标记为 obsolete([~]),加 --force 才真正删除 |
xtodo move 编号... --target-file 文件 [-g 小节] |
移动到另一个文件 |
xtodo recur 编号 --interval 间隔 [-c 次数 / -e 截止] |
生成重复任务(短选项是 -I;-i 表示交互模式) |
xtodo pomodoro [工作] [休息] [-c 循环] [-t 任务号] |
番茄钟计时器 |
xtodo format [--newline lf/crlf] [--collapse-blanks] [--check] |
规范化文件 |
xtodo validate |
按规范检查文件,有问题返回退出码 1 |
xtodo new [路径] [--template] |
新建一个待办文件 |
xtodo help / xtodo --version |
帮助 / 版本 |
常用全局选项:-f/--file(可重复)、-d/--directory、--json、--no-color、--today 日期、--dry-run、--force、-i/--interactive、-q/--quiet。
任务选择
任务编号由 xtodo show 输出(形如 #003,输入时写 3 即可)。命令行接受:
- 单个编号:
xtodo mark 3 --done - 区间或列表:
xtodo rm 1-5,9 --force - 全部:
xtodo tag all -t review - 或者用过滤器批量选择:
xtodo mark -t urgent --active --done
过滤器(show / stats / mark / reschedule / prio / tag / untag / rm / move)
-s, --status <状态> 按状态过滤(open,checked,ongoing,obsolete,in-question,可逗号分隔)
--not-status <状态> 排除某状态
-p, --priority <N> 优先级至少为 N(0 表示不限制)
--priority-exact <N> 优先级恰好为 N
--active 只看未完成任务(open/ongoing/in-question)
-t, --tag <名称[=值]> 必须含有该标签(可重复,全部满足)
--tag-any <名称> 含有其中任意一个标签
--no-tag <名称> 不含该标签
--with-tags / --without-tags
--due-on <日期> 截止日恰好是某天
--due-by <日期> 截止日不晚于某天
--due-after <日期> 截止日不早于某天
--overdue 已逾期
-g, --group <标题> 限定某个小节
--search <文本> 描述中包含该文本
排序:--sort priority|due_date|status|text 配合 --order asc|desc。默认 asc(优先级数值从小到大、日期从早到晚);要看最重要的任务请用 --sort priority --order desc。没有截止日期的任务在任何排序下都排在最后;已勾选([x])的任务始终排在末尾。使用 --sort 时顺序会跨小节,因此不再打印小节标题,以免误导。
4. 日期写法
add --due、reschedule -n、recur -e 以及 --due-on/by/after 都接受同一套写法:
| 写法 | 含义 |
|---|---|
today / tomorrow / yesterday |
今天 / 明天 / 昨天 |
3d / 2w / 1m / 1y / 1w2d / +10d |
相对偏移(月、年按日历精确计算,例如 2024-01-31 加 1 个月 = 2024-02-29) |
next week / next month / next year / 3 days / in 2 weeks |
英文口语写法,等价于 1w / 1m / 1y / 3d / 2w |
2025-12-31 / 2025/12/31 |
具体某天 |
2025-12 / 2025 |
月 / 年(规范规定等于该周期最后一天) |
2025-W42 / 2025/W42 |
ISO 周(最后一天为周日) |
2025-Q4 / 2025/Q4 |
季度 |
重复任务:xtodo recur 5 -i 1w -c 4 会以任务自身的截止日期为起点,按间隔生成 4 条副本(保留原描述、优先级、标签,替换截止日期);-e 2025-12-31 表示一直生成到该日期为止。无截止日期的任务以今天为起点。
5. 输出与 JSON
默认使用颜色(仅在终端中)、小节标题、逾期提醒:
$ xtodo show --active
▌ Work
#001 [ ] !! Fix the parser -> 2024-03-01 #bug (9 days overdue)
#002 [ ] Write tests
with more detail
▌ Personal
#004 [ ] Buy milk #shopping
--json:所有命令都支持结构化输出,便于脚本处理(任务、标签、截止日期、逾期天数等字段)。-l/--show-location:显示文件:行号。--no-id、--no-due、--count:控制显示。- 任务编号规则:文件按路径排序后,逐个文件按行号顺序编号,编号只在本次运行内有效(与 xitkit 一致,不是持久 ID)。
命令反馈与两个容易误解的约定
修改类命令(mark / reschedule / prio / tag / untag / edit / rm)都会回显改动后的整行,方便立刻确认:
$ xtodo reschedule 1 -n 5d
ok: #001 due date set to 2024-03-15 · todo.xit
[x] !! Fix the parser -> 2024-03-15 #bug #urgent
如果目标值本来就是当前值,命令不会白改文件,而是明确告诉你没有变化(否则那一行文本一个字符都不会变,看起来就像命令失效):
$ xtodo reschedule 1 -n 3d
note: nothing changed for #001 (already due on 2024-03-13)
另外两点约定,容易被误认为“没生效”:
- reschedule / prio / tag 不会改变任务状态。 一个
[x]的任务重排期后仍然是[x]。要让任务重新回到未完成,请显式执行xtodo mark 编号 --open。 - 已完成任务(
[x]/[~])总是排在列表末尾(沿用 xitkit 的习惯,避免未完成项被淹没),并在末尾提示有多少个已完成任务被排在最后、可用--active隐藏。只看未完成任务用--active;只看某个任务直接xtodo show 3。
验证改动是否符合预期,推荐:xtodo show 编号、xtodo show -l(显示文件:行号)、xtodo show --json(脚本里区分 changed / unchanged)。
6. 交互模式与番茄钟
交互模式(-i)
-i 让命令"缺什么就问什么",不想背参数时很方便:
xtodo show -i # 先选小节,再选任务
xtodo mark -i # 状态菜单 → 小节 → 任务
xtodo reschedule -i # 日期 → 小节 → 任务
xtodo rm -i # 小节 → 任务 → 删除方式(标记 obsolete / 永久删除)
xtodo tag -i # 小节 → 任务 → 标签
xtodo prio -i / xtodo move -i / xtodo recur -i / xtodo edit -i
xtodo add -i # 没有描述时询问描述
$ xtodo mark -i
New status?
1) open ([ ])
2) checked ([x])
3) ongoing ([@])
4) obsolete ([~])
5) in-question ([?])
choice [1]: 2
Which tasks?
1) #001 [ ] Fix the parser -> 2024-03-01 #bug
2) #002 [ ] Write tests
selection (1,3-4 or all) [all]: 1
ok: #001 marked as checked · todo.xit
[x] Fix the parser -> 2024-03-01 #bug
- 提示写到 stderr、答案读 stdin:默认值直接回车,也可以管道驱动(
printf '2\n1,3\n' | xtodo mark -i),因此交互流程本身也有端到端测试。 - 菜单支持
1,3-5这样的多选和all;越界会报错而不是静默跳过。 - 命令行上已经给出的值不会再问(例如
xtodo prio 3 -i只问优先级)。
番茄钟(xtodo pomodoro)
xtodo pomodoro # 25 分钟工作 + 5 分钟休息,按 q 结束
xtodo pomodoro 50 10 -c 3 # 50/10 三个循环后自动结束
xtodo pomodoro -t 4 -t 5 --complete # 关联任务:工作时 [@],工作结束时 [x]
xtodo pomodoro 0.05 0.02 # 允许小数分钟(秒级演示与测试)
终端里是全屏重绘(倒计时 + 进度条 + 已完成统计),按键:p 暂停、s 跳过、q 退出,会话结束响铃(--no-sound 关闭)。非终端环境(管道、脚本、CI)会自动退化为逐行输出:
[pomodoro] work session 1 of 2 - 00:03
[pomodoro] work 1 finished - next: break 00:01
[pomodoro] break 1 finished - next: work 00:03
[pomodoro] done - 2 work session(s), 1 break(s), 6s focused
- 关联任务用
-t 编号(全局编号,可重复);每个工作会话开始时把它们标成[@],加--complete则在工作结束时标成[x],并立即写回文件,其它终端立刻可见。 - 原始按键模式通过
stty切换并在退出时还原;万一被强杀导致终端异常,执行stty sane即可。 - 计时/会话切换逻辑是纯函数并有单元测试,不需要等待真实时间也能验证。
7. Agent Skill(xtodo)
仓库自带一个 agent skill:skills/xtodo/。装到 ~/.agents/skills/ 之后,会话里就会出现 xtodo 技能——当你说"把这件事记下来""今天还有什么要做的""把逾期的往后推一周"时,agent 会自动按 xtodo 的约定操作,并遵守安全规则(删除默认只标 obsolete、批量写先 --dry-run 预览、edit 前先给你看原文)。
just skill-check # 校验:frontmatter、文档里的命令是否都存在、已装副本是否与仓库一致
just skill-install # 装到 ~/.agents/skills/xtodo(可用 SKILLS_DIR=/path 覆盖目标)
just skill-uninstall # 卸载
- 内容:
SKILL.md(约定与安全规则)+reference/commands.md(命令与--json字段)+reference/workflows.md(收件箱整理 / 每日回顾 / 批量改期 / 周报等剧本)。 - 依赖:只用
bash调xtodoCLI(--json输出),不依赖 MCP、不需要额外进程;未安装 xtodo 时会如实告知而不会假装成功。 - 防漂移:
just skill-check会检查文档里出现的每个子命令是否真实存在(当前 16 个),提交前跑一下即可避免"文档写了不存在的命令"。 - 前置:
xtodo要在 PATH 上(just install或cargo install xtodo-cli);也可以让 agent 用-f指定具体文件。
8. 规范实现要点
解析器严格按 Specification.md v1.1 实现,并参考了 xitkit 的 syntax_guide.txt 中的边界情形:
- 复选框:必须是
[ ][x][@][~][?]三者结构,且后面必须紧跟一个空格;[X]、[o]、[ ]no-space、[ ]、缩进过的复选框都不是任务行(会被原样保留并在validate中报错)。 - 额外空格归描述所有:
[ ] text的描述是空格 + 空格 +text;优先级只能紧跟在唯一那个分隔空格之后,[ ] ! x不构成优先级。 - 优先级:一个或多个感叹号,两侧可以有点号但不能同时出现在两边或中间;
!.!非法,纯点号(...)视为描述文本。 - 描述续行:必须刚好 4 个空格缩进,4 个空格之后的额外空格属于描述内容。
- 截止日期:
->加一个空格的前缀必须在行首、空白或标点(不能是-、/)之后,日期后也必须是空白/标点/行尾(所以-> 2022-01-31very、-> 2022-01-31-0、---> 2022-01-31都不算);分隔符不能混用(2022-01/31不算);只取第一个,多余的日期被忽略(validate会提示)。 - 标签:名称由 Unicode 字母/数字/下划线/连字符组成;值可用单双引号包裹,支持含空格,未闭合引号视为无值;空值与无值等价。
- 小节(标题):不以空白或
[开头;标题会一直作用到下一个标题(因此 markdown 里# 标题与空行之后的复选框也能正确归组)。 - 往返安全:无法识别的行(例如普通说明文字、代码块)永远不会被重写;
format只规范化能识别的行,并提示有多少行未处理。
xtodo validate 会输出 文件:行号: 级别: 说明,错误(error)导致退出码 1,警告(warning)和提示(note)不影响退出码。
9. 与 xitkit 的差异
保留其命令风格与默认行为,但修正了若干缺陷、并刻意选择了更安全的实现:
| 方面 | xitkit (Python) | xtodo |
|---|---|---|
| 写回方式 | 每次修改都按解析结果重建整个文件,注释、散文、空行、代码块都会丢失 | 只改必要的行,其余字节原样保留(有端到端测试保证) |
| 日期运算 | m=30 天、y=365 天,且前缀匹配导致 3d2w 只算 3 天,+1w 不支持 | 月/年按日历精确计算,支持 +10d、1w2d |
| edit | 被禁用(直接报错退出) | 可用 |
| -d/--directory | 相对路径必然报错 | 正常支持相对/绝对路径 |
| 标签 | --tag 必须精确匹配名+值,untag 按“名=值”匹配 | 按名称匹配,自动忽略 # 前缀 |
| recur | 任务无截止日期时仍写文件然后崩溃 | 以今天为起点正常生成 |
| rm | 无 --force 时标记 obsolete | 相同(这是有意的安全默认) |
| 退出码 | 多数错误仍返回 0 | 失败返回 1,validate / format --check 有问题返回 1 |
| 交互模式 | questionary 单选/多选菜单 | 行式菜单(编号 + 范围多选),提示写 stderr、答案读 stdin,因此也能被脚本和测试驱动 |
| 番茄钟 | textual TUI(独立子命令) | ANSI 重绘 + stty 原始按键(p 暂停 / s 跳过 / q 退出),非终端时自动退化成逐行输出;-t 可关联任务(工作时标 [@],加 --complete 结束时标 [x]) |
10. 项目结构
xtodo/
├── Cargo.toml
├── justfile # 构建 / 测试 / 分发 / 安装
├── README.md
├── LICENSE
├── build.rs # 注入编译时间戳(版本号里的 +2026…)
├── docs/
│ ├── xitkit-reference.md # 参考实现的行为分析笔记
│ ├── design-todo-skill.md # skill 设计(已实施)
│ ├── design-mcp.md # MCP 设计(暂缓,未实施)
│ └── decisions.md # 决策记录(D1–D4)
├── skills/xtodo/ # agent skill:SKILL.md + reference/{commands,workflows}.md
├── scripts/skill-lint.sh # skill 文档防漂移检查
├── examples/todo.xit # 演示数据
├── src/
│ ├── date.rs # 儒略日/公历换算、ISO 周、日期模式解析
│ ├── dateexpr.rs # today/tomorrow/3d/1w2d 等表达式与重复日期
│ ├── model.rs # Status / Priority / Tag / DueDate / Item / Group / Document
│ ├── parser.rs # 规范 1.1 解析与规范化渲染
│ ├── mutate.rs # 保文本的编辑操作(状态、优先级、日期、标签、增删、排序)
│ ├── query.rs # 过滤器、编号选择、排序
│ ├── repo.rs # 多文件工作区、文件发现、编号、原子写回
│ ├── render.rs # 终端渲染(颜色、对齐、统计、标签表)
│ ├── stats.rs # 统计计算
│ ├── validate.rs # 规范一致性检查
│ ├── json.rs # 极简 JSON 序列化
│ ├── prompt.rs # 交互模式的行式提示(菜单、多选、确认)
│ ├── pomodoro.rs # 番茄钟状态机 + 终端渲染 + 按键读取
│ ├── cli.rs # 参数解析与帮助
│ ├── commands.rs # 各子命令实现
│ └── main.rs # 入口
└── tests/cli.rs # 38 个端到端测试(真实调用二进制)
11. 开发与测试
cargo test # 全部测试
cargo test --test cli # 只跑端到端测试
XTODO_TODAY=2024-03-10 xtodo show # 固定“今天”,便于复现
测试用的环境变量:XTODO_TODAY(覆盖今天),XTODO_UTC_OFFSET(覆盖本地时区偏移,如 +0800 或分钟数)。