Read, filter, and export coding-agent sessions with ease.
session-scan reads the logs Claude Code, Codex, and Pi already write. Get readable transcripts or a common JSON event stream without adding instrumentation, a database, or a daemon. It makes no model calls.
- Investigate failures. Extract failed tool results across recent sessions instead of opening each transcript.
- Prepare a handoff or review. Export the last few turns, keep failed results, and leave successful tool output behind.
- Analyze across agents. Feed one event format into your own scripts or queries instead of parsing each harness separately.
- Review skill use. Find conversations where a particular skill was explicitly invoked, with surrounding context.
npm install --global session-scanUse Jev to flag your corrections, repeated instructions, and explicit frustration, even when every tool call succeeded. From a repository checkout, with TYPESAFE_API_KEY loaded:
bun src/cli.ts /path/to/session.jsonl --last-turns 10 \
--scanner ./examples/scanners/jev-corrections.tsThis optional scanner makes paid API calls and shares selected message text with TypeSafe. Read the setup and data-sharing details first. Findings include probabilities and session IDs for review; they are not proof that the agent was wrong.
# Failed tool results from the last seven days, across projects.
session-scan --cwd all --error --format md# Last two turns, preserving conversation and failed tool results.
session-scan /path/to/session.jsonl \
--last-turns 2 --tool-results errors --no-thinking --format md# Structured events from recent sessions for a project.
session-scan --cwd my-project > sessions.jsonl
# Extract assistant text with jq.
jq -r 'select(.type == "assistant_message") | .text' sessions.jsonlsession-scan --cwd my-project --skill copywriting --format mdOutput goes to stdout; scan statistics and errors go to stderr. NDJSON is the default. Each record includes a session identifier (sid). Markdown is a reading view that summarizes tool arguments and omits fields such as usage and event IDs.
Use --out <file> for one output file or --out-dir <dir> for separate session files. Existing files are never overwritten. Output paths must not overlap input files, and an output directory must not contain any input. Unsafe session IDs and filename collisions fail rather than writing outside the directory or replacing earlier output. Errors can leave partial output; choose a new destination before retrying.
Run session-scan --help for options. Invalid arguments exit with status 2; read, parse, and write failures exit with status 1. A downstream command closing the stdout pipe is treated as normal termination.
Without a positional file, discovery defaults to the current project name and the last seven days. Project matching is a case-insensitive working-directory substring. --cwd all removes the project filter, not the date filter. Dates use filenames where available and file modification times otherwise, not individual event timestamps. A positional file bypasses discovery filters.
--head selects an inclusive ancestor branch using a Pi entry id or Claude Code uuid. Without it, tree logs are not automatically reduced to the latest branch. A turn starts at a user message and continues until the next user message.
--skill matches exact, case-sensitive canonical skill names, not incidental file reads or mentions. It selects the session before the last-turn window, so the invoking event may fall outside the retained turns. Event filters combine with AND; comma-separated values within a filter use OR. Selected sessions retain their header.
--error means failed tool results, not harness-level errors. Use --type error for the latter. Failure detection depends on the adapter and the information recorded by the harness.
| Input | What to expect |
|---|---|
| Claude Code JSONL | Text, recorded thinking, tool calls/results, usage, and selected errors and compaction records. Drops machinery, some injected messages, and unsupported non-text blocks. Separate subagent transcripts require --include-subagents. |
| Codex rollout JSONL | Text, tools, reasoning summaries, usage, and selected lifecycle events. Omits injected instructions, coalesces response items, and synthesizes event IDs. Usage is associated by response order; tool classification and failure detection include heuristics. |
| Pi JSONL | Text, tools, usage, compaction, and selected custom records. Does not retain assistant thinking blocks or non-text blocks. Skips branch summaries, labels, and unsupported entries. |
| Claude Code text export | Best-effort reconstruction of visible transcript text. Cannot recover hidden thinking, usage, truncated payloads, native tool IDs, or per-event timestamps. Pairing and paragraph unwrapping are heuristic. Prefer native JSONL. |
Install in your project:
npm install session-scanSave as scan.mjs and run with node scan.mjs:
import { scanSession } from "session-scan";
for await (const event of scanSession("/path/to/session.jsonl", {
filter: { lastTurns: 2, toolResults: "errors" },
trim: { noThinking: true },
})) {
if (event.type === "tool_result" && event.isError) {
console.log(event.toolName, event.content);
}
}scanSession handles adapter loading, normalization, context, selection, and trimming. It and the CLI load the bundled Pi adapter automatically; lower-level parser entry points require explicit loading of Pi.
Load another adapter with --adapter <path>. Use --scanner <path> for a custom async generator over one session's filtered events. Scanners replace the trim stage and own their output: --no-thinking and --tool-lines do not trim scanner results.
Install Bun, then run:
bun install --frozen-lockfile
bun run check