Documents

AI Usage Ledger Skill

Try it

Compile, price and attribute local AI agent usage

What it does

| Use | Scope | |---|---| | Usage ledger | Locate, de-duplicate, price and attribute every locally recorded AI coding-agent call on a person's machines. | | Starting point | Any request to count tokens, estimate API-equivalent cost, compare subscriptions, or break usage down by account, model, proj…

The skill document

AI Usage Ledger Skill

Purpose

UseScope
Usage ledgerLocate, de-duplicate, price and attribute every locally recorded AI coding-agent call on a person's machines.
Starting pointAny request to count tokens, estimate API-equivalent cost, compare subscriptions, or break usage down by account, model, project or time.
Operating boundaryRead-only over the tools' directories; identity claims are decoded, never credentials; nothing is transmitted. The ledger store is append-only: nothing already stored is edited or removed.

System Boundary

BoundaryUse
This skillDiscovery, scanning, de-duplication, pricing, account attribution, validation, dashboard and study rendering.
Provider consoles and exportsInvoices and billed amounts. This skill produces API-equivalents at list price, not invoices.
agentic-invoice-skillBilling a client for agentic work; feed it verified figures from the ledger.
agentic-security-review-skillQuestions about what the transcripts contain or who may read them.
Vendor usage dashboards (Codex app, Cursor, Windsurf, Copilot)Their own counters; several include replayed history or omit tokens, see references/harness-catalog.md.

Core Workflow

StepAction
1First use only: onboard. On a terminal run python3 scripts/ledger.py init and answer the prompts. From an agent harness, ask the operator the same questions in chat (brand name, eyebrow, tagline, contact, logo, accent colour, theme, storage backend, working directory, timezone, whether to include other profiles / drives / WSL, whether to build an anonymised copy on every run, whether to archive the raw log files themselves (and compress them), and whether to refresh on a schedule: none / daily / weekly / monthly, time, weekday, install now), then run python3 scripts/ledger.py init --yes --set key=value .... Preferences persist in ~/.ai-usage-ledger/config.json and in the store; later runs never ask again. Installing the schedule changes the operator's Task Scheduler or crontab, so confirm it explicitly before passing schedule.install=y.
2Onboarding auto-detects hosts and roots for this OS (scripts/detect_hosts.py: Windows, macOS, Linux, every WSL distro, other user profiles and drives, VS Code-family global storage, CODEX_HOME-style overrides) and drafts accounts.json from credential claims. Review the draft: labels, prices, and the why on each rule. Add SSH hosts when prompted or in config.json extra_hosts. Record what could not be read and why.
3Run python3 scripts/ledger.py run. It rescans, archives the raw files when archive.raw_logs is on, appends only new calls to the store (stable ids; duplicates are skipped), rebuilds the tables, dashboard and study package, and records the run. Repeat as often as wanted.
4Validate per the checks below; fix a rule and run python3 scripts/ledger.py run --no-scan to rebuild from the store without rescanning.
5Deliver the dashboard, the study package and all_events.csv (or ledger.py export), with exclusions and low-confidence rules stated. For a statement, memo or brief, pick a template from references/ledger-document-catalog.md and render it with python3 scripts/ledger.py doc --template --var prepared_for=... [--pdf --docx]. For anything leaving the organisation, use the anonymised copy (ledger.py run --anonymize, then doc --anonymize) and check the outputs for names before publishing.
6To start over (new brand, different backend, changed hosts) run python3 scripts/ledger.py reinit; the previous store is kept beside the new one with a timestamp. The manual route (run_pipeline.py with a hand-written manifest) still works for one-off or unattended use.
7When the operator asks a specific question ("how many tokens on Tuesday", "which project cost the most in August", "when did I first use gpt-6", "what did I ask about rate limits"), answer it from the store with python3 scripts/ledger.py query [filters] per the Answering Detailed Questions table, not from the summary tables; go into the archived raw logs (query logs) when the answer is in the transcript rather than the counts.
Required FactExamples
Hosts and rootsEvery ~/.claude, ~/.codex, IDE globalStorage, gateway sessions/ directory, with size and date range.
Accountschatgpt_account_id prefix, plan type and e-mail from each auth.json; oauthAccount from each .claude.json.
PricesList rates per model on the snapshot date, with assumed rows named.
ExclusionsLocked directories, closed SSH ports, tools without token fields.

Validation Checks

