Skills and Custom Commands in Claude Code
Create your own skills and slash commands to customize Claude Code. Automate recurring workflows with reusable prompts.
What are commands and skills?
Claude Code is powerful out of the box, but it becomes truly formidable when you customize it. Two complementary mechanisms exist for this: custom commands and skills.
Custom commands are Markdown files that become slash commands (/my-thing). You type /commit in Claude Code, and it executes the prompt you defined. Think of it as a shell alias, but for Claude interactions.
Skills are richer Markdown files stored in your user profile. They describe a behavior, an expertise, a working method. Claude Code loads them automatically when it detects they are relevant, or you invoke them manually.
In short:
| Custom commands | Skills | |
|---|---|---|
| Location | .claude/commands/ in the project | ~/.claude/skills/ in your home |
| Trigger | Explicit slash (/name) | Automatic or /skill-name |
| Scope | Project (shareable with team) | Global (personal) |
| Typical use | Recurring actions (commit, review, test) | Cross-cutting expertise (design system, TDD, debugging) |
Custom commands
Basic structure
Commands live in the .claude/commands/ directory at your project root. Each .md file becomes a slash command.
my-project/
├── .claude/
│ └── commands/
│ ├── commit.md
│ ├── review.md
│ ├── test.md
│ └── deploy.md
├── src/
└── package.json
The filename (without .md) becomes the command name. commit.md gives you /commit. That simple.
The $ARGUMENTS placeholder
Every command can receive arguments. Use $ARGUMENTS in your Markdown file to inject whatever the user types after the command.
/review src/auth/login.ts
In the review.md file, $ARGUMENTS gets replaced with src/auth/login.ts.
Example 1: /commit - Smart commit messages
File .claude/commands/commit.md:
Analyze staged changes (git diff --cached) and generate a commit message.
Rules:
- Format: type(scope): short description
- Allowed types: feat, fix, refactor, docs, test, chore, style, perf
- Description must be 72 characters max
- For complex changes, add a body after a blank line
- The body explains the "why", not the "what"
- Language: English
Examples of good messages:
- feat(auth): add OAuth2 login with Google
- fix(api): handle null response from payment gateway
- refactor(db): extract query builder into separate module
Analyze the changes, propose the message, and wait for my approval before committing.
Usage:
> /commit
Claude Code reads the staged diff, generates a structured message, and waits for your go-ahead.
Example 2: /review - Targeted code review
File .claude/commands/review.md:
Perform a code review of the following file or changes: $ARGUMENTS
Review criteria (by priority):
1. **Bugs and logic errors**: race conditions, null pointers, off-by-one, unhandled edge cases
2. **Security**: injections, unvalidated data, hardcoded secrets, missing permissions
3. **Performance**: unnecessary loops, N+1 queries, excessive allocations, missing cache
4. **Readability**: unclear naming, functions too long (>30 lines), excessive nesting (>3 levels)
5. **Tests**: untestable code, obviously missing test cases
Output format:
- List each issue with its line number
- Classify each issue: 🔴 Critical / 🟡 Important / 🔵 Suggestion
- Propose a concrete fix for each critical and important issue
- End with a summary: X critical, Y important, Z suggestions
If $ARGUMENTS is empty, review uncommitted changes (git diff).
Usage:
> /review src/api/handlers/payment.ts
> /review # reviews current diff
Example 3: /test - Test generation
File .claude/commands/test.md:
Generate tests for: $ARGUMENTS
Approach:
1. Read the source file and understand the logic
2. Identify relevant test cases:
- Nominal case (happy path)
- Edge cases (empty values, null, very large)
- Error cases (exceptions, timeouts, invalid data)
3. Write tests using the project's test framework
4. Each test must be independent and deterministic
Conventions:
- One describe block per public function/method
- Naming: "should [expected behavior] when [condition]"
- Arrange / Act / Assert clearly separated
- No mocks unless necessary (prefer real objects)
- If a mock is needed, explain why
Place the test file next to the source with .test or .spec suffix based on project conventions.
Usage:
> /test src/utils/date-parser.ts
Example 4: /deploy - Deployment workflow
File .claude/commands/deploy.md:
Run the deployment workflow for the environment: $ARGUMENTS
Steps:
1. Verify the branch is clean (no uncommitted changes)
2. Run tests: npm test (or the project's test script)
3. If tests pass, run the build: npm run build
4. Verify the build succeeded without critical warnings
5. Based on environment:
- staging: push to the staging branch, trigger auto-deployment
- production: create a version tag (semver), push the tag
If $ARGUMENTS is empty, deploy to staging by default.
IMPORTANT: NEVER deploy to production without explicit confirmation.
Show me a summary of changes that will be deployed and wait for my "go".
Example 5: /refactor - Guided refactoring
File .claude/commands/refactor.md:
Refactor the following code: $ARGUMENTS
Patterns to apply (in order):
1. **Extract Method**: any duplicated logic or block > 15 lines
2. **Simplify Conditionals**: replace nested if/else with early returns or pattern matching
3. **Remove Dead Code**: delete commented code, unused imports, unread variables
4. **Rename for Clarity**: rename variables/functions whose name doesn't reflect their content
5. **Type Safety**: add or strengthen typing where possible
Constraints:
- Observable behavior does NOT change (same inputs -> same outputs)
- No public API changes without asking first
- Each transformation gets a separate commit with a clear message
- If the refactoring breaks tests, fix the tests first
Show me the refactoring plan before executing. I approve step by step.
Subdirectories for organizing commands
If you have many commands, organize them in subdirectories:
.claude/commands/
├── git/
│ ├── commit.md
│ ├── pr.md
│ └── changelog.md
├── code/
│ ├── review.md
│ ├── test.md
│ └── refactor.md
└── ops/
├── deploy.md
└── monitor.md
Commands become /git:commit, /code:review, /ops:deploy. The : acts as a namespace separator.
Skills
What is a skill?
A skill is an enriched Markdown file that defines an expertise Claude Code can draw on. Unlike commands (which are one-off actions), skills describe a complete operating mode.
Skills live in ~/.claude/skills/ and are global: they apply to all your projects.
~/.claude/
└── skills/
├── design-system/
│ └── skill.md
├── tdd/
│ └── skill.md
└── debugging/
└── skill.md
Anatomy of a skill
A skill is a Markdown file with YAML frontmatter that describes its identity:
---
name: "Skill Name"
description: "Short description. Claude Code uses this description to decide when to activate the skill."
---
The skill content: instructions, rules, examples, templates.
The description field is critical. It is what Claude Code reads to decide whether the skill is relevant in the current context. Be precise and concrete.
Skill 1: Design System
File ~/.claude/skills/design-system/skill.md:
---
name: "Design System Guardian"
description: "Ensures visual and structural consistency of UI code. Active when working on components, CSS, HTML, or style files."
---
## Role
You are the design system guardian. Every UI component must follow the rules below.
## Tokens
Use only the project's design tokens. Never hardcode values.
```css
/* Wrong */
color: #3b82f6;
padding: 16px;
font-size: 14px;
/* Correct */
color: var(--color-primary-500);
padding: var(--spacing-4);
font-size: var(--text-sm);
Components
Rules for every new component:
- Typed props: every prop has a strict TypeScript type (no
any) - Variants: use the variant pattern (via cva, class-variance-authority, or equivalent)
- Accessibility: aria-labels required on interactive elements, ARIA roles when relevant
- Responsive: mobile-first, breakpoints via tokens
- States: handle hover, focus, disabled, loading, error for every interactive element
File structure
components/
├── Button/
│ ├── Button.tsx # Component
│ ├── Button.test.tsx # Tests
│ ├── Button.stories.tsx # Storybook
│ └── index.ts # Export
Automatic review
When I create or modify a UI component, automatically check:
- Tokens used (no magic values)
- Props typed
- At least one test
- Accessible (no clickable div without a role)
- Responsive verified
### Skill 2: TDD (Test-Driven Development)
File `~/.claude/skills/tdd/skill.md`:
```markdown
---
name: "TDD Coach"
description: "Applies the Test-Driven Development workflow. Active when developing a new feature or fixing a bug."
---
## Strict workflow
For every feature or fix:
### 1. Red - Write the test first
Before touching production code:
- Write a test that describes the expected behavior
- The test MUST fail (red)
- If the test passes without modifying code, the test is testing nothing
```bash
# Run the test, verify it is red
npm test -- --watch path/to/file.test.ts
2. Green - Make the test pass
- Write the MINIMUM code to make the test pass
- No optimization, no refactoring, no generalization
- The code can be ugly, that is normal at this stage
3. Refactor - Clean up
- The test is green: you can refactor safely
- Eliminate duplication
- Improve naming
- Extract functions if needed
- Re-run tests after every change
Cycle
Repeat Red -> Green -> Refactor for each unit of behavior.
Rules
- NEVER write production code without a test that justifies it
- One commit per cycle (test + implementation + refactor)
- If a bug is found, write the test that reproduces it BEFORE fixing it
- Test size: one assert per test (unless testing a complete object)
When I request a feature
- Ask me to describe the expected behavior
- Propose a list of test cases
- I approve the list
- We start the first Red -> Green -> Refactor cycle
- We iterate until all cases are covered
### Skill 3: Systematic debugging
File `~/.claude/skills/debugging/skill.md`:
```markdown
---
name: "Systematic Debugger"
description: "Methodical debugging approach. Active when a bug is reported or code is not working as expected."
---
## Method
Never guess. Always follow this sequence:
### 1. Reproduce
- Identify the exact steps to reproduce the bug
- Document: input, expected output, actual output
- If the bug is intermittent, identify the conditions (timing, state, data)
### 2. Isolate
- Narrow the scope: which file, which function, which line
- Bisect technique: comment out half the code, does the bug persist? Continue in that half
- Verify hypotheses one at a time
### 3. Understand
- Read the offending code line by line
- Trace the data flow: where does each variable come from, what path does it take
- Look for side effects: mutations, global state, closures
### 4. Fix
- Fix the root cause, not the symptom
- If the fix is a quick patch, flag it and open an issue for the proper fix
- Write a test that reproduces the bug BEFORE fixing
### 5. Verify
- The reproduction test passes
- Existing tests still pass
- Manually test the original scenario
## Anti-patterns to avoid
- Modifying code randomly hoping it works
- Adding a try/catch to hide the error
- Fixing the symptom without understanding the cause
- Changing multiple things at once (impossible to know which one solved the problem)
## Report format
When fixing a bug, document:
- **Symptom**: what was observed
- **Cause**: why it happens
- **Fix**: what was changed and why
- **Prevention**: how to prevent it from recurring
Frontmatter and metadata
The YAML frontmatter of skills controls how Claude Code discovers and activates them.
---
name: "Readable name"
description: "When and why to use this skill. Be concrete."
---
Writing a good description
The description is the decision criterion. Claude Code reads it to evaluate the skill’s relevance to your request.
Bad examples:
description: "Helps with code" # Too vague
description: "Testing skill" # Says nothing about context
description: "Does stuff with React" # Not specific enough
Good examples:
description: "Applies the project's React conventions: custom hooks, error boundaries, suspense patterns. Active when creating or modifying React components."
description: "Generates optimized SQL queries for PostgreSQL. Active when working on queries, migrations, or database performance issues."
description: "Guides technical documentation writing: README, docstrings, API docs. Active when writing or updating documentation."
Sharing commands with your team
Custom commands in .claude/commands/ are part of the project. Version them in Git and the entire team benefits.
# Add commands to the repo
git add .claude/commands/
git commit -m "feat: add custom Claude Code commands for team workflow"
This creates a shared vocabulary. Everyone can type /review, /test, /commit and get consistent behavior, calibrated to the project’s conventions.
Recommended team convention
Document available commands in your CLAUDE.md:
## Available commands
- `/commit`: generates a conventional commit message
- `/review <file>`: code review with our quality criteria
- `/test <file>`: generates tests following our conventions
- `/deploy <env>`: deployment workflow (staging by default)
- `/refactor <file>`: guided step-by-step refactoring
This way, every new team member immediately knows which shortcuts exist.
Personal skills vs shared commands
The distinction matters:
- Commands (
.claude/commands/): shared, project-bound, in Git. Everyone uses them the same way. - Skills (
~/.claude/skills/): personal, in your home directory. They reflect your way of working, not the team’s.
If a skill proves useful for the whole team, turn it into a command and version it in the project.
Best practices
1. One command = one action
Each command should do one thing well. If your command does “review + test + deploy”, split it into three separate commands.
2. Be explicit in your instructions
Claude Code follows what you write. If you want a specific format, describe it. If you want it to wait for your approval, say so. Leave no room for ambiguity.
<!-- Too vague -->
Make a good commit.
<!-- Explicit -->
Analyze git diff --cached. Generate a message in the format type(scope): description.
Types: feat, fix, refactor, docs, test, chore.
Max 72 characters for the first line.
Propose the message and wait for my confirmation before committing.
3. Use Markdown to its full potential
Commands and skills are Markdown. Take advantage of it: headings, lists, code blocks, tables. Structure helps Claude Code understand and follow your instructions.
4. Test and iterate
Your first version will not be perfect. Use the command, observe what Claude Code does, adjust the prompt. In 2-3 iterations, you will have a reliable command.
5. Document existing conventions
The best commands are the ones that encode conventions your team already follows manually. Start by automating what you do every day.
6. The $ARGUMENTS placeholder is optional
If your command does not need arguments (like /commit), do not include $ARGUMENTS. If it does, also handle the case where the argument is empty (like /review which can review the current diff).
7. Combine commands and CLAUDE.md
Your CLAUDE.md file defines the global project context. Your commands define specific actions. The two complement each other: CLAUDE.md provides the conventions, and commands apply them.
# CLAUDE.md
## Commit conventions
Conventional format, in English, max 72 characters.
## Tests
Framework: Vitest. Convention: .test.ts files next to sources.
The /test command can then say “generate tests following the project’s conventions” and Claude Code will read CLAUDE.md to know what to do.