Skip to content

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.

  1. 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.

  2. You decide something is worth sharing“push what we worked out about billing”. The agent calls ctx_push.

  3. 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_push sends 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.

  4. The agent writes the source page with ctx_write_page(type: "source"), then re-invokes ctx_push with distill_done: true and source_slug. The concept page’s sources: list is patched to point at it, and both go up in the same commit.

  5. 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.

  6. A teammate pullsctx pull, or their agent calls ctx_pull when they start work on the topic.

  7. Their agent starts informed. It reads ctx://index first, opens the pages that matter, and drills into raw/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.

  • 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 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.

This is the stripe-subscription page used in examples throughout these docs, exactly as ctx_write_page serializes it:

.contexo/wiki/concepts/stripe-subscription.md
---
schema: ctx.page.v1
slug: stripe-subscription
type: concept
author: dev
agent: claude
created: 2026-07-20T10:04:00Z
updated: 2026-07-20T10:04:00Z
parent_sha: ""
related:
- webhook-retries
tags:
- stripe
- billing
reasoning_summary: How trials, proration and webhooks fit together in acme-api.
---
## Concepts
Trials are created with `trial_period_days` on the subscription, not with a
100%-off coupon. Proration is disabled on downgrades.
## Agent Reasoning
Considered modelling trials as 100%-off coupons so the billing code had one
path; rejected because refunds then reconcile against a zero-amount invoice
and the finance export double-counts them.
## Open Questions
Whether to prorate on upgrade-then-immediate-downgrade inside one period.

Worth noticing:

  • There is no title field. The index derives a display title from the slug (stripe-subscription → “Stripe Subscription”), so the slug is the name.
  • reasoning_summary is the index entry. It is the one line a teammate’s agent sees in index.md before 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_page fills 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.

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.

  • Not a chat-log dump. The capture buffer is .jsonl under raw/sessions/_pending/; the page store only ever lists, indexes and pushes .md files. The buffer is raw material for one distilled source page, and it is moved to _pending/_archive/ by the distill_done: true push 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 push and ctx pull happen 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?”