Skip to content

ctx_status

ctx_status answers “what is this project wired up to, what have I changed, and is anything blocking a push”. It reads local files only, with no network call, no lock and no writes, so it is the safe first thing to call when a push or pull just failed and the agent needs to know why.

The description is deliberately flat, because this tool is diagnostic rather than conversational: “Show local .contexo status: server, repo, auth, local page count, last pull sha, never-pushed pages, per-state page counts, whether a push needs a distilled session first, and conflicts.” Models reach for it when the user asks whether they are logged in, which repo this project syncs to, or whether anything is unshared.

ctx_status takes no parameters.

Plain text, one fact per line, in this order. Lines in square brackets appear only when they apply:

Server: <server URL>
[Warning: config server_url <config.json server_url> differs from your credentials' server; using <server URL>]
Repo: <repo id>
User: <name> <<email>>
Authenticated: <true or false>
Local pages: <N>
Last pull: <sha>
Never-pushed pages: <N>
Pending capture buffers: <N>
Pages: <n> unchanged, <n> modified, <n> new, <n> conflict, <n> invalid, <n> deleted locally, <n> unverified
Push needs a distilled session first: <yes or no>
[Conflicts: <path>, <path>]
[Warning: <message>]

Reading each line:

Line Meaning
Server The server requests go to (see which server below), or (unresolved — run 'ctx auth login --server <url>')
Warning: config server_url … .contexo/config.json names a different server than your credentials; ctx keeps using the credentials’ server
Repo repo_id from .contexo/config.json, or (none)
User Name and email from the credentials, or (anonymous)
Authenticated true when the credentials hold a token. The token is not checked with the server
Local pages Every parseable page in the store, whatever its origin
Last pull last_pull_sha shortened to 8 characters, or (never)
Never-pushed pages Pages with no recorded sha in .contexo/.sync/state.json: never pushed and never pulled
Pending capture buffers Unarchived session buffers under raw/sessions/_pending/
Pages The page states ctx computes offline (see below)
Push needs a distilled session first yes when a push of your changed pages would hit the distill guard
Conflicts Every page in state conflict, comma-separated
Warning Other warnings from the status run, such as an unreadable file under .contexo/.sync/ or paths that differ only by letter case

The counts come from the same three-way comparison ctx status --offline makes: each page’s working file against its base (the version you last synced), with the server side known only from conflict sidecars. modified and new are what a push would send; conflict pages are the ones a teammate also changed; unverified pages have a base ctx could not confirm, which the next online status, pull, push or ctx repair fixes. Pages that only changed on the server cannot be known offline, so the line has no incoming count: call ctx_pull, or run ctx status, to see them.

Push needs a distilled session first: yes means a ctx_push of the changed pages would pause with PUSH_PAUSED: the changed pages include a concept or analysis page, and a pending capture buffer has at least one turn and was modified within the last 6 hours. It is computed with the same check ctx push and ctx_push use, so it is the reliable predictor, unlike Pending capture buffers, which counts every unarchived buffer whatever its age.

The Server line follows the same rule as every ctx command:

  • Credentials that name a server win. If .contexo/config.json names a different one, ctx still uses the credentials’ server and adds the Warning: line right after Server; the credentials are never sent to the URL from the project config.
  • Project credentials without a server use .contexo/config.json’s server_url.
  • Global credentials without a server leave it unresolved: run ctx auth login --server <url>.
  • With no credentials, the line shows config.json’s server_url, or the default server.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "ctx_status",
"arguments": {}
}
}

The response is one text block holding the lines above.

Missing config, missing credentials and an empty store render as their placeholder values, so the tool is safe to call inside any Contexo project. Two things still fail:

  • an unreadable or corrupt .contexo/config.json or credentials file returns an error result, ctx_status: <error>;
  • if the offline page classification fails, the lines up to Pending capture buffers are still returned, followed by Sync state unavailable: <code>: <message> instead of the Pages, push gate and Conflicts lines.

Outside a Contexo project there is nothing to call: with no .contexo/ the server runs dormant and advertises zero tools.

ctx status is online by default. It makes one listing call to the server per run, so it can also show pages to pull, and it groups the pages that need an action into sections with the command for each:

ctx status
contexo  ● connected

── REMOTE
server     https://contexo.example.com  (from credentials)
repo       acme-api
user       dev <dev@example.com>
auth       authenticated (legacy)

── KNOWLEDGE
pages      4 · 2 conflict · 1 incoming · 1 unchanged
last pull  a4b2c1d3
captures   none pending

── CONFLICTS
    wiki/concepts/stripe-subscription.md   both_modified
    wiki/entities/billing-worker.md        both_modified
keep yours   ctx resolve <path> --ours
take theirs  ctx resolve <path> --theirs

── TO PULL
    wiki/concepts/webhook-retries.md   new on server
run  ctx pull

ctx status --offline makes the same no-network, no-write comparison as ctx_status. With --json it writes the full ctx.status.v1 document; offline, remote.source is none and the server side of a conflict is only the sha recorded in its sidecar:

