| name | session-metrics |
| effort | medium |
| description | Tally Claude Code session token usage and cost estimates from the raw JSONL conversation log. Trigger when the user asks about session cost, token usage, API spend, cache hit rate, input/output tokens, or wants a breakdown of how much a Claude Code session has cost. Also trigger for "how much have we spent", "show me token usage", "session summary", "cost so far", or any request to analyse or display per-turn metrics from the current or a past session.
Do NOT auto-dispatch compare mode (--compare / --compare-prep / --compare-run / --count-tokens-only) from natural-language phrases. The skill body uses $ARGUMENTS[0] as the dispatch key — if the first positional argument is not literally "compare", "compare-prep", "compare-run", or "count-tokens", route to the default single-session report.
|
Session Metrics
Runs scripts/session-metrics.py against the Claude Code JSONL log to produce
a timeline-ordered cost summary with per-turn and cumulative totals.
Dispatch — how to route this invocation
First positional argument received: $ARGUMENTS[0]
Full argument string: $ARGUMENTS
Read $ARGUMENTS[0] above and match it by literal equality against the
table below. Claude Code already tokenized the arguments shell-style, so no
parsing is required — just compare strings.
$ARGUMENTS[0] | Route | Then read |
|---|
all-projects | Instance-wide dashboard aggregating every project under ~/.claude/projects | ## Instance dashboard (all projects) below |
compare | Two-session compare on JSONLs that already exist | ## Model comparison below, then references/model-compare.md before running |
compare-run | Fully automated capture: spawns two claude -p sessions, feeds the suite, then runs --compare | ## Model comparison below, then references/model-compare.md "Workflow A — automated" |
compare-prep | Print manual capture protocol + 10-prompt suite (fallback when headless is unavailable) | ## Model comparison below |
count-tokens | API-key-only tokenizer check | ## Model comparison below |
export | Natural-language export shortcut — scan full arg string to determine session vs project scope | ## Export shortcuts below |
project | All sessions for the current project — timeline + per-session subtotals + grand total | ## Quick usage below (--project-cost); also scan remaining args for --output format flags |
project-cost | Alias for project | ## Quick usage below (--project-cost); also scan remaining args for --output format flags |
| (empty, or any other value) | Default single-session report | ## Quick usage below |
This is the single gate that keeps compare mode off the natural-language
path. Do not infer the route from the user's chat history; only use the
literal value of $ARGUMENTS[0] above.
When the skill auto-triggers from a natural-language question ("how much did
this session cost?", "show me token usage"), there are no positional
arguments — $ARGUMENTS[0] is empty — and you always route to the default.
Phrases like "compare 4.6 vs 4.7 cost" arriving as natural language do NOT
produce $ARGUMENTS[0] = compare and must not route into compare mode;
answer them by running the default report on the current session and
suggesting /session-metrics compare-prep if the user wants a real
benchmark.
Pre-flight context
- skill-dir: ${CLAUDE_SKILL_DIR}
- session-id: ${CLAUDE_SESSION_ID}
Export shortcuts
Reached when $ARGUMENTS[0] is export. Scan the full argument string (not just $ARGUMENTS[0]) to determine scope and formats. Apply these checks in order (first match wins):
- Full arg string contains
all-projects → --all-projects --output <formats>
- Full arg string contains
project (and not already caught above) → --project-cost --output <formats>
- Otherwise → current session
--session ${CLAUDE_SESSION_ID} --output <formats>
Infer format flags from the argument string: html → html, csv → csv, md or markdown → md. Always add json alongside any requested format per the post-export audit convention (see ## Optional post-export audit below).
Always add --quiet to session and project export commands. When exporting, the per-turn detail lives in the written HTML/JSON, so the full stdout timeline is redundant — and at project scope (or for a long session) it can run to thousands of lines, spilling the run into a harness overflow file that buries the [export] path lines you need. --quiet collapses stdout to the legend + grand total + footer (plus the [export] lines), keeping the run inline. Do not add --quiet for --all-projects — its instance dashboard text is already compact and the flag has no effect there.
Examples:
| Full argument string | Command |
|---|
export session | --session ${CLAUDE_SESSION_ID} --quiet --output json |
export session to html | --session ${CLAUDE_SESSION_ID} --quiet --output html json |
export session metrics to html | --session ${CLAUDE_SESSION_ID} --quiet --output html json |
export to html | --session ${CLAUDE_SESSION_ID} --quiet --output html json |
export project | --project-cost --quiet --output json |
export project to html | --project-cost --quiet --output html json |
export project sessions | --project-cost --quiet --output json |
export project sessions to html | --project-cost --quiet --output html json |
export entire project's session metrics to html | --project-cost --quiet --output html json |
export project metrics to html csv | --project-cost --quiet --output html csv json |
export all-projects | --all-projects --output json |
export all-projects to html | --all-projects --output html json |
project and project-cost as the first arg also pick up --output flags from remaining args the same way (e.g. /session-metrics project metrics export to html → --project-cost --quiet --output html json).
Quick usage
uv run python ${CLAUDE_SKILL_DIR}/scripts/session-metrics.py --session ${CLAUDE_SESSION_ID}
uv run python ${CLAUDE_SKILL_DIR}/scripts/session-metrics.py --session <uuid>
uv run python ${CLAUDE_SKILL_DIR}/scripts/session-metrics.py --slug=-home-user-projects-myapp
CLAUDE_PROJECT_SLUG="-home-user-projects-myapp" uv run python ${CLAUDE_SKILL_DIR}/scripts/session-metrics.py
uv run python ${CLAUDE_SKILL_DIR}/scripts/session-metrics.py --list
uv run python ${CLAUDE_SKILL_DIR}/scripts/session-metrics.py --project-cost
uv run python ${CLAUDE_SKILL_DIR}/scripts/session-metrics.py --quiet --output json
uv run python ${CLAUDE_SKILL_DIR}/scripts/session-metrics.py --quiet --output json csv md html
uv run python ${CLAUDE_SKILL_DIR}/scripts/session-metrics.py --project-cost --quiet --output html
${CLAUDE_SKILL_DIR} is expanded by Claude Code to the skill's install directory (plugin cache, project-local copy, or bundled template — whichever applies). When running the script manually from a shell, substitute the actual path.
Quick shell wrapper. For manual runs outside Claude Code, the bundled scripts/session-metrics-quick.sh auto-locates session-metrics.py (including the version-pinned plugin-cache install), detects the current project + newest session, and runs an HTML+JSON export. Pass --session <uuid> (or -s) to target a specific session instead — it resolves across all projects, so you can run it from a fresh low-context session to export an earlier heavy one; the HTML+JSON default still applies unless you pass --output. Other flags (--project-cost, --list, an explicit --output …) pass through verbatim.
Export formats
--output accepts one or more of: json csv md html
Text is always printed to stdout. Exports go to exports/session-metrics/ in the
project root, named session_<id8>_<YYYYMMDD>T<HHMMSS>Z.<ext> (single) or
project_<YYYYMMDD>T<HHMMSS>Z.<ext> (project mode). An index.html
manifest at the export root is refreshed after every export — open it to
browse all runs (newest first, with a latest-run-per-scope strip).
| Format | Contents |
|---|
json | Full structured report with all turns, subtotals, model rates |
csv | One row per turn: session_id, index, timestamp, model, tokens, cost |
md | Summary table + per-session Markdown tables |
html | Dark-theme report with summary cards + insights + chart. 2-page split by default (<stem>_dashboard.html + <stem>_detail.html); pass --single-page for one file. |
HTML-specific flags
| Flag | Purpose |
|---|
--single-page | Emit one self-contained HTML instead of the dashboard+detail split. |
--chart-lib {highcharts,uplot,chartjs,none} | Choose the chart renderer. Default highcharts (richest visualization, vendored, SHA-256-verified, non-commercial license). uplot and chartjs are MIT-licensed alternatives. none emits a detail page with no JS dependency. See scripts/vendor/charts/README.md for per-library license terms. |
--peak-hours H-H | Translucent band on the hour-of-day chart (e.g. 5-11). Community-reported, not an Anthropic SLA. |
--peak-tz <IANA> | Timezone the peak hours are defined in (default America/Los_Angeles). |
Other useful flags
| Flag | Purpose |
|---|
--tz <IANA> | IANA timezone for time-of-day bucketing and timeline/export timestamps. Defaults to the system local tz (auto-detected via TZ env var or the OS setting). |
--utc-offset <H> | Fixed UTC offset, DST-naive. Use --tz for DST-aware. |
--no-cache | Skip ~/.cache/session-metrics/parse/ and always re-parse from scratch. |
--prune-exports <N> | Prune the export directory: keep the newest N runs per retention group (each session id, the project series, each compare pair, the instance dated dirs) and delete older runs' files. audit_* sidecars and unrecognised files are never touched. Dry run by default — add --yes to actually delete. |
--quiet / -q | Suppress the per-turn timeline on stdout — print only the legend, scope header, grand-total subtotal, and footer (the [export] path lines still print). Keeps stdout small on large session/project exports so the export paths aren't buried under an overflow-sized dump; the full per-turn detail still lands in the written HTML/JSON. Session and project scopes only (no effect on --all-projects). |
--no-self-cost | Suppress the self-cost meta-metric (stderr [self-cost] line, HTML KPI card, and JSON self_cost key). |
--redact-user-prompts | Replace freeform prompt_text / prompt_snippet / assistant_text / assistant_snippet with [redacted] on every turn of single-session and project JSON exports, plus compare HTML. Tool inputs, slash-command names, and structured cost / token fields stay visible. HTML / MD / CSV / text are NOT redacted. |
--export-share-safe | One-flag pre-share gesture (v1.36.0+): implies --redact-user-prompts and --no-self-cost, and chmods every written export file to 0600 (rw-------). For full prompt redaction, pair with --output json. |
--no-include-subagents | Skip spawned subagent JSONL files. Subagents are included by default; use this for faster runs when subagent detail is not needed. |
--cache-break-threshold <N> | Turns whose input + cache_creation exceed N are flagged as cache-break events (default 100 000). Matches Anthropic's session-report convention. |
--no-subagent-attribution | Disable Phase-B subagent → parent-prompt token attribution. Default behaviour rolls every subagent's tokens up onto the user prompt that spawned the chain (additional attributed_subagent_* fields, no double-counting). |
--sort-prompts-by {total,self} | How to rank top prompts in HTML/MD output. total (default) = parent + attributed subagent cost, surfaces cheap-prompt-spawning-expensive-subagent turns. self = parent only (pre-Phase-B order). CSV/JSON keep self ordering for stability regardless of this flag. |
Invocation note for the AI. Don't pass --tz or --utc-offset unless the user explicitly asks for a specific timezone. The script auto-detects the user's system tz and renders all human-facing timestamps (timeline, session headers, generated-at banner, block anchors) in that tz. JSON/CSV raw timestamp fields stay UTC ISO-8601 as a machine-readable audit trail. Don't pass --include-subagents — subagents are included by default. Only pass --no-include-subagents if the user explicitly asks for a faster/leaner run without subagent detail.
Output columns
| Column | Meaning |
|---|
# | Deduplicated turn index |
Time | Timestamp of the turn in the user's local timezone (auto-detected; override with --tz / --utc-offset). Header shows the active tz label. Raw timestamp fields in JSON/CSV exports remain UTC ISO-8601 (...Z) for machine-readability. |
Input | Net new input tokens (uncached portion only — cache reads/writes are shown separately) |
Output | Output tokens generated (includes thinking + tool_use block tokens) |
CacheRd | Tokens served from prompt cache (cheap) |