agentabacus
Local-first analytics for AI coding agents. Every agent CLI writes session logs to your disk in its own format. Nothing reads all of them. agentabacus normalizes them into one schema and answers: what did this cost, which model actually finishes the work, and where is the spend going?
No server. No account. No network calls. It reads files that are already on your machine and writes one DuckDB file.
uvx agentabacus collect # read new log data into the archive
uvx agentabacus report # cost and tokens, last 30 days
Why this exists
Three things make the naive version of this tool wrong, and all three are handled here.
1. Summing usage per log line overcounts by 2–3×
Claude Code writes one JSONL line per content block — thinking, text, each tool_use — and every one of those lines repeats the full usage of the parent API response. Measured on a real session: 16 assistant lines, 6 actual requests.
| naive per-line sum | deduped by requestId |
overcount | |
|---|---|---|---|
| input | 5,273 | 1,759 | 3.0× |
| output | 18,555 | 7,861 | 2.4× |
| cache read | 712,283 | 264,865 | 2.7× |
| cache write | 61,219 | 25,647 | 2.4× |
The multiplier depends on how many content blocks a response happened to emit, so it can't be corrected after the fact with a constant. agentabacus keys the turns table on request_id and merges with MAX().
2. Cache writes are not one number
A 1-hour TTL cache write bills at 2× base input. A 5-minute write bills at 1.25×. A cache read bills at 0.1×. Claude Code records the split (cache_creation.ephemeral_1h_input_tokens vs ephemeral_5m_input_tokens); collapsing them into a single cache_creation_input_tokens figure misprices exactly the long sessions where cache tokens accumulate.
3. Subagent transcripts live in separate files
~/.claude/projects/<slug>/<uuid>.jsonl # main transcript
~/.claude/projects/<slug>/<uuid>/subagents/agent-*.jsonl # plain subagent
~/.claude/projects/<slug>/<uuid>/subagents/workflows/wf_*/agent-*.jsonl # workflow subagent
Two things bite here. A projects/*/*.jsonl glob — the obvious one — misses every subagent file. And a */subagents/*.jsonl glob still misses the workflow subagents one level deeper, which on a machine that runs workflows are the majority (measured: 80 of 127). Discovery has to recurse.
Subagent files carry the parent's sessionId plus their own agentId, so the thread is what separates them, not the session. agentabacus report --by thread splits main-loop from subagent spend — a number no other tool surfaces.
Install
uvx agentabacus report # zero-install trial
pipx install agentabacus # permanent CLI
Then:
agentabacus doctor # what's discoverable, what's collected, what has no price
agentabacus collect # incremental; safe to run repeatedly
Collect automatically (Claude Code plugin)
Transcripts get garbage-collected, so collection has to happen without you remembering. The plugin registers a SessionEnd hook that archives each session as it closes:
/plugin marketplace add tripleaceme/agentabacus
/plugin install agentabacus@agentabacus
The CLI must be on your PATH (pipx install agentabacus). No daemon, no cron entry.
Commands
agentabacus report --since 30d --by model # or: source project branch day effort speed thread
agentabacus top --limit 10 # most expensive sessions
agentabacus cache # read share and the 1h/5m write split, priced
agentabacus tools # tool-call volume and error rate
agentabacus doctor # health + pricing gaps
agentabacus export --format parquet # hand the tables to dbt / Metabase
agentabacus sql "select ..." # the schema is yours
--by thread splits main-loop spend from subagent spend — the number most tools can't show you at all.
Where the data lives
~/.agentabacus/agentabacus.duckdb # the archive: everything, all time
Override with AGENTABACUS_HOME. The collector is incremental: it records a byte offset per file and re-reads nothing, so a repeat run over a 350 MB corpus costs one stat() per file.
This matters more than it sounds. Claude Code garbage-collects old transcripts. Project directories with a memory/ folder and zero .jsonl files are what that looks like afterwards — that history is gone permanently. Once cleanup runs, this database is the only copy. agentabacus is an archive with a dashboard on top, not a dashboard.
Privacy
Prompt and response bodies never enter the pipeline. The prompts table stores a SHA-256 and a character count; there is no column for the text. That's a schema property, not a filter you have to trust — "does this leak my code?" is answerable by reading schema.py.
Nothing is uploaded anywhere. There is no telemetry.
Pricing
src/agentabacus/data/pricing.csv — effective-dated, one row per model per speed tier:
model_id,speed,valid_from,valid_to,input_per_mtok,output_per_mtok,cache_read_per_mtok,cache_write_5m_per_mtok,cache_write_1h_per_mtok,source_note
claude-opus-5,standard,2020-01-01,,5.00,25.00,0.50,6.25,10.00,anthropic list price
Cost is computed as tokens × price-at-event-timestamp, via the turns_costed view. Joining against a "current price" table would silently reprice last quarter's sessions.
agentabacus doctor lists any model seen in your data that has no pricing row — that's the alarm for "a new model shipped and the table is stale", which is otherwise a silent undercount.
Adding a model is a one-line CSV edit. Dates currently use an early valid_from so historical sessions price at today's rate; real effective dates are welcome as PRs.
Contributing an adapter
One module exposing parse(path, kind, start_offset) -> Batch, a walker in discovery.py, one line in adapters/__init__.py. See adapters/claude_code.py for the reference and adapters/codex.py for the minimal template.
The rule: be a tolerant parser. These formats are undocumented and change without notice. Route on known shapes, count what you skipped, never raise — a vendor's routine release must not become a crash for every user. Strictness belongs in schema.py, not at the edges.
python tests/test_dedupe.py # pins the dedupe contract, the TTL split, and torn-line handling
Status
| Source | State |
|---|---|
| Claude Code | verified against real transcripts |
| Codex CLI | shape-agnostic, unverified — needs someone with real rollout files |
| Gemini CLI, Cursor, Aider, Cline | not yet written |
Roadmap
- Edit-survival metric from
file-history-snapshot.trackedFileBackups(pre-edit backups are already in the transcript, so no git join is needed for Claude Code) - More adapters
agentabacus dash— local static dashboard- Teams: warehouse sinks (Postgres/Snowflake/BigQuery), redaction policy in version control, a GitHub Action for rollups
dbt_agentabacus: staging models over the parquet export, pricing as a seed, tests as drift detection
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agentabacus-0.1.0.tar.gz.
File metadata
- Download URL: agentabacus-0.1.0.tar.gz
- Upload date:
- Size: 29.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3f1387114ae4f89901a141654a3de8e5adbbd6fb2dbfa00f133640e00308c3ba
|
|
| MD5 |
a8a7cbd6c142a99fc1f4df7a619f6b40
|
|
| BLAKE2b-256 |
b5b129c0363edb1af71b66fbf2ebb0d571de9f33381cd6d1db67694dc71c9890
|
Provenance
The following attestation bundles were made for agentabacus-0.1.0.tar.gz:
Publisher:
release.yml on tripleaceme/agentabacus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agentabacus-0.1.0.tar.gz -
Subject digest:
3f1387114ae4f89901a141654a3de8e5adbbd6fb2dbfa00f133640e00308c3ba - Sigstore transparency entry: 2508962904
- Sigstore integration time:
-
Permalink:
tripleaceme/agentabacus@ed3f537a08b22d01fa3967d119cc3e8f8400e681 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/tripleaceme
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ed3f537a08b22d01fa3967d119cc3e8f8400e681 -
Trigger Event:
push
-
Statement type:
File details
Details for the file agentabacus-0.1.0-py3-none-any.whl.
File metadata
- Download URL: agentabacus-0.1.0-py3-none-any.whl
- Upload date:
- Size: 30.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3fee10df8fb35947e882e452206dc9524a6bf6e9ee6486314f29ff157ee5599b
|
|
| MD5 |
f2ab9c439d151d011aaa52e57dc9d11f
|
|
| BLAKE2b-256 |
5572724a45cb804b1e84d1ff4e49596e2a0008f320814bcca7d24cb5484cc613
|
Provenance
The following attestation bundles were made for agentabacus-0.1.0-py3-none-any.whl:
Publisher:
release.yml on tripleaceme/agentabacus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agentabacus-0.1.0-py3-none-any.whl -
Subject digest:
3fee10df8fb35947e882e452206dc9524a6bf6e9ee6486314f29ff157ee5599b - Sigstore transparency entry: 2508962957
- Sigstore integration time:
-
Permalink:
tripleaceme/agentabacus@ed3f537a08b22d01fa3967d119cc3e8f8400e681 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/tripleaceme
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ed3f537a08b22d01fa3967d119cc3e8f8400e681 -
Trigger Event:
push
-
Statement type: