This article is one chapter of my book Delphi in all its glory – AI-assisted development, the fifth book of the series.
Skills are addictive. Each one solves a real annoyance, each one takes ten minutes to write, and none of them feels expensive. Then one day you run /doctor and it tells you that your 47 skills are eating 5,700 tokens before you have typed a single word.
That number is real, but the conclusion most people draw from it is wrong. Here is what is actually happening.
What gets loaded, and when
At startup Claude Code builds a listing: every skill’s name plus its description. If the skill also has a when_to_use field, that text is appended to the description and counts as part of it. The body of SKILL.md – the instructions, the checklists, the 300 lines of procedure you are so proud of – is not loaded until the skill actually runs.
So a skill’s standing cost is the length of its description. Nothing else. A 20-line description costs more, every single session, than a 900-line skill body costs at rest.
On my machine: 47 skills, 43 of them listed, 22,769 characters of name plus description. Roughly 5,700 tokens.
First find out which problem you have
The listing has a character budget. It defaults to 1% of the model’s context window and is set by skillListingBudgetFraction in settings.json. When the listing does not fit, Claude Code does something clever: it keeps every skill name, always, and starts throwing away descriptions – beginning with the skills you invoke least.
That single behaviour splits the situation into two completely different problems, and the fix is different for each. Before you touch anything, find out which one you have.
If your listing fits inside the budget, you are paying for every character of it, every session. Trimming descriptions is straightforward profit.
If it overflows, the excess never reaches the model and never costs you a token – so trimming saves you nothing. What you have instead is a routing problem. A skill whose description got dropped is a skill Claude sees as a bare name, with no idea when to use it. That is why your carefully written /light-ref-Threading skill never fires on its own: the sentence that was supposed to trigger it was deleted before Claude ever saw it. There, the win from trimming is not tokens – it is deciding which descriptions survive, instead of leaving that choice to an invocation counter.
I assumed I was in the second case. I was not. At 22,769 characters with the budget at 1.5%, every description arrived intact – including the ones for skills I have never once invoked, which are exactly the ones that get cut first. Nothing was being discarded. I was paying the full 5,700 tokens.
Guessing costs you either way, so measure: /context reports the listing’s real size after the budget is applied, which is what the model actually receives. If that number matches the raw size of your description text, you fit. If it is smaller, you are overflowing and losing triggers.
Three levers
1. skillOverrides in settings.json. Four states per skill:
| Value | What Claude sees | In the / menu |
|---|---|---|
| “on” | name + description | yes |
| “name-only” | name only | yes |
| “user-invocable-only” | nothing | yes |
| “off” | nothing | no |
“name-only” is the sweet spot for a skill you keep but rarely need: Claude still knows it exists and can reach it by name, and it costs almost nothing. “user-invocable-only” hides it from Claude completely while you can still type /name. Anything absent from the list behaves as “on”.
You do not have to hand-edit the file. Open the /skills menu, highlight a skill, press Space to cycle the state, Enter to save.
2. disable-model-invocation: true in the skill’s own frontmatter – the flag from earlier in this book (see the chapter “The YAML header options”). Same effect as “user-invocable-only”, but written in the skill file instead of in settings. Use it when the skill file is yours; use skillOverrides when it is not (a shared project repo, a plugin).
3. Trim the description. Per skill, description plus when_to_use is truncated at 1,536 characters (skillListingMaxDescChars). Long trigger-phrase lists are the usual culprit. Keep the distinct phrasings, drop the near-duplicates, and put the key use case first – if the budget bites, the front of the text is what survives.
And if you genuinely want more room, raise skillListingBudgetFraction (0.019 = 1.9% of the context window).
What it looks like after
Four fat descriptions trimmed, 21 never-used skills set to “name-only”. The listing went from 22,769 characters to 13,280 – a 42% cut, and since I was under budget, that is about 2,370 tokens back in every session from now on.
I also raised skillListingBudgetFraction from 1.5% to 1.9% before I had measured anything. That did precisely nothing, because the budget is a ceiling and I was never touching it. It is a good illustration of the rule: the three levers all work, but only one of them addresses whichever problem you actually have. Measure first.
/doctor estimates the listing’s cost and names the biggest contributors – start there.
Sources: Claude Code documentation, https://code.claude.com/docs/en/skills (sections “Control who invokes a skill”, “Override skill visibility from settings”, “Skill descriptions are cut short”, and the frontmatter reference table). All numbers measured directly on the author’s machine, Claude Code 2.1.241.
This was one chapter of a whole book
You just read one chapter of Delphi in all its glory – AI-assisted development: more than 400 pages about Claude Code and AI for Delphi programmers. Written by a Delphi programmer, with the failures documented next to the wins.