ctx evolution
ctx evolution prints every commit that touched a page together with what that
commit changed — ctx history and a per-commit
ctx diff folded into one request.
When you’d reach for it
Section titled “When you’d reach for it”Use it when you’ve just landed on an unfamiliar page and the question is “how did
this get to be what it is?” — a decision that reversed itself, a section that kept
getting rewritten, an entity page that grew for six months. It is also the command
to reach for instead of scripting a loop of ctx diff over ctx history output:
one HTTP call replaces one history call plus N diff calls.
What trips people up is the default rendering. Without --show-diff each commit
gets a one-line summary that names at most four changed items and then says
+N more; unchanged sections are omitted from the summary entirely. It is a
scan-friendly index, not the diff. Pass --show-diff when you actually need to
read the content.
Synopsis
Section titled “Synopsis”ctx evolution <slug>ctx evolution <slug> --show-diffctx evolution <slug> --limit 5ctx evolution <slug> --json| Flag | Type | Default | Description |
|---|---|---|---|
--blame | bool | false | annotate each section with the commit that originally introduced its heading |
--json | bool | false | emit raw JSON instead of formatted text |
--limit | int | 20 | max commits in the evolution |
--show-diff | bool | false | include the full per-section diff under each commit |
--type | string | page type (concept|entity|analysis|source); only needed when the slug is ambiguous |
--limit keeps the N most recent commits and drops everything older. It does not change how any individual
entry is computed: each commit is still diffed against the previous commit that
touched this page, whether or not that commit made the cut. A value of 0 or less
is ignored and the server falls back to its own default of 20.
--show-diff inlines the full ctx diff rendering under each commit, indented
four spaces, including that commit’s own from..to header.
--blame uses the same machinery described on ctx diff — it
annotates each section with the commit that first introduced its heading. Note it
runs that history walk once per entry, so --limit 20 --blame performs twenty
separate walks; keep --limit small when you combine them.
--json emits an array of {commit, diff} objects: commit carries
sha/author/email/time/message, and diff is the same SectionDiff
shape ctx diff --json produces.
| Flag | Type | Default | Description |
|---|---|---|---|
--root | string | project root directory (default: current directory) |
Examples
Section titled “Examples”The trajectory of a page at a glance:
ctx evolution stripe-subscriptionEvolution of wiki/concepts/stripe-subscription.md (2 commits, newest first):
b7e9f204 2026-07-28 dev ctx push (1 pages)
frontmatter, ~ Concepts, + Webhooks, - Open Questions
a4b2c1d3 2026-07-28 dev ctx push (1 pages)
frontmatter, + Concepts, + Agent Reasoning, + Open QuestionsThe summary line under each commit uses the same markers as ctx diff with the
## prefix stripped: ~ modified, + added, - removed, ~> renamed, plus a
bare frontmatter token when any frontmatter field changed and preamble when
the text above the first heading changed.
The same thing with the actual content:
ctx evolution stripe-subscription --show-diffEvolution of wiki/concepts/stripe-subscription.md (2 commits, newest first):
b7e9f204 2026-07-28 dev ctx push (1 pages)
diff: a4b2c1d..b7e9f20
Frontmatter
~ updated
- 2026-07-20T10:04:00Z
+ 2026-07-24T16:31:00Z
+ tags: proration
Sections
~ ## Concepts (2 lines changed)
+ ## Webhooks (2 lines added)
= ## Agent Reasoning
- ## Open Questions (1 lines removed)
a4b2c1d3 2026-07-28 dev ctx push (1 pages)
diff: ..a4b2c1d
Frontmatter
+ agent: claude
+ author: dev
+ created: 2026-07-20T10:04:00Z
+ parent_sha:
+ reasoning_summary: How trials, proration and webhooks fit together in acme-api.
+ related: [webhook-retries]
+ schema: ctx.page.v1
+ slug: stripe-subscription
+ tags: [stripe billing]
+ type: concept
+ updated: 2026-07-20T10:04:00Z
Sections
+ ## Concepts (2 lines added)
+ ## Agent Reasoning (3 lines added)
+ ## Open Questions (1 lines added)Note the oldest entry’s header: diff: ..a4b2c1d. An empty left-hand sha means
that commit is the page’s birth — there was no prior version, so every section and
every frontmatter key shows as added. You only see that when the page’s whole
history fits inside --limit; if the list was truncated, even the oldest entry
shown has a normal parent..sha range.
Recent history only, with attribution:
ctx evolution stripe-subscription --limit 5 --blameHow it works
Section titled “How it works”- The slug is resolved against your local working copy —
.contexo/wiki/concepts/,.contexo/wiki/entities/,.contexo/wiki/analyses/,.contexo/raw/sessions/— and--typenarrows that search when the slug is ambiguous. The page must exist locally even though the content comes from the server. repo_idcomes from.contexo/config.json; the bearer token and server URL come from.contexo/credentials.json, whose URL wins overconfig.json.- The CLI issues
GET /v1/repos/<repo_id>/evolution/<path>?limit=N, appending&blame=trueonly when--blamewas passed. - The server checks membership, runs
git log -n<limit> -- <path>newest-first, and for each commit reads the page’s blob at that sha and at the previous commit that touched the same path, then runs the section differ over the pair. - A commit where the path isn’t present at that sha is skipped rather than failing the whole request.
- Blame, when requested, is applied per entry after the diffs are built and is best-effort — failures are swallowed and the entry comes back unannotated.
- Nothing is written locally and no server state changes.
ctx evolutionis read-only.
Common errors
Section titled “Common errors”sync: page evolution (404): {"error":"path not present in repo"} — the page
exists in your working copy but has never been pushed, so the server has no
history for that path. Push it with ctx push, or use
ctx diff <slug> --local to see what’s there. Note the asymmetry:
ctx history prints No commits found and exits 0 for the same
situation, while ctx evolution treats it as an error.
slug "x" not found in .contexo/ (looked in wiki/{concepts,entities,analyses}/ and raw/sessions/)
— the page isn’t in your working copy. Run ctx pull.
slug "x" is ambiguous (2 matches: [...]); pass --type to disambiguate — the same
slug exists under two page types.
evolution: not authenticated (run 'ctx auth login') — no readable
.contexo/credentials.json.
evolution: server URL or repo_id not configured — set them with
ctx remote.
sync: page evolution (403): {"error":"not a member of this repo"} — you’re
authenticated but not on this repo’s member list.
No evolution found for wiki/concepts/x.md — the server returned commits but
every one of them was skipped because the path wasn’t present at that revision.
Rare; usually means the page was moved rather than edited.
See also
Section titled “See also”ctx diff— one range at a time, and where--blameis explainedctx history— the same commit list without the diffs- Page evolution — the guide this command was built for
ctx_evolution— the MCP tool your agent calls
