·17 min read·agent-skills

Writing Your First SKILL.md: A Step-by-Step Guide

Learn to create a working Agent Skill from scratch. Covers SKILL.md frontmatter, body structure, scripts, reference files, testing, and sharing — with complete copy-paste examples.

DH
Danny Huang

What Is a SKILL.md?

A skill is a folder containing a SKILL.md file that teaches an AI coding agent a specialized capability. The agent reads the file, learns when and how to apply the skill, and executes it — across Claude Code, Codex CLI, GitHub Copilot, Gemini CLI, and 35+ other platforms that support the open standard.

Skills replace the old pattern of pasting long instructions into system prompts or chat messages. Instead of repeating yourself, you write it once in a structured format and every agent that enters your project picks it up automatically.

For the full ecosystem overview — marketplaces, security auditing, cross-agent compatibility, and the Superpowers framework — see the Agent Skills Complete Guide.

Anatomy of a Skill

Every skill lives in a directory with this structure:

my-skill/
├── SKILL.md          # Required. Entry point with frontmatter + instructions.
├── scripts/           # Optional. Executable scripts the agent can run.
├── references/        # Optional. Long-form docs loaded only when needed.
└── assets/            # Optional. Static files — templates, logos, samples.

SKILL.md is the only required file. It contains YAML frontmatter (metadata the agent reads at startup) and a markdown body (the actual instructions). Keep the body under 500 lines — the agent's context window is finite, and bloated skills degrade performance.

scripts/ holds deterministic operations. When your SKILL.md tells the agent to run a script, the agent executes it via bash and receives the output. The script source code never enters the context window — only the result does. This is critical for tasks where you need exact, repeatable behavior (validation, formatting, data transformation).

references/ holds detailed documentation that the agent loads on demand. This is the progressive disclosure pattern: the agent reads SKILL.md first, then pulls in reference files only when the task requires deeper context. API docs, style guides, and protocol specifications belong here.

assets/ holds static files the skill needs — templates, sample outputs, icon files, configuration snippets. These are not instructions; they are materials the skill operates on or references.

Step 1: The Frontmatter

Create a directory and a SKILL.md file:

mkdir -p .claude/skills/commit-msg
touch .claude/skills/commit-msg/SKILL.md

Open the file and start with YAML frontmatter between --- markers:

---
name: commit-msg
description: >
  Enforces Conventional Commits format for all git commit messages.
  Use this skill whenever the user asks to commit changes, create a
  commit message, or when you detect a git commit operation. Ensures
  every commit follows the pattern: type(scope): description.
  Supported types: feat, fix, docs, style, refactor, test, chore, perf, ci.
  Rejects commits that don't match the format.
---

Two fields matter here:

name — Becomes the /commit-msg slash command. Lowercase letters, numbers, and hyphens only. Maximum 64 characters. This is what users type to invoke the skill manually.

description — This is the primary trigger mechanism. When a user's request comes in, the agent reads all skill descriptions to decide which skills to load. A vague description means the skill rarely triggers. A specific description with concrete trigger conditions means it fires reliably.

Writing a Good Description

The description serves two purposes: it tells the agent what the skill does and when to activate it. Be assertive about trigger conditions. Cover edge cases.

Bad description:

Helps with commit messages.

The agent has no idea when to trigger this. "Helps with" is meaningless. No trigger conditions, no specificity.

Good description:

Enforces Conventional Commits format for all git commit messages.
Use this skill whenever the user asks to commit changes, create a
commit message, or when you detect a git commit operation.

This tells the agent exactly what it does (enforce Conventional Commits) and exactly when to activate (commit operations, commit message requests, detected git commit actions). The agent can make a clear yes/no decision.

Optional Frontmatter Fields

allowed-tools — Glob patterns restricting which tools the skill can access. Useful for security-sensitive skills. Example: allowed-tools: ["Bash", "Read"] limits the skill to reading files and running commands, preventing it from making web requests or editing files outside the project.

disable-model-invocation — Set to true to prevent auto-triggering. The skill only activates via the /name slash command. Use this for skills with side effects where timing matters — deployment skills, database migration skills, anything destructive.

Step 2: Writing the Body

The body goes below the frontmatter. Structure it in three sections: when to trigger, what to do, and how to do it.

Here is the complete SKILL.md for our Conventional Commits skill:

---
name: commit-msg
description: >
  Enforces Conventional Commits format for all git commit messages.
  Use this skill whenever the user asks to commit changes, create a
  commit message, or when you detect a git commit operation. Ensures
  every commit follows the pattern: type(scope): description.
  Supported types: feat, fix, docs, style, refactor, test, chore, perf, ci.
  Rejects commits that don't match the format.
---

# Conventional Commit Messages

## When to Activate

