agentleFS
kiloloopkiloloop/kiloloop-skills/skills/usage-cost/SKILL.md

usage-cost

Skill0 starsChanged 3 months agoApache-2.0View on GitHub

Report a coding agent's locally recorded token usage and what that usage would cost at published API list rates. Use when asked what a subscription period's work would have cost per token, which models or cache tiers dominate spend, or how many tokens an agent has actually used.

The file

We split this skill into the rules it gives an agent

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

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈7

    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. Distinctive:

    Read the exit status before reporting anything:

    line 81
  2. Distinctive:

    Records that cannot be trusted are excluded, and counted: Every raw counter — including the flat cache total, both split tiers, and …

    line 152
  3. Distinctive:

    No session or runtime overhead: Only per-request token usage is priced.

    line 289
  4. Very specific:

    No published API price was established for codex-auto-review or gpt-6.1-astra on 2026-10-15 UTC.

    line 387
  5. Distinctive:

    Read the current published rates.

    line 393
  6. Very specific:

    If a cache multiplier changed for every model, edit the table's cache_multipliers.

    line 396
  7. Distinctive:

    Run the fixtures. The exact-cost claim is pinned to the shipped rates, so a rate change is expected to fail it.

    line 399
  8. Distinctive:

    Use whichever fits. For browsing personal usage interactively, the established tool is the better experience.

    line 478
  9. Distinctive:

    Reproduce the figures in your message: Never answer with "see the output above" or a bare file path.

    line 517

≈ “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

Subscription Usage Cost

A subscription bills a flat fee, so nothing attaches a price to any individual request. The tokens are recorded locally anyway, and list rates are published, so the two can be combined into one number: what this period's usage would have cost had it been billed per token through the API.

That number is an equivalence, not an invoice. It does not predict a bill, and it is not what anyone was charged. It answers a narrower question — how much metered work went through a flat-rate plan — and it is computed entirely from files already on disk.

Run it

python scripts/usage_cost.py
python scripts/usage_cost.py --since 2026-08-01 --until 2026-08-31 --format json

A window edge can also be an instant, for a question a calendar day cannot ask — usage since a seat switch at 17:20, or usage up to right now:

python scripts/usage_cost.py --since 2026-08-11T17:20 --until 2026-08-11T18:00
FlagPurpose
--runtimeWhich runtime's records to read. Default claude-code.
--data-rootOverride where those records live.
--since / --untilRestrict the window. A day (YYYY-MM-DD) covers that whole day in --tz; an ISO 8601 datetime is an instant — --since inclusive, --until exclusive. The two forms mix.
--tzZone whose calendar days usage is bucketed into. Default local; also accepts UTC or an IANA name.
--ratesUse a different rate table.
--cwdCodex only: filter tokens to an exact normalized session working directory; the meter stays account-wide.
--formattable (default) or json.

A day edge and an instant edge mean different things, and each keeps its own. --since 2026-08-11 starts at the first moment of that day in --tz; --since 2026-08-11T12:00:00 starts at noon. A datetime --until is exclusive, so --until T and --since T partition the records between them rather than both counting the one stamped exactly at T — which is what makes two adjacent windows sum to the whole. A datetime written without an offset is read in --tz; one written with an offset (2026-08-11T05:00:00-07:00, or a trailing Z) is taken as written. When either edge is an instant the header reports the window as resolved instants, so a figure can be tied to exactly what it covered; a date-only window keeps reporting the days it was given, and its output is unchanged. Records with no usable timestamp cannot be placed against an instant and stay out of such a window, the same rule a day window already applies.

A run with no window leads with an at-a-glance block — today, yesterday, and this week, with tokens rounded to B/M/K — above the per-model detail. An explicit window suppresses it, since the window is the same question asked precisely.

Days follow the reader's local zone, not UTC. Records are stamped in UTC, but "today" means the reader's today: bucketing in UTC files a US evening's work under tomorrow, which is precisely the figure someone is asking about. The week runs Monday to today, so on a Monday it equals today. The zone in force is printed in the header and carried in JSON as timezone — state it when reporting, because the same records give different daily totals in different zones.

The local zone resolves to a real IANA zone (from TZ, else /etc/localtime) so each timestamp gets the offset that applied at that moment rather than the one in force today — otherwise a summer machine buckets winter records an hour off and can move them across midnight. If no IANA zone name can be determined, days are bucketed directly through the platform's own per-instant rules (time.localtime), which apply the same historical transitions, and the header labels the zone system local — a statement of exactly what is known, rather than an abbreviation posing as an IANA zone.

Read the exit status before reporting anything:

ExitMeaning
0Report produced; every model was priced.
2Usage, configuration, or rate-table error.
3Report produced, but at least one model id had no rate. Tokens for those rows are counted and shown; their cost is omitted and the total is marked incomplete.
4The requested runtime has no readable local usage source — the directory is missing, holds no .jsonl transcripts, or none of them could be read. This is distinct from a readable source containing no usage, which is a real zero and exits 0.

Exit 3 is a successful run with a caveat, not a failure. Report the total as partial and name the unpriced model ids.

Data source

Claude Code

The claude-code adapter reads Claude Code's per-session transcripts — the .jsonl files under ~/.claude/projects (or $CLAUDE_CONFIG_DIR/projects). Each line is one JSON record; assistant records carry message.model and a message.usage block holding the token counts the API returned for that request. Those counts are what makes a local reconstruction possible.

Fields read, and nothing else:

FieldUsed for
message.modelRate lookup
message.usage.input_tokensUncached input
message.usage.output_tokensOutput
message.usage.cache_read_input_tokensCache reads
message.usage.cache_creation.ephemeral_5m_input_tokens5-minute cache writes
message.usage.cache_creation.ephemeral_1h_input_tokens1-hour cache writes
message.usage.cache_creation_input_tokensFallback when the split above is absent
message.usage.speedSelects a fast-mode rate where one is defined
message.usage.server_tool_use.web_search_requestsReported, not costed
message.id, requestIdDeduplication
timestampDay bucketing, and ordering copies whose pricing identities conflict

Two properties of the format shape the implementation:

  • Records repeat, and the copies differ. One API response is appended several times as it streams, under a single message.id / requestId pair. Earlier copies hold partial counts and later ones grow toward the final total, so the copies are not interchangeable. Summing every line overstates usage badly — most usage-bearing lines are repeats — while keeping the first copy understates it just as badly, because an early snapshot of a long response can hold a small fraction of its final output tokens. Copies are therefore reconciled per request: the largest aggregate magnitude wins outright. Among copies tied at that magnitude, only one disagreement is decidable from content — an exact cache-lifetime split supersedes one padded from a flat total when model and every other class agree, because the padding is this tool's own approximation rather than a source claim. Every other disagreement between tied copies — token-class composition, model, or a speed variant — is a factual conflict about what to price, settled only by source evidence: the uniquely latest timestamp among the contenders. When nothing orders them, the request is dropped and the drop reported, because pricing it by read order — or by a token-class ordering convention — would be arbitrary. Both the number of merged records and the number of requests whose copies actually differed are reported, so the reconciliation is visible.
  • Cache creation is reported twice. Once as a flat total, and separately split by cache lifetime. The split is preferred because the two lifetimes bill at different multiples of the input rate. The flat total is used whenever the split carries nothing usable — including when cache_creation is present but empty — and is attributed to the 5-minute tier, the cheaper of the two, so an unsplit record understates rather than inflates. A split accounting for less than the flat total has the remainder carried at the 5-minute tier and the shortfall counted; a split claiming more than the flat total — the aggregated multi-iteration record shape, where the legacy flat field lags the split, sometimes reading 0 — is used as stated, with the disagreement counted so it is visible rather than absorbed.
  • Records that cannot be trusted are excluded, and counted. Every raw counter — including the flat cache total, both split tiers, and server-tool counts — is validated before any fallback runs: a counter that is present must be a non-negative integer, so a negative, fractional, boolean, or string value is rejected rather than masked by normalization; only an absent field contributes zero. A record carrying no request identity is counted on its own rather than merged with every other identity-less record. All of these counts appear in the output.

Codex

The codex adapter reads rollout-*.jsonl recursively under ~/.codex/sessions (or $CODEX_HOME/sessions). --data-root replaces the sessions directory. It reads record type, timestamp, turn_context model, the first absolute cwd in session_meta or turn_context, and event_msg payloads of type token_count or thread_settings_applied. Prompts and response text are not used or emitted.

--cwd <path> selects whole sessions by exact normalized path, using that first recorded absolute cwd. ~ and relative paths in the CLI argument are expanded relative to the current shell directory; ./.. and trailing separators are normalized lexically, without resolving symlinks. Child paths, including worktrees, are separate projects; missing cwd never matches. Later turn contexts do not reassign a session. The token table and summaries are filtered, but meters still include all scanned sessions in the requested time window. Table output and JSON project_filter.meter_scope explicitly say the meter is account-wide.

For each file, walk payload.info.total_token_usage in file order. Subtract the previous cumulative snapshot to obtain each positive increment. An equal snapshot contributes nothing; a decrease in total_tokens starts a new segment and its first snapshot contributes in full. Never sum last_token_usage (it can repeat), and never take only the maximum cumulative total (that loses earlier segments). This is the adapter's reconciliation policy, not a guarantee that the rollout format is stable. The Requests column counts positive usage increments, which need not equal user turns or server requests.

Each increment uses the latest preceding turn_context.model, or unknown-codex-model if unavailable. This is the recorded turn model; the rollout does not establish that it was the actual serving model. Apply the window to the increment's timestamp after updating the cumulative baseline, so a window starting mid-session counts only its increments.

OpenAI's prompt-caching guide defines cache reads and cache writes as subsets of input. Normalize uncached input as input_tokens - cached_input_tokens - cache_write_input_tokens. Reasoning tokens are included in output, so retain output_tokens without adding reasoning_output_tokens again. Cache reads map to cache_read; unsplit cache writes use the existing cache_write_5m count slot, with cache_write_1h zero. This preserves tokens without asserting a five-minute lifetime: Codex records no cache lifetime. The Astra rate row prices these writes at its published cache-write rate.

Each increment takes the latest preceding event_msg.payload.thread_settings.service_tier on a thread_settings_applied event in the same file. priority and fast map to Fast, default to Standard; missing or unrecognized settings map to unknown. Ordinary turn contexts do not reset the applied setting. The table has a Tier column, and JSON models has one row per (model_id, service_tier) with tier values fast, standard, or unknown, including separate tokens and requests. These are recorded settings, not proof of the actual served tier.

SOURCE: OpenAI Fast mode and the Astra model page, read 2026-09-21 UTC. Fast and priority are equivalent API tier names. Astra list rates per million are USD 10 input, USD 1 cached input, USD 12.50 cache writes and USD 50 output; Fast costs 2× applicable API rates, independently of Codex credit consumption. The table stores Fast in the model's speeds.fast entry. Above 272,000 input tokens, Astra's long_context rule multiplies input/cache prices by 2 and output by 1.5 before the Fast premium. This calculation treats each positive cumulative increment as a request; batched or partial increments cannot establish exact per-request long-context billing. Unknown tiers, models without rates, and Fast rows lacking a Fast rate stay UNPRICED (exit 3), with tokens counted and cost withheld.

Input, output, and total counters are required; absent cache/reasoning fields contribute zero. Present counters must be non-negative integers, subsets must fit their totals, and input plus output must equal total. Inconsistent snapshots or deltas are skipped and counted, retaining the last valid baseline. Like the Claude adapter, JSON decode failures and non-object lines increment malformed_lines; blank lines are ignored. Unreadable files are counted, and an absent, empty, or wholly unreadable rollout source exits 4.

The separate rate-limit block selects weekly snapshots inside the selected window, including records with repeated usage or info: null. It accepts rate_limits.limit_id == "codex" and integer window_minutes from 10079 through 10081 (one week ±1 minute) in either primary or secondary. Older records with an absent or null limit_id are accepted only with that weekly duration. Other IDs and durations outside that range are omitted.

The newest reset cluster consists of snapshots whose resets_at is within 10 seconds of the greatest eligible reset epoch. This fixed anchor prevents chains of jitter from joining separate windows. Report the highest used_percent in that cluster, breaking ties by newest source timestamp, then reset epoch and source identity. A later snapshot from an older weekly window cannot replace it. The displayed timestamp and reset belong to the selected high-water snapshot; this is observed usage, not a live query or a value derived from local totals. JSON preserves the selected values under rate_limits.primary, adds the original source_slot, limit_id (null for legacy absence or null), scope: account, selection rule and reset tolerance. The original window_minutes is preserved in JSON and table output. Resets are also rendered in --tz. Without an eligible in-window snapshot, the block/key is absent.

Meter diagnostics cover the selected local-day window: invalid_rate_limits counts malformed meter data, while unstamped_rate_limits counts otherwise valid meters whose source timestamp is missing or cannot be parsed as an instant. Such meters cannot be ordered and are excluded from the displayed snapshot without dropping valid usage on the same record. A usable date prefix can still place an unparseable timestamp in a window; without a usable day, the meter is considered only when no window is requested. An instant window is stricter, and deliberately so: a record with no parseable instant cannot be placed against one at all, so its usage and its meter fall outside the window together and the record is counted in filtered_out rather than reaching unstamped_rate_limits. These counters do not change the scope of file-reading or token-reconciliation diagnostics. cwd_filtered_out counts valid, nonduplicate token increments excluded by --cwd inside the selected window; increments outside it are counted in filtered_out instead.

What the source does not cover

  • Only what was written locally. Sessions that never wrote a transcript or rollout here, including work on other machines and deleted records, are invisible. Token totals cover the selected runtime's local files; the Codex server meter is a separate account snapshot.
  • Server-side tool calls are priced per call when the table rates them. Web search bills per call rather than per token, so it cannot ride a per-million-token model row: it gets its own row in the table, carrying its call count and cost, and that cost is inside the total. It contributes no tokens, so the token total is unaffected. A tool with recorded calls that the table does not rate behaves exactly like an unpriced model — counted, marked UNPRICED, excluded from the cost total, and the run exits 3.
  • Every request is priced at the synchronous list rate. The published pricing also defines batch processing at 50% of list rates, but a coding agent's requests are interactive — none of the local records is a batch request, so the discount is never applied.
  • No session or runtime overhead. Only per-request token usage is priced.
  • Records with no timestamp are dropped from a windowed run. They are included only when no --since / --until is given, so a windowed total never quietly absorbs usage from outside the window.
  • Optional absent fields count as zero; present ones must be valid. Claude usage components and Codex cache/reasoning components may be absent. Codex input/output/total counters are required. A present non-integer field makes the record invalid — mapping it to zero would make corruption indistinguishable from absence. Lines that do not parse, and files that cannot be read, are skipped and their counts reported.
  • Subagent turns are included. They are billed like any other request.

Rate table

Rates live in scripts/rates.json, a versioned data file the report stamps by rate_table_version. Rates are per million tokens, stored as decimal strings so loading introduces no floating-point error, and all arithmetic is decimal.

Input and output rates are stored per model. Cache rates are derived from the input rate by multiplier, the way the published pricing defines them. The table's cache_multipliers are the default:

TierDefault
Cache readinput x 0.1
Cache write, 5-minuteinput x 1.25
Cache write, 1-hourinput x 2.0

A model whose published cache pricing departs from the default carries its own cache_multipliers block, which overrides only the tiers it names. Claude Fable 5.1 and Claude Mythos 5.1 bill cache reads at 0.025x input (USD 0.25 per million against USD 10 input), so their entries carry {"read": "0.025"} and keep the default write multipliers. A malformed override is a rate-table error (exit 2), not a model that quietly prices at the default.

A model may also carry a speeds block for a variant that prices differently, such as fast mode. A variant inherits the model's multipliers and may layer its own on top.

An optional per-model long_context block declares input_tokens_above, input_multiplier (also applied to cache rates), and output_multiplier. The threshold is a positive integer and the multipliers are finite, non-negative decimals. The surcharge applies only above the threshold, to the full increment, and combines with the selected speed variant.

Model-id resolution is deliberately narrow: an exact match, then the same id with a trailing -YYYYMMDD release-date suffix removed. Nothing is matched by prefix or similarity. A near-miss would produce a confident figure at the wrong rate, which is worse than reporting the id as unpriced — so claude-opus and claude-opus-5-turbo both resolve to nothing rather than to claude-opus-5.

An unknown model id never gets a guessed price. Its tokens are counted and shown, its cost cell reads UNPRICED, it is excluded from the cost total, the total is marked incomplete, and the command exits 3.

Model ids listed under non_billable_models — locally generated messages that were never sent to the API — are counted separately and excluded from the cost total.

Server-side tools are priced per call, in their own block. server_tools is keyed by tool name; each entry names its unit (only per_request is supported, stated rather than assumed so a future per-hour tool cannot be read as per-call), its rate as a decimal string, and a display_name. Web search is published as USD 10 per 1,000 searches, stored as "0.01" per call, with the block carrying its own source and source_checked because it is verified against the pricing page independently of the model rows. A malformed entry is a rate-table error (exit 2), for the same reason a malformed model row is: a tool listed with a corrupt rate must never read as merely unpriced, which is the one reading that silently understates a cost. The block is hand-maintained — the models.dev catalog prices tokens per model only and carries no per-call tool rate, so refresh_rates.py neither reads nor writes it.