CheckRequirement
Replay proofOne spawned Codex thread's burst equals the parent's first N events value for value and was skipped; a small codex_replay_skipped on a host with sub-agents means the rule is not firing.
Counter cross-checkNon-forked Codex threads within 2% of state_5.sqlite; spawned children explained; Claude stats-cache.json ≥ transcript figure.
Plan fieldCodex calls mostly pro/plus, some self_serve_business_usage_based where a prepaid org exists, ? only for old clients.
Generic rowsAnything with granularity: generic verified against two source files as per-call, not cumulative.
MagnitudeMedian Codex context per call 100 to 200K tokens; hundreds of billions per month on one subscription is a replay bug.
Fixturespython3 tests/make_fixtures.py prints ALL OK and python3 tests/test_store.py prints STORE OK before the first real run.
Append-onlyA second ledger.py run straight after the first reports added {"events": 0, ...}; a host that adds events on every run without new activity is being counted twice (check detect_hosts.py notes for same-volume profiles).
Anonymised outputsBefore publishing, grep the anonymised compiled/ tree and any rendered document for host names, user names, e-mails and directory names from the private map (~/.ai-usage-ledger/anonymize-map.json); the test suite does this on fixtures, the operator does it on real data.
DocumentsEvery ledger placeholder in a rendered document is filled; a leftover {name} is either a --var the template needs (recipient, contract, correction values) or a bug.

Quality Rules

RuleRequirement
Read-onlyNever modify, delete or "clean up" agent directories; do not wsl --shutdown while agents run.
SecretsDecode id_token claims for identity; never print or copy tokens or API keys.
Evidence classesKeep class A (per-call records), class B (tool counters) and class C (priced estimates) apart in every table.
ConfidenceEvery finding and every attribution rule carries a confidence level and its evidence.
UnknownsPrice unpublished models at a stated sibling marked assumed; report the share of cost that rests on them.
CorrectionsWhen a rerun changes a previously reported figure, state the cause alongside the new number.

Resource Guide

ResourceRole
references/harness-catalog.mdForty-plus agents, IDE assistants, gateways and runtimes: paths per OS, record shapes, token fields, scanner support.
references/log-formats.mdExact fields per built-in parser, de-duplication and replay rules, normalised schema.
references/discovery.mdWhere logs and credentials live, sweep commands, reading accounts safely, defensible attribution rules.
references/pitfalls.mdSixteen ways the numbers go wrong.
references/harnesses.mdInstall locations and notes for Claude Code, Codex, Cursor, SSH, WSL and macOS.
references/ledger-document-catalog.mdFifteen ledger documents (executive summary, monthly statement, quarterly review, account statement, subscription memo and renewal recommendation, cache brief, budget forecast, project allocation, billing evidence, model mix, tool adoption, host inventory, data-quality note, correction notice) with their placeholders.
references/template-index.jsonMachine-readable index of the catalog used by the renderer.
templates/Manifest, pricing sheet, account and report-config examples.
examples/A workstation manifest and the CompleteTech brand preset.
scripts/ledger.pyDaily entry point: init (onboarding), run (scan, append, rebuild), status, export, reinit.
scripts/detect_hosts.pyOS-aware discovery of harness roots, profiles, drives, WSL distros and credential claims; prints or emits manifest hosts and an accounts draft.
scripts/ledger_store.pyAppend-only store with SQLite, JSON (JSONL) and CSV backends, stable event ids, prefs and run history, exports.
scripts/run_pipeline.pyOne command: scan every host, merge, analyse, render, zip (used by ledger.py, also usable alone).
scripts/render_ledger_doc.py--list the catalog or --template to fill it from the ledger: Markdown and themed HTML always, --pdf, --docx, --png when the optional libraries are installed; --anonymize reads the anonymised copy.
scripts/render_pdf.pyBranded Markdown to PDF / DOCX (letterhead band, logo, accent tables, footer).
scripts/anonymize.pySalted pseudonyms for hosts, sessions, projects and accounts; paths, prompts and identities removed; private map kept beside the store.
scripts/schedule.pyTask Scheduler (Windows) or crontab (Linux, WSL, macOS) entry that runs ledger.py run daily, weekly or monthly through a wrapper that logs to ~/.ai-usage-ledger/logs/run.log.
scripts/ledger_archive.pyRaw-log archive: copies every source file the scanners read (any tool, any host kind) into ~/.ai-usage-ledger/archive//..., gzip by default, with an index; status, list, grep, restore.
scripts/ledger_query.pyQuestion layer: thirty presets (totals, by day / week / month / tool / host / model / project / account / plan / kind / entrypoint / hour / weekday / effort / version, sessions, one session, raw calls, biggest calls, cache, first-last, prompts, prompt counts), free SQL, and regex search inside the archived logs; filters by date range, tool, host, account, model, project, session, kind, plan, billing; table / csv / json / jsonl output.
scripts/compile_ai_logs.pyscan (14 parsers plus a generic sniffer) and report (merge, price, attribute).
scripts/analyze_events.pyDistributions, time of day, concentration, validation, sensitivity.
scripts/build_dashboard.py, scripts/build_report.pyBranded dashboard and study package.
tests/make_fixtures.pySynthetic logs and totals check for every parser.
tests/make_onboarding_example.py, assets/examples/example-onboarding.mdThe onboarding, question by question, on a synthetic machine; show it to an operator before asking the questions in chat.
tests/test_store.pyEvery backend ingests once and skips the repeat; non-interactive onboarding; two ledger.py runs end with zero new rows and archive every source file exactly once; the anonymised copy holds no fixture host, path or e-mail; schedule dry-runs; presets, SQL, prompt search and archived-log search return the expected rows; all fifteen documents render with no ledger placeholder left.

