Skip to content
Conduit

skill

req-tree

Requirement derivation tree and evidence-gated closure as your single source of truth — zero-dependency Python CLI + AI-agent skill

putaomeiyoupi/req-treecommunitylistedunreviewed

Set up

Skills are installed by placing their folder where your agent loads skills.

The download comes from github.com/putaomeiyoupi/req-tree itself, not from our server, so what you get is exactly what its author published.

Install instructions come from the README

Skills do not use an MCP config. Follow the steps in the README below.

README

synced Oct 22, 2026

req-tree

项目推进中的需求派生与收口的唯一真源。

License: MIT Python Dependencies Platform

  • 零依赖 —— 单个 Python 文件,仅用标准库
  • 跨平台 —— Windows / macOS / Linux
  • 离线 —— 数据就是一份 JSON,存在你自己的仓库里
  • 无服务 —— 没有后端、没有账号、没有网络请求

它解决什么问题

需求是长出来的,不是一次性列出来的:

1. 计价模块            ← 最开始只有这三条
2. 供货合同
3. 文档库
   │
   └─ 做「1」的时候发现 ↓
      1.1 供应商维度筛选
      1.2 导入模板
      1.3 联合索引      ← 推进 1.3 时又发现 ↓
          1.3.1 索引写入放大
          1.3.2 权限过滤会先砍候选集

表格记不下这个结构,于是出现两个典型症状:

  • 说不清「这条为什么存在」 —— 三个月后看到「索引写入放大」,没人记得它是从哪条需求、做什么动作时冒出来的
  • 说不清「哪些还没收口」 —— 收口发生在对话里、在脑子里,表格里只剩一个「状态:完成」。而「完成」到底是「我改完了」还是「已验证」,没人分得清

req-tree 把需求之间的派生关系变成一等公民,并强制每一次收口都留下证据。


五条不变量

这是它和「又一个 todo 列表」的分界:

1. 身份与路径分离。 节点身份是不可变的 R-0007;1.3.2 只是渲染时算出来的编号。 引用一律用 ID —— 一旦插入或移动节点,编号会整体错位,所有历史引用同时失效。

2. done ≠ closed。 done = 我改完了;closed = 已验证、已确认收口。两步分开,防止自己给自己盖章。

3. 收口必须附证据。 close 强制要求至少一条 --evidence(commit: / test: / doc: / file: / user:)。 没有证据的「已关闭」是假关闭,会直接污染真源。

4. 假收口会被拦下。 父需求还有未收口的子项时,close 直接拒绝。要强行收口必须显式写出理由, 而且只接受收口当时存在的那批 —— 收口之后新增的子项照样报错。

5. 只有一个真源,且只能通过 CLI 写。 tree.json 是唯一的事实来源,其余视图都是 render 的派生物、会被覆盖。 手改视图 = 制造第二个真源,这是这套系统最大的威胁。


快速开始

git clone <本仓库地址> req-tree

cd /path/to/your-project

# 用一个别名省去长路径(Windows 见下)
alias todoctl="python /path/to/req-tree/scripts/todoctl.py"

todoctl init --project my-project

todoctl add --title "支持按供应商维度筛选" \
            --why "现有查询接口没有这个维度,业务侧每次都要手工拼" \
            --done-when "筛选结果与手工核对一致,接口测试通过"

todoctl render
todoctl check

打开生成的 TREE.md,树就在里面。

init 会自动判定真源落点,并在输出里明确告诉你落在哪个目录。

真源放在哪

默认规则(--root 或环境变量 TODOCTL_ROOT 可覆盖):

顺序规则
1显式 --root
2环境变量 TODOCTL_ROOT
3当前目录已是真源(docs/requirements/ / .req-tree/requirements/ / requirements/ 下有 tree.json)
4兼容:旧版宿主专有路径 .workbuddy/requirements/ 下已有 tree.json(仅识别,不再新建,且 check 会提示你搬走)
5当前目录已预建 docs/requirements/ 或 .req-tree/requirements/
6检测到 git 仓库 → <仓库根>/docs/requirements/ ← 推荐
7以上都不是 → <当前目录>/.req-tree/requirements/

推荐第 6 条:需求树与代码同版本,git log 就是免费的审计日志。

裸 requirements/ 必须已经是真源才会被优先命中 —— 否则一个存放依赖清单的同名目录会把真源劫持到仓库子目录里,静默产生第二棵树。


日常节奏

todoctl resume                     # ① 开工第一件事:拿回上次现场
todoctl add --parent R-0006 ...    # ② 发现新问题的当下就落库,不要攒
todoctl start R-0006               # ③ 开始做(需要 done_when)
todoctl note R-0006 做到哪 发现了什么   # ④ 做一步记一句,不必加引号
todoctl bench                      # ⑤ 随时看手头同时有几件事
todoctl done R-0006                # ⑥ 实现完成
todoctl close R-0006 --evidence "commit:9f2c1ab"   # ⑦ 验证通过才算收口
todoctl check                      # ⑧ 体检

这套系统的成败不在设计,而在你记不记得记。所以触发时机被做进了命令里: resume 一条命令拿回现场、note 不需要引号、真源写入后视图自动刷新 —— 你不需要记得手动同步任何东西。

交错推进是被支持的

真实工作几乎不会按派生顺序走:做 1.1.2 时下一步该做 1.1.3,但你被拉去做 2.1,然后才回头。

这本身没有数据风险(单人顺序操作不会写坏真源)。风险是三个「看不见」,各有对策:

风险对策
忘了回来bench 显示搁置天数;W5 告警(doing 超过 7 天未动)
不知道自己在绕update <id> --next R-0008 声明计划中的下一步 → start 别处时当场提示绕行,bench 标 ↷绕行中
看不到手头有几件事bench / BENCH.md:全部进行中任务按需求线分组

--next 是节点指针(可校验、能算出绕行),--next-step 是文字说明。两者可并存、互不替代。

跳过了计划下一步不阻断 —— 绕行是正常决策,工具只负责让你「知道自己在绕」,不替你判断该不该绕。

退出码

码含义
0成功
1check 或 render 发现真源有错
2被强校验或参数校验拒绝

⚠️ 容易混的一对:--force 只跳过「未 done 不能收口」的前置检查,跳不过假收口检查; 放行假收口要用 --override-reason。两个都踩到时(状态不是 done 且还有开着的子项)两个都要给。


视图:真源与派生物

文件性质什么时候看
tree.json真源不要手改
journal.jsonl真源(追加式)变更日志,log 的唯一依据
baselines.json真源(追加式)基线 —— 不可变里程碑
snapshots/真源(自动留档)写入前自动快照,无 git 时的回滚依据
TREE.md派生全树;已全部收口的子树折叠成一行
ACTIVE.md派生只列未收口项 ← 日常首选
FOCUS.md派生当前聚焦分支及其派生
RESUME.md派生上次做到哪、下一步、期间变动
BENCH.md派生手头全部进行中任务
BASELINE.md派生最新基线摘要 + 各线进度
dashboard.html派生单文件看板,可按状态/关键字筛选

判别口诀:手改会被覆盖的 → 派生物;手改会破坏追溯链的 → 真源。

派生物会自动刷新 —— 真源任何一次写入成功后,七个视图会被重新生成(--no-render 可关)。 视图过期是「记录对不上」的老根源,所以它不由人负责。 (全新真源要先手动跑一次 render,之后才自动跟随。)


命令速览

组命令
建init
写add update note
状态start block unblock done close drop
恢复resume bench
查看show check log report search audit
治理baseline restore
导出render export

全部子命令、选项与五个完整工作流见 references/entry-map.md。

几个值得单独知道的:

todoctl search "回填"                      # 全文检索,**含 notes 进度轨迹**
todoctl search "commit:abc" --in evidence  # 只搜收口证据
todoctl note R-0016 --file -  < note.txt   # 长文本走 stdin,绕开 shell 引号截断
todoctl audit                              # 回填对账:谁「做完了却没收口」
todoctl bench                              # 台面:手头有哪几件事在同时推
todoctl baseline --name "v0.3 首版"        # 把当前各线成果封成一个不可变时点
todoctl restore --last --yes               # 退回上一次写入之前
todoctl log --line R-0031 --commit-msg     # 按需求线抽提交信息草稿
todoctl export                             # 导出可公开副本(恒脱敏)

三个高频痛点各有专门入口

痛点入口为什么单列一个命令
写了却读不回来searchnote 是最高频的写入动作,notes 也是真源里体积最大的字段(实测占 56%)。但 show 不渲染 notes、七个视图也不导出 note 正文 ⇒ 不给检索通道,笔记就等于只写不读。
长文本经 shell 传参被截断note --file正文含中文直双引号时,命令会静默不执行却看似成功(2026-09-28 实测)。长文本一律走文件/stdin,不靠「记得加引号」。
做完了却没回填状态auditcheck 的 W4/W5 只报「多久没动」,报不出「动过了却没收口」—— 真源与实现之间的漂移没有任何时间维度信号。audit 给出高危候选短名单,但只报线索、不下结论,是否漏收口仍要人工核 git。

安装

方式一:作为独立 CLI(任何平台)

git clone <本仓库地址> req-tree

# macOS / Linux / WSL / Git Bash
alias todoctl="/path/to/req-tree/scripts/todoctl"

# 或直接调 python(最通用,不依赖脚本的可执行位)
alias todoctl="python3 /path/to/req-tree/scripts/todoctl.py"

Windows:把 req-tree\scripts 加进 PATH,直接用 todoctl.cmd。

三个入口 —— scripts/todoctl(POSIX)、scripts/todoctl.cmd(Windows)、scripts/todoctl.py(通用)—— 都只从 PATH 里找 Python,不含任何写死的安装路径:换机器、换平台、换用户都不用改。

要求:Python 3.8 或更高(仅标准库,无需 pip install)。

方式三:给项目装上「漏记 / 漏收口」门禁(强烈建议)

只装 skill 的话,需求捕捉仍然靠「记得调用」 —— 而这正是失效发生的地方。 下面这套机制把提醒下沉到三个绕不过去的时刻,不需要任何人记得。

# 1) 把门禁脚本放进你的项目(只依赖 Python 标准库)
cp <req-tree路径>/scripts/reqtree_gap_gate.py <你的项目>/scripts/
mkdir -p <你的项目>/.githooks

# 2) 生成 hook(只打印不落盘,便于先审阅)
python <你的项目>/scripts/reqtree_gap_gate.py print-hook

# 3) 确认没问题后写入,并让 hook **跟着仓库走**
python <你的项目>/scripts/reqtree_gap_gate.py print-hook > <你的项目>/.githooks/pre-commit
cd <你的项目> && git config core.hooksPath .githooks
git add scripts/reqtree_gap_gate.py .githooks/pre-commit && git commit -m "chore: 接入需求树门禁"

第 3 步的 git config core.hooksPath .githooks 是仓库级配置,把它和那两个文件一起提交, 别人 clone 下来执行这一行就自动生效 —— 不需要装 skill,也不需要改任何本地路径。

⚠️ 门禁只打印提示,绝不阻断提交(退出码恒 0)。 这是刻意的:阻断会变成「来不及登记就提交不了」的新摩擦,反而更容易被 --no-verify 绕开。

方式四:让宿主每次会话都点到名(AGENTS.md)

门禁只在提交那一刻说话。如果你还希望「开工时就把规则摆出来」, 在项目根写一个 AGENTS.md(宿主启动时本来就会读它,不额外加载 skill、不花工具调用):

## 需求真源

本项目需求真源在 `docs/requirements/`(唯一真源,视图禁手改)。

- 用户提出**新需求 / 派生需求**时,先用 `scripts/reqtodo.sh add` 登记,别等做完。
- 推进已有节点用 `start` → `done` → `close`(**done ≠ closed**)。
- 不确定某条需求为什么存在时,先 `todoctl show <ID>` 看派生理由。

只需要三行,不必粘贴整份 SKILL.md。

四层触发,按可靠性递增

层载体何时说话谁来装
1AGENTS.md 三行点名会话开始时项目维护者(一次性)
2core.hooksPath pre-commit提交那一刻(绕不过去)项目维护者(一次性)
3audit 定时巡检每晚 / 每周定时任务(可选)
4直接调用 search / audit随时人或 agent 主动