ctx status --json --offline
{
  "schema": "ctx.status.v1",
  "generated_at": "2026-07-28T09:14:07Z",
  "offline": true,
  "root": "~/projects/acme-api",
  "contexo_dir": "~/projects/acme-api/.contexo",
  "project": {
    "repo_id": "acme-api",
    "config_server_url": "https://contexo.example.com",
    "dashboard_url": "",
    "last_pull_sha": "e5a3b9c807162534435261708f9e0d1c2b3a4958"
  },
  "server": {
    "url": "https://contexo.example.com",
    "url_source": "credentials",
    "capabilities_known": false,
    "capabilities": []
  },
  "auth": {
    "state": "unchecked",
    "credentials_source": "project",
    "token_kind": "legacy",
    "user_name": "dev",
    "user_email": "dev@example.com"
  },
  "remote": {
    "source": "none",
    "page_count": 0
  },
  "counts": {
    "unchanged": 2,
    "modified": 0,
    "new": 0,
    "incoming": 0,
    "incoming_new": 0,
    "conflict": 2,
    "deleted_local": 0,
    "invalid": 0,
    "unverified": 0,
    "ahead": 0,
    "behind": 0
  },
  "pages": [
    {
      "path": "wiki/concepts/stripe-subscription.md",
      "state": "conflict",
      "conflict_kind": "both_modified",
      "type": "concept",
      "slug": "stripe-subscription",
      "tags": [
        "stripe",
        "billing",
        "proration"
      ],
      "author": "dev",
      "agent": "claude",
      "updated": "2026-07-24T16:31:00Z",
      "reasoning_summary": "How trials, proration and webhooks fit together in acme-api.",
      "local": {
        "file": "wiki/concepts/stripe-subscription.md",
        "hash": "sha256:b466741e47980041e9d1dbc835cd11ab7fbc556de2355bbeb937d7f8ccfbe2d6"
      },
      "base": {
        "sha": "d2f0e617a3b5c7d9e1f2031425364758697a8b9c",
        "hash": "sha256:d2d5406cf1ff46eee2577caf20c3509270c93ea29c4a765fe3227446cbe55cde",
        "synced_at": "2026-07-28T09:14:07Z",
        "stale": false
      },
      "remote": {
        "sha": "f0c47d2196385a7b4c3d2e1f0a9b8c7d6e5f4a3b"
      },
      "notes": []
    },
    {
      "path": "wiki/concepts/webhook-retries.md",
      "state": "unchanged",
      "type": "concept",
      "slug": "webhook-retries",
      "tags": [
        "billing"
      ],
      "author": "bob",
      "agent": "claude",
      "updated": "2026-07-26T08:05:00Z",
      "local": {
        "file": "wiki/concepts/webhook-retries.md",
        "hash": "sha256:c766c671da23b495fe1cca2f37d833f77fef8d9f1514a9a851641d9d5be36526"
      },
      "base": {
        "sha": "7d25c6bf018a3e4d5c6b7a8990f1e2d3c4b5a697",
        "hash": "sha256:c766c671da23b495fe1cca2f37d833f77fef8d9f1514a9a851641d9d5be36526",
        "synced_at": "2026-07-28T09:14:07Z",
        "stale": false
      },
      "notes": []
    },
    {
      "path": "wiki/entities/billing-worker.md",
      "state": "conflict",
      "conflict_kind": "both_modified",
      "type": "entity",
      "slug": "billing-worker",
      "tags": [
        "billing"
      ],
      "author": "dev",
      "agent": "claude",
      "updated": "2026-07-25T11:20:00Z",
      "local": {
        "file": "wiki/entities/billing-worker.md",
        "hash": "sha256:e6a61f7de94e56acdcc074437d1700b4b9d6b8a06a140721eb5d9718718b9701"
      },
      "base": {
        "sha": "c1d8a3f50e2b4c6d8f90a1b2c3d4e5f607182930",
        "hash": "sha256:8bae15f3fa732a04b87f2c50d5e25834e1f0009bce68dde191409c51a947b828",
        "synced_at": "2026-07-28T09:14:07Z",
        "stale": false
      },
      "remote": {
        "sha": "3b8e1a0947c2f6d85e90a1b2c3d4e5f60718293a"
      },
      "notes": []
    },
    {
      "path": "wiki/entities/invoice-service.md",
      "state": "unchanged",
      "type": "entity",
      "slug": "invoice-service",
      "tags": [
        "billing"
      ],
      "author": "dev",
      "agent": "claude",
      "updated": "2026-07-26T09:40:00Z",
      "local": {
        "file": "wiki/entities/invoice-service.md",
        "hash": "sha256:4eb24f13bfe77a17a43c54bfd0b0c716f87db53939f5362d791edfd84ead9d87"
      },
      "base": {
        "sha": "e5a3b9c807162534435261708f9e0d1c2b3a4958",
        "hash": "sha256:4eb24f13bfe77a17a43c54bfd0b0c716f87db53939f5362d791edfd84ead9d87",
        "synced_at": "2026-07-28T09:14:07Z",
        "stale": false
      },
      "notes": []
    }
  ],
  "captures": {
    "pending": 0,
    "fresh": 0,
    "blocks_push": false,
    "sessions": []
  },
  "warnings": []
}
FlagTypeDefaultDescription
--jsonboolfalseprint the ctx.status.v1 document
--no-driftboolfalsealias of --offline
--offlineboolfalsemake no network request

--no-drift in the table above is a hidden alias of --offline, kept so older scripts keep working.

Differences worth knowing:

  • The CLI shows incoming pages. Online, ctx status lists pages to pull under TO PULL; ctx_status never contacts the server, so it cannot.
  • The CLI reports initialization. Outside a Contexo project it fails with not_initialized (exit 5 with --json), a state the MCP tool can’t be in, since the server runs dormant with zero tools when there is no .contexo/.
  • Field labels differ: the CLI groups the same facts under REMOTE and KNOWLEDGE, with a captures line for pending buffers.

See ctx status for the full command reference and drift and conflicts for what to do with conflicts.