What Contexo is
Contexo gives a project three things:
.contexo/— a directory of short markdown pages, one topic each.- An MCP server (
ctx mcp) — so your coding agent can read those pages and write new ones itself. - A server-side git repo per project — so a page you push is versioned, attributed, and available to everyone else’s agent.
The unit of sharing is a page: markdown with frontmatter, written by an agent at your request, pushed when you say so. Not a transcript, not a memory blob.
The loop
Section titled “The loop”-
Your agent works. With capture hooks installed, every turn is appended to a local buffer at
.contexo/raw/sessions/_pending/<session-id>.jsonl. No LLM call, no network — nothing leaves the machine. -
You decide something is worth sharing — “push what we worked out about billing”. The agent calls
ctx_push. -
The push pauses to distil. If the batch contains a concept or analysis page and a capture buffer from the last 6 hours exists,
ctx_pushsends nothing. It returns a<PUSH_PAUSED reason=distill_required>directive with the buffer inlined and a template to fill in: Decision, Why this approach, Rejected alternatives, Path of inquiry, Dead-ends, Open questions, Sources. -
The agent writes the source page with
ctx_write_page(type: "source"), then re-invokesctx_pushwithdistill_done: trueandsource_slug. The concept page’ssources:list is patched to point at it, and both go up in the same commit. -
The server commits. Each push is a git commit in that project’s repo, authored as your signed-in identity, and the response carries the new HEAD.
-
A teammate pulls —
ctx pull, or their agent callsctx_pullwhen they start work on the topic. -
Their agent starts informed. It reads
ctx://indexfirst, opens the pages that matter, and drills intoraw/sessions/only when a page cites one. It does not re-derive the decision, and it does not re-walk the dead-ends you already ruled out.
Notice where the human sits in that loop: at step 2, and nowhere else. Capture is local and passive; crossing the boundary into team knowledge is always explicit.
The .contexo/ tree
Section titled “The .contexo/ tree”Directory.contexo/
- config.json server URL, repo id, dashboard URL
- credentials.json written by
ctx login, mode 0600 - index.md generated — every page, grouped by type
- tags.md generated — tag → page
Directorywiki/
Directoryconcepts/
- …
Directoryentities/
- …
Directoryanalyses/
- …
Directoryraw/
Directorysessions/ source pages (
_pending/holds capture buffers)- …
Directory.sync/ sync state — the sha of your last pull, and one per page
- …
ctx init creates all of that except credentials.json (written by ctx login)
and .sync/ (written by the first push or pull). It also appends .contexo/ to
your .gitignore — when the project is a git repo. That is deliberate:
knowledge does not ride along in your project’s git history, it syncs to the
Contexo server’s git repo through ctx push / ctx pull. The two histories stay
separate, so a page can be corrected without touching a code commit.
The four page types
Section titled “The four page types”The type passed to ctx_write_page decides both the meaning and the path —
there is no way to file a page somewhere else.
| Type | Lives at | Use it for |
|---|---|---|
concept |
wiki/concepts/<slug>.md |
How or why something works in this codebase — trials, retries, the auth handshake |
entity |
wiki/entities/<slug>.md |
A named system, service, library, product or database: purpose, where it lives here, gotchas |
analysis |
wiki/analyses/<slug>.md |
A comparison or evaluation across options |
source |
raw/sessions/<slug>.md |
The reasoning trail behind a decision — usually written in response to the push handshake above |
Slugs are a single clean path component: letters, digits, -, _, ., up to
200 characters; no /, no .., no leading - or .. A slug that would escape
the store is rejected before anything is written.
What a good page looks like
Section titled “What a good page looks like”This is the stripe-subscription page used in examples throughout these docs,
exactly as ctx_write_page serializes it:
---schema: ctx.page.v1slug: stripe-subscriptiontype: conceptauthor: devagent: claudecreated: 2026-07-20T10:04:00Zupdated: 2026-07-20T10:04:00Zparent_sha: ""related: - webhook-retriestags: - stripe - billingreasoning_summary: How trials, proration and webhooks fit together in acme-api.---## Concepts
Trials are created with `trial_period_days` on the subscription, not with a100%-off coupon. Proration is disabled on downgrades.
## Agent Reasoning
Considered modelling trials as 100%-off coupons so the billing code had onepath; rejected because refunds then reconcile against a zero-amount invoiceand the finance export double-counts them.
## Open Questions
Whether to prorate on upgrade-then-immediate-downgrade inside one period.Worth noticing:
- There is no
titlefield. The index derives a display title from the slug (stripe-subscription→ “Stripe Subscription”), so the slug is the name. reasoning_summaryis the index entry. It is the one line a teammate’s agent sees inindex.mdbefore deciding whether to open the page, so write it as a claim, not a topic: “Rejected Connect (negative-balance ownership); chose Billing + metered” beats “notes on billing”. With no summary the index falls back to the page’s first sentence.- Required frontmatter is small:
schema,slug,type,author,created. Everything else —agent,updated,parent_sha,sources,related,tags,reasoning_summary— is optional, and you never type any of it.ctx_write_pagefills it in. - The page is short. Concept and entity pages are meant to be a couple of KB so an agent can afford to read several of them.
The Agent Reasoning section
Section titled “The Agent Reasoning section”A page that only says “we use Stripe Billing” saves a teammate five minutes. A page that also says why Connect was rejected saves their agent from re-evaluating Connect, getting confused by the fee-split docs, and burning an afternoon arriving where you already were.
That is what ## Agent Reasoning is for: considered → rejected → because. It is
the section ctx_write_page tells the agent to always include, and it is the
reason a distilled page beats a link to a chat log.
What Contexo is not
Section titled “What Contexo is not”- Not a chat-log dump. The capture buffer is
.jsonlunderraw/sessions/_pending/; the page store only ever lists, indexes and pushes.mdfiles. The buffer is raw material for one distilled source page, and it is moved to_pending/_archive/by thedistill_done: truepush that lands that page. - Not a memory feature. Agent memory is private, implicit and unreviewable. Contexo pages are files: you read them, edit them, diff them, and decide to push them. Nothing is remembered on your behalf.
- Not automatic. There is no background sync.
ctx pushandctx pullhappen because you — or your agent, at your request — ran them. - Not global. Contexo is per project. Knowledge that isn’t about this codebase belongs somewhere else.
- Not a code-blame tool. “Which commit broke this?” is
git blame. Contexo answers “what does the team already know about this topic?”
- Install the CLI
- Quickstart — from nothing to a page your teammate’s agent can read
- Connect your agent — Claude Code, Cursor, Codex, anything else that speaks MCP
