cc-session-core
Typed, lossless parser for both Claude Code transcripts
(~/.claude/projects/**/*.jsonl) and Codex rollout transcripts
($CODEX_HOME/sessions/**/*.jsonl, normally ~/.codex/sessions), plus a
provider-neutral session-analysis layer (timeline, tool-call pairing, usage/cost)
and a context-map tool built on it.
TRANSCRIPT_RECORD_ADAPTER is a shared Pydantic discriminated union. Its callable
discriminator routes Claude record types (assistant, user, system, …) and
Codex rollout types (session_meta, turn_context, response_item, event_msg,
…) without a format flag. Each provider then has nested discriminated unions for
its own content: Claude message blocks/attachments, and Codex response items/events.
Provider-specific adapters remain available when a caller deliberately wants one
format only.
Parsing is lossless: models keep unknown fields (extra="allow"), and an
unmodeled record/block/attachment/event/response-item type lands in an
Unknown* carrier that still holds its payload—nothing is silently dropped.
This matters especially for Codex because its
hook documentation
explicitly says the transcript format is not a stable interface. The typed
Codex model tracks the current open-source
RolloutItem
and
ResponseItem
definitions while retaining forward-compatible fallbacks.
Install
pip install -e . # or: uv pip install -e .
Library
Parse either provider through one shared boundary:
from pathlib import Path
from cc_session_core import (
ParseFailure,
iter_transcript_records,
parse_transcript_line,
)
rec = parse_transcript_line(line) # -> Claude Record | CodexRecord | unknown fallback
for rec in iter_transcript_records(Path("session.jsonl")):
if isinstance(rec, ParseFailure):
... # file, line_number, error, raw
else:
print(rec.type)
The backward-compatible Claude-only parse_line() / iter_records() API and
the explicit Codex-only parse_codex_line() / iter_codex_records() API are
also available.
Per-tool input/result resolution:
from cc_session_core import parse_tool_input, parse_tool_result, tool_name_index, result_tool_name
typed_input = parse_tool_input(block.name, block.input) # model, or raw value
index = tool_name_index(records) # tool_use_id -> tool name
typed_result = parse_tool_result(result_tool_name(rec, index), rec.tool_use_result)
Analyze a whole session. Session.load() auto-detects the provider and returns
the same normalized timeline/tool/cost views:
from cc_session_core import Session
s = Session.load("session.jsonl")
s.timeline() # ordered, decomposed events (text / thinking / tool_use / tool_result / ...)
s.tool_calls() # every tool_use paired with its tool_result, plus the assistant's "why"
s.cost_summary() # token + cost rollup per model (one API request counted once)
s.label()
s.info() # human title + one-line summary
For Codex, response messages, reasoning, tool calls, tool outputs, compaction,
and usage events are normalized into the canonical view. The original typed
rollout remains available as s.codex_records when isinstance(s, CodexSession).
Codex input_tokens includes cached input, so normalization subtracts
cached_input_tokens before filling the canonical uncached-input field; totals
therefore do not double-count cache reads.
Cost uses cc_session_core.cost.pricing (published list rates in EXAMPLE_PRICING); pass your own PriceTable for a different valuation. Rates are a usage valuation, not a billed amount.
CLI
cc-session PATH [--tools] [--queries] [--audit] [--list] [--json] [--strict]
cc-session PATH --export <text|markdown|json|jsonl> [--select k=v ...] [-o OUT]
PATH is a Claude or Codex .jsonl file, or a directory (directories load
recursively). --tools lists paired tool calls; --queries prints the full
why/queried/returned timeline; --list indexes sessions in a directory;
--audit reports Claude schema coverage over the target (field names +
value-types only, safe to share).
--export writes a filtered slice of the session; --select narrows it (space-separated key=comma,values): parts= (text,thinking,tool_use,tool_result,image,other), tools=, types=, uuids=, main_only=true. -o writes to a file instead of stdout.
cc-session-map [TRANSCRIPTS_DIR] [-o OUT_DIR] # default: ~/.claude/projects, .
Aggregates per transcript and overall: turns (main vs sidechain), tool usage, token usage by kind, server web tools, and cost; writes map.json and map.csv into OUT_DIR.
MCP server
cc-session-mcp (stdio) exposes list_sessions, session_summary,
tool_calls, export_session, and audit. Session listing and lookup search
both default Claude and Codex roots. It needs the mcp extra
(pip install "cc-session-core[mcp]"). Register in a Claude Code .mcp.json:
{
"mcpServers": {
"cc-session": { "command": "cc-session-mcp" }
}
}
Tests
uv sync --all-groups # pytest, ruff, pyright, mcp
uv run pytest # fast unit tests on frozen, scrubbed fixtures
CC_SESSION_CORPUS=~/.claude/projects uv run pytest -m corpus # opt-in: asserts zero Unknown*/extra/parse-failures on real data
The corpus test is the lossless coverage gate: it fails if any real line lands in an Unknown* fallback, leaves a field in model_extra, or a modeled built-in tool result falls back to raw. Fixtures are regenerated with python tests/_extract_fixtures.py (CC_SESSION_CORPUS set); free-text, paths, and base64 are scrubbed.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cc_session_core-0.2.0.tar.gz.
File metadata
- Download URL: cc_session_core-0.2.0.tar.gz
- Upload date:
- Size: 80.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
82453571c349703207004a2cf0d4834c4b025101b0df004aa1253e65b5974964
|
|
| MD5 |
cb84e3496dd2803c82e712b6b2200851
|
|
| BLAKE2b-256 |
0cce0fc200df2ee4e2007229171c45bc7493b12b3537f7632636e6ce005ea13c
|
Provenance
The following attestation bundles were made for cc_session_core-0.2.0.tar.gz:
Publisher:
publish.yml on Magic-Man-us/cc-session-core
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cc_session_core-0.2.0.tar.gz -
Subject digest:
82453571c349703207004a2cf0d4834c4b025101b0df004aa1253e65b5974964 - Sigstore transparency entry: 2252384152
- Sigstore integration time:
-
Permalink:
Magic-Man-us/cc-session-core@fc245abf08d28fd56c03d70ae0753aa603df5b39 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Magic-Man-us
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@fc245abf08d28fd56c03d70ae0753aa603df5b39 -
Trigger Event:
release
-
Statement type:
File details
Details for the file cc_session_core-0.2.0-py3-none-any.whl.
File metadata
- Download URL: cc_session_core-0.2.0-py3-none-any.whl
- Upload date:
- Size: 66.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
163085c5f869f151386f90479acbadf1d9a533ce537003eb94b53830868d05fc
|
|
| MD5 |
fbaa6be1bfbb97dca328c04bbfac9d31
|
|
| BLAKE2b-256 |
72bbc83f1a90815579659b7be56b999b3288895ebb5003c71aad583afe14f0ab
|
Provenance
The following attestation bundles were made for cc_session_core-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on Magic-Man-us/cc-session-core
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cc_session_core-0.2.0-py3-none-any.whl -
Subject digest:
163085c5f869f151386f90479acbadf1d9a533ce537003eb94b53830868d05fc - Sigstore transparency entry: 2252384608
- Sigstore integration time:
-
Permalink:
Magic-Man-us/cc-session-core@fc245abf08d28fd56c03d70ae0753aa603df5b39 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Magic-Man-us
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@fc245abf08d28fd56c03d70ae0753aa603df5b39 -
Trigger Event:
release
-
Statement type: