Dogfooding the four read commands (posture, tokens, manifest, whats-active) surfaced four defect classes, all in the seam between what a command template promises and what the scanner behind it actually does. M-BUG-40, fifth arm: posture wrote four temp files it could never read back. #49 closed the $$/cross-block class in four commands, but posture survived it — and so did the guard written to prevent exactly this. The guard compared each $$ path to the block that created it, so a path written once and then read via prose had no second occurrence to flag. Measured live: written from PID 21614, read attempted from PID 23772. The invariant is now blanket (no $$ in any temp path), which also caught fix.md and feature-gap.md. M-BUG-43: 6 of 7 scanners write their payload to stdout when --raw/--json is set even when --output-file was given, and the templates redirected only stderr. Measured: posture 255 182 B, whats-active 35 922 B, drift 28 316 B, manifest 23 825 B, tokens 8 768 B. fix and feature-gap never read the file they wrote, so both recovered one letter grade from a quarter-megabyte dump. tokens swallowed --json and --with-telemetry-recipe: documented, never threaded, so --json returned the humanized payload where the docs promise byte-stable v5.0.0 output. M-BUG-42: manifest's render contract asked for {load}; the payload carries loadPattern, so the Load column rendered blank for all 96 rows. Four new tests (1449 -> 1453), each verified red before the fix. The render-contract test checks {field} names against a live payload from a fixture, since a hardcoded key list would drift. Frozen v5.0.0 snapshots untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VGCk9o27eWo9uXLjkZTXEq
5.1 KiB
| name | description | argument-hint | allowed-tools | model |
|---|---|---|---|---|
| config-audit:manifest | Show ranked token-source manifest — every CLAUDE.md, rule, agent, skill, output style, MCP server, and hook ordered DESC by estimated tokens, each tagged with its load pattern (always-loaded vs on-demand vs external), plus an always-loaded subtotal | [path] [--json] | Read, Bash | sonnet |
Config-Audit: Manifest
Produce a ranked, single-table view of every token source loaded for a given repo path. Where whats-active shows separate tables per category, manifest collapses everything into one ordered list — making it easy to see what's costing the most regardless of category.
Every source is tagged with its load pattern, derived from the published Claude Code loading model:
- always — enters context every turn before you type (project/user CLAUDE.md, unscoped rules, agents, output styles, MCP tool schemas). This is the cost that matters most: it is paid on every request.
- on-demand — loaded only when needed (skill bodies on invoke, path-scoped rules on a matching file read).
- external — runs outside the context window entirely (hooks).
The always-loaded subtotal is the headline number. Sources are component-level (a plugin contributes via its skills/rules/agents/output styles/hooks/MCP, each listed once — there is no coarse "plugin" roll-up, which would double-count).
UX Rules (MANDATORY — from .claude/rules/ux-rules.md)
- Never show raw JSON or stderr output. Always use
--output-file+2>/dev/null. - Narrate before acting. Tell the user what you're about to do.
- Read, don't dump. Read the JSON file and render a formatted table.
- End with context-sensitive next steps.
Implementation
Step 1: Parse $ARGUMENTS
First non-flag argument is the path (default .). Recognized flags:
--json— emit raw JSON instead of the rendered table.--raw— pass-through to the scanner; accepted for CLI surface consistency with the other config-audit commands. The manifest CLI is data-table only (no findings prose), so--rawis a no-op here, but the flag is still threaded through so users get uniform behaviour across--raw.
Step 2: Run the CLI silently
Tell the user: "Building token-source manifest for <path>..."
RAW_FLAG=""
if echo "$ARGUMENTS" | grep -q -- "--raw"; then RAW_FLAG="--raw"; fi
node ${CLAUDE_PLUGIN_ROOT}/scanners/manifest.mjs <path> --output-file /tmp/config-audit-manifest.json $RAW_FLAG >/dev/null 2>/dev/null; echo $?
Exit code handling:
0→ continue3→ tell user: "Couldn't read configuration. Check that the path exists and is a directory." Stop.
Step 3: If --json was requested, cat the file and stop
cat /tmp/config-audit-manifest.json
Do NOT render the table in JSON mode.
Step 4: Read JSON and render
Use the Read tool on /tmp/config-audit-manifest.json. Extract meta.repoPath, total, summary, and sources[]. Lead with the always-loaded subtotal (the headline), then render the top 20 sources (or fewer if the manifest is shorter):
**Token-source manifest for `<repoPath>`** — ~{total} tokens total
- 🔴 **~{summary.always.tokens} tokens enter context every turn** before you type ({summary.always.count} always-loaded sources)
- 🟡 ~{summary.onDemand.tokens} tokens on-demand ({summary.onDemand.count} sources — loaded only when invoked / matched)
- ⚪ ~{summary.external.tokens} tokens external ({summary.external.count} sources — hooks, run outside context)
| Rank | Kind | Name | Source | Tokens | Load |
|------|------|------|--------|--------|------|
| 1 | {kind} | `<name>` | {source} | ~{estimated_tokens} | {loadPattern} |
| ... | ... | ... | ... | ... | ... |
_Load column: **always** / **on-demand** / **external**. Append `°` when `derivationConfidence` is `inferred` (no primary-doc row pins it exactly)._
_Estimates assume ~4 chars/token (Claude ballpark). Real token count varies ±15%._
If sources.length > 20, follow the table with: "Showing top 20 of {N} sources. Run with --json to see the full list."
When narrating, prioritize the always-loaded subtotal: a large always source is worse than an equally large on-demand one, because it is paid on every request. Call out any single always-loaded source that dwarfs the rest.
Step 5: Suggest next steps
**Next steps:**
- `/config-audit tokens` — prompt-cache token-hotspot patterns (cache-breaking, redundant perms, deep imports, MCP budget)
- `/config-audit whats-active` — same data grouped by category, with disable suggestions
- `/config-audit feature-gap` — what *could* improve here, grouped by impact
Tone (key on the always-loaded subtotal — the every-turn cost — not the grand total):
- High always-loaded (>40k): empathetic — "That's a heavy per-turn cost; it taxes every request before you've typed a word. Look at the largest always-loaded sources first."
- Moderate (10–40k): neutral — "Reasonable. Skim the top always-loaded sources to see if anything is unexpectedly large."
- Low (<10k): encouraging — "Tight setup. The model has plenty of room for the actual work each turn."