InfoWok
Beginner

Claude Code Skills Tutorial: Build Your First Skill (2026)

Claude Code Skills turn a repeated prompt into a folder Claude reaches for on its own. Build one real Skill and run it in this hands-on tutorial.

SK
Sukhveer Kaur
Published August 1, 2026
7 min read
Claude Code Skills tutorial title card in a dark terminal-style layout with the AI Engineering indigo accent, subtitled SKILL.md, triggers, and a real reusable skillAI Engineering
CLAUDE CODE SKILLS
On this page +

You keep pasting the same three-paragraph prompt into Claude Code every day. “Write a PR description. Pull the ticket number from the branch name. Flag anything risky in the diff.” Claude Code Skills turn that repeated prompt into a folder Claude reaches for on its own. It takes about ten minutes to build your first one.

This guide builds one real Skill from scratch: pr-notes, a Skill that reads your uncommitted changes, writes a PR description, and flags risks. That’s the exact job a lot of developers still do by hand before every pull request. By the end you’ll have it running. You’ll also know the one mistake that makes most first Skills silently never fire.

🎯 Key takeaways
  • A Skill is just a folder with a SKILL.md file — YAML frontmatter that tells Claude when to use it, plus markdown instructions for what to do.
  • The description field is the trigger, not documentation. Claude matches your prompt against that text, so a vague description means the Skill never loads.
  • You’ll build one real, reusable Skill in this guide, run it against an actual diff, and fix the exact reason first-timers’ Skills go quiet.
🟢 Beginner⏱️ 10 minStack: Claude Code CLI + a git repository with uncommitted changes
Before you start
  • Claude Code installed and working — see Run Claude Code for Free if you haven’t set it up yet
  • A local git repository you can make a throwaway edit in
  • Comfort creating a folder and a text file — that’s the entire skill requirement

What Claude Code Skills Actually Are#

A Skill is not magic — it’s a directory Claude reads on demand, not a special model feature. Every Skill needs exactly one required file, SKILL.md. That file splits into two parts: YAML frontmatter between --- markers that controls when Claude uses it, and markdown content underneath that Claude follows once it loads.

Two things happen at different times, and mixing them up is where the confusion starts:

  • Always in context: the Skill’s name and description — a short listing so Claude knows what’s available.
  • Loaded only when used: the full body of SKILL.md — the actual instructions. These only enter the conversation when the description matches your request, or you invoke the Skill directly.

That second point is what makes Skills cheap. A CLAUDE.md file gets read into every session, whether you need it or not. A Skill’s instructions cost nothing until Claude actually decides to use them. Per the official docs, that’s the whole reason to move a growing checklist out of CLAUDE.md and into a Skill.

The diagram is the entire mental model: your description is a filter, and everything past it only exists once the filter passes.

📌 Note

Skills follow the open Agent Skills standard, so the same SKILL.md format works across other tools that adopt it, not just Claude Code.

Build It: A Real, Reusable Skill#

Skip the toy example — build something you’d actually keep. We’ll make pr-notes, a Skill that turns your uncommitted diff into a PR description with a risk checklist. It uses a scripts/ helper to pull the ticket number out of your branch name.

Start with the folder. Project Skills live in .claude/skills/ and get committed with your repo, so your whole team gets them:

bash
mkdir -p .claude/skills/pr-notes/scripts

Now write SKILL.md. This is the file that matters most. Get the description right and everything else follows:

yaml
---
name: pr-notes
description: Drafts a PR title, description, and risk checklist from your
  uncommitted git changes. Use when the user asks for a PR description, a
  commit summary, or "what should I put in this pull request."
---
 
## Current changes
 
!`git diff HEAD`
 
## Ticket ID
 
!`bash .claude/skills/pr-notes/scripts/branch-ticket.sh`
 
## Instructions
 
Using the diff and ticket ID above, write:
 
1. A one-line PR title, prefixed with the ticket ID if one was found
2. A 2-4 sentence description of what changed and why
3. A short risk checklist: missing error handling, missing tests, hardcoded
   values, or anything that touches auth or payments
 
If the diff is empty, say there is nothing to summarize.

Then add the helper script it references, scripts/branch-ticket.sh. It’s a plain shell script that pulls a ticket ID like PROJ-482 out of your current branch name:

bash
#!/usr/bin/env bash
branch=$(git branch --show-current)
ticket=$(echo "$branch" | grep -oE '[A-Z]{2,}-[0-9]+' | head -1)
if [ -n "$ticket" ]; then
  echo "$ticket"
else
  echo "no ticket ID found in branch name"
fi

Two things are worth noticing here. The !`command` syntax is dynamic context injection. Claude Code runs that command and swaps the line for its output before Claude ever reads the file. The instructions arrive with your actual diff already filled in, not a placeholder Claude has to guess at.

And scripts/branch-ticket.sh sits in its own file on purpose, so it gets executed, not loaded into context. A growing Skill should keep the main file lean and push heavier logic into supporting files — the official guidance recommends keeping SKILL.md under 500 lines.

💡 Tip

Make the script executable once — chmod +x .claude/skills/pr-notes/scripts/branch-ticket.sh — and it runs the same way every time the Skill fires.

Bottom line: the whole Skill is two small files, and neither requires touching any config elsewhere. Claude Code discovers it from the folder alone.

Run It: How Claude Decides to Load Your Skill#

This is the step most tutorials skip, and it’s the one that actually explains why Skills sometimes go quiet. Make a small edit in your repo. Then open Claude Code in that project and try it two ways.

Let Claude pick it up on its own, by asking something close to the description:

text
Draft a PR description for what I just changed

Or skip the guesswork and invoke it by name:

text
/pr-notes

Either way, you should see a title, a short description, and a risk checklist land in the response, built from your real diff. I ran branch-ticket.sh on its own first, on two throwaway branches — one named after a ticket ID, one not — and it printed the right ticket ID in the first case and a clear “no ticket ID found” fallback in the second. That’s the same check worth running before you trust the Skill: verify the script’s logic before you verify the Skill’s trigger.

The trigger itself is the part I rewrote. My first draft of the description just said “helps with pull requests” — technically accurate, but not close to what anyone actually types. The official troubleshooting guidance is blunt about the fix: check whether the description includes the words a user would naturally say. I rewrote mine to spell out “PR description,” “commit summary,” and the literal phrase “what should I put in this pull request” — the version you saw above — because those are closer to what actually shows up in a real prompt.

⚠️ Warning

If the frontmatter YAML is malformed — a missing colon, bad indentation — Claude Code still loads the Skill body with empty metadata. /pr-notes keeps working, but Claude has no description to match against, so it never triggers automatically. Run with --debug if a Skill seems to vanish.

Make It Reusable: Scope, Arguments, and Control#

A Skill that only works in one repo isn’t much better than the prompt you were pasting before. Three frontmatter fields decide how far pr-notes travels and who can fire it.

disable-model-invocation: true restricts a Skill to manual /name invocation only. That’s the right call for anything with side effects, like a deploy or commit Skill you don’t want Claude deciding to run because your code “looks ready.” allowed-tools pre-approves specific tools for the turn that invokes the Skill, so a Skill that shells out to git doesn’t stop and ask permission every time.

Personal vs. project placement decides reach. ~/.claude/skills/pr-notes/ follows you across every project on your machine. .claude/skills/pr-notes/ in a repo ships with the codebase for your whole team instead.

I keep pr-notes project-scoped and leave model invocation on, because drafting a PR description has no real side effects. Worst case, Claude writes one nobody asked for and I ignore it. A deploy Skill in the same repo gets disable-model-invocation: true — I don’t want an eager agent shipping to production just because the diff “looked done.”

🔑 Key point

One description field decides whether Claude ever picks up your Skill on its own. Everything else in this section is about scope and safety once it does.

When to Reach for a Skill vs. a Subagent#

A Skill adds knowledge or a procedure to the conversation Claude is already having with you. Think of it as reference material or a checklist, not a separate worker. A subagent is a different thing: its own context window, running its own task, reporting back a summary instead of a stream of tool calls in your main thread.

pr-notes belongs as a Skill because it’s a quick, single-turn job. A task like “research every open dependency vulnerability across five services” belongs in a subagent instead, because it needs room to explore without flooding your main conversation. If you’re not sure which one a given workflow needs, that decision deserves its own deep dive — see the read-next link below.

Testing It and Common Mistakes#

Verify the basics before you trust a Skill in daily use. Run /pr-notes on a repo with real uncommitted changes. Confirm the ticket ID, title, and risk list all reflect your actual diff, not a hallucinated one. Then deliberately break it once: rename the branch to something without a ticket pattern, and confirm the script prints “no ticket ID found” instead of erroring.

Three mistakes explain almost every “my Claude Code Skills won’t trigger” report:

  • Vague description. “Helps with PRs” matches nothing specific. “Drafts a PR title, description, and risk checklist from your uncommitted git changes” matches the words people actually type.
  • Wrong folder. A Skill dropped in the repo root instead of .claude/skills/<name>/SKILL.md simply isn’t discovered. Claude Code only watches the documented locations.
  • Over-broad scope. A Skill that tries to handle PRs, deploys, and changelog generation all at once ends up with a description too generic to match reliably on any one job. One job per Skill.

What to Build Next#

I’d extend pr-notes next with a second script that checks whether the diff touches any file matching **/migrations/**, and adds a dedicated “database migration” line to the risk checklist. That’s the one category of change that’s bitten me hardest in code review. If your team has a recurring checklist — security review, accessibility, a release runbook — that’s the shape of your next Skill: one folder, one clear job, and a description written in the exact words people already use to ask for it.

🎯 Key takeaways
  • Two files were enough: SKILL.md for the trigger and instructions, scripts/branch-ticket.sh for the one piece of logic that didn’t belong inline.
  • The description field does all the triggering work — write it in the words your team actually says.
  • Keep one job per Skill, and split anything that’s trying to do two things.

Common mistakes, recapped: vague description, wrong folder, over-broad scope — in that order of how often they bite. Fix the description first. It solves most “my Skill never runs” reports on its own.

What repeated prompt are you turning into a Claude Code Skill first?

Read next: Claude Code: Skills vs Subagents vs Agent Teams (coming soon) covers the fuller decision between the three, for when a Skill alone isn’t enough. Related: Claude Code MCP with Local Models for pairing Skills with MCP tools, and What Is an MCP Server? if you’re deciding between building a Skill or an MCP tool for a given job.

Frequently asked questions

What is a Claude Code Skill? +
A Skill is a folder containing a SKILL.md file with YAML frontmatter and markdown instructions. Claude loads the description into context automatically, and loads the full instructions only when the description matches what you're asking for, or when you invoke it directly with /skill-name.
Why doesn't my Skill trigger automatically? +
The most common cause is a vague description field. Claude matches your prompt against that field's text, so if it doesn't contain the words a user would naturally type, Claude has nothing to match against and skips the Skill.
Where do I put a Skill so my whole team gets it? +
Project Skills live in .claude/skills/ and are committed to the repo, so anyone who clones it gets the Skill. Personal Skills in ~/.claude/skills/ only apply on your machine, across all your projects.
Can I stop Claude from running a Skill automatically? +
Yes. Set disable-model-invocation to true in the frontmatter and only you can trigger it, by typing /skill-name. This suits anything with side effects, like a deploy or commit Skill.
What is the difference between a Skill and a slash command? +
Very little today. Files in .claude/commands/ still work, but Skills are the recommended format because a Skill's folder can also hold supporting scripts, examples, and reference files that a plain command file cannot.

References

  1. Extend Claude with skills — Claude Code official docs
  2. Agent Skills open standard
  3. Skill authoring best practices — Claude Platform docs
Written by
Sukhveer Kaur
Sukhveer KaurSoftware Developer & AI Engineer

Sukhveer is a software developer specialising in AI systems and backend engineering. She has hands-on experience designing agentic AI applications, working with large language model pipelines, autonomous agent frameworks, and cloud-native services in Java and Python. At InfoWok, she bridges the gap between cutting-edge AI research and practical implementation — helping developers understand and apply emerging technologies through clear, experience-backed writing.

New AI engineering guides, the day they ship

Real Python, production depth. No digest spam.

Comments