Skip to content

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.

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.

Terminal window
ctx capture status # list pending buffers for this project
ctx capture --help
Capture 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.

Lists the session buffers that are still pending — captured but not yet distilled and archived.

Terminal window
ctx capture status

ctx capture status takes no flags.

ctx capture status
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.

Terminal window
# 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)
FlagTypeDefaultDescription
--agentstringclaudesource agent: claude|codex|cursor
--cwdstringworking directory used to locate .contexo (falls back to stdin payload, then os.Getwd)
--sessionstringsession id (falls back to stdin payload)
--transcriptstringpath 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.

  1. If CONTEXO_CAPTURE_DISABLE=1, return immediately.
  2. Read and JSON-parse stdin, tolerating garbage (non-JSON stdin is simply ignored). Resolve the session id from --session, then the payload’s session_id, then its conversation_id — Cursor has no session_id, only a conversation. With none of them, the id falls back to unknown-<YYYYMMDD>.
  3. Find the project root by walking up from the working directory. No .contexo anywhere above? Silent no-op.
  4. 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. Only text blocks are kept; thinking and tool_result blocks are dropped, and tool_use blocks contribute their names to a tools list.
    • Codex / Cursor — no transcript exists. The prompt-side event (UserPromptSubmit / beforeSubmitPrompt) writes the prompt to a sidecar <session-id>.prompt file 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.
  5. Append one record to .contexo/raw/sessions/_pending/<session-id>.jsonl.
  6. 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.

Nothing consumes the buffer automatically. Three things read it, all on demand:

  • ctx_push pauses 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 with distill_done: true, it archives that buffer into _pending/_archive/.
  • ctx_capture_session hands the agent the most recent buffer from the last 24 hours plus a page template, on request.
  • ctx_status reports the pending-buffer count to the agent. (The CLI’s ctx status does not — that count is MCP-only.)

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.

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.