~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.
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"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)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.
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
/contextalready do this?"/contextshows 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.
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).
| 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.
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% │
└──────────────────────────────────────────────────────────────────────────────┘
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.
----------------------------------------------
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
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┌─ 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.
┌─ 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 ] │
│ │
└──────────────────────────────────────────────────────────────────────┘
| 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 |
1–6 |
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.
| 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 |
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, andrestorehonor the full report output matrix.install-skillalso 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.--cialso implies--yes-proceed-bustingonghost --dangerously-bust-ghosts.
| 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. |
| 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. |
| 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. |
| 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. |
| 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.
| Flag | Short | Description | Type |
|---|---|---|---|
--dry-run |
List orphans without touching the filesystem | bool |
Shared output flags (--json, --quiet, --verbose, --no-color) also apply.
| 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 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-runWhat 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.
--dangerously-bust-ghosts is blocked until a matching dry-run checkpoint exists.
The actual confirmation ceremony is:
[1/3] This will modify your Claude Code configuration. Proceed? [y/N][2/3] Are you sure? This archives agents, disables MCP servers, and flags memory files. [y/N][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.
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 bustsFull-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.
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 changesSafety 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.
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 --yesClassification 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.
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}| 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. |
All report commands emit a JSON envelope with meta and command-specific payload data:
npx ccaudit-cli ghost --json | jq .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.csvQuiet mode emits machine-friendly TSV without decoration:
npx ccaudit-cli ghost --quiet
npx ccaudit-cli trend --quietccaudit honors NO_COLOR and --no-color.
| Tool | Question |
|---|---|
| ccusage | What did you spend? |
| ccaudit | What are you loading vs. actually using, and what can you safely reclaim? |
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
dependenciesfield. - JSONL parsing uses
node:readline, not a streaming JSON library. Lines that fail valibot'ssafeParseare 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_usetoken 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.mdare 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 tofile-size / 4otherwise. Seepackages/internal/src/token/command-estimator.tsfor 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-hooksis 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.
- Skills (lazy):
- Remediation is gated behind a hash-based checkpoint. You must
--dry-runfirst. The dry-run writes a checkpoint with a SHA-256 hash of the current inventory.--dangerously-bust-ghostscompares 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
mtimeof 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
--sinceflag 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.
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.
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.
- 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'sknownItems[]list and at least three known items are present in the inventory. - 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 inSTOP_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. - Tier 3: ungrouped. Anything that matched neither tier is listed in the flat inventory like before.
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/, andskills/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 explicitDOMAIN_STOP_FOLDERSlist 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 | 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--sincewindow. 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.
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.
- Current package version: 1.5.1
- Build source of truth:
apps/ccaudit/package.jsonandapps/ccaudit/src/_version.ts
Items planned for upcoming releases. No firm dates - order may shift.
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 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.
Something visual is in the works. More when it lands.
ccaudit is provided AS-IS with NO WARRANTY. Anthropic does not endorse this tool. Use at your own risk.
