If you've been using Claude Code heavily, you've probably had this moment: you open
your Anthropic billing page and wonder when exactly that happened. Which session?
Which agent? Which project?
There's no built-in answer. Claude Code doesn't expose per-session cost data, and the
billing dashboard shows you totals — not the story behind them.
So I built cast-observe: a local observability layer that hooks into Claude Code's
event lifecycle and writes everything to a SQLite file on your machine.
What You Get
- Per-session token counts and USD cost
- Agent run history (name, status, duration, cost)
- Daily and weekly cost summaries, filterable by project
- Budget alerts when you cross a threshold
- A live TUI dashboard (
cast-observe dash) - Direct SQL access to all your data
No cloud. No telemetry. No SaaS. Just ~/.claude/cast.db.
How It Works
Claude Code supports a hook system — shell scripts that fire on lifecycle events like
SessionStart, SubagentStop, PostToolUse, etc. cast-observe registers eight of them:
| Hook | What It Does |
|---|---|
SessionStart |
Opens a new session row in SQLite |
SessionEnd |
Finalizes the session, triggers budget checks |
SubagentStart |
Records an agent invocation |
SubagentStop |
Logs completion status, duration, and cost |
PostToolUse |
Reads token usage from the tool response, computes USD |
PostToolUseFailure |
Same — failed calls still cost tokens |
PreCompact / PostCompact
|
Handles Claude's context compaction events |
All hooks run with async: true so they never block Claude Code execution.
The Data Flow
Every PostToolUse fires with a JSON payload on stdin containing the model name, input
tokens, and output tokens. A small Python script looks up the model's price per million
tokens from a local pricing config, computes the cost, and writes a row to SQLite with
retry logic to handle lock contention.
# Simplified — see observe-cost-tracker.py for full version
cost = (input_tokens / 1_000_000 * input_price) + \
(output_tokens / 1_000_000 * output_price)
The hook reads from stdin, never from environment variables. This keeps sensitive
session data out of the process table.
The Schema
Four tables:
- sessions — one row per Claude Code session, with aggregated tokens and cost
- agent_runs — one row per agent invocation (name, status, duration, cost)
- routing_events — placeholder for CAST multi-agent routing data
- budgets — your configured limits and alert thresholds
The CLI
# Today's usage and recent agent runs
cast-observe status
# Budget summaries
cast-observe budget
cast-observe budget --week
cast-observe budget --project my-project
# Session history
cast-observe sessions --limit 20
# Raw SQL access
cast-observe db query "SELECT agent_name, SUM(cost_usd) FROM agent_runs GROUP BY agent_name"
# Launch the TUI
cast-observe dash
The TUI is built with Textual and shows live agent
runs, session cost breakdown, and status colors (green = DONE, yellow = DONE_WITH_CONCERNS,
red = BLOCKED).
Installation
Via Homebrew:
brew tap ek33450505/cast-observe
brew install cast-observe
cast-observe install
From source:
git clone https://github.com/ek33450505/cast-observe.git
cd cast-observe
bash install.sh
The install script merges the hook configuration non-destructively into your existing
~/.claude/settings.json, so it won't clobber any hooks you already have.
Requirements: macOS 12+ or Linux, Claude Code, python3, sqlite3.
Why Local SQLite?
Observability tools tend to default to "send it to a server." I wanted the opposite.
- Zero latency — writes are local, no network round trips on every tool call
- Full ownership — your usage data stays on your machine
- SQL as the API — power users can query anything directly
- Works offline — no outage pages, no rate limits
The WAL (Write-Ahead Logging) mode keeps concurrent reads and writes from blocking
each other, which matters because multiple hooks can fire in rapid succession during
an agent run.
Works Alongside CAST
If you use CAST — the multi-agent
framework built on top of Claude Code — cast-observe shares the same ~/.claude/cast.db
database. CAST writes to routing_events; cast-observe reads from it but doesn't own it.
Installing one later won't break the other.
What's Next
- Per-model cost breakdown in the TUI
- Exportable reports (CSV/JSON)
- Session diff view: compare two runs side by side
- GitHub Actions integration for tracking CI-time usage
The repo is at ek33450505/cast-observe,
MIT licensed. PRs, issues, and feedback welcome.
If you're using Claude Code and have been flying blind on cost, give it a try.
Top comments (0)