sessionlint
v0.5.0
Published
A local-first linter, gauge, and guard that identifies Claude Code patterns that often drive API-equivalent cost.
Maintainers
Readme
sessionlint
ccusage shows the bill. sessionlint shows patterns behind it — and helps agent loops land.
sessionlint reads your local Claude Code session history and identifies patterns that often drive API-equivalent cost. It reports honest ranges with labeled assumptions rather than pretending every heuristic is causal, then helps future sessions avoid repeated waste.
Three layers, all in one CLI:
- Audit — a read-only linter over your existing transcripts, with a
replay-verified
--verifymode. - Live session — a statusline burn gauge, per-session budgets, and cheaper-model delegation for subagents.
- Autonomous runs — budgets, a convergence watchdog, and morning-after reports for Ralph/GSD-style loops, headless runs, and CI.
Install
Requires Bun:
curl -fsSL https://bun.com/install | bash # if you don't have Bun
bunx sessionlint # that's the whole setupNo config, no account, no server. It reads Claude Code's transcripts from your own
disk — ~/.claude/projects, or $CLAUDE_CONFIG_DIR/projects if you use a custom
config dir (it even detects the case where Claude Code misplaced them).
What a report looks like
Excerpt of real output, run against this repo's synthetic test fixtures:
$ sessionlint
sessionlint · 2 sessions analyzed
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
session syntheti 2 turns
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⚠ CACHE-NUKE turns 1–2 -$0.03–$0.01
Model switch claude-opus-4-8 -> claude-sonnet-5 was followed by
~8,500 tokens billed as fresh input. The switch occurred within the
default five-minute cache TTL; the cost range spans both cache-hit
and cache-cold counterfactuals.
→ sessionlint explain cache-nuke
session cost: $0.04 API-equivalent · could plausibly have been ~$0.03–$0.04
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
session syntheti 5 turns
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⚠ MISSING-CLEAR-AT-TOPIC-BOUNDARY turns 1–5 $0.19–$2.19
Context grew to ~802,000 tokens and was never cleared or compacted
for the rest of the session — consider /clear or /compact at a
natural topic boundary.
→ sessionlint explain missing-clear-at-topic-boundary
session cost: $2.71 API-equivalent · could plausibly have been ~$0.52–$2.52
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
2 findings across 2 flagged sessions · replay-audit with: sessionlint --verify
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━Every finding names its rule, shows the evidence, and gives a $ range with labeled
assumptions. Findings are always suppressible (--suppress <rule-id>) — sessionlint
tells you what happened; it never overrules you.
Commands
| Command | What it does |
|---|---|
| sessionlint | Lint your history; findings with $ ranges. --json / --md, --dir <path> to point elsewhere |
| sessionlint --ci | CI gate: versioned JSON to stdout, non-zero exit when a finding meets --fail-on <error\|warning\|info> (default error) |
| sessionlint sessions | List sessions: id, date, turns, API-equivalent cost estimate |
| sessionlint explain [<rule>] | What a rule detects, why it costs, how to fix it |
| sessionlint doctor | Environment check: where sessions are read from, how many found, pricing freshness |
| sessionlint export --redact | Write redacted copies of your sessions (prose/paths/secrets removed) so you can share history. --out <dir> |
| sessionlint --verify | Replay-audit findings with real, billed API calls — cost preview + confirmation first |
| sessionlint statusline | Burn gauge for Claude Code's statusLine.command |
| sessionlint budget set <usd> | Per-session $ budget for the statusline sentinel (status / off) |
| sessionlint auto-delegate <model> | Route subagents to a cheaper model from the next session |
| sessionlint watch | Supervise an in-session loop (ralph-loop, GSD) by tailing transcripts — read-only unless you opt in |
| sessionlint loop -- <cmd> | Wrap an external loop with budgets + convergence watchdog |
| sessionlint run --prompt <text> | Budgeted, model-laddered headless claude -p run |
| sessionlint report | Morning-after summary of the last loop run |
| sessionlint help | Full flag reference |
The rules
| Rule | What it catches |
|---|---|
| cache-nuke | Fresh-input processing after a model switch, with TTL-aware attribution and dynamic severity |
| late-compaction | Context filled up until auto-compaction hit, after carrying the excess for many turns |
| giant-file-read | A single read pulling 1,000+ lines into context (offset-limited reads of big files don't count) |
| missing-clear-at-topic-boundary | Context grew huge and never got a /clear or /compact |
| repeated-identical-prompt | The same prompt retried verbatim — the first attempt's cost bought nothing |
Details per rule: sessionlint explain <rule> or docs/rules/.
Privacy & honesty
- Local-only. Your transcripts never leave your machine. No server, no account, no telemetry.
- Read-only by default. Anything that spends money (
--verify,run) shows a cost preview and requires explicit confirmation.--paranoidblocks SessionLint-owned API and webhook calls; it cannot sandbox commands you explicitly launch. - Ranges, not points. Dollar math runs against a pinned, dated pricing table
with a staleness warning (
sessionlint doctorshows its age). Anything not measured exactly is a labeled assumption.
Status
Young but real: dogfooded on the author's own history, with a published test suite. The first replay-verified equivalence audit was inconclusive (0/5 sampled turns matched on a cheaper model, far too small a sample for a general conclusion) and is published as-is because honest uncertainty is the point.
Contributing your history to the validation study
The rules are still heuristics until their precision is measured on real, diverse sessions. If you'd like to help, you can donate a redacted copy of your history:
sessionlint export --redact --dry-run # preview: shows exactly what would be shared, writes nothing
sessionlint export --redact # writes redacted session-NNN.jsonl + a MANIFEST.md receiptThe export removes prose, file contents, paths, filenames, secrets, and free-text
keys, and keeps only what the rules need (model names, timestamps, usage token
counts). It writes a MANIFEST.md receipt describing exactly what's included, and a
self-check flags any residual secret/email pattern. sessionlint never transmits
anything — you review the folder and share it privately yourself. Redaction is
best-effort; please open a couple of files and confirm you're comfortable before sending.
Development
bun install
bun testMIT © Preyash Yadav
