Skip to content
IT

8 · Custom skills

Estimated time: 60 minutesPrerequisites: module 7 and module 4.

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.

  • Write a correct SKILL.md and 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.

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.

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.

  • 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:

SKILL.md
## 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.)

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-skill
description: What this skill does
disable-model-invocation: true
allowed-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.

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
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
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
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.

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-conventions
description: API design patterns for this codebase
---
When writing API endpoints:
- Use RESTful naming conventions
- Return consistent error formats
- Include request validation

Task 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: deploy
description: Deploy the application to production
context: fork
disable-model-invocation: true
---
Deploy the application:
1. Run the test suite
2. Build the application
3. Push to the deployment target

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-summary
description: Summarize changes in a pull request
context: fork
agent: Explore
allowed-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.)

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
.claude/settings.local.json
{
"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”
  1. Create the directory.

    Terminal window
    mkdir -p ~/.claude/skills/summarize-changes
  2. 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`
    ## Instructions
    Summarize the changes above in two or three bullet points, then list any risks
    you notice such as missing error handling, hardcoded values, or tests that need
    updating. 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.

  3. 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-changes

    They should produce the same thing. If the first way does not activate the skill, the problem is the description.

  4. Check that Claude sees it.

    What skills are available?

    If the skill is not in the list, check the path and run /reload-skills.

  5. 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 the EOF line 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" line
    cat > ~/.claude/skills/summarize-changes/rischi.md <<'EOF'
    # Catalogo dei rischi da cercare
    ## Credenziali
    Chiavi 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 loggati
    e poi ignorati.
    ## Confini non validati
    Input esterni usati senza validazione: parametri di query, body di
    richieste, 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.

  6. 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: fork
    agent: Explore
    background: false

    background: false waits for the result in the same turn, instead of launching it in the background.

  7. 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: true
    allowed-tools: Bash(git add *) Bash(git commit *) Bash(git status *)

    Now /summarize-changes runs the git commands without asking you for confirmation at every step, but Claude never invokes it on its own initiative.

Seeing a skill activate tells you Claude found it, not that it did what you wanted. The two things must be measured separately:

  1. 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.
  2. 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.

Turn part of your CLAUDE.md into a skill, and measure the difference.

  1. Find a section in your CLAUDE.md that has become a procedure instead of a fact.
  2. Measure how much it costs in context today.
  3. Move it into a skill, with the right description.
  4. Check that it activates when needed and not when not needed.
  5. 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:

.claude/skills/rilascio/SKILL.md
---
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: true
argument-hint: "[versione]"
---
Rilascia la versione $ARGUMENTS.
1. Verifica che `main` sia allineato con il remoto e che la CI sia verde
2. `npm run typecheck` e `npm test` — entrambi devono passare
3. 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 versione
mettiamo in produzione
qual è 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 fallisce
aggiungi un endpoint
perché la CI è rossa?
riassumi le modifiche
come 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.

  1. Check that the description contains the words you would actually say. It is the criterion by which Claude decides.
  2. Check that it shows up: ask What skills are available?.
  3. Rephrase the request to bring it closer to the description — it helps you find out whether that is where the problem is.
  4. 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.

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 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

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

Official pages consulted, checked on 16 August 2026:

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.