2026 · OPEN SOURCE · AGENT OBSERVABILITY

AgentWrangler

Live on GitHub and npm: a loopback-only dashboard for Claude Code token spend, built offline and published as a gated, curated export.

LIVE Designed and built the local daemon, detectors, dashboard, and the export pipeline that publishes it.
On this page

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.

AgentWrangler screenshot

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.

Local pipeline: transcripts → ingest → SQLite → detectors and effects → localhost dashboard
Source
Claude Code JSONL transcripts in ~/.claude/projectsNothing fetched from a hosted backend; there isn't one
Ingest
Transcript parser and scannerBest-effort secrets filter before storage
Store
SQLite via better-sqlite3 at ~/.agentwranglerPre-migration VACUUM INTO backup; newer-version databases refused
Analyze
Detectors with list-price cost modelingRecommendations and a 14-day effects windowBackground workers and snapshots keep heavy work off the request path
Serve
Daemon on 127.0.0.1 with a browser UI built on ViteOverview, Workspaces, Sessions, Rate my setup, Usage patterns, Recommendations

Key modules

Core

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.

Pipeline

Ingest

A transcript parser reads Claude Code's JSONL session files from ~/.claude/projects; a secrets filter redacts likely secrets before anything is stored.

Data

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.

Pipeline

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.

Feature

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.

Control

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.
Publishing boundary (private repo → public GitHub)
Private main
Manifest: INCLUDE / EXCLUDE / OVERLAY rules decide what may ship; plans, handoffs, research, and export tooling never do
Gates: anonymization grep, raw-content scan, trufflehog secrets scan, failing closed before push
Export PR: an export branch and a PR against public main; public main is never pushed directly

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.
Build registerReturn to the build register

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.

Trust belongs in the schema, not the application.
The operating principle behind every project here. A rule the database enforces can't be forgotten in a hurry. Constraints, denied-by-default access, append-only logs — the controls that hold are the ones the system won't run without.