Skip to content

fabio-dee/ccaudit

Repository files navigation

ccaudit

~5-10% of your Opus 4.7 1M-token context is gone before you type a word. (~50-108k tokens of ghost inventory - almost half a Sonnet 4.6 200k context. See snapshot below.)

Unused agents, skills, MCP servers, commands, hooks, and memory files (call them ghosts) load into context every session.
ccaudit finds them, shows you the cost, and moves them to ~/.claude/ccaudit/archived/ in one command.
Undo any time with ccaudit restore.

TL;DR

Solution: paste this into your terminal

claude -p "please spawn a haiku subagent to use the web tool, \
fetch the readme of this opensource project and explain to me, in very simple terms, \
what it does in 3 sentences and why I should use it (all using bullet points): \
https://github.com/fabio-dee/ccaudit"

Enough talk. Just tell me how to use it now:

npx ccaudit-cli@latest                    # full report of what's loading vs. what's used
npx ccaudit-cli --interactive             # pick exactly which ghosts to archive (TUI)
npx ccaudit-cli --dry-run                 # see the archive plan for ALL ghosts, no files touched
npx ccaudit-cli --dangerously-bust-ghosts # archive ALL ghosts (nothing deleted, undo with restore)

Native alternatives (and where ccaudit fits)

Claude Code ships three first-party tools that overlap with parts of what ccaudit does. None of them cover the full picture:

Tool Covers Doesn't cover
/skills t-sort Skills only - list, reorder. Agents, MCP servers, hooks, memory, commands. No token math. No archive / rollback.
/usage Aggregate token + cost reporting per session. Per-component ghost identification. Doesn't tell you what to remove or move anything.
claude plugin disable Plugin-level on/off. Granular agent / skill / MCP / hook visibility. No token cost. No manifest of what was disabled.

ccaudit's differentiator: cross-component scope (agents + skills + MCP + memory + commands + hooks in one pass), regime-aware token math (eager vs deferred-tools once cc ≥ 2.1.7 flips the ToolSearch threshold), and archive-with-rollback - every action is manifest-logged and reversible via ccaudit restore.

ccusage tells you what you spent. ccaudit tells you what's wasting it.

Current release: v1.5.1 - interactive archive picker, interactive restore picker, fuzzy-match restore by name, archive purge. See CHANGELOG.md.

Image

The --interactive picker (v1.5) lets you tab through every category, toggle individual ghosts, and confirm before any disk write:

AGENTS │ SKILLS │ MCP SERVERS │ MEMORY │ COMMANDS
AGENTS (9/179 · 743 tok)
    ── Ungrouped ──
  ◯ brand-guardian                             65 tok  ~/.claude/agents/design/b…
  ◉ image-prompt-engineer                      97 tok  ~/.claude/agents/design/i…
› ◉ inclusive-visuals-specialist               64 tok  ~/.claude/agents/design/i…
  ◉ ui-designer                                85 tok  ~/.claude/agents/design/u…
  ◉ ux-architect                               64 tok  ~/.claude/agents/design/u…
  ◉ ux-researcher                              84 tok  ~/.claude/agents/design/u…
  ◉ visual-storyteller                        101 tok  ~/.claude/agents/design/v…
  ◉ whimsy-injector                            83 tok  ~/.claude/agents/design/w…
  ◉ ai-engineer                                95 tok  ~/.claude/agents/engineer…
  ◉ autonomous-optimization-architect          70 tok  ~/.claude/agents/engineer…
  ◯ backend-architect                          82 tok  ~/.claude/agents/engineer…
  ◯ data-engineer                              96 tok  ~/.claude/agents/engineer…
  ◯ devops-automator                           59 tok  ~/.claude/agents/engineer…
  ◯ embedded-firmware-engineer                 69 tok  ~/.claude/agents/engineer…
  ◯ frontend-developer                         66 tok  ~/.claude/agents/engineer…
↓ 165 more below
Tab ← → tabs · ↑↓ nav · / search · ? help · Space toggle · a tab-all · Enter → · q cancel
14 of 323 selected across all tabs · ≈ 3k tokens saved

"But doesn't /context already do this?" /context shows what's loaded right now in the current session. ccaudit shows what's been ghost-loading for weeks - agents, skills, MCP servers, and memory files you forgot you installed, silently eating your context budget every session. One is a live snapshot; the other is the longitudinal audit.


Upgrading from 1.3.x

Token totals will change. v1.4.0 replaces the old file-size / 4 wave with evidence-based, per-category formulas derived from Anthropic's published loading documentation and measured session logs.

Most users will see lower totals. The old formula over-counted skills and agents whose descriptions are shorter than the full file. A small number of users with many hooks may see a higher total if they pass --include-hooks (see below).

What's new

Change Detail
New category: commands Slash commands in ~/.claude/commands/ and .claude/commands/ are now inventoried and estimated.
New category: hooks PreToolUse / PostToolUse / SessionStart hooks from .claude/settings.json are now surfaced.
Hooks are advisory by default Hook token costs are not added to the grand total unless you pass --include-hooks. The upper-bound is shown in the summary as advisory.
New flag: --regime eager|deferred Override MCP regime detection (auto by default). In deferred mode a single ToolSearch overhead (~8.7k tokens) replaces per-server costs.
New flag: --include-hooks Add hook upper-bound tokens to the reported grand total.
Auto-memory + import chains ~/.claude/projects/<slug>/memory/MEMORY.md auto-memory files are now surfaced. @-import chains are resolved recursively (depth ≤ 5) and each imported file appears as a separate memory row with importDepth set.

For the full migration guide with before/after formula table see CHANGELOG.md § 1.4.0.


Example snapshot

A typical audit looks like this (default mode, hooks advisory, not included in total):

┌──────────────────────────────────────────────────────────────────────────────┐
│ CCAUDIT - ~64k tokens/session wasted                                         │
│ 👻 Ghost Inventory - Last 7 days                                             │
├──────────────┬──────────────┬─────────────┬──────────────────────────────────┤
│ Agents       │ Defined: 177 │ Used: 12    │ Ghost: 165 ~24k tokens/session   │
│              │              │             │ (70 in frameworks above)         │
├──────────────┼──────────────┼─────────────┼──────────────────────────────────┤
│ Skills       │ Defined: 81  │ Used: 15    │ Ghost: 66 ~2.7k tokens/session   │
│              │              │             │ (61 in frameworks above)         │
├──────────────┼──────────────┼─────────────┼──────────────────────────────────┤
│ MCP Servers  │ Defined: 5   │ Used: 1     │ Ghost: 4 ~7.0k tokens/session    │
├──────────────┼──────────────┼─────────────┼──────────────────────────────────┤
│ Memory Files │ Loaded: 12   │ Active: 1   │ Stale: 11 ~55k tokens/session    │
├──────────────┼──────────────┼─────────────┼──────────────────────────────────┤
│ Commands     │ Defined: 45  │ Used: 8     │ Ghost: 37 ~1.4k tokens/session   │
│              │              │             │ (36 in frameworks above)         │
├──────────────┴──────────────┴─────────────┴──────────────────────────────────┤
│                                                                              │
│ Total ghost overhead: ~64k tokens (~32% of 200k context window)              │
│ (global: ~62k tokens + worst project ~/projects/my-project: ~2.1k tokens)   │
│ Health grade: D (Poor)                                                       │
│ Hooks (advisory - not included in total): ~18k tokens upper-bound (9 hooks) │
│ Pass --include-hooks to add to grand total.                                  │
│ 💡 Potential savings after ccaudit --dangerously-bust-ghosts: ~64k           │
│ tokens/session reclaimed                                                     │
│ [████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 32%                    │
└──────────────────────────────────────────────────────────────────────────────┘

Show your ghost score

Share your audit results in the GitHub Discussion


With --include-hooks the hooks row is promoted into the main table and added to the grand total:

├──────────────┼──────────────┼─────────────┼──────────────────────────────────┤
│ Hooks        │ Defined: 9   │ Used: 2     │ Ghost: 7 ~18k tokens/session     │
├──────────────┴──────────────┴─────────────┴──────────────────────────────────┤
│ Total ghost overhead: ~81k tokens (~41% of 200k context window)              │

Numbers will vary by setup, but the pattern is the same: defined inventory, actual usage, wasted tokens, and the worst-case overhead that would hit a single session.


After --dangerously-bust-ghosts:

----------------------------------------------
  ccaudit --dangerously-bust-ghosts

  Before (from dry-run 2026-04-13T08:19:58Z): ~108k tokens loaded per session
  After:   ~12k tokens
  Freed:   ~96k tokens (48% of context window)

  Health:  23/100 --> 91/100

  Archived: 116 agents, 74 skills
  Disabled: 4 MCP servers
  Flagged:  6 memory files

  npx ccaudit-cli@latest
----------------------------------------------

Manifest: ~/.claude/ccaudit/manifests/2026-04-06T14:26:00Z.json
Restore anytime: ccaudit restore

Interactive mode

ccaudit ghost --interactive (short: -i) opens a TUI picker for selective archival. Instead of archiving every ghost in one shot, you scroll six category tabs, pick exactly what to archive, and confirm on a single screen. Requires a TTY; non-TTY sessions fall back to --dry-run and --interactive combined with --json is a hard error.

npx ccaudit-cli ghost --interactive

The picker

┌─ Archive ghosts ─────────────────────────────────────────────────────┐
│ [ agents (3/12) ] [ skills (0/8) ] [ mcp (1/2) ] [ memory (0/4) ]    │
│ [ commands (0/5) ]                                                    │
├──────────────────────────────────────────────────────────────────────┤
│ ◉ code-reviewer          agent    ~1.2k  28d stale                   │
│ ◯ pencil-dev             agent    ~0.9k  44d stale                   │
│ ◉ doc-writer             agent    ~1.5k  91d stale                   │
│ [🔒] gsd-planner         agent    ~2.1k  Part of GSD (3 used,        │
│                                          9 ghost). --force-partial   │
│ ◉ playwright  ⚠          mcp      ~3.8k  Also in: ./project-b/.mcp   │
│ ↓ 2 more                                                             │
├──────────────────────────────────────────────────────────────────────┤
│ 5 of 32 · ≈ 8.4k tokens saved   Space toggle  /  filter  ?  help     │
└──────────────────────────────────────────────────────────────────────┘

Hook archival deferred - selectable archive coming in a future phase.

The confirmation screen

┌─ Confirm archive ────────────────────────────────────────────────────┐
│                                                                      │
│  Archive the following 5 ghosts?                                     │
│                                                                      │
│    agents:    3  (code-reviewer, doc-writer, +1 more)                │
│    mcp:       1  (playwright)                                        │
│    skills:    1  (my-skill)                                          │
│                                                                      │
│  Estimated savings: ≈ 8.4k tokens per session                        │
│  Manifest:          ~/.claude/ccaudit/manifests/bust-*.jsonl         │
│                                                                      │
│  [ Archive ]   [ Cancel ]                                            │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘

Keybinds

Key Action
Space Toggle selection of focused row
a Toggle-all within the active tab
/ Filter by case-insensitive substring
s Cycle sort (staleness → tokens → name)
? Help overlay (canonical full keybind list)
Tab / Shift-Tab Cycle tabs forward / back with wrap
/ Same as Tab / Shift-Tab
16 Jump directly to a visible tab
Enter Confirm selection and proceed to confirm screen
Esc / Ctrl+C / q Cancel with "No changes made." (exit 0)

Framework-protected rows render dimmed with a [🔒] glyph and are not selectable by default - pass --force-partial to unlock them for the current run (a --force-partial active: framework protection DISABLED. Partial framework splits may corrupt dependent setups. banner appears at the top of the picker). See CLAUDE.md's Safety invariants section for the full rationale behind framework-as-unit protection.

Rollback: ccaudit restore moves everything back. v1.5 adds restore --interactive, a mirror of the archive picker listing every archived item across all manifests (deduplicated, newer-wins) so you can restore a subset. restore --name <pattern> and restore --all-matching <pattern> add fuzzy single-match and bulk-match restore by name. See the restore section below.


Commands

Command What it does Notable options
ghost Default ghost inventory report, plus dry-run and remediation entry point --interactive / -i, --since, --dry-run, --dangerously-bust-ghosts, --yes-proceed-busting, --privacy, --verbose, --no-group-frameworks, --force-partial, --include-hooks, --regime
inventory Full inventory with usage statistics --since, --verbose, --no-group-frameworks
mcp MCP server token costs and frequency --since, --live, --timeout
trend Invocation frequency over time --since
restore Revert a previous bust [name], --list, --interactive / -i, --name <pattern>, --all-matching <pattern>
reclaim Recover orphaned files in ~/.claude/ccaudit/archived/ not referenced by any manifest --dry-run
purge-archive Drain ~/.claude/ccaudit/archived/ of reclaimed and stale entries --dry-run, --yes, --json
install-skill Install the /ccaudit-bust Claude Code skill --dry-run, --force, --project

Shared output flags

These output flags are used across the reporting commands:

Flag Short Meaning
--json -j Structured JSON with a meta envelope
--csv RFC 4180 CSV export
--quiet -q Machine-friendly output only (TSV for report commands)
--verbose -v Extra progress and breakdown output on stderr
--ci CI mode: --json --quiet
--no-color Disable ANSI colors; also respects NO_COLOR

Notes:

  • ghost, inventory, mcp, trend, and restore honor the full report output matrix.
  • install-skill also accepts the shared output flags in help metadata, but its job is writing the skill file, so its meaningful modes are rendered, --quiet, and --json.
  • --ci also implies --yes-proceed-busting on ghost --dangerously-bust-ghosts.

Command options, verified

ghost

Flag Short Description
--since <window> -s Time window for ghost detection (e.g. 7d, 30d, 2w). Default: 7d.
--json -j Output as JSON with a meta envelope.
--csv RFC 4180 CSV export.
--quiet -q Machine-readable TSV only (suppress decorative text).
--verbose -v Show scan details on stderr; expand framework rows into member trees.
--interactive -i Open an interactive TUI picker to archive a subset of ghosts. Implies bust with an inline confirmation screen. Requires a TTY; non-TTY sessions fall back to --dry-run. Mutually exclusive with --json.
--dry-run Preview the change plan without mutating files. Writes a checkpoint to ~/.claude/ccaudit/.last-dry-run.
--dangerously-bust-ghosts Execute the bust plan: archive ghost agents/skills, disable ghost MCP servers, flag stale memory. Requires a prior --dry-run with a matching inventory hash.
--yes-proceed-busting Skip the 3-step confirmation ceremony. Required for non-TTY shells and CI.
--privacy Redact real project paths from output (replaces with project-01, project-02, etc.).
--no-group-frameworks Disable framework grouping. Output reverts to the v1.2.1 layout.
--force-partial Bypass framework-as-unit bust protection. Must match between --dry-run and --dangerously-bust-ghosts runs.
--include-hooks Add hook upper-bound token costs to the grand total. By default hooks are surfaced as advisory only.
--regime <mode> Override MCP regime detection. eager = per-server measured costs; deferred = single ToolSearch overhead (~8.7k tokens). Default: auto.

inventory

Flag Short Description
--since <window> -s Time window for usage analysis (e.g. 7d, 30d, 2w). Default: 7d.
--json -j Output as JSON with a meta envelope.
--csv RFC 4180 CSV export.
--quiet -q Machine-readable TSV only.
--verbose -v Show scan details on stderr; expand framework rows into member trees.
--no-group-frameworks Disable framework grouping. Output reverts to the v1.2.1 layout.

mcp

Flag Short Description
--since <window> -s Time window for ghost detection (e.g. 7d, 30d, 2w). Default: 7d.
--live -l Start MCP servers from your Claude config locally for exact token counts instead of estimates.
--timeout <ms> -t Timeout per MCP server in milliseconds when using --live. Default: 15000.
--json -j Output as JSON with a meta envelope.
--csv RFC 4180 CSV export.
--quiet -q Machine-readable TSV only.
--verbose -v Show scan details on stderr.

trend

Flag Short Description
--since <window> -s Time window for trend analysis (e.g. 7d, 30d, 2w). Default: 7d.
--json -j Output as JSON with a meta envelope.
--csv RFC 4180 CSV export.
--quiet -q Machine-readable TSV only.
--verbose -v Show scan details on stderr.

restore

Flag / Arg Short Description
(no args) Restore all items from every bust manifest (deduplicated, newer-wins).
<name> Restore a single archived item by name: basename without extension for agents/skills/commands (e.g. restore code-reviewer), or server name for MCP entries.
--interactive -i Open a TUI picker listing every archived item across all manifests. Select a subset to restore. Requires a TTY; may be combined with --json for the final result envelope.
--name <pattern> Fuzzy single-match restore (case-insensitive substring). Ambiguous patterns error with a candidate list - never auto-resolve.
--all-matching <pattern> Bulk restore of every item matching the fuzzy pattern. Exits 1 with no archived item matches "<pattern>" on stderr if nothing matches.
--list List all archived items across all bust manifests (read-only).
--json -j Output as JSON with a meta envelope. Includes additive selectionFilter, skipped[], and filteredStaleCount fields - see docs/JSON-SCHEMA.md.
--csv RFC 4180 CSV export.
--quiet -q Machine-readable TSV only.
--verbose -v Show detailed output including warnings.

Listing hygiene (v1.5): restore --interactive, restore --list, and full restore automatically suppress archive ops whose archive_path is missing AND source_path exists (already-restored items or stale test residue). The count of suppressed entries is reported as restore.filteredStaleCount in the JSON envelope. Entries where both paths are missing stay listed so restore can fail loudly on genuinely broken state.

reclaim

Flag Short Description Type
--dry-run List orphans without touching the filesystem bool

Shared output flags (--json, --quiet, --verbose, --no-color) also apply.

install-skill

Flag Short Description
--dry-run Show what would be installed without writing any files.
--force -f Overwrite an existing skill file without prompting.
--project -p Install to .claude/commands/ in the current directory instead of the global location.
--json -j Output as JSON.
--quiet -q Machine-readable output only.

Dry-run and remediation

--dry-run is the safe preview mode. It scans your inventory, builds the change plan, and writes a checkpoint to ~/.claude/ccaudit/.last-dry-run.

npx ccaudit-cli --dry-run

What you get:

  • which agents would be archived
  • which MCP servers would be disabled
  • which memory files would be flagged
  • the checkpoint hash needed for later remediation

When framework detection is active (the default), ghost members of partially-used frameworks are skipped in the change plan. You will see a yellow warning and a PROTECTED section listing what was held back. Pass --force-partial to include them, but you must use --force-partial on both the --dry-run and --dangerously-bust-ghosts steps, because the flag changes which items are eligible and therefore changes the checkpoint hash. Mismatched flags between the two steps will produce a hash-mismatch error.

Bust ceremony

--dangerously-bust-ghosts is blocked until a matching dry-run checkpoint exists.

The actual confirmation ceremony is:

  1. [1/3] This will modify your Claude Code configuration. Proceed? [y/N]
  2. [2/3] Are you sure? This archives agents, disables MCP servers, and flags memory files. [y/N]
  3. [3/3] Type exactly: I accept full responsibility

Use --yes-proceed-busting to skip the ceremony. That flag is required for non-TTY shells and CI, and --ci enables it automatically on the bust path.

npx ccaudit-cli --dangerously-bust-ghosts --yes-proceed-busting

--csv is rejected on the bust path; use --json if you want machine-readable output for remediation.

Rollback

Everything is reversible:

npx ccaudit-cli restore                        # restore every archived item across all manifests
npx ccaudit-cli restore --interactive          # TUI picker - select a subset to restore (v1.5)
npx ccaudit-cli restore --name code-reviewer   # fuzzy single-match restore (v1.5)
npx ccaudit-cli restore --all-matching gsd-    # bulk-restore everything matching the pattern (v1.5)
npx ccaudit-cli restore <exact-id>             # restore one archived item by exact canonical id
npx ccaudit-cli restore --list                 # list all archived items across busts

Full-mode restore walks every manifest in ~/.claude/ccaudit/manifests/ (not just the newest), with deduplication. Items archived by older busts are recovered too. Output reports moved and already-at-source separately so idempotent re-runs don't inflate the success count. In v1.5, listing paths additionally suppress entries whose archive file is gone but whose source is already restored (stale / already-done hygiene) and surface the count as filteredStaleCount in JSON.

159 items restored to their original locations (324 were already at source)

What gets reversed:

  • agents and skills are archived, not deleted
  • MCP servers are commented out in config, not removed
  • memory files are flagged in frontmatter, not rewritten destructively

Items that were protected during a bust (skipped because their framework is partially used) are never archived; they stay in place and do not appear in the manifest. If you used --force-partial to archive framework members anyway, those items are included in the manifest and are fully restorable with ccaudit restore, just like any other busted item.

Reclaim orphans

An "orphan" is a file sitting in ~/.claude/ccaudit/archived/ that is not referenced by any manifest. This happens when:

  • a manifest was lost or truncated (e.g., a bust crashed mid-write)
  • the bust pre-dated v1.4.0's multi-manifest restore fix and the manifest no longer exists
  • files were moved into archived/ manually
ccaudit reclaim            # restore orphans whose source path is absent
ccaudit reclaim --dry-run  # list what would be reclaimed, no filesystem changes

Safety invariant: reclaim never overwrites an existing file at the inferred source path. If the source already exists, the orphan is skipped with a warning.

Note: if the manifest still exists, ccaudit restore is the right tool: it walks all manifests including older ones. reclaim is the rescue path for files whose manifest is gone.

Purge archive (v1.5)

Over time ~/.claude/ccaudit/archived/ accumulates: items you reclaimed back to source, items whose source path has since been re-created from scratch, plus stale manifest entries where the archive file has already been moved. ccaudit purge-archive drains that directory on demand.

ccaudit purge-archive            # same as --dry-run (default)
ccaudit purge-archive --dry-run  # classify; zero filesystem changes
ccaudit purge-archive --yes      # execute the classified plan (destructive)
ccaudit purge-archive --json     # structured envelope, combinable with --yes

Classification per manifest-union archive op:

  • Reclaim - archive exists, source path is free. The archive is moved back to source (same shared mover as reclaim).
  • Drop / source_occupied - archive exists, source path is already occupied. The archive is unlinked. The source file is never overwritten.
  • Drop / stale_archive_missing - archive is already gone, source exists (already-restored or test residue). No disk mutation; a follow-up manifest op is still appended so restore listings stop surfacing it.
  • Skip / both_missing - both paths are absent. Preserved for diagnosis; never auto-resolved.

Scope is archive ops only. Flag ops (memory frontmatter) and MCP disable ops (the name → ccaudit-disabled:name key-rename written by bust) are untouched by this command. Each executed mutation is recorded as an append-only archive_purge op in a fresh purge-<ts>-<rand>.jsonl manifest - the original archive op stays intact for audit. Real purge requires an explicit --yes; there is no prompt fallback. Failures on individual items are reported in purge.failures[] but do not abort the batch.

See docs/JSON-SCHEMA.md § Purge for the purge.summary / purge.failures[] envelope contract.


Audit trail

Every ccaudit invocation is appended to ~/.claude/ccaudit/history.jsonl (mode 0o600, parent directory 0o700). The file uses JSONL format: one JSON object per line, with a header record written once on first use followed by one entry per invocation.

Schema version: history_version: 1. Future readers must refuse files with a higher version number.

{"record_type":"header","history_version":1,"ccaudit_version":"1.4.0","created_at":"2026-04-13T08:00:00.000Z","host_os":"darwin","node_version":"v22.0.0"}
{"record_type":"entry","ts":"2026-04-13T08:19:58.000Z","argv":["--dry-run"],"command":"dry-run","exit_code":0,"duration_ms":412,"cwd":"/home/user/project","privacy_redacted":false,"result":{"planned_archive":146,"planned_disable":4,"planned_flag":6,"checkpoint_hash":"abc123"},"errors":[]}

Each entry's result field contains per-command structured data. See docs/JSON-SCHEMA.md § History audit trail for the full per-command shapes.

Opt-out: set CCAUDIT_NO_HISTORY=1 to disable all history writes. The env var is checked before any filesystem work; no directory is created.

Privacy: --privacy redacts cwd and any path-shaped strings in result.

Rotation: if history.jsonl exceeds 10 MB, a one-time advisory is printed to stderr suggesting manual archival:

ℹ️  ccaudit history.jsonl is >10 MB; archive it with: mv ~/.claude/ccaudit/history.jsonl{,.$(date +%Y%m%d).bak}

Environment variables

Variable Effect
NO_COLOR Disable ANSI colors (equivalent to --no-color).
CCAUDIT_NO_HISTORY=1 Disable all writes to history.jsonl. Checked before any filesystem work.
HOME / USERPROFILE Override the home directory used to locate ~/.claude/ state. Useful in test environments.
XDG_CONFIG_HOME Checked as a fallback base for config lookup on Linux/macOS when HOME is unset.

Output modes

JSON

All report commands emit a JSON envelope with meta and command-specific payload data:

npx ccaudit-cli ghost --json | jq .

CSV

CSV is RFC 4180 compliant and works well for spreadsheets and pipelines:

npx ccaudit-cli ghost --csv > ghosts.csv
npx ccaudit-cli mcp --csv > mcp.csv

Quiet / TSV

Quiet mode emits machine-friendly TSV without decoration:

npx ccaudit-cli ghost --quiet
npx ccaudit-cli trend --quiet

NO_COLOR

ccaudit honors NO_COLOR and --no-color.


Relationship to ccusage

Tool Question
ccusage What did you spend?
ccaudit What are you loading vs. actually using, and what can you safely reclaim?

How it works

ccaudit reads the JSONL session logs that Claude Code already writes to ~/.claude/projects/. It cross-references what is defined (agents in ~/.claude/agents/, skills in ~/.claude/skills/, MCP servers in ~/.claude.json and project .mcp.json, memory in .claude/ directories, commands in ~/.claude/commands/ and .claude/commands/, hooks in .claude/settings.json) against what is actually invoked. Everything is local. No data leaves your machine.

Technical decisions:

  • Zero runtime dependencies. Everything is bundled via tsdown (Rolldown-based, Rust). The published package has no dependencies field.
  • JSONL parsing uses node:readline, not a streaming JSON library. Lines that fail valibot's safeParse are silently skipped; Claude Code's format is not formally documented and varies between versions.
  • Token estimation (v1.4.0 evidence-based formulas):
    • Skills (lazy): 15 + ceil(description_chars / 4), capped at 250 description chars. Only the name and description enter Claude Code's startup invocation index per Anthropic's published loading documentation.
    • Agents (eager): 30 + ceil(description_chars / 4), no cap. The full description enters the Task tool schema at session start.
    • MCP servers (regime-aware): measured per-server in eager mode (recorded from actual tool_use token counts in session logs). In deferred mode (auto-detected when Claude Code ≥ v2.1.7 and MCP tools exceed ~10% of context), collapses to a single ToolSearch overhead of ~8.7k tokens. Override with --regime eager|deferred.
    • Memory: file-size heuristic (1 token ≈ 4 chars) with recursive @-import resolution (depth ≤ 5). Auto-memory files at ~/.claude/projects/<slug>/memory/MEMORY.md are included automatically and capped at 25KB (6,250 tokens) to match Claude Code's own truncation limit.
    • Commands: frontmatter-aware. min(60 + description_chars / 4, 90) tokens when frontmatter is present; falls back to file-size / 4 otherwise. See packages/internal/src/token/command-estimator.ts for the exact formula.
    • Hooks: inject-capable upper bound up to 2,500 tokens per fire event. Advisory by default. Not aggregated into the headline total unless --include-hooks is passed. Rationale: the pessimistic upper-bound dominates the total and masks the signal from the other categories. Most hook configs are small; this upper-bound is the worst case.
  • Remediation is gated behind a hash-based checkpoint. You must --dry-run first. The dry-run writes a checkpoint with a SHA-256 hash of the current inventory. --dangerously-bust-ghosts compares the current hash against the checkpoint and refuses to proceed if anything has changed since.
  • Nothing is deleted. Agents and skills are moved to an archive directory. MCP servers are key-renamed in the JSON (ccaudit-disabled:name). Memory files get a frontmatter flag.
  • mtime is preserved on memory files. Bust and restore preserve the original mtime of memory files so the staleness signal (used by ccaudit's memory scanner) survives flag/unflag cycles.

Limitations:

  • Token estimates are evidence-based heuristics. The actual token count depends on the model's tokenizer. For most files this is within 10–15% of actual.
  • Hook token estimates are pessimistic upper-bounds (2,500 tokens/fire for inject-capable hooks, zero for non-inject-capable hooks whose config does not enter model context). Real costs depend on the hook script's output.
  • The "ghost" classification uses a 7-day window by default. An agent invoked once 8 days ago is technically a ghost. The --since flag lets you widen the window, but the default is deliberately aggressive because most ghost inventory is truly abandoned.
  • Windows support: JSONL paths use forward slashes but tinyglobby handles normalization. Not tested on Windows.

Framework detection

Teams that install GSD, SuperClaude, or n-wave drop dozens of agents at once. Before v1.3.0 those showed up as 12 or 20 unrelated-looking rows in ccaudit ghost and it was easy to archive half a framework without realising the other half was still active. v1.3.0 groups related agents so you see GSD · 12 ghost members · ~58k tokens instead.

The 3-tier algorithm

Every agent and skill flows through three tiers of detection, in order. The first tier that matches wins; later tiers do not override earlier ones.

  1. Tier 1: curated list. ccaudit ships a hand-maintained registry of well-known Claude Code frameworks (gsd, superclaude, nwave, superpowers, ralph-loop, agent-council, greg-strategy, ideabrowser, gstack, hermes). An item matches a curated framework when its filename starts with one of the framework's declared prefixes (case-insensitive, followed by an alphanumeric character) or its path contains one of the framework's declared folder segments or (for frameworks that ship items without a consistent prefix, like gstack) its name is in the framework's knownItems[] list and at least three known items are present in the inventory.
  2. Tier 2: heuristic prefix clustering. Items that do not match Tier 1 have their prefix extracted via name.split(/[-:_]/)[0], lowercased. The prefix must be at least 3 characters long and must not appear in STOP_PREFIXES (api, app, ui, test, user, data, util, etc.). Two or more items sharing the same non-stopped prefix form a heuristic cluster; single items stay ungrouped.
  3. Tier 3: ungrouped. Anything that matched neither tier is listed in the flat inventory like before.

Domain folders are NOT frameworks

Critical negative finding. Folder names like engineering/, design/, marketing/, testing/, sales/, integrations/, strategy/, project-management/, support/, paid-media/, spatial-computing/, examples/, scripts/, product/, specialized/, game-development/, agents/, and skills/ are domain organisation folders, not frameworks. ccaudit refuses to group them, even if they happen to cluster by filename. This is enforced twice: once by gating folder-segment matches through the curated list only (Tier 1), and once by an explicit DOMAIN_STOP_FOLDERS list that Tier 2 consults. If you see a folder name that looks like it should be a framework but is listed above, that is deliberate.

Flag reference

Flag Default Effect
--verbose / -v off Expand each framework row into a tree of its members; used members collapse to a + N used members line.
--no-group-frameworks off Disable grouping entirely. Output reverts to the v1.2.1 layout byte-for-byte (no Frameworks section).
--force-partial off Bypass framework-as-unit protection and archive ghost members of partially-used frameworks. Applies to both --dry-run and --dangerously-bust-ghosts; both runs must use the same value.

Without --force-partial, ccaudit ghost --dangerously-bust-ghosts will skip ghost members of any framework that still has at least one used member. You will see a yellow warning and a PROTECTED section in the change plan. This is intentional. Busting half of GSD while the planner is in use would break the framework. --force-partial is an explicit opt-in override.

ccaudit computes a status for each framework group:

  • fully-used: all members were invoked within the --since window. Nothing to protect, nothing to archive.
  • partially-used: at least one member is active and at least one is a ghost. Ghost members are protected by default (skipped during bust). This is the only status that triggers protection.
  • ghost-all: no members are active. The entire framework is eligible for archival with no special protection.

These values appear in --json output under each framework group's status field.

Scope: only agents and skills are candidates for framework detection. MCP servers and memory files always appear in the ungrouped inventory regardless of their names.

Heuristic display names: Tier 2 groups auto-generate their display name by title-casing the detected prefix (e.g., items prefixed quark- display as "Quark").

Flag interaction: --force-partial has no effect when --no-group-frameworks is active, because framework protection is already disabled. The CLI emits a warning if you combine them.

Contribute a framework

If you maintain or use a framework that isn't in the curated list, open a PR against packages/internal/src/framework/known-frameworks.ts. Each entry is validated at load time by valibot and must include:

Field Type Notes
id string Stable lowercase id (gsd, superclaude, gstack).
displayName string User-facing name.
description string One-line description.
prefixes string[] Filename prefixes including separator (e.g. 'gsd-').
folders string[] Folder segment names, no slashes (e.g. 'superpowers').
knownItems string[] (optional) For frameworks without a consistent prefix, like gstack.
categories ('agent' | 'skill' | 'command' | 'mcp-server' | 'memory')[] Item categories the framework ships.
source string URL to the framework or 'unverified'.
source_type 'curated' Literal, always 'curated'.

Please do NOT submit generic prefixes (anything that would collide with STOP_PREFIXES) or domain folders (anything listed in the negative finding above). If you're not sure, open an issue first.

Declaration order in the registry is authoritative: the first matching entry wins. Place more-specific entries before more-general ones to avoid shadowing.


Version

  • Current package version: 1.5.1
  • Build source of truth: apps/ccaudit/package.json and apps/ccaudit/src/_version.ts

Roadmap

Items planned for upcoming releases. No firm dates - order may shift.

Interactive process killer

An interactive TUI for forcefully terminating running Claude Code instances, mirroring the ghost selection picker (--interactive). Lists every live claude process the user has access to with token usage, runtime, and project context, lets you multi-select with the same keybindings as the archive picker, and signals selected processes (TERM by default, KILL on confirm). For when one rogue session is pinning context across half your machine.

v1.6 stable CLI/JSON API contract

v1.6 will freeze a public JSON CLI surface scoped to embedding tools and downstream consumers. Stability guarantees external consumers will rely on:

  • TUI behavior is not part of the contract.
  • Human-rendered tables are not part of the contract.
  • Hidden environment variables are not part of the contract.
  • Internal TypeScript functions are not part of the contract.
  • Path-shaped canonical IDs are not public unless explicitly declared.

The envelope follows the existing --json shape (see docs/JSON-SCHEMA.md) with an apiVersion field for forward compatibility. Subcommand names and the full contract surface will land in the v1.6 release notes.

A new frontend - surprise

Something visual is in the works. More when it lands.


Author

fabio-dee


Disclaimer

ccaudit is provided AS-IS with NO WARRANTY. Anthropic does not endorse this tool. Use at your own risk.

About

Every Claude Code session loads every agent, skill, and MCP server you configured, used or not. Audit, archive, restore anytime.

Resources

License

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors