Claude Skills, from the basics to building your own

You explain to Claude Code, every new session, how your team writes commit messages. Or how a PR summary is supposed to sound. Or your database schema, again. Every conversation starts from zero, so that explanation repeats.
A Skill is how you stop repeating it: a folder with instructions that Claude reads on its own, only when the task needs it, without taking up any space in the context in the meantime. You write it once and it’s there in every session after that.
Using one that already exists
Claude Code ships with a few out of the box — /run, /code-review,
/doctor, /loop, among others — and you can see all of them with:
/skills
They’re used two ways. Type the request in plain language, and let Claude decide on its own whether it applies:
What did I change in this file?
Or invoke it directly, by name, like a command:
/summarize-changes
Both end up doing the same thing if the Skill is well written: the difference is whether you decide or Claude decides that it’s needed.
How Claude decides when to use one
This is why installing a lot of Skills doesn’t make a conversation slow or expensive: they don’t all load in full up front. They load in three levels, each more expensive than the last, and it only moves up a level if it needs to.
The field that decides everything is the description: it’s the only thing Claude compares against your request to choose which Skill to use, so it has to say two things — what it does and when to use it — not just the name. This one is good, taken from the official docs:
description: Extract text and tables from PDF files, fill forms, merge
documents. Use when working with PDF files or when the user mentions
PDFs, forms, or document extraction.
And these are bad, because they’re vague — Claude has nothing to decide with, whether they apply to your request or not:
description: Helps with documents
description: Processes data
description: Does stuff with files
Building your first Skill, step by step
Let’s build one that summarizes uncommitted changes, so you don’t have to ask for it by hand every time. First, the folder:
mkdir -p ~/.claude/skills/summarize-changes
~/.claude/skills/ is the personal folder: whatever you put there is
available in all your projects, on this machine. If instead you want it
available only to whoever clones this repository, it goes in
.claude/skills/ inside the project itself, and gets checked into version
control.
Inside it, a single file, 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.
The line starting with ! isn’t text: it’s a command that runs before
Claude sees anything, and its result — the real diff of your project at that
moment — replaces that line. If the command fails, the whole Skill is
cancelled right there, so it’s worth it being something reliable.
You test it both ways from before: standing in a repository with some
uncommitted change, you type “what did I change?” and it should trigger on
its own, or you type /summarize-changes to force it.
The frontmatter, from essential to advanced
The only two required fields are name and description. name only
accepts lowercase letters, numbers, and hyphens, up to 64 characters, and
can’t use the words “claude” or “anthropic”. description can’t be empty
and has a maximum of 1,024 characters.
Everything else is optional, and each one solves something specific:
| Field | What it’s for |
|---|---|
disable-model-invocation: true |
Only triggers by hand, with /name — Claude never picks it on its own |
user-invocable: false |
The opposite: only Claude uses it, doesn’t show up in the / menu |
allowed-tools: Bash(git add *) |
Pre-approves specific tools ahead of time, without asking every time |
context: fork |
Runs it in a separate subagent, without the conversation history |
argument-hint: [issue-number] |
The hint text that shows up when autocompleting in the / menu |
For example, a deployment Skill makes sense with manual invocation only — you don’t want Claude deciding on its own when to deploy — and with the shell tools already approved, because they’re always the same:
---
name: deploy
description: Deploy the application to production
disable-model-invocation: true
allowed-tools: Bash(npm run build) Bash(npm run test) Bash(git *)
---
Advanced: a Skill with its own script
When the task is always the same, it’s better for a script to handle it than for Claude to write new code every time: it’s more reliable, and the script’s code never enters the context — only what it prints does. This is the structure of a Skill that builds a visual map of a repository:
~/.claude/skills/codebase-visualizer/
├── SKILL.md
└── scripts/
└── visualize.py
SKILL.md doesn’t explain how the script works: it tells Claude to run it,
and what to do with the result.
---
name: codebase-visualizer
description: Generate an interactive collapsible tree visualization of your codebase. Use when exploring a new repo, understanding project structure, or identifying large files.
allowed-tools: Bash(python3 *)
---
# Codebase Visualizer
Generate an interactive HTML tree view that shows your project's file structure.
## Usage
```bash
python3 ${CLAUDE_SKILL_DIR}/scripts/visualize.py .
```
This creates `codebase-map.html` in the current directory and opens it in
your browser.
${CLAUDE_SKILL_DIR} is a variable Claude resolves on its own, with that
Skill’s exact folder — so the script gets found no matter which project
you’re standing in when you use it.
How to write a good description
We already saw the good example and the bad ones. Two more rules, also from the official guide:
Write it in third person. The description gets inserted as-is into the system instructions, so if it’s in first person (“I can help you with…”) it sounds out of place there and confuses more than it helps.
Name the trigger, not just the action. “Analyzes data” doesn’t say when to use it. “Analyzes spreadsheets, builds pivot tables and charts. Use it with Excel files, spreadsheets, or .xlsx data” does: it gives Claude concrete words to match against the request.
One thing to check before installing someone else’s
A Skill gives Claude instructions and, if it has them enabled, tools to carry them out. A malicious one can get Claude to call tools or run code in ways that have nothing to do with what the Skill claims to do.
The official recommendation is simple: use only your own Skills or
Anthropic’s, and if you need one from another source, audit the whole thing
first — SKILL.md, any script, any image — looking for network calls, file
access, or anything that doesn’t match what it says it does.
In this blog’s article about Archify
we installed a real Skill, from a public repository, and before running
anything we read the whole SKILL.md to understand exactly what it expected
from us. It’s the same step, applied.
Where each Skill lives
Besides personal and project, there are a couple more places, and when two Skills share a name, whichever is higher on this list wins:
| Location | Path | Scope |
|---|---|---|
| Enterprise | centralized org configuration | every user |
| Personal | ~/.claude/skills/<name>/ |
you, in any project on this machine |
| Project | .claude/skills/<name>/ |
whoever clones this repository |
| Nested | <subfolder>/.claude/skills/<name>/ |
sessions inside that subfolder, with /subfolder:name |
| Plugin | <plugin>/skills/<name>/ |
wherever the plugin is enabled |
To confirm a Skill’s YAML is well-formed before sharing it, claude plugin validate checks it without needing to invoke it:
claude plugin validate ~/.claude/skills/my-skill
Comments
Sign in to comment and to like this article.
No comments yet. Be the first to write one.