Skip to main content
caveman learn shows where your coding agent’s tokens go, and what to fix first. It reads the setup files and session history already on your computer. Nothing is sent anywhere. You get:
  • a Setup Score from 0 to 100 (higher is leaner)
  • a list of findings: places your tokens go, biggest first, each with a suggested fix
  • a local HTML report and a JSON file
It never edits your files. Fixes happen inside Claude Code or Codex, through the caveman-learn skill, one edit at a time, and only after you say yes. Every number is an estimate from your own history (inferred in JSON). No local number is promoted to verified; see Accounting and evidence.

Quick start

Privacy: what it reads and writes

Analysis runs locally. export writes a file for you to inspect and sends nothing.

Reads

The memory and rules doctor also reads .claude/CLAUDE.md, CLAUDE.local.md, .claude/rules/*.md and ~/.claude/rules/*.md (path-scoped rules with a paths: frontmatter are skipped because they load on demand), and Claude Code auto memory at <claude root>/projects/<repo-key>/memory/MEMORY.md plus its topic files, honoring CLAUDE_CODE_PROJECT_DIR_NAME. Two other inputs: the session_outcomes finding reads commit timestamps with git in up to 12 repositories (3 seconds per call, budgeted overall), and the wrap_measured block reads rows the Caveman proxy already recorded in caveman.db.

Writes

All paths are under $CAVEMAN_HOME (default ~/.caveman) unless noted. CAVEMAN_DB overrides the database path. Report files are created with mode 0600.

Commands and flags

The default window is --since 30d. The scan times out after 120 seconds (CAVE_LEARN_TIMEOUT, seconds). Examples:
Notes per command:
  • apply on a behavioral or load_bearing sink returns applied: false with a reason; there is no mechanical fix. A recurring_context candidate carries locators (file, JSONL line, block index, SHA-256), never the block text.
  • applied needs a fix kind. It is taken from the sink’s evidence or a default (claude_md_weight, claude_md_sections, config_tax, dead_load, dumbzone_advice); otherwise pass --fix-kind. The “before” value comes from the newest report snapshot older than the current scan, so a scan run after your edit is not used as the baseline. The edited file is fingerprinted at record time.
  • simulate accepts only claude_md_weight:*, claude_md_sections:*, dead_load:skills and recurring_context:repaste:*. Config sinks are tokens per turn times scanned provider-counted turns; recurring blocks are occurrence sums minus the largest occurrence and one pointer per occurrence. Overlapping selections on the same file are counted once. No dollars, no forward projection.
  • experiment report defaults its window to the experiment’s start.
  • export carries sink ids, classes, magnitudes, the score, spend totals and per-model rows. It carries no paths, locators, evidence maps, repository names or session text.
  • reconcile matches CSV columns by header name only and fails on any ambiguous header. Coverage is a token comparison, not a saving.

Setup Score

The Setup Score (cave_score in JSON) grades how lean your local setup is. It starts at 100 and subtracts four capped penalties. The caps sum to 100. Config tokens per turn = user CLAUDE.md + every CLAUDE.md from cwd to / + Claude skill descriptions. AGENTS.md has its own finding and is not in this sum. A component that cannot be measured has penalty 0, measured: false, and a detail string saying why. What it is not:
  • Not money. It carries no currency and is not derived from spend.
  • Not billed usage. It is transcript and file arithmetic.
  • Not the Caveman Cloud Cave Score, which scores organization traffic on a different scale. The two will not match.
The terminal and TUI show the score only once at least one block has recurred across three or more sessions (a recurring_context sink exists). Before that, learn prints the session count and top findings without a score. --json always includes it.

Sink classes

Sinks are ranked: forward-rate sinks first by tokens per day, then historical sinks by observed tokens. Classes (the report’s label in brackets):
  • reducible (“safe fix”): a mechanical fix exists and must pass the net-token-negative gate.
  • recurring_context (“repeated text”): content re-established across sessions; fix is a cavemem offload.
  • behavioral (“habit”): a measured habit. Numbers are stated; suggestions are soft.
  • load_bearing (“needed”): measured config you need. Listed so the score stays honest; never edited.

Fixing

caveman learn implement (or the TUI’s “Implement with agent”) opens Claude Code or Codex with the caveman-learn skill, installing it if missing. The skill:
  1. Reads caveman learn report --json and shows the score, components and ranked sinks.
  2. For each sink you choose, proposes one concrete diff with before and after tokens per turn, and asks yes or no.
  3. Applies only approved edits, with the agent’s own file tools.
  4. Re-measures. Net-token-negative gate: if tokens per turn did not drop, the edit is reverted.
  5. For a cavemem offload, also checks that caveman mem recall returns the content and a pointer is in place; otherwise it reverts (caveman mem forget <id> and restores the source).
  6. After an edit passes its gate, runs caveman learn applied <sink_id>.
load_bearing sinks are never edited. procedure_repeat candidates skip the gate and go to an experiment instead, because a skill adds prefix tokens every session and pays back only on some. You can install the skill directly: caveman tools skills install caveman-learn (add --user for all repos, --agent codex for Codex).

Applied ledger and verdicts

Each later scan compares sessions after applied_at with the recorded before-value and reports a row in confirmed: Provenance compares the file’s SHA-256 with the fingerprint taken at applied time: intact, changed_since (someone edited past the fix), target_missing, or not_fingerprinted. changed_since and target_missing force confidence to low.

Savings

caveman learn savings lists every recorded fix, including regressions, grouped by measurement method (rung), strongest first: Rungs are never summed together, and there is no blended headline. Each row carries standing confounders for its method. Only positive savings are priced, as a per-day rate over the scanned window at your measured effective input rate, when the plan has a spend block. total_saved_usd_by_rung sums within one rung only. Nothing is projected to a month or labeled verified.

Experiments

A holdout compares arms over your own sessions:
  1. experiment start <label> [--sink <id>] [--fix-kind <kind>] [--note <t>] opens the on arm.
  2. experiment arm <label> off (later on again) switches arms. Switching to the running arm is a no-op.
  3. experiment report <label> assigns each session to the arm active at its start and compares median tokens per session.
  4. experiment stop <label> closes it.
When the on-arm has more than 1.25x the off-arm’s tool errors per turn, the report says so. Arms run at different times on different work, so the result is still inferred.

Reverting

Every edit is made by the skill and reported exactly, so it can be undone with your own tools or version control. An offload reverts with caveman mem forget <id> plus restoring the trimmed text. A regressed verdict is shown with no dollar figure; the skill offers the revert path.

Autopilot

Autopilot rescans in the background and tells you about new findings without you running learn. It keeps its own report under runtime/learn-autopilot/reports/: its scans run with whatever working directory and environment the last session-end hook had, so they never overwrite the canonical report, dated snapshots or trend history your own caveman learn writes.

Session-start nudge

After a background scan, autopilot may show one line at the next session start, for example:
Rules:
  • Sinks of class reducible or recurring_context with tokens_per_turn >= 2000 qualify. This includes memory_health:duplicate_rules and memory_health:memory_orphans when they are reducible and above the threshold.
  • memory_health:broken_imports and memory_health:memory_truncation qualify with no token threshold: they break what the agent loads. When they are all that is new they lead the line (caveman learn: memory files (<repo>) — <title>. Run \caveman learn —all` to review.`); otherwise they are appended as ”, plus N memory-file findings”.
  • Only sinks not seen by an earlier autopilot scan. The first scan records a baseline and announces nothing.
  • While a nudge is still unclaimed, newer sinks stay unseen and are announced after it.
  • Shown only on Claude Code, Codex and Gemini CLI SessionStart with source startup or clear; never on resume, compact or fork. It is delivered as a user-visible systemMessage, not model context.
  • Claimed with one atomic rename, so concurrent sessions announce it once. The claim parks it in runtime/learn-autopilot-nudge.inflight.json with the claimer’s token; it counts as announced only when that claimer confirms, after the line was written to the host (the fast hook owns the token and confirms after relaying the delegated output). At most one nudge is in flight: a newer nudge waits behind an unconfirmed one. An unconfirmed nudge, for example after the delegate hit its 3 s timeout, is shown once more after 10 minutes, never twice.
  • Suppressed when autopilot is disabled.
The trends block compares your own sessions week over week. Metrics: Only the session-derived score components are recomputed per week; config_tax and dead_load depend on today’s config and are not replayed. Full score history comes from the dated report snapshots (score.history). movers lists up to three sinks that grew and three that shrank by at least 10 tokens/turn versus the newest snapshot at least 7 days old (else the newest one). A trend is not a saving and does not show what caused the change.

Output files and JSON

caveman learn --json and caveman learn report --json print one document with schema: "caveman.learn.v1" and basis: "inferred". The same plan, plus moves, generated_at and generation, is saved as reports/caveman-learn.json. The Go types are in proxy/internal/store/learn_types.go. Other subcommands print their own schemas: caveman.learn.savings.v1, caveman.learn.simulate.v1, caveman.learn.experiment.v1, caveman.learn.digest.v1, caveman.learn.reconcile.v1. spend is what the scanned window cost at published rates. It is a floor when unpriced is non-empty, it is not an invoice, and on a subscription plan the marginal cost is zero. The compact plain view folds several unpriced models into one line; --all, --md, JSON and the HTML report list each model.

Limits and known gaps

  • Headless sessions can consume the nudge. A scripted claude -p or codex exec fires SessionStart with source startup, so it can claim and confirm the nudge where no one sees it.
  • Unsupported agents. Only the five sources above are scanned. Cursor, Windsurf, Hermes, Pi and other agents are not read.
  • aider is off unless CAVEMAN_AIDER_ROOT points at a directory to walk. aider history has no provider usage, so it adds nothing to depth, dumbzone, spend or trends.
  • Context windows. A window comes from the shared model catalog. For un-cataloged models learn assumes 1M if the model id contains 1m, 400k for OpenAI, 1,048,576 for Gemini, and 200k otherwise. If a session’s context grows past the assumed window, that whole session is re-measured against 1M (then 2M). A 1M-window session that never exceeds 200k is still measured against 200k. Assumed windows add a caveat and are excluded from the excess token floor and from cross_provider:depth.
  • Turn-1 prefix includes the first user prompt, so the measured prefix is an upper bound.
  • Plugins are counted but their tokens are not measured.
  • claude_md_sections sees only visible transcript text. Claude Code injects CLAUDE.md into the system prompt, so the finding is emitted only when at least one section echoes (a positive control).
  • --repo drops Gemini sessions (their transcripts have no working directory) and omits retro.
  • Auto memory location ignores a custom autoMemoryDirectory setting.
  • Time budget. With --retro, a scan that hits its budget covers partial history; repository, section-echo, trend and applied-fix blocks are omitted rather than zero-filled.
  • Ledger coverage. memory_health fixes can be recorded with applied but have no after-value measurement yet, so they stay insufficient_data. simulate does not accept memory_health sinks.
  • Savings rungs. Confirmed ledger rows are currently produced only at the deterministic_remeasure and interrupted_time_series rungs. controlled_holdout results appear in experiment report, not in savings, and no command yet produces counterfactual_replay rows.