Predict what an agentic coding task will cost and how long it takes — so you can build software estimates and plan timelines in the metered era. One file, no dependencies, Python 3.8+ (or open the no-install browser version).
Estimation used to be a human sizing a ticket. That no longer predicts the bill: a Microsoft/Stanford study found human-rated difficulty only weakly tracks actual token cost, and the same task can vary up to 30x. So TokenCast doesn't ask you to guess. It reads the session logs your agent already writes to disk, learns what tasks like the one you're planning have actually cost and taken, and returns a range (p50 / p90 / p95) for a single task or a whole sprint.
Use it where you scope work. Drop a forecast into a plan or a ticket and you have a cost-and-time number to defend before the work is committed, plus a budget and timeline for the whole sprint before you size it. Setup is pointing it at the logs your agent already writes; there's nothing to instrument, and nothing leaves your machine.
pipx install tokencast # light tier as a CLI (no dependencies)
uvx tokencast forecast --files 8 --tools 30 # or run it ephemerally
pip install tokencast # or into your current environmentThat gives you the stdlib-only light tier (forecast / report / estimate / budget /
demo). The heavy optimizer tier (the tokencast-optimize command, which needs the Claude
Agent SDK) installs via the extra:
pip install 'tokencast[optimize]'No install at all? Just double-click tokencast.html (see below), or run
python tokencast.py … directly from a clone.
Most cost tools are rear-view: they tell you what you already spent. That's the right answer for a finance review and the wrong one when you're scoping next sprint. TokenCast estimates the cost and time of a task before you run it, using your own history as the calibration set. Pair it with a rear-view tool — ccusage or Anthropic's Usage and Cost API — when you want to reconcile a forecast against what actually happened.
Read the forecast as a calibrated bet, not a quote. The same agent on the same task can vary up to 30x (Microsoft Research / Stanford), which is why a single point estimate misleads and the p90 is the number to budget.
Open tokencast.html by double-clicking it. No Python, no terminal, no setup.
Click Load demo data to try it instantly, or Choose your ~/.claude/projects folder
to run it on your real history. Everything is parsed in the browser — nothing is uploaded.
Works on any OS.
# 1. Generate synthetic logs so you can try it with no setup
python tokencast.py demo --out ./sample_logs
# 2. Estimate one task (cost + time) from your history
python tokencast.py forecast ./sample_logs --files 8 --tools 30
# 3. Roll a sprint of 12 similar tasks into a budget + timeline
python tokencast.py forecast ./sample_logs --files 8 --tools 30 --count 12
# 4. Annotate a plan/ticket file with a p90 cost+time per line + a sprint total
python tokencast.py estimate plan.md ./sample_logs --files 8 --tools 30
# 5. (secondary) Attribute past spend, and see what a cap would clip
python tokencast.py report ./sample_logs --cap 5The light tier is pure stdlib, so you can just run python tokencast.py …. To get a
tokencast command on your PATH (no dependencies installed):
pipx install tokencast # or, ephemerally:
uvx tokencast forecast --files 8 --tools 30Both expose the light tier (forecast/report/estimate/demo/budget). The heavy
optimizer tier ships as the separate tokencast-optimize command (pip install 'tokencast[optimize]').
Point it at your real Claude Code logs by passing ~/.claude/projects (the default):
python tokencast.py report
python tokencast.py forecast --files 12 --tools 40- forecast (the primary command) — finds the k most similar past tasks by feature vector
(files touched, tool calls, output tokens, turns, and cache-read tokens — the real cost
driver) and returns a cost and time estimate as a range. It's a distance-weighted
k-nearest-neighbors: closer past tasks count more, skewed features are compared on a log
scale, and the output reports a one-word "neighbor fit" (tight / moderate / loose) so you
can tell a calibrated estimate from a thin-history guess. Pass
--count Nto roll up a sprint/project total via Monte Carlo. This is the number you put in your estimate — calibrated on your history, not a human guess. - estimate — the "cost line in the plan." Takes a plan/ticket markdown file and annotates
each ticket (any
-/*/+bullet, GitHub- [ ]checkbox, or1./2)ordered-list item; headings, blanks, and prose are ignored) with its p90 cost + p90 time, then prints a sprint total (sum of per-ticket p90s plus a Monte-Carlo p50/p90). Uses the same kNN forecaster and the same accurate-vs-floor labeling asforecast. A ticket can hint its own size inline, e.g.- Add OAuth (files=8 tools=30 output=4000)— those override the global--files/--tools/--outputfor that ticket; otherwise each falls back to your history mean. Tickets with no hint all land on that same mean, so they get the same number — hint the ones whose size actually differs (the command prints a note when un-sized tickets share one estimate). - report (secondary) — total spend, per-task distribution (p50/p90/p95/max), breakdown by
project and model, your most expensive tasks, and an optional
--capoverlay showing how many tasks a hard ceiling would have cut off mid-work. Tools like ccusage already do this well. - demo — writes synthetic JSONL matching Claude Code's schema so you can try everything without real data.
Time estimates use wall-clock session duration (first-to-last event), so they include human think/idle time — treat them as a rough timeline proxy, not billed compute.
A single Claude Code session often holds several distinct tasks — you start something, walk
away, then come back hours later on something new. By default TokenCast treats one log file as one
task (one session = one task), which can lump those together. Pass --segment to split each session
into task-sized units wherever there's a long idle gap, so "a task" matches the unit a planner
actually estimates (a ticket / a feature):
# Split sessions wherever there's a >30-min idle gap (the default threshold)
python tokencast.py forecast ./sample_logs --files 8 --tools 30 --segment
# Tune the gap, or also cut at fresh user turns between tasks
python tokencast.py forecast ./sample_logs --segment --gap-min 20 --split-on-user
python tokencast.py report ./sample_logs --segment--segmentis opt-in and backward-compatible: with no flag, behavior is exactly as before.--gap-min Nsets the idle-gap threshold in minutes (default30;0disables the gap rule).--split-on-useradditionally cuts at a fresh user turn once the current segment already has work in it (off by default — pure user-turn splitting over-segments).- Costs and token/call counts of the segments sum to the whole-session totals (a file touched in
two segments counts in each, so
files_touchedmay not); segmentation changes the unit of estimation, not the underlying token accuracy (the floor caveat below still applies).
The browser version mirrors this: tick Segment long sessions into tasks in the forecast panel and set the idle-gap minutes.
Claude Code's JSONL logs undercount input tokens — input_tokens is a streaming placeholder
that's 0 or 1 in ~75% of entries, while the real volume lives in the cache fields (which are
reliable). So TokenCast's absolute numbers are a floor, and the tool flags this in its output.
Once you run tasks through the optimizer tier (tokencast-optimize run/auto), those runs are
measured accurately and written to ./runs with an accuracy marker. forecast then prefers
them: with ≥5 accurate runs that carry a usable (>0) cost signal it calibrates on real token
counts and drops the floor caveat (point
it at a different dir with --runs DIR; the default ./runs is relative to your current
directory). Until then it's still a floor — and says so.
Until then, use the ranges for what they're good at: relative planning. Which tasks are expensive, where a budget cap starts cutting off work mid-task, how one sprint compares to the next — all of that holds up even when the absolute floor runs low.
Anthropic publishes no machine-readable price feed, so TokenCast ships with built-in defaults (Opus $5/$25, Sonnet $3/$15, Haiku $1/$5 per 1M tokens; cache write 1.25×, read 0.10×) and can pull live rates from the community LiteLLM cost map, which is day-0 updated.
- Browser: prices auto-refresh from the feed on load (silent fallback to defaults if offline); there's also a manual ↻ Refresh from live pricing button. Any field is editable.
- CLI: add
--refresh-pricesto fetch and cache the latest (~/.tokencast_prices.json); it falls back to the cache, then to built-in defaults, if the network is unavailable.
python tokencast.py report --refresh-pricesVerify high-stakes numbers against Anthropic's pricing page. The feed is community-maintained, not official, so double-check anything you're committing a budget to.
The forecast/report layer is agent-agnostic — it consumes a list of per-session summaries and
does the percentile/kNN math on them. Only the reader (the parser) differs per tool. Readers live
in a small registry (READERS in tokencast.py); a reader maps one log file to zero-or-more of the
same summary dicts the Claude Code parser produces. Add a tool by registering a reader; nothing else
changes.
Pick a reader with --format on forecast/report (default auto, which detects Claude Code and
falls back gracefully — a directory can even mix tools):
python tokencast.py forecast ./logs --format auto # detect per file (default)
python tokencast.py forecast ./logs --format claude-code # today's Claude Code JSONL
python tokencast.py forecast ./logs --format generic # the portable schema belowThe generic schema is the documented minimal shape any agent that logs per-message token
usage can emit — one JSON object per line (JSONL), one per assistant message:
{"timestamp": "2026-01-01T00:00:00Z",
"model": "claude-sonnet-4-6",
"usage": {"input_tokens": 1234, "output_tokens": 567,
"cache_creation_input_tokens": 0, "cache_read_input_tokens": 0},
"tools": ["Edit", "Bash"],
"files": ["src/a.py"]}Only model + usage.{input_tokens,output_tokens} are required; cache fields default to 0,
tools/files are optional feature signals. Cost uses the same model-family pricing as Claude Code
(a claude-* id prices correctly; an unknown id falls back to Sonnet, flagged). The input-token
undercount honesty carries over: placeholder input_tokens<=1 with real output still counts as a
suspect entry, so a generic-log forecast is a floor unless the run was harness-measured (set
"tokencast_accurate": true on a line to mark it accurate).
Native readers for Cursor, Copilot/gh, Codex, and Aider are future work that slots into the
same registry — write the tool's read + sniff and register them; or pre-process its logs into
the generic JSONL above. The browser (tokencast.html) stays Claude-Code-focused for now; multi-
format in the browser is future work. Contributions for new readers are welcome.
See docs/superpowers/specs/2026-06-06-multi-agent-readers-design.md.
In the metered era teams set hard spend caps (e.g. a per-engineer quarterly budget). TokenCast
can track spend against a cap and tell you your runway — entirely optional, stdlib-only,
offline. Create a tokencast_budget.json:
{
"period": "quarterly",
"period_start": "2026-04-01",
"budgets": [
{ "scope": "global", "amount": 15000 },
{ "scope": "project:tokencast", "amount": 2000 }
]
}python tokencast.py budget # consumed / remaining / burn rate / runway
python tokencast.py budget --scope project:tokencast --per-task 12.50
python tokencast.py budget --forecast 800 # would an $800 sprint fit what's left?It counts two spend streams, labeled separately: your real Claude Code usage
(~/.claude/projects, a floor due to the input-token undercount) and accurate TokenCast
runs (./runs). With no tokencast_budget.json, nothing changes and budget just prints a hint.
The forecast/report/demo commands above are the stdlib-only, offline core. A separate
optimizer tier wraps a locally-running Claude via the Claude Agent SDK to measure runs
accurately — the SDK returns real token counts, so this path is not subject to the JSONL
undercount floor that the core warns about.
The recommended entry is the /tokencast-optimize skill — describe your task, project, and
budget, and it drives everything below: it does the judgment (drafting the eval set, choosing what
to optimize, reading the numbers) and shells out to these CLIs for the deterministic math. Under
the hood it's a handful of composable steps, each usable on its own and each gating real spend
behind a confirmation:
| Step | Command | What it does |
|---|---|---|
| Forecast | python tokencast.py forecast |
pre-flight cost/time from your history |
| Draft an eval set | tokencast-optimize eval init |
exit criteria → scorable dimensions |
| Score a config | tokencast-optimize eval run |
composite quality + accurate cost/time |
| Optimize (+ decompose) | tokencast-optimize auto … --decompose |
rank configs across every axis, promote the winner, compare task splits |
| Track budget | python tokencast.py budget |
spend vs cap → runway |
The axes auto optimizes: model, instructions, tools, skills, MCP servers, and
task decomposition — cost-first under a quality floor, budget-aware, all optional.
Install the extra and run one task under one config:
pip install -e ".[optimize]" # pulls claude-agent-sdk + pyyaml
tokencast-optimize run examples/task.md --config configs/baseline/ --budget 2.00 --out runs/
python tokencast.py report runs/ # the core reads the accurate logs backA config dir mirrors Agent Optimizer's layout: metadata.yaml (model, budget, max_turns),
instructions.md (system-prompt append), tools.json (allowed/disallowed tools, MCP servers),
and an optional skills/ dir. Before any real spend the CLI prints a pre-flight cost estimate
and asks you to confirm.
Security note. An eval set's
commandrule runs itscmdthrough the shell with your user privileges (that's how it runs your real test suite).eval initcan draft an eval set with an LLM — treat that draft as untrusted and read it before running. File paths (task ids, check paths) are confined to the sandbox; the shell command itself is not.
run is the foundation; the full closed-loop optimizer is built on top of it — eval harness,
cost-first ranking, failure-driven candidate generation, skills/MCP axes, and task decomposition,
tied together by the auto command and the /tokencast-optimize skill (below). See
docs/superpowers/specs/ for the design of each piece.
Score a config against an eval set (one or many tasks). Each task runs in an isolated sandbox (fresh temp dir, optionally seeded from a dir or a git worktree), then is scored by deterministic rule checks (tests pass, file exists) plus an LLM judge for qualitative dimensions:
# draft an eval set from your repo (LLM; review before running)
tokencast-optimize eval init --root . --out evals/generated/
# run an eval set under a config, scored
tokencast-optimize eval run evals/generated/evalset.yaml --config configs/baseline/ --out runs/eval run prints a composite quality score, pass rate, and total cost/time — the numbers the
optimize loop (next sub-project) ranks candidate configs on. Generated eval sets are drafts:
their rule checks are LLM-authored shell commands, so review them before running.
Rank candidate configs against an eval set and promote the cheapest that holds quality:
# try opus/sonnet/haiku variants of the baseline, ranked cost-first under its quality floor
tokencast-optimize optimize evals/generated/evalset.yaml --config configs/baseline/ --model-sweep
# frame the win against a budget: how much more work fits the same cap?
tokencast-optimize optimize evalset.yaml --config configs/baseline/ --model-sweep \
--budget-remaining 15000 --need-tasks 1000It prints a ranked table (* winner, + Pareto frontier), the winner-vs-baseline cost delta, and —
when a budget is supplied (--budget-remaining, or --budget-config/--budget-scope to read your
tokencast_budget.json ledger) — the runway gained ("winner affords ~1,428 tasks vs 830, same
budget") plus a --need-tasks N fit verdict. The winner config is written to runs/promoted/
(and to --promote DEST if given); your live CLAUDE.md is never touched.
Add --generate N to have an LLM read the baseline's eval failures and propose N candidate
configs (rewritten instructions / adjusted tool lists) — they're evaluated and ranked alongside
the rest:
tokencast-optimize optimize evalset.yaml --config configs/baseline/ --generate 3The generator only ever changes instructions and tool allow/deny lists (the skills & MCP axes come later); its proposals run through the same sandbox + ranking as any candidate.
A config can also declare which skills it uses (a skills: name list in metadata.yaml,
selecting from your ~/.claude/skills + plugins) and bring its own skills via a skills/
subdir (staged into each run's sandbox). MCP servers are set via tools.json's mcp_servers.
These become optimization axes the generator can tune in a later step.
With --generate N, the generator now tunes those axes too: it's shown each failing dimension's
definition (rubric / rule) and the skills + MCP servers you have available, and may propose adding
some to a candidate. Point it at a specific catalog with --skills-dir DIR (default
~/.claude/skills) and --mcp-catalog FILE (default ~/.claude.json); proposed names are
validated against those catalogs and added on top of the baseline's.
Sometimes the cheapest win isn't a different config — it's splitting the task into smaller
sub-tasks (and routing the easy ones to a cheaper model). decompose compares the whole-task run
against LLM-proposed decompositions and reports which is actually cheaper at an acceptable quality:
tokencast-optimize decompose evalset.yaml --config configs/baseline/ --generate 3Each decomposition's sub-tasks run in sequence in one sandbox (later steps see earlier file
changes) and are scored on the same dimensions; per-task it reports the monolithic vs the winning
decomposition's cost/time. Use --by time to optimize wall-clock and --min-quality to set the
floor (default: the monolithic run's quality).
auto chains the common path — forecast, optimize (model + instructions + tools + skills + MCP),
promote the winner — behind a single confirmation, and optionally compares task decompositions too:
tokencast-optimize auto evalset.yaml --config configs/baseline/ --generate 3 --decompose--generate N is the optimize axis (candidate configs); --decompose proposes its own
decompositions on the winner, --decompose-generate N per task (default 2), so --decompose
always tries real splits even without --generate. It writes a consolidated auto.json. For a
conversational driver that drafts the eval set, forecasts, and interprets the results for you, use
the /tokencast-optimize skill — it calls these
CLIs for the deterministic work (forecast, scoring, ranking) and handles the judgment (what to
optimize, how to read the numbers) itself.