前两层解决漏记,第 3 层解决漏收口(当天看不出,只能隔夜扫)。 gate 只看本次提交,拦不住历史漏收口 —— 两者互不重叠,须成对使用。

方式二:接入你的 AI 宿主(默认推荐)

通用做法(首选):把整个目录放进宿主的 skills 目录,或把 SKILL.md 软链成宿主认的名字 —— 不需要 npm、不需要插件市场。下面表格里 WorkBuddy / Claude Code / Cursor / Codex / Gemini 都走这条。

本仓库只带一份 SKILL.md(带 frontmatter 的标准 skill 定义)。

各宿主认的"规则文件"名字并不统一,但内容本质相同。所以不要为每个宿主写一份 —— 那是多个真源,必然漂移。正确做法是让同一个 SKILL.md 暴露成各宿主认的名字:

宿主认的文件 / 位置接入方式
WorkBuddy<skills>/req-tree/SKILL.md整个目录放进 skills 目录(默认推荐)
Claude Code~/.claude/skills/req-tree/整个目录放进 skills 目录
Codex CLI(OpenAI)项目根的 AGENTS.mdln -s /path/to/req-tree/SKILL.md AGENTS.md
Cursor.cursor/rules/ 下的规则文件软链或复制 SKILL.md 进去
Gemini CLIGEMINI.md同上
其他任何「项目/工具说明」文件认哪个名字,就链到哪个名字

用符号链接(Windows 用 mklink):一份内容、多个入口,改一处到处生效。

之后对 agent 说「记一下这个需求」「哪些还没收口」「这条为什么会有」,它会按这套流程走: 先 resume 拿回现场,发现新需求当场 add,收尾时 close 附证据。


常见问题

需要数据库吗? 不需要。数据就是一份 JSON 加一份追加式日志。

会污染我的仓库吗? 全是纯文本文件。不想要视图就在 .gitignore 里排除那几个 .md,或用 --no-render 关掉自动刷新。 真源建议提交 —— 那是 git log 作为审计日志的前提。

能多人协作吗? 设计是单写者。写入有跨进程锁保护(并发写会被拒绝并给出提示),但没有合并机制。 多人请各写各自项目的真源,或串行操作。

树会越长越乱吗? 深度和宽度是如实记录出来的,不是「乱」:层级反映需求之间真实的派生关系,所以工具刻意不提供 改父(reparent)通道,也不建议为了好看凭空插中间层或另立新根 —— 那样是伪造逻辑关系。 阅读成本另想办法消化:深度 > 3 层、同父未收口 > 7 个各给一条提醒,并告诉你它在哪 (check / resume / 各视图页眉都有一行结构概览:最深 · 最宽层 · 最大的一棵树 + 位置编号; TREE.md 与看板顶部另有完整区块)。外加已全部收口的子树自动折叠成一行、每周 report 巡检停滞项。

能导出给外部看吗? todoctl export 恒脱敏(清空 why/notes,把标了 internal 的节点连同整条子树剔除)。 唯一无法自动脱敏的是标题,所以导出时会提醒你人工复核。 (export 这个命令的全部意义就是「产出可公开副本」,因此它没有「不脱敏」模式。)

改错了怎么退回去? 每次写入前自动快照。todoctl restore --list 看有什么,todoctl restore --last --yes 退回上一次写入之前。 有 git 的话 git 也是退路。

能当看板用吗? 可以。dashboard.html 是单文件、零依赖,双击就在浏览器里打开。打开即待办视野: 状态默认只显示 未开始 / 进行中 / 阻塞(已完成 / 已收口 / 已放弃 默认隐藏, 免得稀释「还有几件事要做」),统计默认只算叶子节点;要考古时逐个点开状态标签, 或按关键字 / 告警筛选。

数据坏了会怎样? check 会给出带错误码的清单(ID 重复、成环、假收口、缺证据……), render 在真源有错时不会静默产出空树 —— 它返回 1 并打印错误清单。


设计取舍与已知限制

诚实列几条:

  • 单写者设计。 有写锁防并发覆盖,但不做多写者合并。这是刻意的 —— 一旦引入合并, 「唯一真源」就会变成一场谈判,而那正是这套工具要消灭的东西。
  • 真源是单个文件。 好处是一次读取拿到全貌、diff 清晰;代价是同一文件里多条需求线的改动 在 git diff 层面无法自动切分。(journal.jsonl + log --line 解决的是叙述的交叉, 不是 diff 的交叉。)
  • 不做关键路径与甘特图。 depends_on 记录了跨分支依赖,但不做关键路径计算。 这套工具的目标是「记得住、收得口、看得清」,不是排期。
  • 真源落点已中性化。 非 git 环境下降级到 .req-tree/requirements,不绑定任何宿主的私有目录; 旧版的 .workbuddy/requirements 仍会被识别(迁移兼容),check 会提示你把它搬走。
  • 规模实测:800 节点时单次写入约 0.3 秒,其中约 0.24 秒是 Python 进程启动本身 —— 几百节点的日常规模完全无感。

自检

todoctl check        # 强校验:ID 重复 / 成环 / 假收口 / 缺证据 / 依赖失效 …

退出码 0 表示真源干净;1 表示发现问题(会逐条列出错误码)。 render 在真源有错时不会静默产出空图 —— 它返回 1 并打印错误清单。


项目结构

req-tree/
├── SKILL.md              # 给 AI agent 的入口:工作流与纪律
├── README.md             # 你正在读的这份
├── references/
│   ├── entry-map.md      # 功能与入口索引:「我要做 X → 打哪条命令」
│   ├── schema.md         # 数据模型与状态机
│   └── discipline.md     # 触发时机与写入纪律
├── scripts/
│   ├── todoctl.py        # 工具本体(零依赖,仅标准库)
│   ├── todoctl           # macOS / Linux / WSL / Git Bash 启动
│   └── todoctl.cmd       # Windows 启动
└── docs/requirements/    # 本项目自己的真源(仅本仓库有)

开发与发布(dev / prod 分离)

改这个 skill 时,别让改动直接落在宿主正在索引的那份副本上。那等于「项目正在调用的版本随你每一次编辑而变」,而多个会话并行时还会在同一个 working tree 上互相干扰(实测过一次:他方未提交的改动在工作树里横跨 14 小时没人发现)。

两份目录,角色分明:

角色位置干什么
dev宿主 skills 根之外的任意位置(本机实例:F:\Workbuddy\_skills-dev\req-tree)改代码、跑 selftest.py、git commit —— 唯一修改入口
prod宿主 skills 根之内,如 <skills 根>/req-tree只读。只接受 dev 的单向推送

位置只有两条硬要求:① 在宿主索引的那个根之外(放进去会被扫成两个 skill);② 能真实落盘(有些环境的沙箱会把工作区外写入落进隔离层:报成功但没生效)。换位置前先用探针实测,见宿主记忆 sandbox-fs-delete.md §1.3。

发布用仓库根之外的 publish-req-tree.py(本机脚本,不随本体发布)。四道门,任一不过即中止且不落任何改动:

[1/4] dev 工作树干净     未提交的东西不允许发布
[2/4] dev selftest 全绿   回归没跑过就不许出门
[3/4] prod 工作树干净     有人绕过脚本直接改过 prod 就报警
[4/4] 可快进              prod 不得存在 dev 没有的提交(防分叉)
python publish-req-tree.py --status     # 看两侧版本与工作树状态
python publish-req-tree.py --dry-run    # 预演:跑全部门禁,但不落地
python publish-req-tree.py              # 发布(快进合并 + 回读校验)
python publish-req-tree.py --push       # 发布后推 GitHub(对外备份,需显式指定)

发布走 git merge --ff-only,不做强推、不产生分叉;落地后回读两侧 HEAD 是否一致。

为什么不是 vendor 进项目(把 todoctl.py 复制到项目内):那样工具就有了两份,每次修改都要手工同步 —— 正是这套分离机制要避免的问题。skill 应被视为从 git 安装的外部依赖,项目侧只留一个薄入口。


许可

MIT,见 LICENSE。

↑↓ navigate openesc close