The two totals have different scopes, and the report says so. The token total counts every record observed; the cost total covers only priced, billable rows. When those differ, the run states how many of the tokens shown the cost actually covers, and --format json carries the same split as totals.priced_tokens and totals.priced_requests. A token count and a cost sitting on one line would otherwise read as the price of those tokens.

Codex model price coverage

The table includes GPT-6.1 Sol and Terra, GPT-5.6 Sol/Terra/Luna (and the documented gpt-5.6 Sol alias), plus the API-key models still named in the Codex model guide: GPT-5.5, GPT-5.4, GPT-5.4 Mini, GPT-5.2 and GPT-5.3 Codex. These rows record their official model-page sources. GPT-6.1 Terra is cross-checked against OpenAI's pricing page; the other rows in this list also carry a pinned OpenAI catalog cross-check. GPT-6.1 Sol and Terra read cache at 0.05× input; GPT-5.5 and earlier charge no cache-write premium, so separately recorded write tokens use 1× input rather than the table default.

GPT-5.5 and GPT-5.4 have Standard long-context prices but no published long-context Fast price. Their Fast rows remain UNPRICED: the current table cannot restrict a speed variant by context length. Other added models have 2× Fast rates. Ultrafast is not resolved by the adapter and remains an unknown tier, even where OpenAI publishes its price. An unknown tier cannot be fixed by adding model data; the report keeps its tokens and withholds its cost.

No published API price was established for codex-auto-review or gpt-6.1-astra on 2026-10-15 UTC. They remain UNPRICED; neither is treated as a free, non-billable local message or an alias of another model.

Updating it

  1. Read the current published rates.
  2. Edit scripts/rates.json: add or amend entries under models, and set rate_table_version to the date of the change.
  3. If a cache multiplier changed for every model, edit the table's cache_multipliers; if it changed for one model, give that entry its own block.
  4. Run the fixtures. The exact-cost claim is pinned to the shipped rates, so a rate change is expected to fail it; re-derive the expected figure by hand and update the derivation comment alongside the number.

New model ids are additive: adding one prices previously-unpriced usage without changing any existing figure.

Refreshing from the models.dev catalog

scripts/refresh_rates.py compares the table with the models.dev catalog — an open, MIT-licensed database of model pricing — read from its repository at one exact commit, and merges new or changed rows on request. It is a maintenance step for when a model launches, not something the report runs: the report never touches the network.

$ python scripts/refresh_rates.py --commit 860de7fed98d7a80ea77c8323b210f63eb6108b9
anomalyco/models.dev@860de7fed98d (anthropic) vs rates.json (rate_table_version 2026-10-15)
  absent upstream, left as-is: claude-mythos-5, claude-mythos-5-1, gpt-5.2, gpt-5.3-codex, gpt-5.4, gpt-5.4-mini, gpt-5.5, gpt-5.6, gpt-5.6-luna, gpt-5.6-sol, gpt-5.6-terra, gpt-6-astra, gpt-6-luna, gpt-6-sol, gpt-6.1-sol
  dated aliases covered by their base id: claude-haiku-4-5-20251001, claude-opus-4-5-20251101, claude-sonnet-4-5-20250929
  every catalog row agrees with the table

Exit 0 means the table agrees with the catalog, 1 that differences were found and not written, 2 that the catalog or the table could not be read or an argument was refused. Add --write to apply additions and price changes; the script then moves rate_table_version to the change date and records the commit it read under catalog_cross_check. The commit is a full 40-character SHA and nothing else: branches, tags, and abbreviated SHAs are refused before anything is read, because the catalog's published JSON carries no version and only an exact commit names one catalog. --write always needs --commit, even against a local --catalog-dir, so the provenance block is never written empty.

Two things the catalog cannot express stay hand-maintained from the published pricing page, and the script says so rather than guessing: models it does not list (limited-availability models are reported as absent upstream and left untouched), and the 1-hour cache-write rate (the catalog carries a single cache-write price, compared against the 5-minute tier and never written). source_checked, the date a person last read the published page, is never moved by the script.

Runtime support

RuntimeStatus
claude-codeSupported — reads local session transcripts.
codexSupported — reads local rollouts and their server meter snapshots; reports per-tier rows at published GPT-6/6.1, GPT-5.6 and API-key Codex model list rates; unknown tiers/models remain unpriced. Exits 4 when no rollout source is available.

