ctx capture
ctx capture is the local reasoning buffer: a plain JSONL file per agent
session that the capture hooks append to, and that ctx_push and
ctx_capture_session later hand to the agent to distill into a source page.
When you’d reach for it
Section titled “When you’d reach for it”Almost always to answer one question: is anything actually being captured?
ctx capture status is the check you run when a push doesn’t pause for the
distill handshake, or when ctx_capture_session says there’s no recent buffer —
an empty list means the hooks aren’t firing, which is a
ctx hooks problem, not a capture problem. The mental model that
saves time: capture is a dumb append-only buffer. It runs no model, makes no
network call, and never writes a knowledge page. It records turns so that the
agent has raw material to distill later, and it is deliberately built to fail
silently rather than break your agent mid-turn.
Synopsis
Section titled “Synopsis”ctx capture status # list pending buffers for this projectCapture utilities for the agent-reasoning buffer
Usage:
ctx capture [command]
Available Commands:
status Show pending capture buffers
Flags:
-h, --help help for capture
Global Flags:
--root string project root directory (default: current directory)
Use "ctx capture [command] --help" for more information about a command.Only status is listed: ctx capture turn is a hidden command that the hooks
invoke — see below.
ctx capture status
Section titled “ctx capture status”Lists the session buffers that are still pending — captured but not yet distilled and archived.
ctx capture statusctx capture status takes no flags.
No pending capture buffers.Nothing had been buffered when that was recorded. You get the same line outside
a Contexo project, or before any hook has fired: the command treats a missing
pending directory as “nothing pending” rather than an error. If you expected
turns here, check ctx hooks status first.
When buffers do exist, the command prints one line per file in
.contexo/raw/sessions/_pending/, most recently written first, with the number
of turn records and the file’s age (rounded down to the minute). Archived
buffers, under _pending/_archive/, are not listed — so a buffer disappearing
from this output after a successful push is the expected outcome, not data loss.
ctx capture turn
Section titled “ctx capture turn”# What the hooks are configured to run:ctx capture turn # Claude Code (Stop)ctx capture turn --agent codex # Codex (Stop, UserPromptSubmit)ctx capture turn --agent cursor # Cursor (beforeSubmitPrompt, afterAgentResponse)| Flag | Type | Default | Description |
|---|---|---|---|
--agent | string | claude | source agent: claude|codex|cursor |
--cwd | string | working directory used to locate .contexo (falls back to stdin payload, then os.Getwd) | |
--session | string | session id (falls back to stdin payload) | |
--transcript | string | path to the session transcript JSONL (falls back to stdin payload) |
--session, --transcript and --cwd exist only as a fallback: the hook
payload on stdin is the real input, and each of them wins over the matching
payload field. --agent has no payload counterpart — it picks the extraction
path (parse Claude’s transcript, or pair Codex/Cursor’s two inline events),
which is why the installed hook command carries it. One inherited flag does
not apply — ctx capture turn never consults the global --root.
It resolves the project from --cwd, then the payload’s cwd, then the
process’s working directory, and walks up from there looking for a .contexo
directory. That’s what lets a hook fire from a subdirectory of your repo and
still find the project.
How it works
Section titled “How it works”- If
CONTEXO_CAPTURE_DISABLE=1, return immediately. - Read and JSON-parse stdin, tolerating garbage (non-JSON stdin is simply
ignored). Resolve the session id from
--session, then the payload’ssession_id, then itsconversation_id— Cursor has nosession_id, only a conversation. With none of them, the id falls back tounknown-<YYYYMMDD>. - Find the project root by walking up from the working directory. No
.contexoanywhere above? Silent no-op. - Extract the exchange, which differs per agent:
- Claude — parse the transcript JSONL at
transcript_path, take the last assistant record and the user record before it. Onlytextblocks are kept;thinkingandtool_resultblocks are dropped, andtool_useblocks contribute their names to atoolslist. - Codex / Cursor — no transcript exists. The prompt-side event
(
UserPromptSubmit/beforeSubmitPrompt) writes the prompt to a sidecar<session-id>.promptfile and exits; the response-side event (Stop/afterAgentResponse) reads that sidecar, deletes it, and pairs it with the assistant text carried inline in its own payload.
- Claude — parse the transcript JSONL at
- Append one record to
.contexo/raw/sessions/_pending/<session-id>.jsonl. - Delete pending buffers not written to in the last 30 days.
A record is one JSON object per line: a timestamp, a turn index, user,
assistant and tools. The buffer is bounded on three axes — the user text is
truncated at 2 KB and the assistant text at 4 KB (with a trailing ...), a
record whose turn index already exists is dropped as a duplicate, and once a
buffer reaches 500 records the oldest 100 are discarded and replaced by a single
marker record carrying "truncated": {"dropped": 100, "reason": "buffer_cap"},
so a reader can tell that history was cut rather than never recorded.
Where the buffer goes next
Section titled “Where the buffer goes next”Nothing consumes the buffer automatically. Three things read it, all on demand:
ctx_pushpauses with a distill directive when the batch contains a concept or analysis page and a buffer was written in the last 6 hours. Re-invoked withdistill_done: true, it archives that buffer into_pending/_archive/.ctx_capture_sessionhands the agent the most recent buffer from the last 24 hours plus a page template, on request.ctx_statusreports the pending-buffer count to the agent. (The CLI’sctx statusdoes not — that count is MCP-only.)
Failure behaviour
Section titled “Failure behaviour”ctx capture turn is written never to fail the agent’s turn: every error path
returns success. A broken transcript path, an unwritable buffer, a failed prune
— each prints a line to stderr beginning ctx capture turn: and then exits 0.
Being outside a Contexo project, or having capture disabled, produces no output
at all.
That means an agent will never show you a red hook error for capture, and it
also means a silently absent buffer is expected behaviour rather than a crash.
When turns aren’t showing up in ctx capture status, check in this order:
ctx hooks status (is the hook installed for that agent?), whether the agent
was restarted since installing it, and whether CONTEXO_CAPTURE_DISABLE is set
in the environment the agent runs in.
Common errors
Section titled “Common errors”Everything here is a warning, not a failure — ctx capture turn still exits 0.
| Message (stderr) | Cause and fix |
|---|---|
ctx capture turn: capture: empty transcript path |
Ran as Claude (the default --agent) with no transcript_path in the payload — usually a hand-run of the command, or a hook wired to the wrong event. Codex and Cursor need --agent codex / --agent cursor, which take no transcript at all. |
ctx capture turn: capture: open transcript: … |
The transcript file named by the payload isn’t readable. That turn is skipped; the next one will still record. |
ctx capture turn: append: … |
The buffer under .contexo/raw/sessions/_pending/ couldn’t be written — check permissions and free space. |
capture: list pending: … (from ctx capture status) |
The pending directory exists but can’t be read. Unlike a missing directory, this is a real error and exits non-zero. |
See also
Section titled “See also”ctx hooks— installs the hooks that callctx capture turnctx push— where a buffer becomes a source pagectx_capture_session— the agent-facing way to read a buffer- Global flags and environment — including
CONTEXO_CAPTURE_DISABLE
