xtodo:基于 Rust 标准库的命令行待办管理项目

在终端中高效管理纯文本待办事项,支持任务增删改查、标签、优先级、截止日期、重复任务、番茄钟和批量操作。实现 xit! 格式规范 v1.1,零第三方依赖,仅用 Rust 标准库,产物为单个可执行文件,支持 JSON 输出和 Agent Skill。【此简介由AI生成】

Branch1Tags0

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)

另外两点约定,容易被误认为“没生效”:

  1. reschedule / prio / tag 不会改变任务状态。 一个 [x] 的任务重排期后仍然是 [x]。要让任务重新回到未完成,请显式执行 xtodo mark 编号 --open。
  2. 已完成任务([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 调 xtodo CLI(--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 或分钟数)。

12. 许可与致谢

  • 代码以 MIT 许可发布。
  • 格式规范由 Jotaen 编写,采用 CC0 / OWFA 许可(见 ../xit/Specification.md)。
  • 命令设计参考 xitkit(MIT)。

Introduction

在终端中高效管理纯文本待办事项,支持任务增删改查、标签、优先级、截止日期、重复任务、番茄钟和批量操作。实现 xit! 格式规范 v1.1,零第三方依赖,仅用 Rust 标准库,产物为单个可执行文件,支持 JSON 输出和 Agent Skill。【此简介由AI生成】

Customize your domain