Activate this skill when:
- The user asks you to commit changes
- The user asks you to write a commit message
- You are about to run `git commit` as part of a larger task
- The user asks to amend a commit message

## Commit Format

Every commit message MUST follow this pattern:

type(scope): short description

[optional body]

[optional footer]


### Allowed Types

| Type | When to Use |
|------|------------|
| feat | A new feature visible to users |
| fix | A bug fix |
| docs | Documentation-only changes |
| style | Formatting, semicolons, whitespace — no logic change |
| refactor | Code restructuring that doesn't fix a bug or add a feature |
| test | Adding or correcting tests |
| chore | Build, tooling, dependency updates |
| perf | Performance improvement |
| ci | CI/CD configuration changes |

### Rules

1. Type is required. Scope is optional but encouraged.
2. Description starts with lowercase, no period at the end.
3. Description is imperative mood: "add feature" not "added feature".
4. Body wraps at 72 characters.
5. Breaking changes MUST include `BREAKING CHANGE:` in the footer.
6. If multiple logical changes exist, suggest splitting into separate commits.

### Examples

Good:
- `feat(auth): add OAuth2 login with Google provider`
- `fix(api): handle null response from payment gateway`
- `refactor(db): extract connection pooling into shared module`

Bad:
- `updated stuff` — no type, vague description
- `Fix: Bug` — wrong format, not descriptive
- `feat: Added new feature for users.` — past tense, period, vague

## Process

1. Analyze the staged changes with `git diff --cached`
2. Determine the appropriate type based on the nature of changes
3. Identify the scope from the primary directory or module affected
4. Write a concise description in imperative mood
5. If changes are complex, add a body explaining the "why"
6. Present the commit message to the user for approval before committing

The structure is deliberate. The agent reads "When to Activate" to confirm the skill applies. It reads the format section to understand the rules. It reads the process section to know the execution steps. Each section is self-contained — if a search engine or an AI overview surfaces any single section, it makes sense on its own.

Step 3: Adding Scripts

Some tasks should be scripts, not instructions. The rule: if the task is deterministic and the natural-language instructions would be fragile, write a script.

Commit message validation is a good example. Instead of hoping the agent correctly implements regex-based validation from prose instructions, give it a script:

mkdir -p .claude/skills/commit-msg/scripts

Create .claude/skills/commit-msg/scripts/validate.sh:

#!/bin/bash
# Validates a commit message against Conventional Commits format.
# Usage: ./validate.sh "feat(auth): add login"
# Exit 0 = valid, Exit 1 = invalid with error message.

MSG="$1"

if [ -z "$MSG" ]; then
  echo "Error: No commit message provided."
  exit 1
fi

PATTERN='^(feat|fix|docs|style|refactor|test|chore|perf|ci)(\([a-z0-9-]+\))?: .+$'

FIRST_LINE=$(echo "$MSG" | head -n1)

if ! echo "$FIRST_LINE" | grep -qE "$PATTERN"; then
  echo "Invalid commit message: '$FIRST_LINE'"
  echo ""
  echo "Expected format: type(scope): description"
  echo "Allowed types: feat, fix, docs, style, refactor, test, chore, perf, ci"
  exit 1
fi

if echo "$FIRST_LINE" | grep -qE '\.$'; then
  echo "Invalid: description should not end with a period."
  exit 1
fi

echo "Valid commit message."
exit 0

Then reference it in the SKILL.md body:

## Validation

Before committing, validate the message by running:

