A local CLI that turns your Claude Code transcripts into a personal introspection dashboard — showing how you prompt the agent, what kinds of work you actually drive it to do, what tools it reaches for, and what it costs. Runs entirely on your machine, never sends data anywhere, and persists nothing more sensitive than counts and labels.
Built on Bun + TypeScript with SQLite local storage. Prompt intent is classified with a local zero-shot Natural Language Inference (NLI) model (BART-MNLI via transformers.js).
Claude Codewrites a structured record of every session to
~/.claude/projects/.
agent-insight reads those JSONL files and builds a report:
Prompt Intent Classification (avg/prompt)
intent n % conf in tok out tok turns tools
inquisitive 480 51.1% 53% 69 9,120 4.2 3.8
commanding 217 23.1% 38% 43 22,983 5.0 5.0
corrective 117 12.5% 52% 178 9,444 3.2 3.0
affirmative 68 7.2% 46% 40 27,424 14.9 15.4
clarifying 28 3.0% 34% 27 16,165 10.6 10.8
exploratory 22 2.3% 43% 17 7,263 4.8 4.0
constructive 7 0.7% 24% 1,098 13,465 2.7 4.1
Agent Tool Use Mix
WriteEdit[code] 1322 (24.9%)
Shell[build_pkg_git] 1140 (21.5%)
Read[code] 526 (9.9%)
WriteEdit[other] 471 (8.9%)
Shell[search_like] 392 (7.4%)
TaskUpdate 303 (5.7%)
Shell[etc] 281 (5.3%)
Shell[other] 250 (4.7%)
...
Activity Mix From Tool Composition
activity turns % in tok % in out tok % out out/turn cost $
coding 1751 31.5% 12,144 23.3% 4,625,730 48.0% 2,642 $661
exploring 1217 21.9% 8,234 15.8% 1,195,498 12.4% 982 $324
building 1103 19.9% 1,739 3.3% 525,497 5.5% 476 $306
other 940 16.9% 11,906 22.8% 1,553,265 16.1% 1,652 $263
planning 409 7.4% 1,911 3.7% 1,537,508 16.0% 3,759 $146
debugging 63 1.1% 844 1.6% 75,942 0.8% 1,205 $9
web 57 1.0% 15,239 29.2% 48,019 0.5% 842 $19
delegating 16 0.3% 140 0.3% 66,063 0.7% 4,129 $5
total $1,732
File ops by category
tool code data docs other total
Read 526 30 177 30 763
Edit 1112 59 265 29 1465
Write 210 51 54 13 328
Code Output
files touched 671
files created (new) 220 (32.8%)
files reverted 35 (5.2%)
test files touched 94 (14.0%)
lines added 37,465
lines removed 6,300
net lines 31,165
churn (added+removed) 43,765
test lines added 11,360 (30.3% of added)
Code Output by Activity
activity files lines+ lines- net revert%
coding 325 27,537 2,012 25,525 6.5%
exploring 333 9,907 4,287 5,620 4.2%
...
Lines vs Cost
net lines produced 31,165
total cost $1,732
$ / 1000 net lines $55.57
Cache hit ratio
98.74% (reads 2,620,172,838 / creates 33,529,052)
Bash anti-pattern rate (cat/grep/ls-like)
27.9% (575 / 2063 Bash calls)
Parallel tool-call rate
6.0% (264 / 4459 tool-bearing turns)
A few of the patterns this surfaces in real data:
- The "affirmative surge" — short
"ship it"prompts (7% of prompts) trigger the biggest implementation bursts (27k tokens, 15 turns avg). correctiveprompts are 12% of input — roughly 1 in 8 prompts is "no, undo that" — a re-engagement signal worth tracking.Shell[build_pkg_git]is the second-biggest tool row (~22% of all calls) — about a fifth of what the agent does is run package managers, builds, or git.planningis only 7% of turns but ~16% of output tokens — planning turns average ~3,700 tokens, ~3× a coding turn.Bash anti-pattern rateflags how often the agent shells out tocat/grep/lsinstead of using its nativeRead/Grep/Glob.- Outcomes: ~30% of every added line is test code; coding turns have a
6.5% revert rate vs exploring's 4.2%; ~$56 per 1000 net surviving lines.
Derived from Claude Code's
file-history/snapshots.
agent-insight report --info prints the full methodology. The short version:
Reads JSONL transcripts under ~/.claude/projects/<encoded-cwd>/. Each file is
one Claude Code session. No network calls are made to read your data —
everything is local.
Prompt text, assistant text, tool inputs, tool outputs, file contents, and command strings are never persisted. The DB stores only:
- token counts and lengths
- tool names and
bash_kindclassifications - file extensions (not paths — paths are sha256-hashed)
- timestamps and model IDs
- classification labels with confidence scores
- boolean heuristic flags (e.g.
contains_error)
Prompt text enters the zero-shot classifier in-process and is discarded after scores are stored. A test verifies that no parsed row contains a known secret string from its source.
A single Claude API call (one requestId) emits 1–4 JSONL records (thinking,
text, tool_use). agent-insight aggregates consecutive same-requestId
records into one logical turn before any metric is computed. Token columns
sum, tool_use_count sums, end-of-turn flags come from the last record. This
fixes a common gotcha where naive "1 record = 1 turn" counts run 30–40% high.
A JSONL file may span days; metrics on a single file would blend distinct work
periods. Each file is split into chunks of ≤3h wall-clock from first to last
turn. Each chunk is one row in the sessions table.
Each logical turn is tagged based on its tool composition (precedence rules, first match wins):
| Tag | Trigger |
|---|---|
coding |
any Edit / Write / NotebookEdit |
delegating |
any Agent call |
planning |
only Task* tools, or no tools and the turn ends in a question |
web |
only WebSearch / WebFetch |
exploring |
only Read / Grep / Glob / cat-grep-ls-like Bash |
debugging |
test_runner Bash with read-only support |
building |
build / pkg_mgr / git Bash with read-only support |
other |
mixed-purpose |
Plus: any turn whose preceding prompt contained an error/stacktrace gets
promoted to debugging (unless coding/delegating/planning already wins).
Bash commands are tokenized after stripping setup prefixes — env-var
assignments (FOO=bar cmd), export X=y && cmd, cd path && cmd,
source file && cmd. The operative first token then maps to one of 11
buckets: read_like, search_like, list_like, text_munge, git,
pkg_mgr, test_runner, build, network, container, or other.
Each user prompt is classified with Xenova/bart-large-mnli, a zero-shot
NLI model loaded locally via transformers.js.
- The model runs on CPU. Under default polite mode on macOS, the process runs in Darwin background QoS (efficiency cores only), so it doesn't kick the fan on or starve foreground apps.
- Inference is ~1.2s/prompt warm. Prompt text is truncated to 600 chars before tokenization (BART has a 1024-token window shared with the hypothesis template — symbol-heavy code can hit 2 chars/token).
- Label set:
inquisitive,exploratory,constructive,commanding,corrective,affirmative,clarifying. All 7 scores are stored per prompt; the top-rank label is shown in the report. - Average confidence is typically 30–60% — normal for 7-way zero-shot — and the top label still reliably tracks intent.
The classifier model (~400 MB headline, ~1.5 GB on disk after first ingest —
transformers.js downloads multiple weight variants on demand) downloads on
first run to <repo>/node_modules/@huggingface/transformers/.cache/ and is
reused thereafter.
Reclaim that space at any time with:
agent-insight clean # dry run — shows what would be deleted
agent-insight clean --yes # delete now
agent-insight clean --if-unused-for=14d --yes # only delete if you haven't
# ingested in 14 days (safe to
# schedule on a cron)Re-running ingest after a clean will re-download on first use. Subsequent
runs reuse the cache, same as initial install.
For every session, Claude Code writes pre-edit snapshots of each touched file
to ~/.claude/file-history/<session-id>/ as <hash>@v1, @v2, etc. — full
file contents at each version. The JSONL's file-history-snapshot records
provide the <hash> → file_path mapping (relative paths get resolved against
the session's cwd).
agent-insight walks the snapshot tree per session and computes one row per
(session, file) in the file_ops table:
lines_added/lines_removed— linear-time line-multiset diff between consecutive snapshot versions, summed across all edits to that file.net_lines— diff between the first and last snapshot. What survived to end of session. Can differ fromadded − removedwhen content cycles in and out.churn_lines—added + removed. Gross activity. High churn / low net ratio is the signature of throw-away work.was_reverted— true when|net| / churn < 10%(the agent edited a lot but ended near the original).was_new— true when the first op on this path wasWriteand noReadhappened first. Heuristic for "agent created this from scratch."is_test— from path heuristics:*.test.*,*_test.*,/tests/,/spec/,FooTest.{java,kt,...}.read_before_edit— agent didReadbefore its first mutating op. A discipline signal.
Snapshot content stays in memory only for the duration of the diff. Same privacy boundary as the rest of the parser — only counts and booleans persist.
Costs are estimates at Anthropic's public list prices:
| Model | Input | Output |
|---|---|---|
| Opus 4.x | $5 / 1M | $25 / 1M |
| Sonnet 4.x | $3 / 1M | $15 / 1M |
| Haiku 4.5 | $1 / 1M | $5 / 1M |
Cache reads priced at 10% of input. Cache writes priced at the 5min ephemeral rate (1.25× input); some calls use the 1h rate (2× input) but we can't split in the data, so cache-write cost may be off by up to ~30%. Unknown model IDs default to Opus. This is a tooling estimate, not a billing source of truth.
Requires Bun 1.3+ (for bun:sqlite and bun:ffi).
brew tap oven-sh/bun
brew install bunVerify:
bun --version # should print 1.3.x or newerAlternative installers (Linux, manual, etc.) are on bun.sh.
git clone https://github.com/<you>/agent-insight.git
cd agent-insight
bun installThat's it. The CLI runs straight from source:
bun run src/cli.ts ingest # parse Claude Code transcripts
bun run src/cli.ts report # show the dashboardIf you want a global binary, bun link in the repo gives you agent-insight
on your $PATH.
agent-insight ingest # scan transcripts and update the local DB
agent-insight report # print the metric report
agent-insight status # show DB size + cursor state
agent-insight clean # delete the cached classifier model
--since <span> skip files older than <span>. Examples: 14d, 4w,
2026-04-01, "all". Default: 14d for global scans,
"all" when --project is set.
--project <id|name> scope to one project (id hash or display name)
--no-classify skip zero-shot classification (faster, no costs
until --rescan-prompts later)
--full-speed run on P-cores instead of E-cores. ~8× faster
classification but the fan may spin up.
--reclassify re-parse files where some prompts are missing
classifications (use after first --no-classify run)
--rescan-prompts re-parse JSONLs to refresh prompts.contains_error
and lengths without re-classifying
--rescan-tool-calls re-parse JSONLs to refresh bash_kind / file_ext
/ file_category without re-classifying
--rescan-file-ops re-parse JSONLs + file-history snapshots to
rebuild the file_ops table (lines added/removed,
reverts, new files, test touches)
--retag recompute turns.activity from existing tool_calls
in place (no JSONL re-parse, no classifier)
--project <id|name> scope to one project
--breakdown expand component breakdowns where applicable
--json emit JSON instead of the text report
--list-projects list projects with row counts and exit
--info print methodology + glossary instead of metrics
clean deletes the local classifier model files (~1.5 GB after first
ingest). It's a dry-run by default — pass --yes to actually delete.
--model <hf-id> hub ID of the model to delete (default: the
classifier's current default)
--yes confirm deletion (otherwise: dry-run)
--if-unused-for <span> only delete if agent-insight hasn't been used
in the given span. Examples: 14d, 4w,
2026-04-01. "Last used" = mtime of the SQLite
DB file (every ingest writes it).
Useful pattern:
agent-insight clean # see what would be deleted
agent-insight clean --yes # reclaim disk space now
agent-insight clean --if-unused-for=14d --yes # safe to schedule;
# no-op when active# Ingestion scan of claude metadata files
agent-insight ingest # 14d default
agent-insight ingest --since=all # all time or use an ISO date
agent-insight ingest --full-speed # ingest faster, more CPU load
# View an aggregate report based ingestion currently in the local DB
agent-insight report
# look at a specific project
agent-insight report --project some-project
agent-insight report --list-projects # to find IDs/names- Local DB:
~/.agent-insight/db.sqlite(SQLite, WAL mode) - Classifier model cache:
<repo>/node_modules/@huggingface/transformers/.cache/(transformers.js v4 caches inside its own package; useagent-insight cleanto reclaim) - Source data (read-only):
~/.claude/projects/<encoded-cwd>/*.jsonl— session transcripts~/.claude/file-history/<session-id>/<hash>@v<N>— pre-edit file snapshots
agent-insight is built entirely on reverse-engineered consumption of
Claude Code's local data files:
~/.claude/projects/<encoded-cwd>/*.jsonl— session transcripts~/.claude/file-history/<session-id>/<hash>@v<N>— pre-edit file snapshots
These formats are not a public API. Anthropic has not committed to keeping them stable, hasn't documented them, and ships changes to Claude Code on a fast cadence. Concrete things this tool depends on that could change in any release:
- Record types:
assistant,user,attachment,file-history-snapshot,last-prompt,permission-mode, etc. - The shape of
message.usage(input_tokens, output_tokens, cache_read_input_tokens, cache_creation_input_tokens, ephemeral tier names). - That logical turns are split across 1–N JSONL records keyed by
requestId. tool_use.input.file_pathbeing absolute;trackedFileBackupskeys being relative (resolved againstcwdin the same record).- File-history snapshots being full file contents at
<hash>@v<N>with the hash → path map living infile-history-snapshotrecords'trackedFileBackups. - The
~/.claude/file-history/<session-id>/per-session directory layout.
When Anthropic changes any of this, agent-insight will likely break —
either silently (numbers go to zero or look wrong) or loudly (parse errors,
schema mismatches). This is somewhat expected for a
The classifier model (~400 MB) downloads on first run. Then every prompt has to be classified once. For ~1000 prompts across many projects, expect:
- Default polite mode: 3–4 hours (E-cores only, fan stays off).
--full-speed: ~20–25 minutes (P-cores saturate, fan likely spins up).--no-classify: ~1 minute (skips ML; you can--reclassifylater).
After the first run, normal ingest invocations are sub-second because
cursors gate every file and only changed JSONLs get re-parsed. A 5-minute
cron typically processes 0–5 new prompts and finishes in seconds.
Trade-off matrix:
| You want… | Run |
|---|---|
| Get something useful fast | ingest --since=14d --no-classify |
| Backfill everything overnight | ingest --since=all (polite) |
| Quick full backfill, accept fan noise | ingest --since=all --full-speed |
| Add classifications to existing data | ingest --reclassify |
Rarely, you might see ONNX print "Gather node out of bounds" stderr line — that's a prompt whose tokenized form overflowed BART's 1024- token window after our 600-char input cap. It's caught and skipped; the final ingest summary reports the count of skipped prompts.
7-way zero-shot NLI averages 30–60% top-label confidence. The
model isn't fine-tuned on this task. The labels are directionally right but
not perfect, especially clarifying, exploratory, and constructive
which the model tends to under-fire. The data still perhaps surfaces useful
patterns in aggregate.
Cache-write tier (5min vs 1h ephemeral) can't be split in the source data, so the cache-write cost line is approximate. Don't use this to reconcile against an Anthropic invoice — use it to compare across activities/projects/ time periods within your own data.
Shell[other] is the bucket for commands the classifier doesn't recognize.
It shrinks significantly after env-var/setup prefix stripping (export, cd,
source, FOO=bar), but custom CLIs and one-off scripts will always be there.
Adding to src/ingest/bash.ts is the way to teach it new patterns.
bun:ffi is used to set Darwin background QoS for polite mode. On Linux,
polite mode falls back to plain nice -n 19 (just lower priority, no
E-core restriction).
This is a personal introspection tool, it's built to look at your own data on your own machine. There's no upload, no auth, no cohort comparison. The schema deliberately stores only what's needed locally and the same shape could be the upper bound of any future opt-in upload.