Your project has 48 facts, 31 of them implemented: code-backed, verified by command. 12 are specs your agent is working through. 5 are rough drafts you'll refine later. You know all of this because you ran facts check.
# auth
- users authenticate via OAuth2 @implemented
- sessions expire after 24 hours @implemented
- failed logins rate-limited to 5 per minute @spec
- label: bcrypt password hashing with cost factor 12
command: grep -q 'bcrypt.*12' src/auth.ts
# data
- all timestamps stored in UTC @implemented
- soft deletes only, no rows dropped @spec
- PII encrypted at rest @draft
# api
- REST with versioned endpoints @implemented
- rate limiting on all public routes @spec
- structured error responses with error codes @spec
That's a .facts file, where each line is one atomic claim about your project. Tags track where each fact is in its lifecycle: @draft (rough idea), @spec (precise, ready to build), @implemented (true, code-backed). Your agent manages the transitions. The format is a flat list of claims: short enough to read in full, structured enough to manage, and when a fact has a shell command, the machine verifies it so the agent doesn't have to.
Give your agent the facts skill:
npx skills add av/factsThen ask it to Init facts. It detects your stack, creates a .facts file with initial project truths, and sets up the full workflow.
Manual install
curl -fsSL https://av.codes/facts.sh | shnpm install -g @avcodes/facts # or npm
pipx install facts-cli # or pipxIt's a single Rust binary with two dependencies, running on Linux, macOS, and Windows.
Then scaffold your project:
cd your-project
facts init # scaffold .facts + install skills
facts init api # create api.facts instead
facts checkFour skills ship with every install. Your agent uses them to manage the full lifecycle without you directing every step.
| Skill | What it does |
|---|---|
| facts | Core operations: read the spec, check it, add and edit facts |
| facts-discover | Scan the codebase, classify every fact by lifecycle stage, add missing truths |
| facts-refine | Pick up @draft facts, sharpen them into precise @spec facts with you |
| facts-implement | Pick up @spec facts, build them in code, verify, tag @implemented |
The workflow is a loop: you write rough ideas as @draft. The agent refines them into specs. Then it implements them, runs facts check, and tags what it built. You see the progress in the fact sheet, right there in the spec itself.
@draft → @spec → @implemented
Every fact moves through this pipeline. At any point you can run facts check and know exactly where your project stands: what's done, what's in progress, and what's still just an idea.
Describe what should be true by writing claims as plain strings, one fact per line. Use # headings to organize by domain, tag each fact with its lifecycle stage, and for facts the machine can verify, add a command that exits 0 when the claim holds.
Verify your facts with facts check, which lints all files, runs every command, and sends manual facts to an AI agent for verification. It groups results by status: green pass, red fail, yellow manual/unresolved. Use --agent claude (or any preset: codex, pi, droid, opencode, mi) or set a persistent default with facts config set agent claude. Auto-detects agents on your PATH. It exits 0 when everything passes, non-zero when anything fails. Plug it into CI or let your agent run it after every change.
Implement against the spec: your agent reads the fact sheet to understand the project, picks up @spec facts, builds them, and runs facts check to verify its own work. When a fact passes, it tags @implemented, and the spec updates itself as the project evolves.
A .facts file is valid Markdown and valid YAML per section.
# section
- a plain string fact @tag
- label: a fact with a check command
command: test -f src/main.rs
tags: [core, mvp]
| Key | Required | Purpose |
|---|---|---|
label |
yes | The claim |
command |
no | Shell command, exit 0 = true |
tags |
no | Freeform tokens for filtering |
id |
no | Override the auto-generated ID |
Tags filter with boolean expressions: --tags "core and not blocked". Three well-known tags (@draft, @spec, @implemented) drive the lifecycle, but any tag works.
Files: .facts is the default. Additional sheets use semantic names (cli.facts, api.facts). All *.facts files in the project root are discovered automatically. The --file flag accepts subdirectory paths (e.g. --file src/api) — subdirectory files are only visible when explicitly targeted.
Sections use Markdown headings. Nesting creates hierarchy addressable by path (api/auth). Created when you add to them, removed when empty.
IDs are short hashes of the label, stable as long as the label doesn't change.
Common short aliases (all extra args are passed through to the real command):
ll=list --lightls=listrm=removeat <id> <tag>=edit <id> --add-tag <tag>rt <id> <tag>=edit <id> --remove-tag <tag>
facts # list all facts (default)
facts ll # list in markdown-like skim format
facts ll --tags "draft" # combine alias + filter
facts check # verify everything (auto-detects agent)
facts check --agent claude # verify with a specific agent
facts check --no-agent # command-facts only, skip agent
facts check --id abc --agent claude # verify one fact with the agent
facts check --tags "mvp and not blocked" # filter by tag expression
facts check --section api/auth # filter by section path
facts check --search "auth and rate" # full-text search filter
facts check --file api.facts # filter to one file
facts check --has-command # check only facts with commands
facts check --manual --agent claude # check only manual facts with agent
facts check --depth 2 # limit section nesting depth
facts check --timeout 30 # per-command and per-batch timeout
facts check --strict # fail on unverified manual facts (CI)
facts check -q # quiet: exit code only (for CI)
facts check -v # verbose: show agent prompts/output
facts config set agent claude # persistent agent for checks
facts config set batch-size 5 # facts per agent batch
facts config show # view all config
facts config get agent # read a config value
facts config rm agent # remove a config key
facts add "claim" --section api # add a fact
facts at <id> spec # quick tag add (or use the full edit form)
facts rt <id> draft # quick tag remove
facts edit <id> --remove-command # convert to manual fact (remove command)
facts remove <id> # remove a fact
facts get <id> # look up a single fact
facts move <id> --section new/path # relocate a fact
facts list --section api/auth # filter by section
facts lint # validate structure
facts fmt # normalize all files
facts init # scaffold .facts + install skills
facts init api # create api.facts instead
facts add "claim" --file src/api # add to a subdirectory file
facts uninit # remove facts from project
Extended documentation and the full article index live in the facts GitHub wiki:
- Fact-Driven Development — the methodology in depth with examples
- .facts File Format — complete syntax, sections, tags, and IDs reference
- Facts CLI Reference — all subcommands, aliases (ll, at, rt), filters, and TUI
- Facts Lifecycle — @draft → @spec → @implemented workflow with agent skills
- Facts with AI Agents — integration with Claude, Cursor, and other agents
- Facts Check — verification, agent checks, CI, filters, and output flags
The Home page and sidebar index every guide.
This repo uses a .facts file to describe itself: 224 facts, 154 verified by command, none failing.
$ facts check
...
154 passed, 0 failed, 70 manual
Clone the repo, install facts, and run facts check to see it work on itself.
MIT