Runtime Permissions

CapabilityBoundary
Files readAgent transcripts, tool databases and credential files under the user's own profiles and hosts named in the manifest; bundled templates, references and assets/logo.png.
Files written~/.ai-usage-ledger/ (or $AI_USAGE_LEDGER_HOME): config.json, the store, generated manifest.json, accounts.json, pricing.json, report_config.json, anonymize-map.json, `run-ledger.cmd
Local commandspython3 for the scripts; wsl.exe -l -q to list distros during detection; wsl.exe, ssh and scp only for hosts in the manifest; schtasks or crontab only when the operator asks for a scheduled refresh (`schedule install
Not requiredOutbound network access (pricing pages are fetched by the operator, not the scripts), credential use, persistence, privilege escalation, destructive file operations, background services.

Renderer

TaskCommand
Onboard (interactive)python3 scripts/ledger.py init
Onboard (agent or CI, no prompts)python3 scripts/ledger.py init --yes --set brand.name=Acme --set store.kind=sqlite --set detect.all_profiles=y
Refresh everythingpython3 scripts/ledger.py run
Rebuild without rescanningpython3 scripts/ledger.py run --no-scan
Scan a subset of hostspython3 scripts/ledger.py run --hosts lighthouse,wsl-ubuntu
What is configured and storedpython3 scripts/ledger.py status
Export the storepython3 scripts/ledger.py export --table events --format csv --out events.csv (json, jsonl; tables events, sessions, prompts)
Start overpython3 scripts/ledger.py reinit
Anonymised copy for publicationpython3 scripts/ledger.py run --anonymize (or --set anonymize.on_every_run=y at onboarding); outputs under /anonymized/; ledger.py export --anonymize for tables
List ledger documentspython3 scripts/ledger.py doc --list [--stage finance] [--type memo]
Render a documentpython3 scripts/ledger.py doc --template monthly-usage-statement --var prepared_for="Finance" --var month=2026-08 --pdf --docx
Render from the anonymised copypython3 scripts/ledger.py doc --template executive-summary --anonymize --pdf
Scheduled refreshpython3 scripts/ledger.py schedule install --frequency daily --time 03:00 · schedule status · schedule remove (--dry-run prints the command only)
Raw-log archivepython3 scripts/ledger.py archive status · archive list --host wsl-ubuntu --grep-path 2026/08 · archive grep "ECONNRESET" --since 2026-08-01 · archive restore --host lighthouse --to /tmp/restored --match sessions/2026/08 · one-off: ledger.py run --archive
Detailed questionspython3 scripts/ledger.py query --presets · query totals --since 2026-08-01 · query by-project --tool codex --since 2026-08-01 --until 2026-08-31 --limit 10 · query sessions --project libreevolve · query session --session · query prompts --grep "rate limit" · query sql "SELECT ..." · query logs "pattern" --since 2026-08-01
Just look at what is on this machinepython3 scripts/detect_hosts.py --all-profiles (--json for the manifest shape)
Manual pipeline (no store)python3 scripts/run_pipeline.py --manifest manifest.json [--only report,analyze,build] [--hosts a,b]
Parser and store self-testspython3 tests/make_fixtures.py · python3 tests/test_store.py
Quality gatepython3 scripts/validate_quality.py
Preference key (--set)Values
brand.name, brand.eyebrow, brand.tagline, brand.contact, brand.logo, brand.accent, brand.footerFree text; logo is a PNG/SVG path or URL; accent is hex.
themelight, dark, system (initial theme of both pages; the viewer's toggle still wins).
store.kindsqlite (one file, indexed), json (JSONL files plus id index), csv (CSV files plus id index).
workdir, timezonePaths and IANA zone.
detect.all_profiles, detect.wsly / n.
anonymize.on_every_runy / n: also build /anonymized/ on every run.
archive.raw_logs, archive.compress, archive.pathy / n keep the raw log files; y / n gzip them (n keeps them re-scannable in place); directory (default ~/.ai-usage-ledger/archive).
schedule.frequency, schedule.time, schedule.weekday, schedule.installnone / daily / weekly / monthly; HH:MM; mon..sun; y installs the task or cron entry during onboarding (ask first).
OutputPath
Ledger store (source of truth after the first run)~/.ai-usage-ledger/ledger.sqlite, or ledger-json/, ledger-csv/ (tables events, sessions, prompts, prefs, runs)
Event log (primary artefact)compiled/all_events.csv
Aggregatescompiled/by_*.csv, subscription_vs_api*.csv, SUMMARY.md, summary.json, analysis.json
Dashboardcompiled/agent-ledger.html
Study packagereports/_/ (USAGE_REPORT.md, report.html, ledger.json, pricing.json, accounts.json, README.md, SHA256SUMS) and .zip

Branding and Themes

ElementSource
Brand presetexamples/report_config.completetech.json: CompleteTech LLC logo, eyebrow, tagline, contact, footer and palette (#1E3A8A accent, #0F172A ink, #EEF2FF soft accent, #64748B muted, #E2E8F0 border, #F8FAFC zebra).
Logoassets/logo.png, inlined as a data URI so both pages stay self-contained.
ThemesLight, dark and system in both pages via prefers-color-scheme, a fixed Light / System / Dark toggle persisted in the browser, and an explicit data-theme stamp from a host.
Overridesbranding.light / branding.dark may set any token (bg, surface, surface-2, ink, ink-2, ink-3, line, line-2, tool colours, seq1..seq7); fonts via google_fonts_url; extra_css appended verbatim. Keep tool colours CVD-safe.

Store and De-duplication

ElementRule
Event idTool request id when the tool records one (Claude message id); otherwise SHA-1 of tool, host, session, source file name, timestamp, model, the four token counts and an ordinal among identical keys in that file. Re-scanning an append-only log yields the same ids.
IngestINSERT OR IGNORE (SQLite) or an id index (JSON, CSV). Rows are never updated or deleted; a changed rule is applied at report time, not in the store.
Sessions and promptsSame scheme keyed on tool, host, session and file (sessions) or timestamp, session and text (prompts).
Prefs and runsEvery preference is mirrored into the store; each run records what it added, skipped and produced.
ReinitMoves the store aside as .before-reinit- and onboards again; nothing is deleted.

Anonymisation

ElementRule
ReplacedHost names to host-<6 hex>, session and request ids to 12-hex digests, working directories to project-<8 hex>, account keys to :acct-<6 hex>, any path, URL or e-mail in a free-text field to path-/user- tokens.
RemovedSource file paths, prompts, account labels, e-mails, organisations, evidence strings, host descriptions and the primary-host note in the report config.
KeptTool, model, plan type, timestamps, token counts, costs, kind, entrypoint kind, version, effort: the study itself.
StabilityPseudonyms are SHA-256 of a private salt generated at onboarding, so reruns agree and outsiders cannot reverse them; the real-to-pseudonym map stays in ~/.ai-usage-ledger/anonymize-map.json.
BrandingThe publisher's own branding stays on anonymised outputs; change it at onboarding if the publisher differs from the operator.

Raw-Log Archive

ElementRule
What is keptEvery file a scanner read on the last run (the src of each event, the file of each session), plus the counters beside the roots (stats-cache.json, state_5.sqlite, history.jsonl, session-store.db). Credential files are never archived.
Harness-agnosticThe archive follows the scanners' source paths, so a new parser or a generic-root tool is archived without any archive-side change; nothing in the archive knows tool formats.
HostsLocal and share hosts by copy; WSL through the \\wsl$ share; SSH hosts through one ssh tar czf - -T - stream after a size/mtime check, so unchanged files are not transferred.
Append-onlyA file is copied when new or grown; the tools only append, so the latest copy is a superset. The index keeps a version count and nothing is deleted.
Reading backarchive grep streams through gzip and reports file, line and the timestamp found on the line; archive restore decompresses a host (or a path match) into a directory that the scanners can read again.
SizeExpect the size of the tools' log trees (tens of GB for heavy Codex use) at roughly one tenth when compressed. Say so before enabling it on a small disk.

Answering Detailed Questions

Question shapeCommand
How much on a day / week / month, optionally one tool or hostquery by-day --since D --until D [--tool t] [--host h], by-week, by-month, totals
Which projects, models, accounts, plans, entry points, effort levels, client versionsquery by-project, by-model, by-model-month, by-account, by-account-month, by-plan, by-entrypoint, by-effort, by-version
Working patternquery by-hour, by-weekday, by-kind (main vs sub-agents)
Biggest or specific sessions and callsquery sessions [--project p], session --session , biggest-calls, calls --since D --until D --limit N
Cache behaviour, first and last usequery cache, first-last
What was askedquery prompts --grep "text" [--since D], prompt-count
Anything else about the counts`query sql "SELECT ... FROM events
What the transcript actually said, errors, tool outputquery logs "regex" [--since D] [--host h] [--project path-fragment] over the archived raw files (needs archive.raw_logs)
Output for a document or a spreadsheetadd `--format csv

Accounts in query come from the same accounts.json rules the report uses (acct() / bill() SQL functions), so per-account answers agree with the dashboard. JSON and CSV stores are loaded into an in-memory SQLite database, so every preset works on every backend.

Ledger Documents

ElementRule
Source of figurescompiled/summary.json and analysis.json of the configured ledger (or --compiled ); nothing is typed in by hand.
Recipient facts--var prepared_for=, reference=, notes=, contract_id=, billing_period=, correction values.
Scopes--var month=YYYY-MM, --var account=, --var project=; defaults are the latest month, the largest account, the largest project.
FormatsMarkdown and themed HTML always; PDF (reportlab) and DOCX (python-docx) with --pdf / --docx; PNG preview with --png.
WordingEvery document states that figures are API-equivalents at list price and not invoices; the billing evidence template feeds agentic-invoice-skill, it does not replace it.

Scheduling

ElementRule
Windowsschtasks task "AI Usage Ledger" running ~/.ai-usage-ledger/run-ledger.cmd daily, weekly (--weekday) or monthly (day 1) at --time.
Linux, WSL, macOSOne crontab line tagged # ai-usage-ledger running run-ledger.sh; macOS may need Full Disk Access for cron.
LoggingEach run appends to ~/.ai-usage-ledger/logs/run.log; ledger.py status shows the entry and the last log lines.
ConsentOnboarding asks whether to install; an agent passes --set schedule.install=y only after the operator has agreed in chat. --dry-run prints the exact command.

Definitions

input_uncached fresh prompt tokens · cache_read served from cache · cache_write written to cache (Anthropic, split 5m/1h) · output incl. reasoning · total = sum. API-equivalent = tokens × list price. Cache saving = same call priced with all prompt tokens uncached, minus actual. Subscription months are counted only when that account made a call. Usage-based calls are real spend and stay out of the subscription comparison.

Network Boundary

BoundaryRequirement
Local-only runtimeThe scripts make no outbound network calls; SSH and WSL are used only for hosts the operator names in the manifest.
PublishingDashboards and study packages are published only when the operator asks (Artifacts, files, or a repository).
DataScanned transcripts and the compiled ledger never leave the operator's machines through this skill.

Related skills

Join a video meeting as an AI bot with voice, avatar, and screenshare across four operating modes.

by johnpatternai22 installs8 stars

Stores durable facts in a categorized, plain-markdown vault on disk, alongside your agent's built-in memory.

by Iván559 installs18 stars

Fetch raw ad creative, app, ranking, and revenue data from AdMapix as structured JSON.

by fly0pants4.3k installs296 stars

Generate and edit Draw.io, Mermaid, and Excalidraw diagrams from natural language using a structured JSON spec.

by nssa.io1.0k installs47 stars

Find why your productivity system keeps failing, then apply the smallest fix — capacity math, bottleneck routing, durable local notes.

by Iván855 installs69 stars

More from completetech

Browse all skills

Create polished, review-ready sales and lifecycle email sequences for agentic development services, then export them as branded PDF artifacts. Use for outrea...

by completetech15 installs1 stars

Generate branded invoice and billing-document PDFs for agentic development services, including deposits, milestones, retainers, change orders, pass-through e...

by completetech16 installs

Generate branded CompleteTech Agentic Development Services Agreement packages from approved provider, client, project, commercial, and governance terms. Use...

by completetech15 installs

Create CompleteTech LLC delivery execution artifacts for approved agentic development engagements, including kickoff agendas, access checklists, project plan...

by completetech15 installs

Create CompleteTech LLC case studies, testimonials, and proof assets for completed agentic development engagements, including intake questionnaires, outcome...

by completetech13 installs

Create CompleteTech LLC customer success and account management artifacts for agentic development clients, including account profiles, contact maps, approved...

by completetech12 installs