An unsupported runtime returns a stated unavailable reason and a distinct exit code. It never returns an estimate. A runtime with no readable records is a gap in what can be measured, and reporting a guess would misrepresent it.

Adding a runtime means adding one adapter in scripts/runtime_adapters.py and one registry entry. An adapter answers two questions — can this runtime be read here, and what usage does it show — and yields a normalized record per request. Deduplication, pricing, and reporting are all runtime-agnostic, so a second adapter needs no changes elsewhere.

Prior art

Open-source tools already read Claude Code's local usage data; ccusage (npm, MIT) is the established one, and it is more featureful for interactive exploration — daily and monthly breakdowns, live monitoring, richer terminal output.

This skill is not a reimplementation of it and takes no dependency on it. It differs in three ways:

  • Skill form. It is agent instructions plus a script, so an agent can run it and interpret the exit status as part of a larger task, rather than a CLI a person drives.
  • Fixture-backed claim. The exact-cost figure is pinned by a synthetic transcript with hand-derived arithmetic, so a rate or parsing change that moves the number fails a test instead of quietly reporting a different total.
  • Explicit unknowns. Unknown model ids and unsupported runtimes are first-class reported states with their own exit codes, rather than rows that silently read zero.

Use whichever fits. For browsing personal usage interactively, the established tool is the better experience.

Reporting results

This skill is read by a person, not piped into another program. The script's output is the evidence; the reply is the answer. Do not paste the raw table and stop — terminal output does not reliably reach the person on the other side, and an unexplained wall of figures is not a report.

Reply to the human in this shape

Lead with the at-a-glance block, then the model breakdown, then only the caveats the run actually produced.

The values below are placeholders chosen to show the shape. Substitute what the run actually printed; never copy these figures through.

**Today: $X.XX** · 10.0M tokens · 100 requests

| Period | Tokens | Cost |
| --- | --- | --- |
| Today (YYYY-MM-DD) | 10.0M | $X.XX |
| Yesterday (YYYY-MM-DD) | 20.0M | $X.XX |
| This week (from YYYY-MM-DD) | 100.0M | $X.XX |

| Model | Requests | Tokens | Cost |
| --- | --- | --- | --- |
| <model> | 60 | 7.0M | $X.XX |
| <model> | 40 | 3.0M | $X.XX |

At list rates, not a bill — a subscription charges a flat fee and prices no
individual request. Rate table <rate_table_version>, days bucketed in <timezone>,
read from <n> local transcript files.

Rules for that reply:

  • Reproduce the figures in your message. Never answer with "see the output above" or a bare file path.
  • Abbreviate tokens to B/M/K in prose and tables. Exact counts belong in the script's own detail table, which is the checkable artifact; a reader comparing periods wants magnitude.
  • Keep cost exact to the cent. Rounding money hides the thing being asked about.
  • Always state the three qualifiers: at list rates rather than billed, the rate-table version, and the zone days were bucketed in. A cost with no rate version and no window is not checkable.
  • Say what was excluded, if anything. On exit 3 name the unpriced model ids and say the total is partial — never present a partial total as complete. Mention web search requests when any were recorded, and the duplicate-record count only when explaining how the total was reached.
  • Do not editorialize about the amount. Report it; whether it is high is the reader's call.

Choosing the run

  • Asked about recent usage, or asked nothing specific — run with no window. The summary block answers today, yesterday, and this week in one pass.
  • Asked about a named span — pass --since / --until. The summary block is suppressed for a windowed run, because the window is that question asked precisely.
  • Feeding another tool, or needing the cache-tier split — use --format json.

Reading the numbers

Observations the report supports, for when the reader asks what drives the figure. These are readings of the report, not advice; whether anything should change is the reader's call.

  • Cache reads are the cheap class. A cache read bills at the model's read multiplier — 0.1x input on most models, 0.025x on Claude Fable 5.1 and Claude Mythos 5.1 — so a large cache-read share means repeated context was re-read at a small fraction of the price of resending it uncached.
  • Cache writes bill above the input rate — 1.25x for the 5-minute lifetime, 2x for the 1-hour. Writes rivaling reads in the JSON cache split suggest the cached prefix keeps changing, so it is being re-written rather than re-used.
  • Output is the expensive class per token. On every model in the shipped rate table, output bills at five times the input rate, so an output-heavy total is priced by what was generated more than by what was read.

The levers behind these numbers — caching strategy, context management, model selection — are covered in Anthropic's cost-optimization cookbook.

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.