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

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

Branch1Tags0
FilesLast commitLast update
26 days ago
27 days ago
26 days ago
26 days ago
26 days ago
26 days ago
26 days ago
26 days ago
26 days ago
26 days ago
27 days ago
26 days ago
26 days ago
26 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)

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

  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