8 · Custom skills
Module 7 closed with “commands/ is the short form, skills/ is the full form”: this
module is that full form. A skill is a packaged instruction — a
folder containing a SKILL.md file — that Claude loads only when needed: like the cards of a
recipe book, which you consult only when you cook that dish. The difference from a simple command:
a skill can bring supporting files along, can decide who invokes it (you, Claude, or both), and
can activate on its own when the request concerns it. It is the mechanism by which you teach Claude your
trade without paying for it in context in every session.
Objectives
Section titled “Objectives”- Write a correct
SKILL.mdand know which frontmatter fields you really need. - Choose between
CLAUDE.md, a rule and a skill based on when the information must be in context. - Control who invokes a skill: you, Claude, or both.
- Run a skill in a subagent so it does not consume the main context.
- Find out whether a skill really works, instead of hoping it does.
Key concepts
Section titled “Key concepts”The advantage of skills is lazy loading. A skill’s body is loaded only when it is
used: long reference material costs almost nothing until it is needed. That is the substantial difference
from CLAUDE.md, which is loaded in full in every session.
So the criterion for choosing is about time, not content:
| Needed… | Goes in |
|---|---|
| Always, in every session | CLAUDE.md |
| When Claude touches certain files | A rule in .claude/rules/ with paths: |
| When that procedure is needed | Skill |
| Always, without exception, as code | Hook |
The signal that something has become a skill: you have a section of CLAUDE.md that has grown until it
became a procedure instead of a fact. Or you are pasting the same instructions into the chat for the third
time.
Two parts, one file. Every skill has a SKILL.md with YAML frontmatter between --- that tells Claude
when to use it, and markdown content with the instructions to follow. The directory name becomes the
command.
The body stays in context for the whole session. When a skill is invoked, its content enters the conversation as a single message and stays there. Claude Code does not re-read the file in later turns. Two practical consequences: every line is a recurring token cost, so the body must be kept concise; and guidance that must hold for a whole task should be written as standing instructions, not as steps to run once.
The description is the activation mechanism. At startup Claude sees only the names and descriptions of the
skills: it is on that text that it decides whether a skill is relevant. A vague description produces a skill that never
activates; one that is too broad produces a skill that always activates.
Where skills live
Section titled “Where skills live”| Level | Path | Applies to |
|---|---|---|
| Enterprise | See managed settings | Everyone in the organization |
| Personal | ~/.claude/skills/<nome>/SKILL.md |
All your projects |
| Project | .claude/skills/<nome>/SKILL.md |
This project only |
| Plugin | <plugin>/skills/<nome>/SKILL.md |
Wherever the plugin is enabled |
Skills are also loaded from .claude/skills/ directories nested under the working directory: when
Claude reads or edits a file in a subdirectory, the skills in that .claude/skills/ become
available. A package in a monorepo can thus provide its own skills, even if the session started
from the repo root.
Name conflict resolution is the one seen in module 7: enterprise > personal > project, skill beats command, plugins use a separate namespace.
The structure of a skill
Section titled “The structure of a skill”Directory.claude/skills/
Directorysecurity-review/
- SKILL.md required — overview and navigation
- checklist.md Detailed reference, loaded when needed
- examples.md Usage examples, loaded when needed
Directoryscripts/
- scan.py Utility script: executed, not loaded into context
Supporting files keep SKILL.md focused on the essentials. Long documentation, API specs or
collections of examples should not enter the context every time the skill starts. They should be referenced from
SKILL.md, so Claude knows what they contain and when to open them:
## Risorse aggiuntive
- Per i dettagli completi delle API, vedi [reference.md](reference.md)- Per esempi d'uso, vedi [examples.md](examples.md)(An “Additional resources” section pointing to reference.md for full API details and to examples.md for usage examples.)
Frontmatter reference
Section titled “Frontmatter reference”Starting from zeroFrontmatter and YAML in thirty seconds
The frontmatter is the little block of options enclosed between two --- lines
at the top of the file. It is written in YAML: chiave: valore (key: value) lines, and indentation
(the spaces at the start of a line) provides the structure. Two golden rules when you copy it: do not touch the spaces,
and do not use Tab — only spaces. Everything below the second --- is the normal content
of the file.
All fields are optional, with one clarification: if you omit description, the first
paragraph of the content is used — so something describing the skill is always there. Writing it
explicitly is still the right choice, because it is the text on which Claude decides when to activate it.
---name: my-skilldescription: What this skill doesdisable-model-invocation: trueallowed-tools: Read Grep---
Le istruzioni della skill...Boolean fields accept yes, no, on, off, 1 and 0 in any combination of upper and lower case,
as well as true and false.
Identity and activation
Section titled “Identity and activation”| Field | What it does |
|---|---|
name |
Name shown in lists. Default: the directory name |
description |
What the skill does and when to use it. If omitted, the first paragraph of the markdown is used. Put the main use case first: description and when_to_use together are truncated to 1,536 characters in the listing |
when_to_use |
Additional context on when to invoke it — trigger phrases, example requests. It adds to description and counts toward the same limit |
argument-hint |
Hint shown in autocomplete, e.g. [issue-number] or [filename] [format] |
arguments |
Named positional arguments, for $nome substitution. Accepts a space-separated string or a YAML list |
Who invokes it
Section titled “Who invokes it”| Field | What it does |
|---|---|
disable-model-invocation |
true prevents Claude from loading it on its own. For workflows you want to launch by hand with /nome. It also prevents preloading in subagents and running from a scheduled task. Default: false |
user-invocable |
false when only Claude should invoke it: it disappears from the / menu and typing /nome does not run it. For background knowledge the user should not launch. Default: true |
Tools and model
Section titled “Tools and model”| Field | What it does |
|---|---|
allowed-tools |
Tools Claude can use without asking permission, during the turn that invokes the skill. The permission lapses at your next message |
disallowed-tools |
Tools removed from the available pool while the skill is active. For autonomous skills that must never call certain tools, for example AskUserQuestion in a background loop |
model |
Model to use while the skill is active. It applies to the rest of the current turn and is not saved in the settings. Accepts the same values as /model, or inherit |
effort |
Effort level: low, medium, high, xhigh, max. The available levels depend on the model |
Running in a subagent
Section titled “Running in a subagent”| Field | What it does |
|---|---|
context |
fork runs the skill in a separate subagent context |
agent |
Which subagent type to use, when context: fork is set |
background |
Only with context: fork. false waits for the result in the turn that invoked the skill, instead of running it in the background. Default: true |
hooks |
Hooks that Claude Code registers when the skill is invoked, and that stay active for the rest of the session |
The value syntax of allowed-tools deserves a line, because it recurs throughout the course:
Read on its own pre-approves a whole tool; Bash(git add *) pre-approves the Bash tool only for
commands that start with git add — the pattern in parentheses narrows it, and the asterisk is the wildcard.
The space before * matters: Bash(git add *) approves git add src/, not git addqualcosa.
Two kinds of content
Section titled “Two kinds of content”Reference content — knowledge Claude applies to the current work: conventions, patterns, style guides, domain knowledge. It runs inline, so Claude uses it together with the context of the conversation.
---name: api-conventionsdescription: API design patterns for this codebase---
When writing API endpoints:- Use RESTful naming conventions- Return consistent error formats- Include request validationTask content — step-by-step instructions for a specific action: deploy, commit, code
generation. You usually invoke it yourself with /nome instead of letting Claude decide.
---name: deploydescription: Deploy the application to productioncontext: forkdisable-model-invocation: true---
Deploy the application:1. Run the test suite2. Build the application3. Push to the deployment targetArguments and dynamic context
Section titled “Arguments and dynamic context”Arguments work as for the commands in module 7:
$ARGUMENTS for everything, $ARGUMENTS[N] or $N by position.
Dynamic context !`comando` runs the command before the content reaches Claude and
replaces the placeholder with the output:
---name: pr-summarydescription: Summarize changes in a pull requestcontext: forkagent: Exploreallowed-tools: Bash(gh *)---
## Pull request context
!`gh pr diff`
## Your task
Riassumi la PR per un revisore che non ha seguito il lavoro.(The last line asks Claude to summarize the PR for a reviewer who has not followed the work.)
Visibility from settings
Section titled “Visibility from settings”skillOverrides controls a skill’s visibility from the settings, instead of from its
frontmatter. It is for skills whose SKILL.md you do not want to touch — typically those committed in a
shared repo.
| Value | Listed to Claude | In the / menu |
|---|---|---|
"on" (default) |
Name and description | Yes |
"name-only" |
Name only | Yes |
"user-invocable-only" |
Hidden | Yes |
"off" |
Hidden | Hidden |
{ "skillOverrides": { "legacy-context": "name-only", "deploy": "off" }}The /skills menu writes it for you: highlight a skill, press Space to cycle the states, Enter to
save. Plugin skills are not affected by skillOverrides: they are managed with /plugin.
Guided example: from the first skill to one that works
Section titled “Guided example: from the first skill to one that works”-
Create the directory.
Terminal window mkdir -p ~/.claude/skills/summarize-changesTerminal window New-Item -ItemType Directory -Force "$env:USERPROFILE\.claude\skills\summarize-changes" -
Write
SKILL.md.~/.claude/skills/summarize-changes/SKILL.md ---description: Summarizes uncommitted changes and flags anything risky. Use when the user asks what changed, wants a commit message, or asks to review their diff.---## Current changes!`git diff HEAD`## InstructionsSummarize the changes above in two or three bullet points, then list any risksyou notice such as missing error handling, hardcoded values, or tests that needupdating. If the diff is empty, say there are no uncommitted changes.Look at the
description: it says what it does and when to use it, with the phrases the user would actually say — “what changed”, “commit message”, “review their diff”. That is the activation mechanism. -
Test it in two ways. Open a git project, make a small change and run
claude.Letting Claude decide:
What did I change?Invoking it directly:
/summarize-changesThey should produce the same thing. If the first way does not activate the skill, the problem is the
description. -
Check that Claude sees it.
What skills are available?If the skill is not in the list, check the path and run
/reload-skills. -
Add a supporting file. Long instructions should not live in
SKILL.md. The command below uses a heredoc, a bash syntax for writing a multi-line file in one go: everything between<<'EOF'and theEOFline ends up in the file. Alternatively you can simply create the file with any text editor — the result is identical, and it is the way to go on Windows.Terminal window # writes the file rischi.md with all the text up to the "EOF" linecat > ~/.claude/skills/summarize-changes/rischi.md <<'EOF'# Catalogo dei rischi da cercare## CredenzialiChiavi API, token, password, URL di connessione con credenziali inline.Cerca anche i file `.env` aggiunti allo staging.## Errori silenziati`catch` vuoti, `except: pass`, promise senza `.catch`, errori loggatie poi ignorati.## Confini non validatiInput esterni usati senza validazione: parametri di query, body dirichieste, variabili d'ambiente, argomenti da riga di comando.EOF(The file is a catalog of risks to look for: credentials in the code, silenced errors, unvalidated external input.)
Then reference it from
SKILL.md, adding at the end:## Risorse aggiuntive- Per il catalogo completo dei rischi, vedi [rischi.md](rischi.md)The file is read only when needed: it does not weigh on the context of every invocation.
-
Run it in a subagent — a separate helper with its own context window, the star of module 11. If the skill reads many files, keep those reads out of your context. Add to the frontmatter:
context: forkagent: Explorebackground: falsebackground: falsewaits for the result in the same turn, instead of launching it in the background. -
Lock it down for manual use. If it is a skill with side effects that you do not want Claude to launch on its own:
disable-model-invocation: trueallowed-tools: Bash(git add *) Bash(git commit *) Bash(git status *)Now
/summarize-changesruns the git commands without asking you for confirmation at every step, but Claude never invokes it on its own initiative.
Finding out whether a skill works
Section titled “Finding out whether a skill works”Seeing a skill activate tells you Claude found it, not that it did what you wanted. The two things must be measured separately:
- Activation — does Claude invoke it on the prompts where it should? Write five requests that should activate it and five that should not, and try them.
- Output — when it activates, is the result what you expect? Define first what you consider correct.
There is also a dedicated skill, skill-creator, for running evals on a skill — automated
tests that measure whether the skill really produces the behavior you wrote it for.
Exercise
Section titled “Exercise”Turn part of your CLAUDE.md into a skill, and measure the difference.
- Find a section in your
CLAUDE.mdthat has become a procedure instead of a fact. - Measure how much it costs in context today.
- Move it into a skill, with the right
description. - Check that it activates when needed and not when not needed.
- Measure again.
Solution
Steps 1–2. The typical candidate is a section like “How to release” or “Checklist before the
PR”: eight steps that matter once a week but that you pay for in every session. Measure with /context
and note the line for the project CLAUDE.md.
Step 3. Move the content and — this is the part that matters — rewrite the description. In
CLAUDE.md it was not needed, because the content was always there. In a skill it is everything:
---description: Procedura di rilascio in produzione. Usala quando l'utente chiede di rilasciare, fare una release, taggare una versione, o chiede "come si mette in produzione".disable-model-invocation: trueargument-hint: "[versione]"---
Rilascia la versione $ARGUMENTS.
1. Verifica che `main` sia allineato con il remoto e che la CI sia verde2. `npm run typecheck` e `npm test` — entrambi devono passare3. Aggiorna il CHANGELOG con le modifiche da `git log --oneline <ultimo-tag>..HEAD`4. Bump della versione in `package.json`5. Commit `chore(release): v$ARGUMENTS`, tag `v$ARGUMENTS`, push con `--follow-tags`6. Verifica che il deploy sia partito e riporta l'URL
Fermati e chiedi conferma prima del passo 5. Non procedere se il passo 2 fallisce.(A production release procedure, triggered when the user asks to release, tag a version or go to production:
check main and CI, run typecheck and tests, update the CHANGELOG, bump the version, commit/tag/push, verify the deploy.
Claude stops for confirmation before step 5 and does not continue if step 2 fails.)
The trade terms inside the skill, for those who are not familiar with them: “CI is green” = the repository’s automated
checks pass; the CHANGELOG is the file that lists what changes in each version;
the “bump” is the increment of the version number; the tag is the git label that marks the exact
point of a release (and --follow-tags sends it to GitHub together with the commit).
Note two deliberate choices: disable-model-invocation: true, because a release must not start because
Claude deemed it relevant; and the two final sentences, written as standing instructions — they stay in
context for the whole session, so they must hold for the whole session.
Step 4. The activation check must be done with disable-model-invocation temporarily
removed, otherwise you are only testing manual invocation. Five prompts that should activate it:
come rilascio la 2.3.0?possiamo fare una release?è ora di taggare la versionemettiamo in produzionequal è la procedura di deploy?(How do I release 2.3.0? / Can we do a release? / Time to tag the version / Let’s go to production / What is the deploy procedure?)
And five that should not:
questo test fallisceaggiungi un endpointperché la CI è rossa?riassumi le modifichecome si chiama questa funzione in inglese?(This test fails / Add an endpoint / Why is CI red? / Summarize the changes / What is this function called in English?)
If “perché la CI è rossa?” activates the release skill, the description is too broad: it contains concepts (CI, production) that show up in different contexts. Narrow it.
Put disable-model-invocation: true back when you have finished testing.
Step 5. Run /context again in a new session. CLAUDE.md is shorter; in its place you only pay for
the description line in the skill listing. You pay for the body the two times a month you release.
Common mistakes & troubleshooting
Section titled “Common mistakes & troubleshooting”The skill does not activate
Section titled “The skill does not activate”- Check that the
descriptioncontains the words you would actually say. It is the criterion by which Claude decides. - Check that it shows up: ask
What skills are available?. - Rephrase the request to bring it closer to the description — it helps you find out whether that is where the problem is.
- Invoke it directly with
/nome-skill, if it is user-invocable.
If the frontmatter YAML is malformed, Claude Code loads the body with empty metadata: /nome-skill
still works, but Claude has no description to match against. It is the most insidious case, because
it seems to work. Run with --debug to see the parsing error.
Descriptions get truncated
Section titled “Descriptions get truncated”Claude Code loads into context a listing of names and descriptions. The listing always contains all the names, but if you have many skills the descriptions are shortened to fit the budget — which is 1% of the model’s context window. When the listing overflows, the descriptions of the skills you invoke least are dropped first.
Remedies, in order of preference:
| Remedy | How |
|---|---|
| Shorten the text at the source | Main use case first; the per-entry limit is 1,536 characters |
| Free up budget | "name-only" in skillOverrides for low-priority skills |
| Raise the budget | skillListingBudgetFraction (e.g. 0.02 = 2%) or SLASH_COMMAND_TOOL_CHAR_BUDGET |
| Raise the per-entry cap | skillListingMaxDescChars |
/doctor estimates the context cost of the listing and its largest contributors. The Skills line of
/context reports the size after the budget is applied, so it matches what the
model actually receives.
Symptom table
Section titled “Symptom table”| Symptom | Cause | Fix |
|---|---|---|
| The skill never starts on its own | Vague or truncated description |
Rewrite it with the user’s words; check the listing budget |
| The skill starts too often | description too broad |
Make it more specific, or disable-model-invocation: true |
/nome works but Claude never invokes it |
Malformed YAML frontmatter | --debug shows the parsing error |
| The skill stops influencing behavior after the first answer | The content is still there, but the model is choosing something else | Strengthen description and instructions, or use a hook |
| After a compaction the skill no longer has any effect | You invoked many of them: the oldest drop first from the 25,000 total tokens | Invoke it again to restore the full content |
| Half of the skill’s instructions are ignored | Truncation keeps the first 5,000 tokens | Put the important instructions at the top of SKILL.md |
| The new skill does not show up | Directories not rescanned | /reload-skills |
| A skill from a cloned repo has permissions you did not expect | allowed-tools is not gated by workspace trust |
Review the frontmatter of committed skills before running Claude Code in the repo |
!`...` commands do not run |
The skill is synced from claude.ai | Expected behavior: synced skills do not run local commands |
Cheat sheet
Section titled “Cheat sheet”Minimal structure
.claude/skills/<nome>/SKILL.md → /<nome>---description: Cosa fa e quando usarla, con le parole dell'utente---
Le istruzioni.(The description says what the skill does and when to use it, in the user’s own words; the instructions follow.)
Frontmatter, the fields you will actually use
| Field | When |
|---|---|
description |
Always |
when_to_use |
When you need explicit trigger phrases |
disable-model-invocation: true |
Workflows with side effects, to be launched by hand |
user-invocable: false |
Background knowledge the user should not invoke |
allowed-tools |
To avoid repeated confirmations during the turn |
disallowed-tools |
To take tools away from an autonomous skill |
context: fork + agent |
When the skill reads a lot and must not consume your context |
argument-hint |
When it accepts arguments |
model / effort |
When the task needs more or less power than usual |
Substitutions and syntax
$ARGUMENTS |
Everything that follows the name |
$0, $1, $2 |
Positional arguments |
!`comando` |
Runs and inserts the output before Claude reads |
Numbers
| What | Value |
|---|---|
Recommended SKILL.md |
Under 500 lines |
description + when_to_use |
Truncated to 1,536 characters in the listing |
| Listing budget | 1% of the context window |
| Re-injection after compaction | 5,000 tokens per skill, 25,000 in total |
| Chainable skills | The first plus five more |
Sources
Section titled “Sources”Official pages consulted, checked on 16 August 2026:
- Extend Claude with skills — structure, full frontmatter, supporting files, arguments, subagents,
skillOverrides, troubleshooting - Commands reference — bundled skills and built-in commands
- Subagents —
context: forkand skill preloading - Hooks — hooks in skills and when they are needed instead of instructions
- Settings —
skillListingBudgetFractionandskillListingMaxDescChars - How Claude remembers your project — when to use a skill instead of
CLAUDE.mdor a rule
A course by Giuseppe Bosi · © 2026 · textCC BY 4.0, code MIT · last updated 29 October 2026 · contact: info@giuseppebosi.com
Author: Prof. Giuseppe Bosi (LinkedIn) · More courses (in Italian):JSON & API ·llama.cpp ·Build your own AI memory (RAG)· Source code on GitHub
Independent course, not affiliated with or endorsed by Anthropic or Google. Claude and Claude Code are trademarks of Anthropic; Antigravity and Gemini are trademarks of Google.
Every module lists its official sources with the date they were consulted. This site uses no cookies and collects no personal data.