ctx pull
ctx pull brings the server’s page changes into .contexo/ without ever overwriting a local edit. Pages you have not touched are updated, new server pages are created, and anything you changed is left alone and listed as skipped, with the command that deals with it.
When you’d reach for it
Section titled “When you’d reach for it”At the start of a session, before you or your agent start editing pages, so you begin from what the team knows. Again whenever ctx status lists pages under TO PULL.
Pull is safe to run at any time. It is not a fetch-and-overwrite: a page you edited is never replaced, even when the server has a newer version. Such a page is skipped, and if both sides changed it, it becomes a conflict for ctx resolve to settle. Skips are normal results, and pull still exits 0.
Synopsis
Section titled “Synopsis”ctx pullctx pull --json| Flag | Type | Default | Description |
|---|---|---|---|
--full | bool | false | accepted for compatibility; pull always reconciles every page |
--json | bool | false | print the ctx.pull.v1 JSON document |
--json prints the ctx.pull.v1 document on stdout and nothing else. See JSON output and exit codes.
--full is accepted for compatibility and changes nothing: pull now compares every page with the server on every run. In human mode it prints a note saying so; with --json it is silent.
ctx pull also accepts the global flag:
| Flag | Type | Default | Description |
|---|---|---|---|
--root | string | project root directory (default: current directory) |
What happens to each page
Section titled “What happens to each page”Pull looks at every page’s state (see ctx status) and does one of three things.
Written. An incoming page (you have not changed it, the server has a newer version) is overwritten with the server’s version, keeping the file’s permissions. An incoming_new page (on the server, not here) is created. They appear under written with kind update or create.
Adopted. A page in conflict or unverified whose content already equals the server’s version is adopted: the server’s version becomes its base and the conflict, if any, is cleared. Your file is not touched. These appear under adopted.
Skipped. Everything pull will not write is listed under skipped with a reason, and the human output says what to do:
| Reason | Why | What the human output says |
|---|---|---|
modified |
You changed the page and the server did not. | kept your local edits; run ctx push |
conflict |
Both sides changed it (or both created it). | conflicts with the server version; run ctx resolve <path>, then --theirs or --ours |
unverified |
Its base could not be verified, and your file differs from the server’s version. | base not verified; run ctx repair |
invalid |
The file does not parse or validate. | invalid page; fix it, then pull again |
deleted_local |
You deleted it. Deletes are not synced, and pull never brings a page back on its own. | deleted locally; ctx resolve <path> --theirs restores it |
case_collision |
Two paths differ only by letter case, which some file systems cannot hold side by side. | differs only by letter case from another page; rename one of them |
changed_during_pull |
The file changed while pull was running, or the page vanished from the server mid-pull. | changed during the pull; run ctx pull again |
Only a conflict skip leaves a record behind: pull stores the server’s version of the page under .contexo/.sync/ as a conflict, so ctx status --offline and the VS Code extension still know about it later. The other skips write nothing.
unchanged and new pages are left alone.
Examples
Section titled “Examples”The normal case when nothing changed on the server:
ctx pullAlready up to date--full is still accepted, and says it is no longer needed:
ctx pull --fullnote: --full is no longer needed; ctx pull always reconciles every page with the server
Already up to dateAfter a teammate pushed three pages, two of which you had also edited. The JSON run writes the one new page and skips the two conflicts; a second, human run shows the advice for each skipped page:
ctx pull --jsonctx pull{
"schema": "ctx.pull.v1",
"server_url": "https://contexo.example.com",
"repo_id": "acme-api",
"head": "e5a3b9c807162534435261708f9e0d1c2b3a4958",
"written": [
{
"path": "wiki/concepts/webhook-retries.md",
"sha": "7d25c6bf018a3e4d5c6b7a8990f1e2d3c4b5a697",
"kind": "create"
}
],
"adopted": [],
"skipped": [
{
"path": "wiki/concepts/stripe-subscription.md",
"state": "conflict",
"reason": "conflict",
"server_sha": "f0c47d2196385a7b4c3d2e1f0a9b8c7d6e5f4a3b"
},
{
"path": "wiki/entities/billing-worker.md",
"state": "conflict",
"reason": "conflict",
"server_sha": "3b8e1a0947c2f6d85e90a1b2c3d4e5f60718293a"
}
],
"reindexed": true,
"warnings": []
}
$ ctx pull
Pulled 0 page(s), adopted 0; HEAD=e5a3b9c8
Skipped 2 page(s):
wiki/concepts/stripe-subscription.md: conflicts with the server version; run ctx resolve wiki/concepts/stripe-subscription.md --theirs|--ours
wiki/entities/billing-worker.md: conflicts with the server version; run ctx resolve wiki/entities/billing-worker.md --theirs|--oursHow it works
Section titled “How it works”- Checks that
.contexo/exists, then resolves the server, token and repo id. The server is the one your credentials belong to: theirserver_urlwins; project credentials without one useserver_urlfrom.contexo/config.json; global credentials without one fail withserver_unresolved(exit 5 with--json) rather than send your token to a URL taken from the project. Whenconfig.jsonnames a different server, pull uses the credentials’ server and adds aserver_url_mismatchwarning. - Takes
.contexo/.sync/lock, waiting up toCONTEXO_LOCK_TIMEOUT(default 10 seconds) if another ctx process, often an agent’s push or pull, holds it. After that it fails withbusy(exit 12). - Lists the repo’s pages once, repairs missing or stale base snapshots over that listing (like
ctx repair), and classifies every page, taking recorded conflicts into account. - Fetches everything it needs before writing anything: the pages changed since your last pull in one request, and any still missing either in one more bulk request (when there are more than 8) or one by one, at most 4 at a time. The server’s bytes are cleaned of terminal escapes and other control codes, bidirectional overrides and zero-width characters, exactly as
ctx show --at serverprints them. If any fetch fails, nothing is written. - Acts on each page in path order. Right before writing or adopting a page, it re-reads your file; if the file changed since step 3, the page is skipped as
changed_during_pull. - Saves the new bases, the conflict records and
.contexo/.sync/state.json, includinglast_pull_sha(the server’s HEAD). When at least one page was written, it regenerates.contexo/index.mdand.contexo/tags.mdso the always-loaded index reflects what arrived; a failure there becomes areindex_failedwarning, not an error.
Every request carries the X-Contexo-Client header (ctx-cli, unless CONTEXO_CLIENT says otherwise), so the server’s activity feed shows what pulled.
Common errors
Section titled “Common errors”With --json, each failure is a one-line envelope on stderr with the exit code shown; without it, a message and exit 1. The --json exit codes are 0, 4, 5, 7, 8, 11 and 12 (and 1 and 3).
pull: not authenticated, with try ctx auth login: no credentials. Run ctx login. Exit 4.
pull: server URL unresolved: the credentials have no server_url, with try ctx auth login --server <url>. Exit 5.
pull: repo not configured, with try ctx remote set-repo <id>: see ctx remote. Exit 5.
no .contexo/ directory in <root>: not a project. cd to its root, pass --root, or run ctx init. Exit 5.
forbidden (HTTP 403): your token works, but you are not a member of that repo. Ask an owner for an invite and run ctx join, or fix the id with ctx remote set-repo. Exit 4. From the real binary:
{"schema":"ctx.error.v1","exit_code":4,"error":{"code":"forbidden","message":"pull: sync: list pages (403): {\"error\":\"not a member of this repo\"}","advice":[],"retryable":false,"http_status":403}}repo_not_found (HTTP 404): the server has no repo with that id. Exit 8.
busy: another ctx process held the lock past CONTEXO_LOCK_TIMEOUT. Exit 12; retry when it finishes.
network, server_error, rate_limited: exit 7, safe to retry. Nothing was written.
See also
Section titled “See also”ctx push: the other half of the loopctx resolve: settle the pages pull skipped as conflictsctx status: tells you whether a pull is neededctx_pull: the MCP tool your agent calls- Drift and conflicts: when to pull, and what to do about a page that diverged
