cairn

Cookbook

Every command here is extracted and run against the real program by the test suite, because a manual that claims a flag nobody implemented is worse than no manual.

Short recipes for what people actually arrive wanting. Each is a sequence you can paste.

Every command on this page is extracted by tests/manual.rs and run against the real binary, which refuses any the program would reject. That test exists because the manual twice claimed a flag that was never implemented.

Adopt cairn where GitHub issues already exist

cairn init --name Nimbus
cairn import --from github --repo owner/name --dry-run
cairn import --from github --repo owner/name
cairn check --strict

The dry run first, because an import is the one operation that creates a hundred files at once. GitHub is read through gh, which you have already authenticated, so cairn never handles a token. Importing again later updates rather than duplicates — provenance travels in source.

Triage a week’s arrivals without typing an identifier twice

cairn list --filter 'milestone=,category=open' --ids
cairn set --filter 'milestone=,type=bug' milestone=v0.1 priority=p1

The first line is the reading pass. The second changes everything it matched, after showing you the list and asking. If you run the same query weekly, put it in cairn.toml as a view and give it a name.

Make the roadmap unable to drift

cairn check --strict --render

One line in CI. --strict makes warnings fail; --render additionally fails when the committed ROADMAP.md no longer matches what the items say.

Give an agent a scoped slice

cairn agent --write AGENTS.md
cairn next --filter 'milestone=v0.1' --json
cairn claim --next --filter 'type=bug'

The instruction block is generated from your schema, so it cannot describe a workflow you do not have. Scoping the claim rather than the listing is what makes the last line safe: doing it as list-then-claim leaves a gap in which another agent can take the item.

An agent that speaks the Model Context Protocol should be given cairn mcp instead — the same operations, with no command lines to get wrong.

Move a backlog between two different schemas

cairn export > backlog.json
cairn import --from json backlog.json --dry-run

The document carries the schema alongside the items, so the receiving project can interpret status names it has never seen. Names that do not exist there map by category — one project’s shipped is another’s done — which is why categories are a fixed axis under the names you choose.

Find everything one person is holding

cairn list --assignee alice --filter 'category=active'
cairn list --filter 'assignee=,category=active'

The second line is the more useful one: work in progress and assigned to nobody, which is what a stalled item looks like. An empty value means unset.

Recover after a bad merge

cairn check
cairn render
cairn check --render --strict

A merge the hook did not see — a rebase, a cherry-pick, a forge merge — can leave two items with one number. cairn renumber repairs it; at a merge it also moves the references that came with the renumbered item. Resolve conflicting edits, regenerate the roadmap, and validate.

Keep a personal backlog that is not a code project

cairn init --name Oddur --preset minimal --bare
cairn new now -t horizon
cairn new next -t horizon
cairn new someday -t horizon
cairn new 'Migrate the other projects' -t task --set horizon=now

Cairn needs a cairn.toml and nothing else — not a git repository, not source code. So a directory in your home is a tracker for the work that belongs to you rather than to a repository: the thing that spans four projects, the idea with no home yet, the decision you keep deferring.

The one change worth making to the schema is what work is filed under. A release-numbered milestone is meaningless here, so name the grouping type after the question you actually ask:

[[type]]
name = "horizon"
groups = "one"

Now now, next and someday are what the board columns and the roadmap are built from, and cairn list heads the column HORIZON. Nothing else in the schema changes, because cairn looks for the type that says it groups work and never for the word milestone.

Run it from anywhere with -C, which takes the directory holding the cairn.toml: cairn -C ~/cairn next. A shell alias is worth the ten seconds.

Keep it out of a remote unless you mean it. An untracked item and a private repository are the same idea; the second one is honest about it.