If you keep retyping a prompt, make a skill
If you are telling Claude the same prompt multiple times, make a skill.
Many of us learned Claude Code by just messing around, and we did not take the time to actually learn to use it properly. It is time to fix that.
The fastest way to get better at Claude Code is not to write longer prompts. It is to stop retyping prompts that should have become reusable workflows.
That is what skills are for.
What is a skill?
A skill is a reusable instruction bundle for Claude Code.
At the smallest size, it is a folder with one file:
my-skill/
SKILL.md
SKILL.md has two parts:
---
description: Reviews local changes for bugs, missing tests, and regression risk. Use when I ask for a code review, PR review, diff review, or "what is wrong with this change?"
---
Lead with findings.
Order findings by severity.
Ignore style nits unless they hide a bug.
Mention missing tests.
Do not quote the whole diff back to me.
The frontmatter tells Claude when the skill is useful. The markdown body tells Claude what to do.
Claude Code can use the skill two ways.
You can invoke it directly:
/my-skill
Or Claude can load it automatically when your request matches the description:
Can you review this diff?
The important part is that the full skill body does not have to live in every conversation. Claude keeps the description available so it knows the skill exists. The body loads when the skill is actually used.
That makes skills different from CLAUDE.md. CLAUDE.md is always-on context. A skill is task-specific context.
Put permanent project facts in CLAUDE.md.
Put repeated procedures in skills.
How to write a skill
Start with the sentence you keep pasting into Claude.
Do not start by designing a giant framework. Do not make twenty skills. Pick one repeated workflow and make it real.
For a personal skill, create it under ~/.claude/skills/:
mkdir -p ~/.claude/skills/review-diff
code ~/.claude/skills/review-diff/SKILL.md
For a project skill, create it inside the repo:
mkdir -p .claude/skills/review-diff
code .claude/skills/review-diff/SKILL.md
Then write the SKILL.md.
---
description: Reviews local git diffs for behavior bugs, missing tests, and regression risk. Use when I ask for a code review, PR review, diff review, or whether a change is safe to merge.
allowed-tools: Bash(git status *) Bash(git diff *)
---
## Current changes
```!
git status --short
git diff --stat HEAD
git diff HEAD
```
## Instructions
Lead with findings.
Order findings by severity.
For each finding, include:
- The file and line when possible
- The concrete failure mode
- Why it matters
- The smallest fix
Look for:
- Behavior changes without tests
- Error handling regressions
- Data migration assumptions
- Auth, permission, or privacy issues
- Race conditions and retry problems
If there are no issues, say that clearly and mention any remaining test gaps.
That example has four pieces worth noticing.
First, the directory name becomes the command. review-diff becomes /review-diff.
Second, the description is not a slogan. It is the matching surface. It says what the skill does and includes phrases I might actually type.
Third, allowed-tools pre-approves only the narrow commands the skill needs. It does not remove access to other tools, but it prevents permission prompts for those commands while the skill is active.
Fourth, the fenced block that starts with ```! is dynamic context injection. Claude Code runs those commands before Claude sees the skill content, then replaces the block with the output. Claude receives the real current diff, not a guess.
Keep the body short. Once a skill loads, it stays in the conversation. If the body gets large, use progressive disclosure:
review-diff/
SKILL.md
references/
security.md
migrations.md
examples/
good-review.md
scripts/
changed-files.sh
Then tell Claude when to open the extras:
If the diff touches authentication or authorization, read references/security.md.
If the diff includes a database migration, read references/migrations.md.
The common path stays small. The special cases load when they matter.
Types of skills
Skills can live at different levels.
| Type | Path | Who gets it |
|---|---|---|
| Enterprise | managed settings | Everyone in the organization |
| Personal | ~/.claude/skills/<skill-name>/SKILL.md |
You, across all projects |
| Project | .claude/skills/<skill-name>/SKILL.md |
Anyone working in that repo |
| Plugin | <plugin>/skills/<skill-name>/SKILL.md |
Anyone who installed and enabled the plugin |
Use personal skills for your own habits.
Use project skills for team workflows that belong with the repo.
Use enterprise skills for organization-wide standards.
Use plugin skills when you want to distribute something across many repos, many teams, or the public.
If ordinary skills share the same name, the priority is:
enterprise > personal > project
Plugin skills are different. They are namespaced by the plugin, so they do not collide with personal or project skills.
Plugin naming conventions and conflicts
A plugin has a manifest:
my-plugin/
.claude-plugin/
plugin.json
skills/
hello/
SKILL.md
The manifest defines the plugin name:
{
"name": "my-plugin",
"description": "A small plugin with a hello skill",
"version": "1.0.0"
}
Plugin names should be kebab-case with no spaces. That name becomes the namespace.
This skill:
my-plugin/skills/hello/SKILL.md
is invoked as:
/my-plugin:hello
That namespace is the conflict solution. Ten plugins can all have a hello skill because the real names are /plugin-a:hello, /plugin-b:hello, and so on.
For standalone skills, be more careful. review is too broad. rails-migration-review is better. commit might be fine as a personal skill, but it is risky as a shared project skill unless the team agrees exactly what it does.
One more rule: if a skill and an old custom command share the same name, the skill takes precedence. Existing .claude/commands/ files still work, but new work should usually be a skill because skills can have supporting files, scripts, automatic discovery, and invocation controls.
When should you not use a skill?
Do not use a skill for everything.
Use CLAUDE.md instead when the instruction is always true in the repo:
- Use
pnpm - Run
npm test - Never edit generated files
- Components live in
src/components - API handlers return this error shape
Use a slash command instead when the thing is just a tiny prompt snippet and you want to invoke it manually.
Use a hook when the behavior must happen on an event, like before a tool call, after a file write, or when a prompt is submitted.
Use a subagent when the work should happen in a separate context window, with its own tools and role.
Use an MCP server when Claude needs to talk to an external system: GitHub, Jira, a database, a browser, an internal service, or anything else that should expose tools or resources.
And do not use a skill when the workflow has side effects you do not want Claude to trigger automatically.
For dangerous or state-changing skills, make them explicit:
---
description: Stages and commits the current changes
disable-model-invocation: true
allowed-tools: Bash(git status *) Bash(git add *) Bash(git commit *)
---
Create a commit for the current changes.
Use a one-line imperative commit message.
Show me the final commit SHA.
disable-model-invocation: true means Claude will not decide to run it on its own. You have to type the command.
That is what you want for commits, deploys, database changes, production operations, billing operations, or anything else where surprise is bad.
Other techniques
Skills are one tool in the Claude Code customization stack.
Here is the short version.
| Technique | Use it for |
|---|---|
CLAUDE.md |
Always-on project memory and repo facts |
| Skills | Reusable task-specific workflows Claude can discover or you can invoke |
| Slash commands | Small manual commands or prompt snippets |
| Hooks | Deterministic event handling around tool calls, prompts, stops, and file changes |
| Subagents | Specialized workers with separate context, tools, and prompts |
| MCP servers | External tools, resources, prompts, and integrations |
The mistake is using one mechanism for all of these jobs.
A skill is not a hook. It does not fire because a file changed.
A skill is not an MCP server. It does not create a new external integration.
A skill is not a subagent, although a skill can run in a forked subagent with context: fork.
The clean mental model is this:
CLAUDE.md tells Claude what is always true.
Skills tell Claude how to do a recurring kind of work.
Hooks enforce behavior at specific events.
Subagents isolate work.
MCP servers connect Claude to the outside world.
How to publish a plugin
Start local. Publish later.
First, create the plugin:
mkdir -p my-plugin/.claude-plugin
mkdir -p my-plugin/skills/review-diff
Create my-plugin/.claude-plugin/plugin.json:
{
"name": "my-plugin",
"description": "Reusable Claude Code workflows",
"version": "1.0.0",
"author": {
"name": "Your Name"
}
}
Create my-plugin/skills/review-diff/SKILL.md:
---
description: Reviews local changes for bugs and missing tests
disable-model-invocation: true
---
Review the selected code or recent changes.
Lead with bugs.
Mention missing tests.
Keep the review concise and actionable.
Test it locally:
claude --plugin-dir ./my-plugin
Then invoke it:
/my-plugin:review-diff
When the plugin works, create a marketplace. A marketplace is just a catalog that tells Claude Code where plugins live.
Example:
my-marketplace/
.claude-plugin/
marketplace.json
plugins/
my-plugin/
.claude-plugin/
plugin.json
skills/
review-diff/
SKILL.md
Create my-marketplace/.claude-plugin/marketplace.json:
{
"name": "my-marketplace",
"owner": {
"name": "Your Name"
},
"plugins": [
{
"name": "my-plugin",
"source": "./plugins/my-plugin",
"description": "Reusable Claude Code workflows"
}
]
}
Validate it:
claude plugin validate .
Test the marketplace:
/plugin marketplace add ./my-marketplace
/plugin install my-plugin@my-marketplace
To publish it for real, put the marketplace in a GitHub repo, GitLab repo, private repo, or another supported source. Users add it with:
claude plugin marketplace add your-org/claude-plugins
or from inside Claude Code:
/plugin marketplace add your-org/claude-plugins
Then they install:
/plugin install my-plugin@my-marketplace
If you want to submit to Anthropic’s official marketplace, use the official submission flow from Claude.ai or Console. For teams, a private marketplace is usually the simpler first move.
Versioning matters. If plugin.json has "version": "1.0.0", users do not get updates until you bump that version. If you omit version for a git-hosted plugin, Claude Code can use the commit SHA as the version, so every commit can be treated as an update.
Now try to write your first skill
Do this now, before making it clever.
Find one prompt you have typed into Claude at least three times.
Good candidates:
- Review this PR the way I like
- Write a commit message in my format
- Summarize these notes into a blog outline
- Debug this Rails migration
- Check this API change for backwards compatibility
Create a personal skill:
mkdir -p ~/.claude/skills/my-first-skill
code ~/.claude/skills/my-first-skill/SKILL.md
Use this template:
---
description: Describe what this skill does. Use when I ask for the exact phrases I normally type.
---
Do the task this way:
1. First step
2. Second step
3. Third step
Output format:
- What I want first
- What I want second
- What I do not want
Then test it two ways:
/my-first-skill
and:
Ask Claude naturally for the thing the description should match.
If Claude does not pick it up automatically, do not blame the body. Fix the description.
That is the whole loop.
Notice the repeated prompt. Write the skill. Test the trigger. Tighten the description. Move on.
The goal is not to build a giant personal operating system in a weekend.
The goal is to stop retyping instructions that should have become tools.