Search, browse, and extract information from the local session transcripts written by your coding agents — Claude Code, Codex, and BotBandit.
Every major coding agent writes its full session history to disk as JSONL. Session Bandit indexes those files locally — no API calls, no auth, no network. Just point it at your session directories and search across everything you've ever done with every agent.
- Unified listing across Claude Code, Codex, and BotBandit sessions, sorted by most recent first, with filters by agent, project, and time period.
- Full transcripts — read any session's normalized transcript with tool calls, inputs, outputs, and status indicators.
- Full-text search across all session messages and tool calls (inputs and outputs), with agent, project, and time-period filters. Uses ripgrep as a fast pre-filter when available, and skips condensed BotBandit wrappers whose full Codex original is in the index.
- Agent recaps, compactions, and memories captured — Claude's
while-you-were-away recaps, Codex's context-window compactions, and
BotBandit's memory/compaction events are carried as
summarymessages and fed to the digest so the synthesizing LLM can use them. - Parsing health check —
doctorcommand validates that Session Bandit's parsing assumptions match your real session files (format drift, injection markers, unrecognized types, silent skips). - Token usage & context-window stats —
statssurfaces per-session and aggregate token usage (input/output/cache/reasoning), the model's context-window limit, and peak/final context size. Codextoken_countevents, Claudemessage.usage, and BotBanditturn_end/loop_endusage are all captured.stats --globalaggregates every agent from your transcripts, broken down per agent; adding--agent claudelayers in Claude's own~/.claude/stats-cache.jsonlifetime totals. - Redaction preview for publishing —
redact-checkreports what would be redacted from a session before a Markdown export writes public artifacts. - Markdown publishing artifact —
export-mdwrites a redacted, reviewable Markdown file with provenance, digest, summaries, transcript, and collapsible tool calls. - Gorgeous GitHub Pages template — the bundled skill template renders those Markdown artifacts as a polished static archive without changing the deterministic export path.
- Works as a library too —
@session-bandit/coreexposes a programmatic API for indexing and querying sessions from your own code.
npm install -g session-banditRequires Node.js 22+.
git clone https://github.com/janole/session-bandit.git
cd session-bandit
pnpm install
pnpm -r build
npm install -g packages/cliRequires Node.js 22+ and pnpm 10+. The npm install -g packages/cli step
installs the CLI globally from the built output (core is bundled into the
CLI, so no separate install needed).
Session Bandit ships an agent skill in the skills/session-bandit/ directory. The skill
teaches Claude Code, Codex, and other npx skills-compatible agents how to use
Session Bandit to write handoff notes and memory notes from past sessions.
Install it globally for Claude Code:
npx skills add janole/session-bandit --skill session-bandit -g -a claude-code -yInstall it globally for Codex:
npx skills add janole/session-bandit --skill session-bandit -g -a codex -yYou can also install directly from the skill path:
npx skills add https://github.com/janole/session-bandit/tree/main/skills/session-bandit -g -a claude-code -yOr manually copy the skills/session-bandit/ directory to the relevant agent skill directory,
for example ~/.claude/skills/session-bandit/ for Claude Code or
~/.codex/skills/session-bandit/ for Codex.
The skill's SKILL.md includes instructions for the agent to install the CLI
via npm install -g session-bandit if it's not already available.
# List all sessions (JSON lines, most recent first)
session-bandit list
# List with a human-readable table
session-bandit list --pretty
# Filter by agent
session-bandit list --agent claude
session-bandit list --agent codex
session-bandit list --agent botbandit
# Filter by project (substring match on project path / cwd)
session-bandit list --project botbandit
# Show the full transcript of a session (accepts ID prefix)
session-bandit show 342647fa-5bf
# Full-text search across session messages and tool-call input/output
session-bandit search "tool approval" --pretty
# Search within a specific agent
session-bandit search "adapter" --agent claude --pretty
# Emit a structured digest of a session (substance, files, key turns,
# recaps/compactions) for LLM ingestion — the payoff feature for
# handoffs / memories
session-bandit extract 342647fa-5bf --pretty
# Wrap the digest in a ready-to-send synthesis prompt
session-bandit extract 342647fa-5bf --prompt handoff
session-bandit extract 342647fa-5bf --prompt memory
# Preview redaction findings before publishing/exporting
session-bandit redact-check 342647fa-5bf --pretty
session-bandit redact-check 342647fa-5bf --redact strict
# Export a redacted Markdown session artifact
session-bandit export-md 342647fa-5bf --out ./session.md
session-bandit export-md 342647fa-5bf --out ./session.md --report-out ./redaction-report.json
# Token usage and context-window stats for a session
session-bandit stats 342647fa-5bf --pretty
# Aggregate usage across all sessions (Claude stats cache + summed per-session stats)
session-bandit stats --global --pretty
# Find the sessions where something actually happened (by substance score)
session-bandit list --sort importance --pretty
# Drop the trivial / hello-only sessions
session-bandit list --min-importance moderate --pretty
# Limit to the last week (relative: 7d, 24h, 2w, 3m)
session-bandit list --since 7d --pretty
# Limit to a date window
session-bandit list --since 2026-06-01 --until 2026-06-15 --pretty
# Search within a time period
session-bandit search "adapter" --since 3d --prettysession-bandit list [--agent <name>] [--project <path>] [--sort recent|importance] [--min-importance <tier>] [--since <date>] [--until <date>] [--pretty]
session-bandit show <sessionId> [--agent <name>]
session-bandit search <query> [--agent <name>] [--project <path>] [--since <date>] [--until <date>] [--pretty]
session-bandit extract <sessionId> [--agent <name>] [--prompt handoff|memory] [--full] [--pretty]
session-bandit redact-check <sessionId> [--agent <name>] [--redact strict|cautious|minimal|none] [--pretty]
session-bandit export-md <sessionId> --out <path> [--agent <name>] [--title <title>] [--redact strict|cautious|minimal|none] [--report-out <path>] [--yes]
session-bandit stats [sessionId] [--agent <name>] [--global] [--pretty]
| Flag | Description |
|---|---|
-a, --agent <name> |
Filter by agent: claude, codex, or botbandit |
-p, --project <path> |
Filter by project (substring match on project/cwd) |
--sort <field> |
list: sort by recent (default) or importance (substance score) |
--min-importance <tier> |
list: drop sessions below tier (trivial|light|moderate|substantive|heavy) |
--since <date> |
list/search: only entries at/after this time — absolute date (2026-06-01) or relative (7d, 24h, 2w, 3m) |
--until <date> |
list/search: only entries at/before this time — absolute date or relative (7d, 24h, 2w, 3m) |
--prompt <kind> |
extract: wrap the digest in a synthesis prompt (handoff|memory) |
--full |
extract: include the complete de-noised transcript |
--redact <mode> |
redact-check: choose strict, cautious (default), minimal, or none |
--out <path> |
export-md: Markdown output path |
--report-out <path> |
export-md: optional redaction report JSON output path |
--yes |
export-md: required with --redact none |
--pretty |
Print human-readable output instead of JSON lines |
--global |
stats: aggregate usage across all sessions, broken down per agent (add --agent claude for Claude's lifetime cache) |
Output defaults to JSON lines (one object per line) for machine
consumption and piping. Use --pretty for terminal browsing.
Session Bandit deliberately stops at a redacted Markdown artifact and redaction report. It does not call an LLM, push to a remote, or generate a generic HTML site. That keeps the core path offline and reviewable while still making the artifact easy to publish in a GitHub Pages-style repository.
For new publishing repos, use the default template in
skills/session-bandit/templates/github-pages-default/. It is a self-contained Jekyll/GitHub
Pages site with custom layouts and CSS for generated session Markdown. The
agent skill can copy this template into an empty repo, then export sessions into
sessions/<slug>/index.md with a sibling redaction-report.json.
Recommended flow:
# 1. Pick a session by ID, search, or importance-ranked list
session-bandit list --sort importance --pretty
session-bandit search "apple watch interface" --pretty
# 2. Preview redaction risk
session-bandit redact-check 342647fa-5bf --pretty
# 3. Export Markdown plus the machine-readable report
mkdir -p sessions/apple-watch-interface
session-bandit export-md 342647fa-5bf \
--out sessions/apple-watch-interface/index.md \
--report-out sessions/apple-watch-interface/redaction-report.json \
--title "Apple Watch interface"
# 4. Review, then commit/push with your normal git workflow
git diff -- sessions/apple-watch-interfaceFor public artifacts, keep the default --redact cautious or use
--redact strict. --redact none is intended for local debugging and requires
--yes.
If you want a polished page, treat the Markdown as the canonical source and let an agent or site generator render it after review. A future HTML renderer should consume the same redacted bundle or Markdown rather than re-reading private session files.
list --pretty:
agent sessionId startedAt msgs model project
--------------------------------------------------------------------------------
codex 019ee0ad-2eb 2026-06-19T16:18:26 64 gpt-5.5 /Users/ole/projects/chat-bandit
claude 342647fa-5bf 2026-06-19T10:38:38 153 claude-opus-4-8 /Users/ole/projects/chat-bandit
codex 019eda02-434 2026-06-18T09:14:02 65 gpt-5.5 /Users/ole/projects/chat-bandit
...
list (JSON lines):
{"agent":"claude","sessionId":"342647fa-5bf0-41b4-b21d-1e7d0d78b371","project":"/Users/ole/projects/chat-bandit","cwd":"/Users/ole/projects/chat-bandit","startedAt":"2026-06-19T10:38:38.122Z","endedAt":"2026-06-19T14:41:00.972Z","model":"claude-opus-4-8","messageCount":153}show <sessionId>:
Session: 342647fa-5bf0-41b4-b21d-1e7d0d78b371
Agent: claude
Project: /Users/ole/projects/chat-bandit
Model: claude-opus-4-8
Started: 2026-06-19T10:38:38.122Z
Messages: 153
--- #1 USER [2026-06-19T10:38:38.122Z] ---
Do you remember us working on the tool approval yesterday?
--- #2 ASSISTANT [2026-06-19T10:38:44.405Z] ---
I don't have a running memory of our actual conversation yesterday, but I do have a saved note that matches what you're describing.
--- #4 ASSISTANT [2026-06-19T10:38:45.880Z] ---
✓ Read
input: {"file_path":"/Users/ole/.claude/projects/.../memory/plan085.md"}
output: <system-reminder>This memory is 7 days old...</system-reminder>
search <query> --pretty:
[claude] 005f7977-937 #440 user
So what about going for simple saving of tool approvals / denials with regexes...
[claude] 005f7977-937 #535 assistant
Written to `docs/architecture.md` — the repo's canonical systems doc...
2 matches
stats <sessionId> --pretty:
Session: 019f834e-5e48-7d31-91f4-21158d035248
Agent: codex
Project: /Users/ole/projekte/codex-workspaces/janole/botbandit-ng
Model: gpt-5.6-sol
Messages: 71
Tokens
input 261,974 (cached: 3,601,408)
output 10,264 (reasoning: 8,413)
total 3,873,646
Context window
limit 258,400
peak 90,134 (35%)
final 90,134 (35%)
Per turn
# 1 in 32,722 out 166 ctx 32,722 (13%)
# 2 in 439 out 134 ctx 32,951 (13%)
# 3 in 4,045 out 107 ctx 36,557 (14%)
...
stats --global --pretty — every agent, summed from the transcripts on disk:
Per-session totals (from transcripts on disk)
claude 100 sess in 2,457,734 out 28,904,729
codex 748 sess in 93,171,236 out 2,846,545
botbandit 667 sess in 656,565,837 out 6,899,516
└ via codex 243 sess in 119,854,320 out 4,282,405
total 1,515 sess in 752,194,807 out 38,650,790
cached 4,909,014,405 reasoning 2,544,235
(└ = codex transcripts running under botbandit sessions — same work, not added to the total)
BotBandit can drive codex as its provider, so those conversations exist twice on
disk — once as the BotBandit session you ran, once as the codex transcript beneath
it. The └ row shows the latter without adding it to the total.
stats --global --pretty --agent claude additionally layers in Claude's own
lifetime cache, which is Claude-only and counts sessions whose transcripts have
since been rotated away — so its totals legitimately exceed the on-disk ones:
All-time (Claude Code cache, since 2026-01-22T10:42:06.325Z)
sessions 434 (128 transcripts still on disk)
messages 136,552
longest 5,378 msgs (8d 11h) 4e4f7ab3
Tokens by model (Claude aggregate)
claude-opus-4-8
in 4,616,653 out 45,713,062 cache-read 5,329,110,624 cache-create 170,472,123
...
Per-session totals (from transcripts on disk)
claude 100 sess in 2,457,603 out 28,841,365
cached 2,041,544 reasoning 0
Busiest hours (Claude only, messages)
22:00 36
23:00 33
...
The @session-bandit/core package exposes the indexing engine and adapters
for programmatic use — no CLI required.
import {
indexSessions,
claudeAdapter,
codexAdapter,
botbanditAdapter,
} from "@session-bandit/core";
// Index all sessions from all adapters
const sessions = indexSessions([
{ adapter: claudeAdapter },
{ adapter: codexAdapter },
{ adapter: botbanditAdapter },
]);
// Or just one agent
const claudeSessions = indexSessions([{ adapter: claudeAdapter }]);
// Each session is normalized to a common shape:
// {
// agent, sessionId, filePath, project, cwd,
// startedAt, endedAt, model, messageCount, messages[], stats?
// }
//
// Messages use a `role` of user | assistant | system | tool | summary.
// `summary` messages carry runtime-generated summaries (Claude recaps,
// Codex/BotBandit compactions, and BotBandit memories) with a `subtype`
// such as "recap", "compaction", or "memory".interface Adapter {
readonly agent: AgentName; // "claude" | "codex" | "gemini" | "botbandit"
defaultRoot(): string; // e.g. "~/.claude/projects"
discover(root: string): string[];// find session files under root
parse(filePath: string): Session;// parse one file → normalized Session
}A new agent is a new adapter file — nothing else changes. See
packages/core/src/adapters/ for reference implementations.
Session Bandit scans these directories by default:
| Agent | Default location |
|---|---|
| Claude Code | ~/.claude/projects/<encoded-cwd>/*.jsonl |
| Codex | ~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl (+ legacy flat files) |
| BotBandit | ~/.botbandit/sessions/*.jsonl |
Codex has used three file formats over time — legacy .json (single object),
flat .jsonl (no envelope), and modern envelope .jsonl. The Codex adapter
handles all three transparently.
pnpm install # install deps
pnpm -r build # build both packages
pnpm -r typecheck # type-check (strict mode)
pnpm -r test # run all tests
# Run the CLI from source (no build needed, uses tsx):
pnpm dev list --pretty
pnpm dev show <sessionId>
pnpm dev search "query" --pretty
pnpm dev doctor --prettysession-bandit is a self-contained npm package: tsup bundles the whole
@session-bandit/core engine into the CLI dist/, so the published package
has exactly one runtime dependency (commander) and no workspace:*
references. End users need only Node.js 22+ — no pnpm, no monorepo checkout.
The publishable package is packages/cli (the repo root and core package are
not published). The prepublishOnly / prepack lifecycle hooks rebuild
dist/ (core first, then CLI) before any npm publish or npm pack, so the
bundle is never stale.
pnpm run ok # quality gate (build + typecheck + lint:fix + test)
cd packages/cli
npm pack --dry-run # rebuild dist/ + inspect tarball contents
npm publish # live (requires npm login)The version is sourced from packages/cli/package.json. Tag the release
(git tag v<version>) after publishing.
packages/
core/ @session-bandit/core — indexing engine
src/
types.ts normalized Session/Message/ToolCall model
adapter.ts Adapter interface
index.ts indexSessions() + exports
jsonl.ts JSONL reader
diagnose.ts doctor diagnostics (format drift, injection markers)
stats-cache.ts Claude aggregate stats-cache reader (stats --global)
adapters/
claude.ts Claude Code adapter
codex.ts Codex adapter (3 formats)
botbandit.ts BotBandit event-log adapter
test/ fixtures + tests
cli/ session-bandit — CLI
src/
bin.ts entry point
index.ts Commander program + cli()
scan.ts scanAll() + filters + sorting
format.ts output formatters (JSON, table, transcript)
commands/
list.ts list command
show.ts show command
search.ts search command
extract.ts extract command
doctor.ts doctor command (parsing health)
stats.ts stats command (token usage + context window)
test/ tests
docs/
prd.md product requirements document
extract.md session extracts & digest design (primary v2 feature)
decisions.md decision log (the "why" behind the structure)
adapters.md how to add an agent / adapt to format drift
format-claude.md Claude Code on-disk format reference
format-codex.md Codex on-disk format reference (3 historical formats)
format-botbandit.md BotBandit event-log format reference
skills/session-bandit/
SKILL.md Agent skill (handoff + memory note generation)
See docs/adapters.md for how to add a new agent adapter
or adapt an existing one when a provider changes its on-disk format. Per-agent
format details live in docs/format-claude.md,
docs/format-codex.md, and
docs/format-botbandit.md. The rationale behind
the structural choices is in docs/decisions.md.
Done:
-
Session extracts — the primary v2 feature.
extractcomputes a structured digest (substance score, files touched, commands, errors, key turns) and can emit a ready-to-send synthesis prompt. Seedocs/extract.md. -
doctorcommand — parsing health check that validates adapter assumptions against real files (format drift, injection markers, unrecognized types). -
BotBandit adapter — parses
~/.botbandit/sessions/*.jsonlevent logs, including memory and compaction events as summary messages. -
Token usage & context-window stats —
statscaptures per-session and aggregate token usage, the context-window limit, and peak/final context size across Claude, Codex, and BotBandit;stats --globalaggregates every agent from your transcripts, broken down per agent; adding--agent claudelayers in Claude's own~/.claude/stats-cache.jsonlifetime totals.
Next:
- Gemini adapter — the adapter guide uses Gemini as its worked example; implementing it would dogfood the guide and round out the big three agents.
v0.1.0 — the full search, browse, extract, and doctor feature set, plus the agent skill and npm packaging — was built by GLM-5.2 in a single session.
MIT