session-stats
Purpose
Render the quantitative metrics of a Claude Code session as one self-contained, dark-theme HTML
page. The output is stats only: KPI cards for headline numbers (duration, prompts, tool calls,
files written/edited, output tokens, est. cost), a per-tool usage table with share bars, and a
token-bucket table (input, output, cache created, cache read). No prompts, observations, tone
analysis, or recommendations.
Two bundled scripts form an offline pipeline with no third-party Python packages (standard library only; requires Python 3). scan_jsonl.py parses a
session .jsonl transcript and computes every metric, including a cost estimate derived from each
assistant message's token usage times per-model pricing. build_stats_html.py injects the result
into a fixed dark template and writes a standalone document.
When to Use
Use this skill to produce a shareable visual of session numbers - duration, prompts, tool calls,
files written/edited, output tokens, est. cost, per-tool usage breakdown, and token buckets - styled
in the dark template, written to a standalone .html file that opens in any browser with no external
assets, scripts, or fonts.
Do not use this skill for full session summaries, narrative retros, or recommendations - this skill
emits stats only.
Process
Get the scan JSON. ${CLAUDE_PLUGIN_ROOT}/lib/session-stats/scripts/scan_jsonl.py parses a Claude Code .jsonl transcript and emits
the metrics JSON, computing cost from token usage times per-model pricing (the transcript stores
token counts, not dollars, so cost is always computed here). Find the current session file, then
scan it:
# Locate the current session transcript (POSIX: macOS / Linux / WSL / Git Bash)
ls -t "$HOME/.claude/projects/<project-slug>"/*.jsonl | head -1
# Scan it
python "${CLAUDE_PLUGIN_ROOT}/lib/session-stats/scripts/scan_jsonl.py" --in <session.jsonl> > /tmp/scan.json
On Windows/PowerShell, resolve the newest transcript natively:
Get-ChildItem "$env:USERPROFILE\.claude\projects\<project-slug>\*.jsonl" |
Sort-Object LastWriteTime -Descending | Select-Object -First 1
Or just use the Glob tool (**/.claude/projects/<project-slug>/*.jsonl, newest first) to stay
shell-agnostic. Invoke the scripts with python (the usual command on Windows); if that is not
found, try py -3 - note python3 is often absent on Windows.
Pricing follows the current Claude API catalog via a per-model table (Opus, Sonnet, Haiku, Fable),
defaulting to Opus 4.8 = $5/$25 per MTok in/out, cache write $6.25, cache read $0.50. A model with
no table entry renders cost as N/A rather than $0. Override rates without editing the script via
env vars (per MTok): IN_RATE, OUT_RATE, CW_RATE, CR_RATE. promptCount excludes slash-command
and system turns.
Render the HTML. Run the builder, or pipe the two stages directly:
python "${CLAUDE_PLUGIN_ROOT}/lib/session-stats/scripts/build_stats_html.py" --in /tmp/scan.json --out session-stats.html
python "${CLAUDE_PLUGIN_ROOT}/lib/session-stats/scripts/scan_jsonl.py" --in <session.jsonl> \
| python "${CLAUDE_PLUGIN_ROOT}/lib/session-stats/scripts/build_stats_html.py" --out session-stats.html
When a scan holds multiple sessions, select one by id/slug prefix:
python "${CLAUDE_PLUGIN_ROOT}/lib/session-stats/scripts/build_stats_html.py" --in /tmp/scan.json --session 657aa92f --out session-stats.html
Report. State the output path and confirm the page is self-contained (no <script>, no
external assets). Offer to open it or change the destination. Render values come only from the
transcript - never invent metrics; missing fields render as N/A.
Bundled files
${CLAUDE_PLUGIN_ROOT}/lib/session-stats/scripts/scan_jsonl.py - Parses a Claude Code .jsonl transcript into the metrics JSON,
computing cost from token usage times per-model pricing. Pure stdlib, no third-party packages.
Supports IN_RATE/OUT_RATE/CW_RATE/CR_RATE env overrides. Cost groups assistant messages by
model and sums input/output/cache-write/cache-read tokens times their rates, divided by one million;
an unpriced model makes the whole cost render N/A, never $0. Also emits a per-tool call breakdown
(toolBreakdown, sorted by count) and summed token buckets (tokens: input/output/cacheCreated/cacheRead)
that feed the usage and token tables. The API errors metric is a heuristic (substring match on
assistant text), so treat it as approximate.
${CLAUDE_PLUGIN_ROOT}/lib/session-stats/scripts/build_stats_html.py - Converts the metrics JSON into the stats HTML. Handles session
selection (--session prefix match), duration/cost/int formatting, HTML-escaping, and template
injection. --template <path> overrides the bundled ${CLAUDE_PLUGIN_ROOT}/lib/session-stats/assets/template.html. Pure stdlib. Exit
codes: 0 ok, 1 no session / bad JSON, 2 template missing.
${CLAUDE_PLUGIN_ROOT}/lib/session-stats/assets/template.html - The fixed dark-theme shell, an inline static-CSS port of the design (no
Tailwind, no <script>, no external assets). The script replaces the single <!-- CONTENT --> marker
with the rendered stats body. To restyle, edit the CSS variables in :root (e.g. --accent, --ember,
--ink, --coal); build_content() in build_stats_html.py emits the matching .kpi / table.t /
.bar classes, so keep those class names in sync if structural rules change.
Validation
1---2name: session-stats3description: session-stats4---5# session-stats67## Purpose89Render the quantitative metrics of a Claude Code session as one self-contained, dark-theme HTML10page. The output is **stats only**: KPI cards for headline numbers (duration, prompts, tool calls,11files written/edited, output tokens, est. cost), a per-tool usage table with share bars, and a12token-bucket table (input, output, cache created, cache read). No prompts, observations, tone13analysis, or recommendations.1415Two bundled scripts form an offline pipeline with no third-party Python packages (standard library only; requires Python 3). `scan_jsonl.py` parses a16session `.jsonl` transcript and computes every metric, including a cost estimate derived from each17assistant message's token `usage` times per-model pricing. `build_stats_html.py` injects the result18into a fixed dark template and writes a standalone document.1920## When to Use2122Use this skill to produce a shareable visual of session *numbers* - duration, prompts, tool calls,23files written/edited, output tokens, est. cost, per-tool usage breakdown, and token buckets - styled24in the dark template, written to a standalone `.html` file that opens in any browser with no external25assets, scripts, or fonts.2627Do not use this skill for full session summaries, narrative retros, or recommendations - this skill28emits stats only.2930## Process31321. **Get the scan JSON.** `${CLAUDE_PLUGIN_ROOT}/lib/session-stats/scripts/scan_jsonl.py` parses a Claude Code `.jsonl` transcript and emits33 the metrics JSON, computing cost from token `usage` times per-model pricing (the transcript stores34 token counts, not dollars, so cost is always computed here). Find the current session file, then35 scan it:3637 ```bash38 # Locate the current session transcript (POSIX: macOS / Linux / WSL / Git Bash)39 ls -t "$HOME/.claude/projects/<project-slug>"/*.jsonl | head -140 # Scan it41 python "${CLAUDE_PLUGIN_ROOT}/lib/session-stats/scripts/scan_jsonl.py" --in <session.jsonl> > /tmp/scan.json42 ```4344 On Windows/PowerShell, resolve the newest transcript natively:4546 ```powershell47 Get-ChildItem "$env:USERPROFILE\.claude\projects\<project-slug>\*.jsonl" |48 Sort-Object LastWriteTime -Descending | Select-Object -First 149 ```5051 Or just use the Glob tool (`**/.claude/projects/<project-slug>/*.jsonl`, newest first) to stay52 shell-agnostic. Invoke the scripts with `python` (the usual command on Windows); if that is not53 found, try `py -3` - note `python3` is often absent on Windows.5455 Pricing follows the current Claude API catalog via a per-model table (Opus, Sonnet, Haiku, Fable),56 defaulting to Opus 4.8 = $5/$25 per MTok in/out, cache write $6.25, cache read $0.50. A model with57 no table entry renders cost as `N/A` rather than $0. Override rates without editing the script via58 env vars (per MTok): `IN_RATE`, `OUT_RATE`, `CW_RATE`, `CR_RATE`. promptCount excludes slash-command59 and system turns.60612. **Render the HTML.** Run the builder, or pipe the two stages directly:6263 ```bash64 python "${CLAUDE_PLUGIN_ROOT}/lib/session-stats/scripts/build_stats_html.py" --in /tmp/scan.json --out session-stats.html65 ```6667 ```bash68 python "${CLAUDE_PLUGIN_ROOT}/lib/session-stats/scripts/scan_jsonl.py" --in <session.jsonl> \69 | python "${CLAUDE_PLUGIN_ROOT}/lib/session-stats/scripts/build_stats_html.py" --out session-stats.html70 ```7172 When a scan holds multiple sessions, select one by id/slug prefix:7374 ```bash75 python "${CLAUDE_PLUGIN_ROOT}/lib/session-stats/scripts/build_stats_html.py" --in /tmp/scan.json --session 657aa92f --out session-stats.html76 ```77783. **Report.** State the output path and confirm the page is self-contained (no `<script>`, no79 external assets). Offer to open it or change the destination. Render values come only from the80 transcript - never invent metrics; missing fields render as `N/A`.8182### Bundled files8384- **`${CLAUDE_PLUGIN_ROOT}/lib/session-stats/scripts/scan_jsonl.py`** - Parses a Claude Code `.jsonl` transcript into the metrics JSON,85 computing cost from token `usage` times per-model pricing. Pure stdlib, no third-party packages.86 Supports `IN_RATE`/`OUT_RATE`/`CW_RATE`/`CR_RATE` env overrides. Cost groups assistant messages by87 model and sums input/output/cache-write/cache-read tokens times their rates, divided by one million;88 an unpriced model makes the whole cost render `N/A`, never $0. Also emits a per-tool call breakdown89 (`toolBreakdown`, sorted by count) and summed token buckets (`tokens`: input/output/cacheCreated/cacheRead)90 that feed the usage and token tables. The `API errors` metric is a heuristic (substring match on91 assistant text), so treat it as approximate.92- **`${CLAUDE_PLUGIN_ROOT}/lib/session-stats/scripts/build_stats_html.py`** - Converts the metrics JSON into the stats HTML. Handles session93 selection (`--session` prefix match), duration/cost/int formatting, HTML-escaping, and template94 injection. `--template <path>` overrides the bundled `${CLAUDE_PLUGIN_ROOT}/lib/session-stats/assets/template.html`. Pure stdlib. Exit95 codes: `0` ok, `1` no session / bad JSON, `2` template missing.96- **`${CLAUDE_PLUGIN_ROOT}/lib/session-stats/assets/template.html`** - The fixed dark-theme shell, an inline static-CSS port of the design (no97 Tailwind, no `<script>`, no external assets). The script replaces the single `<!-- CONTENT -->` marker98 with the rendered stats body. To restyle, edit the CSS variables in `:root` (e.g. `--accent`, `--ember`,99 `--ink`, `--coal`); `build_content()` in `build_stats_html.py` emits the matching `.kpi` / `table.t` /100 `.bar` classes, so keep those class names in sync if structural rules change.101102## Validation103104- [ ] Output is one complete `<!DOCTYPE html>` document with no external references and no `<script>` tags.105- [ ] Stats only: no narrative sections, observations, or recommendations.106- [ ] Every number traces to the transcript; absent metrics show `N/A`, never a guess.107- [ ] The page is self-contained and opens in any browser with no external assets, scripts, or fonts.