How to Build Claude Code Skills That Actually Work
Most people using Claude Code are repeating themselves every single session.
They open a new conversation. They explain how they like things done. They correct the tone. They fix the format. They remind Claude about their preferences. Again.
Every single time.
Then the session ends and all of that context disappears. Next session, you start over. Like Groundhog Day except you're paying for it.
This is the default experience. And most people just accept it because they don't know there's a better way.
There is.
Skills Are Just Markdown Files
A skill is a plain text file written in normal English that tells Claude how to do something specific. That's it.
Not code. Not a plugin. Not an API integration. A markdown file in a folder.
You write what you want in plain language. Claude reads it. Claude follows it. Every session, automatically, without you saying a word.
The reason this matters is that most people think extending Claude Code requires some kind of technical setup. It doesn't. If you can write a sentence, you can write a skill.
Let me show you what I mean.
The Email Problem
Say you use Claude to help you write emails. You open a session and say "write an email to this client about the project delay."
Claude writes something like this:
Dear John,
I hope this message finds you well. I wanted to reach out regarding the timeline for our ongoing project. Due to unforeseen circumstances, we are experiencing a slight delay in our deliverables. Rest assured, we are working diligently to ensure everything is completed to the highest standard.
Please don't hesitate to reach out if you have any questions or concerns.
Best regards
You read that and want to throw your laptop out the window.
Nobody talks like this. You don't talk like this. Your clients would think you got replaced by a robot. Which, technically, you did.
So you tell Claude: "No, write it more casually. Shorter sentences. Don't say 'I hope this message finds you well.' Just get to the point."
Claude rewrites it. Better. Maybe not perfect, but usable.
Next day, new session. You ask Claude to write another email.
"Dear Sarah, I hope this message finds you well."
Back to square one.
The Fix Takes 60 Seconds
Create a folder called skills inside your .claude directory. Inside that, create a folder called email-writer. Inside that, create a file called SKILL.md.
That's your skill. Here's what mine looks like:
---
name: email-writer
description: Use when writing any email, reply, or message to a client, collaborator, or contact.
---
Write emails the way I actually talk. Not how AI thinks professionals talk.
Rules:
- Short sentences. Get to the point immediately.
- Never open with "I hope this message finds you well" or any variation of that.
- Never close with "don't hesitate to reach out" or "please let me know if you have any questions."
- No filler. If a sentence doesn't add information, delete it.
- Sign off with just my name. Not "Best regards," not "Warm regards," not "Sincerely." Just "— Ethan"
- Match the tone to the situation. Casual for people I know. Professional but still human for new contacts.
- Always end with a specific next step. Not "let me know" — tell them exactly what happens next.
Bad example:
"Dear John, I hope this message finds you well. I wanted to reach out regarding the timeline update. Please don't hesitate to reach out with any questions. Best regards, Ethan"
Good example:
"Hey John — quick update on the timeline. We're pushing the delivery to Friday. The API integration took longer than expected but it's done now. I'll send you the staging link tomorrow morning so you can review before we go live. — Ethan"
That's it. That's the whole skill.
Plain English. No code. No configuration beyond the three lines at the top. Anyone who reads this file can understand exactly what it does.
Now every time you ask Claude to write an email, it reads this skill automatically and follows it. Same prompt as before — "write an email to this client about the project delay." Here's what Claude writes now:
Hey John — quick heads up. We're pushing delivery to next Friday. The API integration took longer than expected but it's wrapped up now. I'll send you the staging link Wednesday morning so you can review before we go live.
— Ethan
That's it. No "I hope this finds you well." No "please don't hesitate." Just the information, the reason, and a concrete next step. Sounds like a human wrote it because the skill taught Claude how you actually communicate.
You never correct the tone again. You never explain your preferences again. It just works.
Why This Matters More Than You Think
The email example is simple on purpose. But the principle underneath it is the thing that separates people who are productive with AI from people who are just using it.
Every time you correct Claude, you're doing work that a skill could do for you permanently.
Every preference you repeat is a skill you haven't written yet.
Every "no, not like that" is a lesson that dies when the session ends unless you capture it somewhere.
Skills are how you capture it. They're how Claude gets better at working with you specifically over time. Not better in general. Better for you.
The people at Anthropic who build Claude Code have hundreds of skills running internally. Not because they're fancy. Because they got tired of repeating themselves too.
What Goes In a Skill
A skill is a folder, not just a file. The SKILL.md is the entry point, but you can put anything else in the folder alongside it. Reference documents. Examples. Templates. Scripts.
Claude sees the whole folder and can pull from it when it needs to.
The SKILL.md has two parts.
The frontmatter at the top:
---
name: what-you-call-it
description: When should Claude use this skill. Write this for the AI, not for humans.
---
The description is how Claude decides whether to use the skill. When you start a conversation, Claude scans all your skill descriptions and matches them to what you're asking for. So write it like a trigger condition, not a summary.
Bad: "This skill helps with emails."
Good: "Use when writing any email, reply, or message to a client, collaborator, or contact."
Then below the frontmatter, write your instructions in plain language. Tell Claude what to do. What not to do. Show examples of good and bad output. Be specific about the things that matter to you.
The Gotchas Section Is Everything
The most valuable part of any skill is the section where you capture what goes wrong.
Every time Claude does something you don't like while using a skill, add it to the gotchas. "Don't use bullet points in the email body." "Never suggest a meeting when the client just needs information." "Stop capitalizing every word in subject lines."
These accumulate over time and they're what make a skill go from decent to genuinely good.
The people at Anthropic call this the highest-signal content in any skill. Not the instructions. The gotchas. Because instructions tell Claude what you want in theory. Gotchas tell Claude what actually goes wrong in practice.
Don't Over-Explain
Claude knows a lot. You don't need to teach it how to write. You don't need to explain what an email is. You don't need to give it a course on communication.
You need to tell it the things that are specific to you. Your tone. Your preferences. Your pet peeves. The stuff Claude can't know unless you tell it.
If you find yourself writing paragraphs of instructions, you're probably explaining things Claude already knows. Cut it down to what's different about how you specifically want things done.
The best skills are short. Ten to thirty lines. Focused on one thing. Clear about what not to do.
Progressive Disclosure
When a skill gets more complex, don't cram everything into SKILL.md. Use the folder.
Put your detailed reference material in separate files. Put your examples in an examples folder. Put your templates in a templates folder.
In your SKILL.md, just reference them:
See @examples/good-cold-email.md for the format I want.
See @references/brand-voice.md for tone guidelines.
Claude reads them when it needs them. It doesn't load everything upfront. This keeps the skill fast and focused while still giving Claude access to deep context when the task requires it.
Skills You Should Build First
Start with the things you do most often. The workflows where you keep correcting Claude.
Some ideas:
Email writer. We covered this. Your tone, your format, your sign-off, your pet peeves about AI-sounding language.
Content writer. If you write blog posts, newsletters, or social media content, capture your voice. What words you never use. What structure you prefer. How long your paragraphs are. Include examples of your actual writing.
Code reviewer. What you look for when reviewing code. What you consider a real issue versus a nitpick. Your team's conventions that Claude wouldn't know about.
Project scaffolding. How you like new projects set up. Your preferred stack. Your folder structure. Your naming conventions. The things you always add on day one.
Meeting notes. How you like meeting summaries formatted. What information matters. What gets skipped.
Pick one. Build it in 60 seconds. Use it for a week. Add gotchas as they come up. Then build the next one.
Controlling Which Tools a Skill Can Use
Here's where things get interesting. You can lock down exactly which tools a skill has access to with allowed-tools in the frontmatter.
---
name: auto-context
description: Maintain project context files at the end of coding sessions.
allowed-tools: Read, Write, Edit, Glob
---
This skill can read files, write files, edit files, and search for files. That's it. It can't run shell commands. It can't make API calls. It can't touch your git history.
Why does this matter? Two reasons.
First, safety. If you build a skill that auto-runs on certain triggers, you probably don't want it deleting files or pushing code. Restricting tools means the skill can only do what you explicitly allow, even if the instructions are ambiguous.
Second, focus. When a skill has access to everything, it tends to over-reach. A code review skill with Bash access might start running tests and fixing things instead of just reviewing. Constraints make skills more predictable.
For most skills you won't need this. But for anything that runs automatically or touches critical workflows, it's worth thinking about what the minimum set of tools is.
Skills That Call Other Skills
Skills don't have to be isolated. You can invoke a skill from within another skill using the /skill-name pattern, the same way you'd call one from the command line.
Say you have a deploy skill that handles your production deployment checklist. Inside it, you might reference:
Before deploying, run /code-review on all changed files.
After deploying, run /auto-context to update the project docs.
Claude reads those as triggers. It runs the code review skill, gets the results, uses them to decide whether to proceed with the deploy, then runs the context update at the end.
This is skill composition. Each individual skill stays small and focused. But chained together, they form workflows that are more sophisticated than any single skill could be.
The key is that each skill in the chain maintains its own instructions, its own gotchas, its own tool restrictions. You're not building one massive skill that does everything. You're building small, reliable pieces that work together.
Multi-File Skills for Complex Workflows
The email skill was one file. That's fine for simple preferences. But some workflows need more context than you can fit in 30 lines.
Here's what a real project scaffolding skill looks like in my setup:
.claude/skills/scaffold-project/
├── SKILL.md
├── templates/
│ ├── next-app/
│ │ ├── package.json
│ │ ├── tsconfig.json
│ │ └── tailwind.config.ts
│ └── api-service/
│ ├── package.json
│ └── Dockerfile
├── examples/
│ └── good-project-structure.md
└── references/
└── naming-conventions.md
The SKILL.md is still the entry point. It stays short and focused on the decision logic — when to use which template, what questions to consider, what the output should look like.
---
name: scaffold-project
description: Use when creating a new project, app, or service from scratch.
---
Determine the project type from context and scaffold accordingly.
For web apps, use @templates/next-app/ as the base.
For API services, use @templates/api-service/ as the base.
Follow @references/naming-conventions.md for all file and variable names.
See @examples/good-project-structure.md for what the final result should look like.
Rules:
- Always include a .gitignore from day one
- Always set up ESLint and Prettier with our config
- Never add dependencies we don't need yet
- README should have setup instructions that actually work
Claude reads the SKILL.md first. When it hits the @templates/ references, it pulls those files in. When it needs naming conventions, it reads that reference file. It doesn't load everything upfront — it grabs what it needs when it needs it.
This is progressive disclosure in practice. The skill stays fast to parse and easy to maintain, but Claude has access to deep, detailed context when the task requires it.
Writing Descriptions That Actually Trigger
The description field is the most underrated part of a skill. It's not documentation for humans. It's a trigger condition for Claude.
When you start a conversation, Claude scans all your skill descriptions and pattern-matches them against what you're asking. If the description doesn't match, the skill doesn't activate. Doesn't matter how good the instructions are inside.
Bad descriptions are vague:
description: This skill helps with code.
Claude will never confidently match that to anything because it matches everything. Good descriptions are specific trigger conditions:
description: Use when reviewing pull requests, examining code changes, or when the user asks for a code review on files or commits.
Now Claude knows exactly when to activate it. Pull requests. Code changes. The words "code review." Specific, actionable, unambiguous.
Write your descriptions like you're writing an if-statement, not a summary.
Hooks: Skills That Run Themselves
Skills are activated by Claude matching your request to a description. But hooks are different. Hooks are shell commands that run automatically in response to specific events.
You configure them in your settings, not in a skill file. And they fire every time the event happens, no matching required.
{
"hooks": {
"post-tool-use": [
{
"matcher": "Write",
"command": "eslint --fix $FILE_PATH"
}
]
}
}
This hook runs ESLint on every file Claude writes. Automatically. Claude doesn't decide whether to lint. It just happens.
The difference matters. Skills are Claude following instructions. Hooks are the system enforcing constraints. Skills can be ignored if Claude decides the context doesn't warrant it. Hooks can't be ignored because they run outside of Claude's decision loop.
Use skills for preferences and workflows. Use hooks for guardrails and automation that should never be skipped.
Some hooks I use:
- Auto-format on save. Every file Claude writes gets run through Prettier. No more inconsistent formatting.
- Test on edit. When Claude edits a test file, the test suite runs automatically. Claude sees the results in real time.
- Lint before commit. Before any commit goes through, the linter runs. If it fails, the commit fails. Claude fixes the issues and tries again.
Hooks turn Claude from an assistant that follows instructions into a system with real guardrails. You're not hoping Claude remembers to lint. You're guaranteeing it.
Making Skills Better Automatically
Once you have a skill that's working, you can make it better without doing any manual work.
The concept is simple. Define what "good" looks like as a checklist of yes/no questions. Then let Claude test the skill, score the output, make one small change, test again, and keep or revert based on whether the score went up.
For the email skill, your checklist might be:
- Does the email get to the point in the first sentence?
- Is it free of phrases like "I hope this finds you well" or "don't hesitate to reach out"?
- Does it end with a specific next step?
- Is the total length under 100 words?
- Does the tone match the context — casual for existing contacts, professional for new ones?
Run Claude in a loop against those checks. It makes tiny improvements to the skill prompt, tests them, keeps what works. Your skill gets tighter with every round.
This is an adaptation of Karpathy's autoresearch method. He used it for ML code. It works on anything you can score.
Someone ran this on landing page copy and went from 56% to 92% on their quality checks with zero manual work. Another person used it on page load time optimization and went from 1100ms to 67ms in 67 rounds of automated improvement.
The key insight is that 3 to 6 checklist questions is the sweet spot. More than that and the skill starts gaming the checklist instead of actually getting better. Like a student who memorizes answers without understanding the material.
CLAUDE.md: The Skill That's Always On
Before you build a single skill, there's a file that matters more than any of them. CLAUDE.md in the root of your project.
This file is loaded into every single conversation automatically. No frontmatter, no description matching, no trigger conditions. Claude reads it first, every time.
This is where you put the stuff that applies to everything. Your project's architecture. The build commands that actually work. The conventions your team follows. The gotchas that burn everyone once.
# Project
Next.js 16 with Tailwind. Neon for the database. Deployed on Vercel.
## Commands
- `npm run dev` — start dev server
- `npm run build` — build for production
- `npm run lint` — run ESLint
## Conventions
- All API routes go in app/api/
- Database queries use the getDb() helper in lib/db.ts
- Never commit .env files
## Gotchas
- Next.js 16 has breaking changes from 15. Read the migration docs before assuming anything.
- The Neon connection requires channel_binding=require in the connection string.
Think of CLAUDE.md as the permanent context layer. Skills are specialized tools. CLAUDE.md is the baseline understanding that every skill and every conversation inherits.
Keep it under 200 lines. If it gets longer, you're putting things in there that should be skills or separate reference docs.
Sharing Skills
Skills live in two places.
Project skills go in .claude/skills/ inside your project. They get committed to git. Everyone on the team gets them.
Personal skills go in ~/.claude/skills/. They're yours. They follow you across every project.
If you build something good, put it in the project so your team benefits. If it's specific to how you personally work, keep it personal.
The best skills start personal and get promoted to project-level once they prove themselves. You don't need a formal process for this. Just move the folder when it's ready.
The Compound Effect
Here's what happens when you actually commit to this.
Week one, you build an email skill. Saves you 30 seconds per email, a few minutes per day. Not life-changing.
Week two, you add a code review skill. Now Claude catches the things you always catch in PRs, without you reading every line. That's 20 minutes back per review.
Week three, you add a project scaffolding skill. New projects start with your exact setup instead of generic boilerplate. That's an hour saved every time you start something new.
Week four, you add hooks. Auto-formatting, auto-linting, auto-testing. Now Claude isn't just following your preferences — it's operating inside a system with real constraints. The quality floor goes up.
By month two, you have a dozen skills and a few hooks. Claude works the way you work. Not in general. Not approximately. Exactly.
The gap between you and someone who opens Claude cold every session isn't talent. It's accumulated context. Every skill you write is a piece of institutional knowledge that makes every future session faster, better, and more consistent.
A skill takes 60 seconds to create. It saves you from repeating yourself for as long as you use Claude.
Start with one. The thing that annoys you most. Write it down in plain English. Drop it in the folder.
Then never repeat yourself again.
Share