CONTEXT
The work started here.
Claude Code token spend is opaque: cost and waste are invisible until the session ends, and the transcripts that explain them sit unread in ~/.claude.
BUILD
What was built.
A local observability dashboard for Claude Code token spend. A Node daemon bound to 127.0.0.1 reads Claude Code's JSONL transcripts, runs a best-effort secrets filter, and stores the result in SQLite under ~/.agentwrangler. Detectors flag costly patterns, recommendations suggest a change, and an effects view watches a 14-day window to see whether the change helped, saying "Effect not measured" when it can't tell. Optional guardrail hooks install into Claude Code on request. Install with npx agentwrangler and open the dashboard on localhost; the code is Apache-2.0 on GitHub. Built offline: 3,686 commits across 57 active days on a private repo, roughly 135k lines of TypeScript, and 600+ test files by static count. The public GitHub repo is a curated export of that work, so its history is much shorter.

PROJECT DESIGN
How it was shaped.
Built offline on a private local repo, then published by export. The stack call is written down: ADR-100 weighed a lightweight desktop shell (Tauri preferred) and chose a TypeScript/Node 22 daemon with better-sqlite3 and a browser UI on localhost instead. The public repo is generated from the private one by an export script that reads an INCLUDE/EXCLUDE/OVERLAY manifest (most-specific rule wins), substitutes a public README and CLAUDE.md, gates the tree, pushes an export branch, and opens a PR against public main. The sync point is derived from tree content rather than a stored commit hash, and a fix made only on the public side is reverted by the next export, so private main stays the one source of truth.
Key modules
Daemon
Boots, migrates, scans, and serves HTTP on 127.0.0.1 only, one daemon per database. Heavy work (history, recommendations, diagnostics, scanning, WAL checkpoints) runs in background workers, and pages read prepared snapshots instead of waiting on them.
Ingest
A transcript parser reads Claude Code's JSONL session files from ~/.claude/projects; a secrets filter redacts likely secrets before anything is stored.
Store
SQLite via better-sqlite3. Migrations take a VACUUM INTO backup first (skipped above 512 MB), and a database written by a newer version is refused rather than downgraded.
Detectors → recommendations → effects
Detectors find costly patterns and model their list-price cost; recommendations turn a finding into a change; effects observe the following 14 days and report the result or that it couldn't be measured.
Rate my setup
A setup grade plus an optional "Judge me" review that runs the user's own claude CLI with no tools, no MCP servers, and no saved session.
Guardrail hooks
Optional Claude Code hooks for context budget, loop detection, limit burn, a dangerous-command denylist, and a pre-compaction checkpoint, installed only on explicit request.
Key features
Did the change help?
AgentWrangler pairs each recommendation with an effects view: once you apply a change, it watches the next 14 days and reports what moved, or says "Effect not measured" when the data can't support a call. It's candid about the limits. Dollar figures are list-price equivalents, not your subscription bill; modeled savings are not achieved savings; and observed improvement does not prove the change caused it. The README says all three out loud.
Judge me, with the payload shown first
The Rate my setup page can send your setup to a model for a second opinion. It runs your own claude CLI with no tools, no MCP servers, and no saved session, shows the exact payload before anything is sent, and blocks the send if it spots a suspected secret. The changelog puts a typical run at $0.30 to $0.40.
SECURITY & OPS
The operating decisions.
- Loopback only: the daemon binds 127.0.0.1, with no telemetry, no hosted backend, and no account.
- Cross-origin writes are refused. A CSRF check reads Sec-Fetch-Site, then requires an exact-match Origin from 127.0.0.1, localhost, or [::1]; a null or foreign Origin gets a 403, and write requests also need a per-run token.
- Credentials aren't passed down. ANTHROPIC_API_KEY is stripped from the environment of any claude process AgentWrangler starts, and usage limits are read by running the user's own claude -p /usage, so the tool never handles the Claude login token. That was a redesign from the first release's approach.
- One-click apply is off: the route returns 403 APPLY_DISABLED, and the guardrail hooks no longer skip Claude Code's permission prompt. The alternatives are copying the prompt or an opt-in Open in Claude Code action that is off by default.
- The privacy doc says what's stored, honestly: aggregates and structural data plus secrets-filtered command text and bounded error excerpts. The filter is described as best-effort pattern matching, not a guarantee. Every network path is listed there too: the usage check runs the user's own claude CLI and can be switched off, and GitHub sync, calibration, and Judge me each need the user's own token, key, or click.
BUILDER NOTES
What the build taught.
- The commit count and the public history disagree on purpose. The 3,686 commits live on the private repo, many from agent-driven worktree branches (2,065 are on the first-parent merge line); GitHub only shows the export PRs.
- The release gate holds the daemon's event-loop p99 at or under 50 ms and checks each of 67 budgeted API routes against its p95 latency budget, 250 ms for most routes, per the changelog.
- Detectors ship with the changelog's claim that each is tested with a must-fire and a near-miss fixture: one input that should trigger it and one that almost should, so a detector proves it knows the difference.
LESSONS
What changed afterward.
- Keep one source of truth when you publish from a private repo. Fixes land on private main and then export; a fix made only on the public side gets overwritten by the next export, by design, so nobody maintains two diverging copies.
- Label modeled numbers as modeled. List price, the subscription bill, and achieved savings are three different numbers, and the README draws all three lines explicitly.
- A localhost tool is still a web target. Any page open in the browser can fire requests at 127.0.0.1, so writes need a matching Origin and a per-run token, and the riskiest action, one-click apply, ships turned off.
WHAT CHANGED
The public repo became an export, not the history.
The build ran on a private local repo. The public repo started on Aug 20 as a placeholder with a planning README; at launch it got a fresh tree built from a manifest of what may ship, not the private history. Plans, handoffs, research, and the export tooling itself never leave the machine. Every update since reaches GitHub as an export PR, never a direct push, which is why the public history is a small fraction of the real one.
CONTROL
The constraint that held.
An export can't push until it clears an anonymization grep, a raw-content scan, and a trufflehog secrets scan that fails closed, and public main only changes through a PR.
EVIDENCE
Where to inspect it next.
- Loopback only: the daemon binds 127.0.0.1, with no telemetry, no hosted backend, and no account.
- Cross-origin writes are refused. A CSRF check reads Sec-Fetch-Site, then requires an exact-match Origin from 127.0.0.1, localhost, or [::1]; a null or foreign Origin gets a 403, and write requests also need a per-run token.
- Credentials aren't passed down. ANTHROPIC_API_KEY is stripped from the environment of any claude process AgentWrangler starts, and usage limits are read by running the user's own claude -p /usage, so the tool never handles the Claude login token. That was a redesign from the first release's approach.
- One-click apply is off: the route returns 403 APPLY_DISABLED, and the guardrail hooks no longer skip Claude Code's permission prompt. The alternatives are copying the prompt or an opt-in Open in Claude Code action that is off by default.
- The privacy doc says what's stored, honestly: aggregates and structural data plus secrets-filtered command text and bounded error excerpts. The filter is described as best-effort pattern matching, not a guarantee. Every network path is listed there too: the usage check runs the user's own claude CLI and can be switched off, and GitHub sync, calibration, and Judge me each need the user's own token, key, or click.
CARRIED FORWARD
What survived the project.
Privacy by construction (a loopback-only daemon, no telemetry, no account) and an export pipeline that decides what ships by manifest, gated by a fail-closed secrets scan.
WRITING
Field notes from this work.
No public field notes for this project yet.