On July 30 my claude doctor output told me two contradictory things at once. Four lines apart, it said Last update attempt: failed (install_failed) — 2026-07-30 and No installation issues found. I had been working all day on a build that thought it was keeping itself current and was not. That contradiction is the whole argument for auditing your Claude Code setup: nothing in it will announce that it has quietly stopped being good. It just keeps working, slightly worse, until you go looking.
So I went looking, by hand, with commands you can copy. The audit found roughly 21,000 tokens of context I was paying for on every session before typing a character. And the part that surprised me most cuts against every write-up on this topic: my CLAUDE.md files were fine. The bloat was somewhere nobody was pointing.
Here is the shell-side output that started it, verbatim from my Mac:
Claude Code doctor
Running: native (2.1.220)
Commit: 4073f59596e2
Platform: darwin-arm64
Path: /Users/mejba/.local/share/claude/versions/2.1.220
Config install method: native
Search: OK (bundled)
Auto-updates: enabled
Auto-update channel: latest
Last update attempt: failed (install_failed) — 2026-07-30
No installation issues found.

There Are Two Doctors, and Only One of Them Audits Anything
The naming trips everyone up, so first the split.
claude doctor from your shell is a read-only install check. Version, commit, platform, install path, auto-update status. It reads settings in the current directory without a trust prompt and never touches your skills, MCP servers, hooks, or memory files. Run it when Claude Code misbehaves and you want to rule out the boring causes in one second.
/doctor inside a session is the actual audit. It reads settings, skills, MCP servers, plugins, hooks, and memory files, proposes changes, and asks before applying them. /checkup is an alias.
The capability landed incrementally, and the changelog tells the story: v2.1.200 changed the default permission mode to Manual, v2.1.203 added /doctor proposals to trim checked-in CLAUDE.md files of content Claude could derive itself, v2.1.206 taught it to report an externally managed launcher at ~/.local/bin/claude, and v2.1.207 is where /doctor became a full setup checkup that can diagnose and fix, with /checkup added as the alias. v2.1.212 added claude auto-mode reset for restoring the default auto-mode configuration.
One honesty note: /doctor is interactive, so I could not pipe it into an article. Everything below is the shell-side check plus each audit category reproduced manually, so you see raw numbers instead of a summary you have to trust.
Why This Audit Matters Right Now
The doctor push did not arrive in a vacuum. Alongside the Claude 5 model family, Anthropic published context-engineering guidance revealing that it removed more than 80 percent of Claude Code's system prompt for Opus 5 and Fable 5, with no measurable loss on its coding evaluations. Reading its own transcripts, Anthropic found stacked, conflicting instructions ("leave documentation as appropriate" in one layer, "DO NOT add comments" in another) that the model burned attention reconciling before writing any code.
The replacement philosophy: judgment over rules, progressive disclosure over upfront context, tool descriptions over system-prompt repetition, and a lightweight CLAUDE.md that carries repository gotchas rather than facts derivable from the repo. I dug into how the new flagship behaves under that philosophy in my Claude Opus 5 benchmarks breakdown.
The uncomfortable implication for anyone who has built on Claude Code for a year: the elaborate context scaffolding you wrote was a workaround for models that needed it. On current models it is a tax. The doctor tooling exists because Anthropic knows most of us will not audit that scaffolding voluntarily.
What the Audit Found on My Machine
My environment is a fair stress test: 35 personal skills, ten plugin marketplaces, MCP servers, hooks, a global and a project CLAUDE.md, and a large content agent. Findings in the order I would tell you to check.
Finding 1: the silently failed update
The opening block is the finding. A failed auto-update does not crash anything; the build just gets older while you ship. If your claude doctor shows Last update attempt: failed, run claude install latest and check the reported path. Mine also showed a related smell: which -a claude resolved /Users/mejba/.local/bin/claude multiple times, duplicate PATH entries pointing at the same launcher, exactly the class of thing the 2.1.206 externally-managed-launcher check was added to surface.
Finding 2: my CLAUDE.md files were not the problem
Everyone says to slash your CLAUDE.md, so I measured mine first:
$ wc -w -c ~/.claude/CLAUDE.md
295 2083 /Users/mejba/.claude/CLAUDE.md
295 words global, 658 words in the project file, roughly 1,700 tokens combined. Real, but noise. Both files are already pointers rather than content dumps: workflow rules, security constraints, hard conventions. If your CLAUDE.md is already lean, trimming it further is theater. The savings were somewhere else, and I only found them because I kept measuring instead of stopping at the obvious suspect.
Finding 3: 21,000 tokens of skill descriptions I never asked for
Every skill's name and description frontmatter is injected into every session's system prompt whether you ever invoke the skill or not. The full SKILL.md body loads only when the skill fires. So I summed the always-on tier across every SKILL.md on disk:
| Source | Skills | name+description chars | Estimated tokens |
|---|---|---|---|
Personal (~/.claude/skills/) |
35 | 12,808 | ~3,200 |
Plugin (~/.claude/plugins/cache/) |
191 | 71,932 | ~18,000 |
| Total | 226 | 84,740 | ~21,200 |
The heaviest offenders were four Higgsfield image-generation skills at roughly 1,050 tokens between them, loaded into every session, including the ones where I am debugging a Laravel migration and could not care less about product photography.
Run it yourself:
python3 - <<'PY'
import re, glob, os
def fm(p):
t = open(p, encoding='utf-8', errors='ignore').read()
m = re.match(r'^---\s*\n(.*?)\n---\s*\n', t, re.S)
return m.group(1) if m else ''
def name_desc(f):
n = re.search(r'^name:\s*(.*)$', f, re.M)
d = re.search(r'^description:\s*(.*(?:\n(?!\w+:).*)*)', f, re.M)
return (n.group(1).strip() if n else '', d.group(1).strip() if d else '')
rows = []
for p in glob.glob(os.path.expanduser('~/.claude/skills/*/SKILL.md')):
n, d = name_desc(fm(p))
rows.append((os.path.basename(os.path.dirname(p)), len(n) + len(d)))
rows.sort(key=lambda r: -r[1])
total = sum(r[1] for r in rows)
print(f"{len(rows)} skills, {total} chars, ~{total//4} tokens always-on")
for name, c in rows[:10]:
print(f" {name:<34} {c:>5} chars ~{c//4} tok")
PY
Two caveats I will not bury: chars divided by four is an estimate, not a tokenizer result; run /context in a live session for the real breakdown. And plugin enablement is per-project, so treat 21K as my setup's ceiling, not a universal constant.
Finding 4: five skills with empty descriptions
While parsing frontmatter, five of my SEO skills returned zero description characters. Their SKILL.md files opened with an HTML comment instead of YAML frontmatter, so the parser read an empty description. No error, no warning. But a skill's description is the only signal Claude has for deciding whether to load it, which means those five skills had been effectively un-triggerable for months. They still worked when invoked by name; they just never got picked on their own. Any skill returning 0 chars from the script above is a skill Claude cannot reason about.
Finding 5: the 24,700-token agent definition nobody audits
The biggest single object in my environment:
$ wc -w -c .claude/agents/aria.md
15018 98733 .claude/agents/aria.md
15,018 words. About 24,700 tokens. My content agent, written in the Opus 4.x era when exhaustive upfront instruction was how you got consistent output. Under the Claude 5 guidance it is the exact pattern Anthropic deleted from its own prompt: rules where judgment now suffices, one monolithic file where progressive disclosure belongs.
Put the two numbers side by side. The CLAUDE.md files every guide told me to trim: 1,700 tokens. The agent file nobody mentions: 24,700. My audit instinct was aimed at six percent of the problem.
What I Changed, in Order
- Fixed the install.
claude install latest, thenclaude doctoragain to confirm. - Fixed the empty descriptions. Two sentences each: what the skill does, when to invoke it, stop.
- Pruned marketplaces, not skills. With 191 plugin skills across ten marketplaces, removing the two marketplaces I had not invoked in a month was a far bigger lever than trimming words inside any one description.
- Rewrote the four Higgsfield descriptions. Mode lists and model names moved into the skill bodies, which only load on invocation.
- Split the agent definition. The 15,018-word file became a short index pointing at sub-files for brand voice, SEO rules, and publish gates, loaded only when the task needs them. This is where the real reduction came from, and it pairs with the in-session hygiene I covered in Claude token limits and context hygiene.
The Re-Audit, Two Weeks Later
Numbers from re-running the same script today, August 14:
claude doctornow reportsLast update attempt: success → 2.1.232 (2026-08-14). The install fix held.- Personal skills: 35, now ~13,800 chars (~3,400 tokens). Slightly up, because honest descriptions are longer than empty ones.
- Plugin skills: 197 files, ~65,900 chars (~16,500 tokens), down from ~18,000 despite more skills, because the fat descriptions got trimmed.
- Four of the five empty descriptions are fixed. One (
seo-programmatic) is still empty, which I only know because I re-ran the script. Audits are not one-time events. which -a clauderesolves twice now instead of three times. PATH hygiene is a lifestyle.
The honest cost claim: I cannot give you a clean before/after dollar figure, because I changed six things in one afternoon and any attribution would be invented. What I can state from the mechanism is that the always-on block is the floor of every cached prompt prefix, and editing anything upstream of it re-bills the whole prefix. The effect you notice first is not the bill anyway; it is that long sessions stay coherent deeper, which matches what I found stress-testing Claude Code's 1M-token context over 30 days: degradation starts earlier when the fixed overhead is larger. If you want the aggressive end of the trimming spectrum, the caveman token-optimization skill is the same instinct applied to output instead of setup.
Quick Answers
What does /doctor do in Claude Code?
It runs a full setup checkup inside a session, reading settings, skills, MCP servers, plugins, hooks, and memory files, then proposes fixes you approve before anything changes. /checkup is an alias. It became a full diagnose-and-fix checkup in v2.1.207.
Is claude doctor the same as /doctor?
No. The shell command is a read-only install check (version, path, update status). The slash command is the audit that can also fix things. Run the first to rule out install problems, the second to audit context.
How many tokens do Claude Code skills consume?
Name and description load every session; the body loads on invocation. My 226 installed skills came to roughly 21,000 estimated always-on tokens before trimming. Measure yours with the script above, then confirm with /context.
Should I trim CLAUDE.md?
Only if it restates things Claude could derive from the repo. Keep gotchas, conventions, and constraints. Mine totaled 1,700 tokens and needed nothing; the real weight sat in skill descriptions and a 24,700-token agent file.
Run /doctor tonight, read the findings, and press nothing. Then run the frontmatter script. Twenty minutes, and something in your setup is broken; the only question is whether it is the update that has been failing or the skill that has been invisible since February. If you would rather have a second pair of eyes on an agent environment that has grown for a year, an audit like this one is exactly what I do for client teams: get in touch and bring your /context output.
Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.