```bash
bash .claude/skills/commit-msg/scripts/validate.sh "your commit message here"

If validation fails, fix the message and re-validate.

When the agent runs this script, it executes via bash and only receives the stdout/stderr output. The script source never enters the context. This keeps the context window clean and the validation behavior perfectly deterministic.

Step 4: Reference Files

Reference files solve the problem of SKILL.md getting too long. If your skill needs to know about a large API, a detailed style guide, or a complex protocol, put that information in a reference file and tell the agent when to read it.

Create .claude/skills/commit-msg/references/team-conventions.md:

# Team Commit Conventions

## Scope Naming

Our project uses these scope names consistently:

| Scope | Meaning |
|-------|---------|
| api | Backend API routes |
| ui | Frontend React components |
| db | Database schemas and migrations |
| auth | Authentication and authorization |
| config | Configuration and environment |
| ci | CI/CD pipelines |
| deps | Dependency updates |

## Breaking Change Policy

Breaking changes require:
1. `BREAKING CHANGE:` footer in the commit
2. A linked issue or RFC number
3. Migration notes in the commit body

## Multi-Package Repos

For monorepo commits affecting multiple packages:
- Use the most-affected package as scope
- List other affected packages in the commit body
- If truly cross-cutting, omit scope entirely

## Release Automation

Our CI reads commit types to auto-generate changelogs:
- `feat` → "Features" section, triggers minor version bump
- `fix` → "Bug Fixes" section, triggers patch version bump
- `BREAKING CHANGE` → triggers major version bump
- All other types → excluded from changelog

Reference this file in SKILL.md:

## Team-Specific Conventions

For scope naming, breaking change policy, and release automation rules,
read the reference file at `references/team-conventions.md`.
Only load this file when the user asks about scopes, breaking changes,
or when you encounter an ambiguous situation.

This is progressive disclosure in action. The agent loads SKILL.md (short, focused). Only when the task involves scope naming or breaking changes does it load the reference file (detailed, specific). Context stays lean. A full explanation of progressive disclosure and its impact on agent performance is covered in the Agent Skills Complete Guide.

Step 5: Testing Your Skill

A skill that has never been tested is a skill that does not work. Here is how to verify yours.

Install the Skill

If you placed it in .claude/skills/, it is already installed for Claude Code. The skill auto-registers when Claude Code starts a session in your project.

For other agents, the installation path varies — Codex CLI uses .codex/skills/, Copilot uses .github/skills/. The SKILL.md format is the same across all of them. For a full comparison of AI CLI tools and their skill directories, the linked guide covers every platform.

Manual Invocation

Type the slash command directly:

/commit-msg

Claude Code loads the full SKILL.md body and enters the skill's context. Now test it with a prompt:

I've staged changes to the auth module. Write me a commit message.

Verify that the agent follows your format rules, picks the right type, and suggests an appropriate scope.

Auto-Trigger Testing

Test that the skill activates without the slash command. Start a fresh session and ask:

Commit the current staged changes.

If your description is written well, the agent should auto-load the commit-msg skill and produce a Conventional Commits-formatted message. If it does not trigger, your description needs work — make the trigger conditions more explicit.

Debug with Trial Prompts

Test edge cases with specific prompts:

  • "Commit these changes" (should trigger)
  • "Write a commit message for this refactor" (should trigger)
  • "What did the last commit change?" (should NOT trigger — this is a read operation, not a commit operation)
  • "Amend the last commit message" (should trigger)

If the skill triggers on prompts where it should not, narrow the description. If it fails to trigger on prompts where it should, broaden the description or add more trigger conditions.

The Termdock Workflow

The fastest testing loop is editing the SKILL.md in one terminal pane while testing it in another. Change a rule in SKILL.md, switch to the agent pane, invoke the skill, observe the behavior, switch back, iterate. No window management overhead, no tab switching — just a tight feedback loop between authoring and testing.

Try Termdock Drag Resize Terminals works out of the box. Free download →

Step 6: Sharing Your Skill

Skills live at three levels:

Personal — Place skills in ~/.claude/skills/ (or the equivalent for your agent). These load in every project you open. Good for personal preferences: your commit style, your code review checklist, your preferred error handling patterns.

Project — Place skills in .claude/skills/ within the project repository. These load for everyone who works on the project. Good for team conventions: the project's commit format, its API design rules, its testing requirements. Commit the skill directory to git so the whole team gets it.

Marketplace — Publish to a skill marketplace (SkillsMP, Skills.sh, ClawHub) for anyone to install. These are skills with broad utility: a "conventional commits" skill, a "code review" skill, a "dependency audit" skill. Publishing requires packaging your skill directory and submitting it to the marketplace's registry.

Installing from GitHub

Skills can be installed directly from a GitHub URL:

claude skill install github.com/username/my-commit-skill

This clones the skill directory into your local skills folder. The skill is available immediately — no restart needed.

For Codex CLI:

codex skill add github.com/username/my-commit-skill

The format is identical. The agent reads the same SKILL.md, the same scripts, the same references. Write once, run on any agent that supports the standard.

Common Mistakes

Description too vague. A description like "helps with code" will never auto-trigger because it matches everything and nothing. The agent cannot distinguish when to use it. Fix: add explicit trigger conditions — "Use when the user asks to...", "Activate when you detect..."

SKILL.md too long. Skills over 500 lines consume too much context. The agent's reasoning quality degrades when context is full of instructions instead of the actual code being worked on. Fix: move detailed content to reference files. SKILL.md should be an overview and navigation hub, not an encyclopedia.

Over-specifying HOW instead of WHY. Instructions like "run git add . then run git commit -m" are brittle. The agent knows how to run git commands. What it does not know is your team's conventions — the WHY. Fix: explain the principles and rules. Let the agent figure out the implementation. "All commits must use Conventional Commits format with these allowed types..." is better than a step-by-step shell script.

No trigger conditions. A SKILL.md that explains WHAT to do but never says WHEN to activate is a skill that only works via slash command. If you want auto-triggering, the body needs a clear "When to Activate" section that the agent can match against the current task.

Hardcoded paths. Skills that reference absolute paths (/Users/john/project/src/...) break for everyone else. Use relative paths from the project root, or let the agent discover paths dynamically.

Full Example: A "Code Review" Skill

Here is a complete, working skill you can copy into your project and use immediately. It includes a SKILL.md and a reference file.

Directory Structure

.claude/skills/code-review/
├── SKILL.md
└── references/
    └── review-checklist.md

SKILL.md

---
name: code-review
description: >
  Performs structured code reviews following the team's quality standards.
  Use this skill when the user asks to review code, review a PR, review
  changes, check code quality, or when you detect a code review request.
  Covers correctness, security, performance, readability, and test coverage.
  Outputs a structured review with severity ratings for each finding.
---

# Code Review Skill

## When to Activate

Activate when:
- The user asks you to review code or a pull request
- The user asks to "check" or "audit" specific files
- The user asks about code quality of recent changes
- You are asked to compare code before and after a change

Do NOT activate when:
- The user asks you to write or implement code (that is a coding task, not a review)
- The user asks about git history without requesting quality feedback

## Review Process

1. **Identify the scope.** Determine which files or changes to review.
   - If reviewing staged changes: `git diff --cached`
   - If reviewing a branch: `git diff main...HEAD`
   - If reviewing specific files: read them directly

2. **Analyze each file.** For every file in scope, evaluate against
   the checklist in `references/review-checklist.md`.

3. **Classify findings.** Every finding gets a severity:
   - **Critical** — Will cause bugs, security issues, or data loss. Must fix.
   - **Warning** — Code smell, performance risk, or maintainability issue. Should fix.
   - **Suggestion** — Style preference, minor improvement. Nice to fix.

4. **Output the review** in this format:

Code Review: [scope description]

Summary

[1-3 sentence overall assessment]

Findings

Critical

  • [file:line] — [description of issue and why it matters]

Warnings

  • [file:line] — [description and recommendation]

Suggestions

  • [file:line] — [description and recommendation]

Verdict

[APPROVE / REQUEST CHANGES / NEEDS DISCUSSION]


5. **If no issues found**, say so explicitly. Do not invent problems.

## Principles

- Review the code, not the author.
- Every finding must explain WHY it is a problem, not just WHAT is wrong.
- Prefer fewer high-quality findings over many nitpicks.
- If you are uncertain about a finding, flag it as "Suggestion" with a note.
- Acknowledge good patterns when you see them.

references/review-checklist.md

# Code Review Checklist

## Correctness
- [ ] Does the code do what it claims to do?
- [ ] Are edge cases handled (null, empty, negative, overflow)?
- [ ] Are error paths tested, not just happy paths?
- [ ] Do async operations handle failures and timeouts?

## Security
- [ ] No secrets hardcoded (API keys, passwords, tokens)
- [ ] User input is validated and sanitized
- [ ] SQL queries use parameterized statements
- [ ] Authentication checks are present where needed
- [ ] No overly permissive CORS or CSP headers

## Performance
- [ ] No N+1 query patterns
- [ ] Large collections are paginated, not loaded entirely
- [ ] Expensive operations are cached where appropriate
- [ ] No unnecessary re-renders in React components
- [ ] Database indexes exist for queried columns

## Readability
- [ ] Functions are under 40 lines
- [ ] Nesting depth is 3 levels or fewer
- [ ] Variable names describe what they hold
- [ ] Comments explain WHY, not WHAT
- [ ] No dead code or commented-out blocks

## Testing
- [ ] New code has corresponding tests
- [ ] Tests cover both success and failure cases
- [ ] Tests are deterministic (no flaky assertions)
- [ ] Test names describe the behavior being verified
- [ ] Mocks are minimal — prefer real implementations

## Architecture
- [ ] Changes follow existing project patterns
- [ ] No circular dependencies introduced
- [ ] Public API surface is minimal
- [ ] Breaking changes are documented
- [ ] Backward compatibility is maintained

Copy the code-review directory into .claude/skills/ in any project. Start Claude Code (or any compatible agent), and type /code-review or ask "review the changes on this branch." The agent loads the skill, follows the review process, references the checklist as needed, and outputs a structured review.

This is a skill you will use daily. Adapt the checklist to your team's standards. Add reference files for domain-specific rules — a security checklist for fintech, a performance checklist for real-time systems, an accessibility checklist for frontend code.

DH
Free Download

Ready to streamline your terminal workflow?

Multi-terminal drag-and-drop layout, workspace Git sync, built-in AI integration, AST code analysis — all in one app.

Download Termdock →
#skill-md#agent-skills#claude-code#tutorial

Related Posts