Skip to content

export ​

Export a session transcript as Markdown or JSON across Claude Code, Codex, GitHub Copilot (CLI and VS Code), Pi, and OpenClaw.

Usage ​

bash
claudex export <selector> [--format markdown|json]
                           [-o/--output <path>]
                           [-p/--project <substr>]

Flags ​

FlagDefaultDescription
<selector>—Session ID prefix or project name substring. Required.
--formatmarkdownmarkdown or json.
-o, --output <path>stdoutWrite to a file instead of stdout.
-p, --project <substr>—Disambiguate when the selector matches more than one session.

Example ​

bash
# List recent sessions to find a prefix
claudex sessions --limit 5

# Export as Markdown to stdout
claudex export e1a2f4

# Save to a file
claudex export e1a2f4 --output session.md

# JSON instead
claudex export e1a2f4 --format json --output session.json

# Ambiguous prefix? Add --project
claudex export ab --project claudex

Markdown output ​

The Markdown export renders the full conversation as:

markdown
# Session: <short-id>

**Project:** <path>
**Date:** <YYYY-MM-DD HH:MM UTC>
**Model:** <model>

---

## User

_<timestamp>_

<content>

## Assistant

_<timestamp>_

<content>

Each turn is a section. Tool calls and tool results are inlined. This is the shape you want for pasting into docs, wikis, or PR descriptions.

JSON output ​

If the selector resolves to one session, JSON export is one object. If it resolves to multiple sessions, JSON export is an array of those objects. Each object has top-level keys: session_id, project, date, model, message_count, messages, normalized_messages, and records. messages and records preserve every raw JSONL record from the source session file, in chronological order; normalized_messages is the cross-provider role/text view.

json
{
  "session_id": "<uuid>",
  "project": "<path>",
  "date": "<iso-8601>",
  "model": "<model>",
  "message_count": 14,
  "provider": "claude",
  "file_path": "<absolute-path>",
  "extras": null,
  "normalized_messages": [
    { "role": "user", "timestamp": "<iso-8601>", "text": "..." }
  ],
  "messages": [
    {
      "type": "user",
      "uuid": "...",
      "parentUuid": null,
      "sessionId": "<uuid>",
      "timestamp": "<iso-8601>",
      "cwd": "<path>",
      "gitBranch": "<branch>",
      "version": "<claude-code-version>",
      "userType": "external",
      "isSidechain": false,
      "permissionMode": "default",
      "entrypoint": "cli",
      "promptId": "...",
      "message": { "role": "user", "content": "..." }
    }
  ],
  "records": [
    {
      "type": "user",
      "uuid": "...",
      "parentUuid": null,
      "sessionId": "<uuid>",
      "timestamp": "<iso-8601>",
      "cwd": "<path>",
      "gitBranch": "<branch>",
      "version": "<claude-code-version>",
      "userType": "external",
      "isSidechain": false,
      "permissionMode": "default",
      "entrypoint": "cli",
      "promptId": "...",
      "message": { "role": "user", "content": "..." }
    }
  ]
}

For Claude, raw records keep the Anthropic-shaped message payload. Codex, Copilot, Pi, and OpenClaw records keep their native provider shapes (VS Code Copilot Chat exports the replayed session document).

Useful jq:

bash
# Count messages by role
claudex export <sid> --format json \
  | jq '[.normalized_messages[].role] | group_by(.) | map({role: .[0], count: length})'

# Extract only the text content
claudex export <sid> --format json \
  | jq -r '.normalized_messages[] | select(.role == "assistant") | .text'

Notes ​

  • Selector precedence. Session-ID prefix wins when it matches one or more sessions, even when --project is set. If the selector looks like a Claude session-ID prefix and matches nothing, claudex does not fall back to project-name matching.
  • No --json flag. Use --format json — it's the format of the export, not a summary layer.
  • Multiple matches. Markdown writes the matching sessions sequentially. JSON returns a proper array instead of concatenated objects.
  • Large sessions. Markdown export is streaming where possible, but huge sessions (100+ MB JSONL) can produce very long Markdown files. Use --format json if you want to slice the output yourself.

Released under the MIT License.