agentleFS
eldenjheldenjh/claude-usage-hud/skills/session-cost/SKILL.md

session-cost

Skill0 starsChanged 3 months agoMITView on GitHub

Compute token usage and USD cost for Claude Code sessions by parsing local transcript files, and track work against a self-imposed spend budget. Use when the user asks what a session cost, how many tokens were used, "what's my spend", "cost breakdown", "token usage", asks for /cost-style information, compares cost across sessions or models, or asks to set a budget/target and keep working until it is reached.

  • Reads credentials

4 other projects use a version of this.

Adopted by

Most recently taken up by

  • Other versions4 projects use a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 4

    1. dewil/ai-toolkit★ 9same name
    2. dewil/claude-toolkit★ 9same name
    3. PeteChu/skills★ 1same name
    4. MikkoNumminen/claude-skills★ 0same name

    How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.

The file

We split this skill into the rules it gives an agent

4 rules. About 0 are common, 2 are distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Run it from the directory the session belongs to — the project slug is derived from cwd (path with / replaced by -).

    line 36
  2. Distinctive:

    Do a meaningful chunk of real work — a feature, a fix, a file.

    line 166
  3. Very specific:

    Run --status and read the verdict.

    line 167
  4. Distinctive:

    The budget is a ceiling, not a quota to spend: If the task is done at 30% of budget, stop and say so.

    line 172

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Session Cost

The built-in /cost command is handled by the CLI and cannot be invoked by Claude. This skill reconstructs the same information from the transcript files on disk.

Where the data lives

~/.claude/projects/<cwd-slug>/<session-uuid>.jsonl — one JSON object per line. Every assistant turn carries message.model and message.usage:

  • input_tokens — uncached input
  • output_tokens
  • cache_creation.ephemeral_5m_input_tokens / ephemeral_1h_input_tokens
  • cache_read_input_tokens
  • speed — "fast" means fast mode (premium pricing on Opus 5 / 4.8)

There is no costUSD field in current versions, so cost is derived from a pricing table in the script. usage.iterations is a per-attempt breakdown of the same turn — do not add it to the top-level totals.

Usage

python3 $HUD/cost.py              # current session (newest transcript)
python3 $HUD/cost.py --all        # every session for this cwd
python3 $HUD/cost.py --session a1b2c3d4   # by id or prefix
python3 $HUD/cost.py --project ~/.claude/projects/<cwd-slug>
python3 $HUD/cost.py --json       # machine-readable

Run it from the directory the session belongs to — the project slug is derived from cwd (path with / replaced by -). "Current session" = most recently modified transcript in that directory, which is the live one.

Budgets — working toward a target

Two independent gates: real plan limits (from Anthropic) and a self-imposed spend budget (local). --status checks both and stops on whichever trips first.

Plan limits — real, but via an undocumented endpoint

Nothing on disk carries quota state (transcripts have no rate_limit_info / resetsAt — verified across the full record-key set). But the live numbers are one authenticated GET away, which is how openusage does it:

token ← macOS keychain "Claude Code-credentials"     (source of truth on macOS)
      ← $CLAUDE_CONFIG_DIR/.credentials.json         (often stale — check expiresAt)
      ← $CLAUDE_CODE_OAUTH_TOKEN
GET https://api.anthropic.com/api/oauth/usage
    Authorization: Bearer <token>
    anthropic-beta: oauth-2025-04-20

Response carries a limits[] array (kind, percent, severity, resets_at) plus flat five_hour / seven_day objects and extra_usage. Handling rules:

  • Never print, log, or echo the token. read_oauth_token() returns it to one caller which puts it straight into a header and drops it. Keep it that way.
  • This is not documented API surface. It can change shape or vanish; every call path degrades to "Plan limits unavailable: <reason>" rather than failing the run.
  • It throttles. Responses cache for 5 minutes (CACHE_TTL); on 429/5xx the skill backs off 15 minutes (BACKOFF) and serves the stale copy. Do not poll per turn.
  • Token refresh is out of scope. Claude Code keeps the keychain entry fresh. On a 401 the skill says the entry looks stale rather than writing new credentials itself.

Model-scoped windows. A weekly_scoped entry carries scope.model.display_name naming the model it caps, and is_active: true marks the window actually binding right now. limit_rows() renders these as weekly:Fable* — a bare "weekly_scoped" label is useless, since the whole question is which model.

Gating is model-aware, and must stay that way. A Fable-scoped cap at 100% must not halt an Opus session. report_status() compares each scoped window against the model in use (taken from the transcript's most recent usage record) and sorts them into blocked vs advisory; only blocked returns exit 2. Unscoped windows (session, weekly_all) always apply. Every window shows its own reset countdown — ↻6h55m in the statusline, in 6h55m (23:00) in the table.

Always-on statusline

$HUD/statusline.py renders limits + live spend on every prompt, wired into ~/.claude/settings.json as statusLine.command. Output looks like:

5h 8% │ wk 75% │ scoped 100% │ ↻23:00 │ $15.26 │ Opus 5

Green / yellow / red track severity. It costs zero tokens — the CLI runs it and prints to the terminal; the output never enters the model's context.

The statusline payload is the primary source — read it before adding anything. Claude Code passes a rich JSON blob on stdin, which already carries context_window (used_percentage, context_window_size, total_input_tokens), rate_limits (five_hour / seven_day with used_percentage and epoch resets_at), cost, model, effort, and exceeds_200k_tokens. All of it is free and always fresh. Dump stdin to a file from the script to inspect the current shape rather than guessing.

Consequently the statusline prefers payload values for the 5h/weekly windows and calls the API cache only for what the payload lacks: model-scoped caps (weekly:Fable) and severity. Older CLI versions send no rate_limits, so the API path remains as fallback.

cost.total_cost_usd in the payload is not the session total. It counts from CLI process start, so a restart or resume zeroes it while the transcript keeps accumulating — observed $6.95 (payload) vs $34.49 (transcript) for the same session. Both are correct over different spans; this skill reports the transcript figure, which is the session's whole life. Growth per turn matches between the two, which is how the gap was diagnosed.

Two invariants keep it fast enough to run per keystroke-render (~26ms):

  • Never fetch inline. It reads the cached usage blob and, when stale, spawns a detached cost.py --limits --refresh. The current render uses slightly old data rather than waiting on the network.
  • Never re-parse the transcript. Spend is scanned incrementally from a byte offset in cache/spend-<session>.json. Transcripts are append-only, so each render parses only new bytes.

Dedupe on requestId when touching either scanner. One assistant turn is written as several records sharing a requestId, each repeating the same usage block — counting them all inflates spend roughly 2.5x. scan() and spend_usd() both guard against it, and they should agree to the cent; if they diverge, that guard is the first suspect.

Where the scripts live

$HUD below is this bundle's scripts/ directory:

  • installed as a plugin → ${CLAUDE_PLUGIN_ROOT}/scripts
  • cloned standalone → the repo's scripts/ directory

Resolve it once, then use the commands as written. Runtime state (budget targets, the cached usage blob) lives in ~/.claude/session-cost/, overridable with SESSION_COST_HOME — deliberately outside the install directory so a plugin update or re-clone cannot wipe it.

Commands

python3 $HUD/cost.py --limits                    # plan windows only
python3 $HUD/cost.py --limits --refresh          # bypass the 5-min cache
python3 $HUD/cost.py --set-budget 12.00 --note "refactor the API layer"
python3 $HUD/cost.py --set-token-budget 2000000
python3 $HUD/cost.py --status                    # budget + limits + verdict
python3 $HUD/cost.py --status --no-limits        # budget only, no network
python3 $HUD/cost.py --clear-budget

--status stops on whichever gate trips first: any window at severity: critical or past --limit-threshold (default 95%), or the local budget.

Targets persist per session in targets/<session-uuid>.json, so --status works across turns. --status exit codes are the branch point:

exitmeaningwhat to do
0under budgetkeep working
2budget reachedstop, wrap up, report to the user
3no budget setask the user for a target, or just report spend

How to run a budgeted work loop

  1. Set the budget once, with a --note naming the actual goal.
  2. Do a meaningful chunk of real work — a feature, a fix, a file.
  3. Run --status and read the verdict.
  4. On CONTINUE, do the next chunk. On STOP, finish cleanly and report.

Two rules that keep this honest:

  • The budget is a ceiling, not a quota to spend. If the task is done at 30% of budget, stop and say so. Never invent filler work to consume the remainder — a budget authorizes spending, it does not require it.
  • Check between chunks, not between tool calls. Polling --status after every command burns tokens measuring token burn.

For a genuinely open-ended loop, pair this with the /loop skill (self-paced repeats) rather than hand-rolling one. Note also that this harness has a native token target: the user can append +500k-style directives to a prompt, which populates budget.total / budget.spent() / budget.remaining() inside the Workflow tool. That one is enforced by the runtime — agent() calls throw once the target is hit — whereas this skill's budget is advisory and depends on Claude actually checking it.

Reporting results

Report the total and the per-model breakdown. Two caveats worth stating once:

  • These are list prices. On a Pro/Max subscription there is no per-token bill, so the figure is notional spend — point the user at /usage for actual plan limits.
  • Cache writes bill at 1.25x (5m TTL) / 2x (1h TTL) the input rate; cache reads at 0.1x. Long sessions are usually dominated by cache reads, which is why the total is far below what raw token counts suggest.

Maintaining the pricing table

PRICING in cost.py is USD per million tokens, (input, output). It is a cached snapshot — when a new model ships or rates change, update that dict. Do not write prices from memory; load the claude-api skill, which carries the current table, or fetch https://platform.claude.com/docs/en/pricing.md. Unknown models fall back to a longest-prefix match (so dated snapshots like claude-haiku-4-5-20251001 resolve), and anything still unmatched is reported as unpriced rather than silently costed at zero.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.