Skip to content

Latest commit

 

History

50 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

agent-insight

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).


What it surfaces

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).
  • corrective prompts 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.
  • planning is only 7% of turns but ~16% of output tokens — planning turns average ~3,700 tokens, ~3× a coding turn.
  • Bash anti-pattern rate flags how often the agent shells out to cat/grep/ls instead of using its native Read / 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.

Methodology

agent-insight report --info prints the full methodology. The short version:

Data source

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.

Privacy boundary

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_kind classifications
  • 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.

Turn aggregation

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.

Session chunking

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.

Activity tagging

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 classifier

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.

Prompt intent classification (the only "ML" piece)

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.

File ops & outcomes (file-history)

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 from added − removed when content cycles in and out.
  • churn_linesadded + 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 was Write and no Read happened first. Heuristic for "agent created this from scratch."
  • is_test — from path heuristics: *.test.*, *_test.*, /tests/, /spec/, FooTest.{java,kt,...}.
  • read_before_edit — agent did Read before 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.

Cost estimation

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.


Installation

Requires Bun 1.3+ (for bun:sqlite and bun:ffi).

macOS — install Bun via Homebrew

brew tap oven-sh/bun
brew install bun

Verify:

bun --version    # should print 1.3.x or newer

Alternative installers (Linux, manual, etc.) are on bun.sh.

Clone and install dependencies

git clone https://github.com/<you>/agent-insight.git
cd agent-insight
bun install

That'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 dashboard

If you want a global binary, bun link in the repo gives you agent-insight on your $PATH.


Usage

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

ingest flags

--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)

report flags

--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 flags

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

Typical flow

# 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

Where data lives

  • 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; use agent-insight clean to reclaim)
  • Source data (read-only):
    • ~/.claude/projects/<encoded-cwd>/*.jsonl — session transcripts
    • ~/.claude/file-history/<session-id>/<hash>@v<N> — pre-edit file snapshots

Caveats

This depends on Claude Code's internal log format — a private API

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_path being absolute; trackedFileBackups keys being relative (resolved against cwd in the same record).
  • File-history snapshots being full file contents at <hash>@v<N> with the hash → path map living in file-history-snapshot records' 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 first ingest is slow

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 --reclassify later).

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.

Classification accuracy is bounded

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.

Cost estimates aren't billing data

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.

Bash classification has a long tail

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.

macOS is the targeted platform

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).


Project status

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.

License

Apache License 2.0. See LICENSE and NOTICE.

About

A CLI application to introspect your code-agent use. Creates an analysis report from Claude Code session metadata. Completely local.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages