关于 facts
facts 是用于以 `.facts` 文件记录项目事实并验证其真实性的命令行工具。它支持通过命令检查事实,也可选择让 AI Agent 验证手动事实。
主要功能
- 01在 `.facts` 文件中记录事实
- 02用 Shell 命令验证事实
- 03委托 AI Agent 检查手动事实
- 04按标签和章节筛选整理事实
适用场景
- 01描述项目应有的行为
- 02在开发中重复运行验证
- 03让 AI Agent 检查手动事实
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ av/ facts --skill facts安装后在 Claude Code 中输入/facts确认已加载。
详细介绍
facts
用于事实驱动开发的 CLI。你可以用它声明项目必须满足的条件,然后验证实际情况是否符合。
项目:https://github.com/av/facts
安装
如果尚未安装 facts,请使用以下任一方式安装:
curl -fsSL https://raw.githubusercontent.com/av/facts/main/install.sh | sh
npm install -g @avcodes/facts
pipx install facts-cli
使用 facts --version 验证。命令始终是 facts,绝不要使用 npx facts。
核心理念
.facts 文件是项目原子事实陈述的扁平列表。每条事实都可以选择附带一条用于验证的 shell 命令。事实表既是规范(应该满足什么条件),也是文档(实际满足什么条件)——区别只在于你从哪个方向着手。
- the API returns JSON
- label: project builds
command: cargo build
- label: tests pass
command: cargo test
tags: [ci, core]
这就是全部格式。简单事实使用纯字符串,需要命令、标签或显式 ID 时则使用映射。允许的映射键:id、label、command、tags,仅限这些。
必备命令
推荐使用的简短别名(所有额外参数都会透传):
ll=list --light——最常用的“快速浏览”视图(Markdown 标题 + 淡化显示的 ID)ls=listrm=removeat <id> <tag>=edit <id> --add-tag <tag>(支持多个 ID;标签后还可添加--label/--new-id等参数)rt <id> <tag>=edit <id> --remove-tag <tag>
这些是日常使用和 Agent 使用中最值得优先采用的快捷方式。
查看全部内容:
facts ll
facts ll --tags "not implemented"
facts ll --has-command
facts list --light # or the alias: facts ll
验证:
facts check # default: command-facts only; manual stay ?
facts check --tags "ci"
facts check --id abc # check a single fact by ID
facts check --section cli/check # check one section
facts check --agent # opt in: .config agent or PATH preset
facts check --agent claude # opt in: specific agent
facts check --no-agent # explicit skip (redundant with default)
facts check --has-command # check only facts with commands
facts check --manual --agent claude # check only manual facts with agent
facts check --strict # fail on unverified manual facts (CI)
facts check --strict --agent claude -q # CI: verify everything, fail on gaps
facts check -q # quiet: exit code only (for CI)
facts check -v # verbose: show agent prompts and output
check 是主要的反馈循环。它会先检查文件格式(如有结构错误则中止),然后运行每条带命令的事实,并报告通过/失败/手动验证状态。请经常运行。退出码为 0 表示所有带命令的事实均通过;手动事实不会影响退出码,除非 Agent 验证后发现失败,或使用了 --strict(该选项会将任何未经验证的手动事实视为失败)。
手动事实(输出中的 ?)由你负责。 这类事实没有命令——你需要阅读相关代码来验证。对于每条 ? 事实:阅读其声明,检查代码,然后给出 PASS 或 FAIL,并附上一行理由。未逐条检查就报告“有 N 条手动事实”是不可接受的——这些事实之所以存在,是因为它们描述了重要行为。
配置项目以支持 Agent 验证:
facts config set agent claude # persist agent choice in .facts
facts config set batch-size 5 # how many facts per agent call
facts config show # see current config
facts config rm agent # remove a config key
添加事实:
facts add "users can sign up" --section features/auth
facts add "signup returns 201" --command "curl -s -o /dev/null -w '%{http_code}' localhost:3000/signup | grep 201" --section features/auth
编辑/添加事实标签(生命周期转换):
facts at <id> "implemented" # most common: quick add tag
facts rt <id> "spec" # quick remove tag
facts at <id> "spec" --new-id xyz # extra flags still work
facts edit <id> --label "corrected statement"
facts edit <id> --command "new check command"
facts edit <id> --remove-command # convert back to manual fact
facts edit <id> --remove-command --add-tag "draft" # demote to manual + draft
优先使用 at/rt(或完整的 --add-tag / --remove-tag 形式),而不是完整替换 --tags。后者会静默替换所有标签。使用 --remove-command 可移除验证命令,将事实转换回手动事实(在生命周期中降级时很有用)。
移除事实:
facts rm <id> # or the long form: facts remove <id>
搭建新项目:
facts init
运行 facts <command> --help 查看完整的标志参考。
事实的工作方式
文件: 默认使用 .facts。其他事实表使用语义化名称(cli.facts、api.facts)。项目根目录中的所有 *.facts 文件都会自动发现。
分区: Markdown 标题(#、## 等)会创建可通过路径寻址的层级分区(例如 cli/subcommands)。添加事实时会创建分区,删除其中最后一条事实时会移除分区。
标签: 使用 @word 标记进行筛选。普通字符串使用行内标签(- some fact @mvp),映射则使用 tags: 键。显示和计算 ID 哈希前,标签会从名称中剥离。使用布尔表达式筛选:--tags "mvp and not blocked"。
生命周期标签: 三个已知标签用于驱动 Agent 工作流:
@draft— 粗略想法,需要完善并拆分@spec— 精确且可执行,已准备好实现@implemented— 内容属实且有代码支撑- 未加标签的事实属于基本事实——已根据代码库验证
生命周期按 @draft → @spec → @implemented 流转。每项配套技能负责其中一个转换。
ID: 每条事实都会根据其名称的哈希生成一个简短 ID(至少 3 个字符)。只要名称不变,ID 就保持稳定。使用 --id 或 --new-id 覆盖。
验证: 命令会在项目根目录中通过 $SHELL(回退到 sh)运行。退出代码为 0 表示事实成立。编写快速且幂等的命令——每次检查都会运行。
配置: .config 事实(显式 id: .config)在主 .facts 文件中存储项目级设置。已知键:agent(预设名称或 shell 模板)、batch-size(每次 Agent 调用处理的事实数)。使用 facts config set/get/show/rm 管理。
Agent 验证
facts check 可以委托 AI Agent 验证手动事实(即没有命令的事实)。Agent 会读取代码库,并报告每项事实是否成立。
仅限主动启用: 只有传入 --agent 时才会运行 Agent 验证。不传入时,手动事实会保持为 ?。
传入 --agent 时的解析顺序(采用第一个匹配项):
--agent <value>— 预设名称或包含%p的 shell 模板- 单独使用
--agent—.config agent键(通过facts config set agent claude设置) - 单独使用
--agent且未配置 — 按预设顺序扫描 PATH,查找已知 Agent 二进制文件
预设: claude、codex、pi、droid、opencode、mi。每个预设都对应一个命令模板(例如,claude 会变成 claude -p %p)。自定义模板使用 %p 作为提示词占位符:--agent "my-agent run %p"。
工作方式:
- 首先运行命令事实(按顺序执行并立即输出)
- 将剩余的手动事实分批处理(默认每批 10 项,可通过
--batch-size或.config batch-size配置) - 将每批事实作为单条提示词发送给 Agent,其中包含事实 ID 和标签
- 解析 Agent 输出中的判定 XML 标签
判定格式 — 如果你就是被调用的 Agent,请为每项事实回复一个 XML 标签:
<fact-verdict id="abc">PASS</fact-verdict>
<fact-verdict id="def">FAIL the feature is missing from auth.rs</fact-verdict>
规则:
- 对成立的事实使用
PASS,对不成立的事实使用FAIL reason - 在
FAIL后附上一行原因 - 每项事实都要给出一个判定,不要跳过任何一项
id属性必须与提示词中的事实 ID 一致- 解析前会移除 Markdown 围栏和 ANSI 代码
- 如果同一个 ID 出现多次,以最后一个判定为准
- 没有匹配判定的事实会报告为“未解决”(按失败处理)
标志:
--no-agent— 跳过 Agent 验证(未传入--agent时的默认行为)--id <ID>— 仅检查指定事实(可重复使用,并可与所有筛选条件组合)--section "path"— 按分节路径筛选(不区分大小写地匹配子路径,包含子级)--tags "expr"— 布尔标签筛选(例如"ci and not draft")--search "expr"— 在分节、标签和标签名中进行全文搜索--file "name"— 筛选指定的.facts文件--has-command— 仅检查带有验证命令的事实(跳过手动事实)--manual— 仅检查手动事实(没有验证命令)。适合与--agent搭配使用--depth N— 限制分节嵌套深度--timeout <secs>— 每条命令和每批 Agent 验证的超时时间--strict— 将未经验证的手动事实视为失败(返回非零退出码)。对于要求每项事实都必须通过命令或 Agent 验证的 CI 流水线来说,这一点至关重要--quiet/-q— 不输出内容,仅返回退出码(0 = 全部通过)--verbose/-v— 显示 Agent 提示词和原始输出,以便调试
编写高质量的事实
事实应描述项目的行为,包括其行为方式、功能和约定,而不是它包含哪些文件或使用哪些库。判断标准:如果 Agent 仅依据事实表重新实现该项目,结果的行为是否正确?
- 描述行为 — 从用户的角度描述会发生什么(“过期令牌会被拒绝,并返回 401”),而不是代码中有什么(“存在一个身份验证模块”)
- 保持原子性 — 每项事实只陈述一个真相,且可独立验证
- 使用陈述句 — 陈述事实,而不是指示该怎么做(“使用 PostgreSQL”,而不是“设置 PostgreSQL”)
- 保持稳定 — 不应随每次提交而改变(“测试通过”,而不是“有 47 个测试”)
- 可证伪 — 可以设想一种有缺陷的实现,使该事实不成立
结构性事实(依赖项、文件布局)是辅助细节,而非主要内容。事实表如果充斥着“使用 X 库”和“有 Y 目录”,就无法告诉 Agent 项目实际上如何运作。
优秀的验证命令应快速、幂等,并且只测试一件事。优先使用 test -f、grep -q 和简短的脚本检查,而不是运行完整构建。
好事实的特征
以下每项质量特征,都是对真实的 Agent 维护项目中观察到的一种失败模式的反面。
- 规格优先——事实应在代码编写前写下,而不是事后补上。事实表是规格说明,不是变更日志。完成后添加
@implemented标签,而不是为已完成的工作添加新事实。 - 行为标签——标签描述用户或调用方能观察到的现象。源文件名、行号、分支名和覆盖率百分比都不应出现在事实标签中——这些内容应放在测试里。
- 黑盒检查——命令应从外部进行测试:运行工具、调用 API、检查输出。如果命令会解析源文件以查找变量名、函数签名或特定字符串位置,就应改为测试行为。
- 独立价值——移除该事实会导致信息缺失。如果现有测试已验证了相同的主张,那么该事实只是一个透传包装器——应改为陈述不同的主张,或直接跳过。
- 主动维护——添加事实前,先检查现有事实是否已涵盖该行为。合并相关事实,避免事实表无限膨胀。单个文件超过约 80 条事实时,应拆分。
- 契约级别——每项功能契约对应一条事实,而不是每条代码路径对应一条事实。如果三条事实分别描述同一端点的成功、错误和边界情况路径,就应将它们合并为一条关于该端点契约的事实。
领域词汇
主 .facts 文件中的 ## domain 部分定义了项目的关键实体及其关系。这些也是事实——原子化的真实陈述——但描述的是概念模型,而非具体行为。
实体事实用于命名和定义概念,常见格式为 a <Name> is <definition>。
关系事实说明实体之间如何关联。请在自然的陈述句中使用已定义的实体名称——没有固定语法,但关联应具体明确。
## domain
- a FactSheet is a parsed *.facts file containing sections and facts
- a Section is a heading-delimited group of facts, nestable via heading depth
- a Fact is an atomic truth statement: a label, optional command, optional tags
- a FactSheet contains a preamble and nested Sections
- check validates command-bearing Facts by running them in the project root
- Tags filter Facts via boolean expressions
请在整个事实表中始终使用这些名称。领域部分位于主 .facts 文件中。它由 facts-discover 构建,由 facts-refine 完善;实现过程中出现新概念时,则由 facts-implement 扩展。
Agent 工作流
开始工作时——始终先执行此步骤:
facts ll # read the full spec (skim view)
facts check # see what holds and what doesn't
编写代码前,先通过事实表了解情况。它是项目应有状态以及已验证内容的事实来源。
定义规格(最常见的用户意图): 当用户说“处理事实”“添加事实”或“定义规格”时,他们希望协作定义哪些内容应该成立,而不是审查现状。
facts add "users can sign up" --section features/auth --tags "draft"
facts add "signup returns 201" --command "curl -s ..." --section features/auth --tags "spec"
- 与用户讨论项目应有的状态
- 将初步想法添加为
@draft——之后会将其完善为精确规格 - 将精确、可执行的事实添加为
@spec——这些事实已可供实现 - 不要移除
@draft或@spec事实——它们代表预期的未来工作 - 除非用户明确要求与实际情况同步,否则不要运行
facts-discover
跟踪生命周期进度:
facts ll --tags "draft" # rough ideas to refine (ll = list --light)
facts ll --tags "spec" # ready to implement
facts ll --tags "implemented" # done
facts ll --tags "not implemented" # all remaining work
facts check # verify
在编码过程中保持准确: 添加功能时,添加相应的事实。修复错误时,确认相关事实仍然成立。移除代码时,移除过时的事实。添加事实前,检查现有事实表是否已涵盖该行为——合并相近的重复事实,不要一味累积。
facts check # find failing facts
facts at <id> "implemented" # tag done (alias for edit --add-tag)
facts rm <id> # remove obsolete facts
facts add "new truth" --section foo # add discovered truths
配套技能
每项技能负责一次生命周期转换:
- facts-discover——扫描代码库,并按生命周期阶段(
@draft、@spec、@implemented)对每项事实进行分类。用于分流或初始化。 - facts-refine——选取
@draft事实,与用户协作将其完善为精确的@spec事实。用于需要进一步明确草稿的情况。 - facts-implement——选取
@spec事实,在代码中实现,然后添加@implemented标签。用于规格已准备就绪、可以开始构建的情况。