You've used Claude for a week and you keep typing the same thing. Every time you commit code, you paste the same three rules about your commit format. Every time you write a doc, you re-explain your style. Claude does it well, then forgets the moment the chat ends, and tomorrow you type it all again.
A skill fixes that. It's a small folder you write once that teaches Claude a workflow permanently, so it applies every session without you asking.
Here's how it works, at a glance: a skill is a folder with one file inside. Claude keeps a one-line summary of it in view at all times, and pulls in the full instructions only when your request matches. That's the whole mechanism.
In this guide we build one real skill from nothing: commit-messages, which writes git commits in your exact format. If you have Claude installed and nothing else, you can follow every step.
What you'll end up with
At its core a skill is one folder with one required file, SKILL.md. Three optional folders come in later as the skill grows:
1your-skill-name/2├── SKILL.md # Required - the main skill file3├── scripts/ # Optional - executable code4├── references/ # Optional - documentation5└── assets/ # Optional - templates, etc.
SKILL.md itself has two parts: a short header that tells Claude when to use the skill, and instructions below it that tell Claude what to do. The reason for the split matters. Claude reads the header constantly, so it always knows the skill exists, but it only loads the instructions when your request matches. Keep that distinction in mind, because almost everything else in this guide follows from it.
Create it
Skills live in a folder called .claude/skills inside your home directory, which both Claude Code and the desktop app read from. It's hidden and probably doesn't exist yet, so the quickest way to create it, along with your skill's folder, is a single command.
On Mac, open Terminal and run:
1mkdir -p ~/.claude/skills/your-skill-name
On Windows, open PowerShell and run:
1New-Item -ItemType Directory -Force -Path "$HOME\.claude\skills\your-skill-name"
The folder name isn't cosmetic. Claude uses it as the skill's identifier, and one formatting rule trips people up more than any other:
- Use kebab-case: notion-project-setup ✔
- No spaces: Notion Project Setup ✖
- No underscores: notion_project_setup ✖
- No capitals: NotionProjectSetup ✖
Inside that folder, create a file named exactly SKILL.md and open it in any text editor. Everything from here is what goes in that file.
Design it before you write it
The skills that work start with two decisions, made before you write a line of the file. Both feel skippable and neither is.
First, decide exactly when the skill should fire. Write down two or three real situations in the words a user would actually type:
- "commit these changes"
- "write a commit message for this diff"
- "stage and commit"
This isn't busywork. These phrases become the raw material for your description and your tests later, and a skill designed without them tends to be vague in precisely the way that keeps it from ever triggering.
Second, decide how you'll know it works. The bar that matters above all others is whether the skill loads on its own, without you naming it. If you have to invoke it manually every time, the skill technically runs but has failed at its actual job. Worth watching alongside that: whether it finishes the task without you correcting it midway, and whether it gives you the same shape of result across separate sessions.
The description is what makes or breaks it
Of everything in the file, the description in the header does the most work, because it's the only part Claude reads when deciding whether to load the skill at all. Your instructions could be flawless and it wouldn't matter, since Claude never gets to them if the description doesn't match. This is where most skills that "don't work" actually fail.
A strong description answers two questions in one sentence: what the skill does, and when Claude should reach for it. That second half is the one people leave out.
Here's the difference:
1# weak - says what it is, gives Claude nothing to match a request against2description: Helps with git commits.34# strong - names the moments it should fire on5description: Writes git commit messages in Conventional Commits format. Use when the user asks to commit changes, write a commit message, or stage and commit files.
The weak version tells Claude the skill exists but never connects it to anything you'd say. The strong version names the actual phrases, so when you type "commit these changes," Claude has something to match against. Name the words a user would really use, keep the whole thing under 1024 characters, and don't put < or > inside it.
When a skill won't trigger, the fix is almost always here. Add the phrasings you actually use. If you say "save my work" but the description only mentions "commit," Claude has no way to link the two. And if the opposite happens and the skill fires when it shouldn't, narrow the description or add a negative trigger:
1description: Writes git commit messages in Conventional Commits format. Use when committing changes. Do not use for writing code comments or documentation.
There's a quick way to check your work before you rely on it. Ask Claude directly:
"When would you use the commit-messages skill?"
Claude will read your description back in its own words. If that doesn't line up with when you actually want the skill firing, you've found your problem, and it's in the description, not in the instructions underneath.
Write instructions Claude actually follows
Below the header comes the body, in plain Markdown. This is where your real workflow lives, and two habits separate instructions Claude follows from ones it quietly drifts away from.
The first is being specific. Claude acts on concrete instructions and glosses over vague ones, so the more exact you are, the more reliably it behaves:
1# Bad2Validate the commit before finalizing.34# Good5Run `python scripts/validate.py "<message>"`.6If it fails, fix these:7- Invalid type: use feat, fix, docs, refactor, test, chore8- Summary over 60 chars: shorten it
The second is ordering. Claude weights what it reads first, so a rule buried at the bottom of a long file is a rule that gets missed. Put anything that must not be broken at the top, under a heading that signals it:
1## Important2- Summary line under 60 characters, always3- Present tense only: "add", not "added"
There's also a limit to what language can guarantee. Instructions are interpreted, which means Claude follows them well but not identically every time. When a check genuinely has to pass on every run, don't describe it in prose, move it into a script and have the instructions run it. Code does the same thing every time; a sentence doesn't. (That's what the scripts/ folder is for, covered next.)
A structure that holds up across most skills looks like this:
1# Skill Name23## Important4Critical rules that must not be missed.56## Instructions7Step by step, specific and actionable.89## Examples10Concrete input and output. Claude copies examples more reliably than it follows rules.
Keep the file lean. The moment it starts growing past its core instructions is the moment to move the extra detail out, which is exactly what the optional folders are for.
Scripts, references, assets
Everything so far produces a skill that gives Claude instructions. The three optional folders turn it into a skill that gives Claude tools, and this is where a skill does things a plain prompt can't.
scripts/ holds code Claude runs, for anything that has to be exact. Rather than trusting Claude to eyeball whether a commit is formatted right, you hand it a script that checks:
1# scripts/validate.py2import sys3msg = sys.argv[1]4types = ("feat", "fix", "docs", "refactor", "test", "chore")56if msg.split(":")[0] not in types:7 print(f"Invalid type. Use: {', '.join(types)}")8elif len(msg.split("\n")[0]) > 60:9 print("Summary too long (over 60 chars)")10else:11 print("OK")
Then you tell Claude to use it in SKILL.md:
1Before finalizing, run `python scripts/validate.py "<message>"`2and fix anything it flags.
Now the format rule is enforced by code that runs the same way every time, instead of depending on Claude to remember to check.
references/ holds documentation that loads only when needed. Say your commit conventions run to two pages of scopes, footers, and edge cases. Put all of that in SKILL.md and it loads on every single commit, even a one-liner. Move it into a reference file instead:
1your-skill-name/2├── SKILL.md3└── references/4 └── conventions.md
And point to it from the main file:
1For the full convention list, see references/conventions.md
Claude opens that file only when the task calls for it. This is the whole reason skills stay cheap to run: the heavy detail sits on disk until it's actually relevant, rather than riding along in context every time.
assets/ holds files the skill uses in its output rather than reads for guidance, like a template, a config file, or a logo. A commit skill doesn't need any, but a skill that generates reports might keep a template.md here and fill it in each time, so every report comes out with the same structure.
Taken together, these three folders are the difference between a skill that tells Claude how you work and one that hands Claude the exact tools to do the work your way.
The one thing to remember
A skill isn't teaching Claude a new ability. It already knows how to write a commit. What the skill does is make it do the job your way, every time, without you spelling it out again.
And when a skill doesn't work, the cause is almost never the instructions you labored over. It's the description. Claude decides whether to load the skill from that single line, before it ever reads the work underneath. Get the description right and everything below it finally gets used.
If this was useful, head to my profile and follow. I write about tech, AI, and systems that actually run.





