Extending Claude Code with skills
Agents like Claude Code, Codex or Cursor are great tools. Out of the box, you already get impressive results. Personally, I see them more as frameworks with a wide capacity for customization. In previous articles, I covered a few mechanisms already: hooks and memory, custom MCPs. Today, let’s tackle the customization tool that changes the game: skills.
Thanks to them, the agent goes from a generic assistant to a partner that follows my method, and that knows my projects and my constraints.
A prompt library for repetitive tasks
To put it bluntly, a skill is a quick way to send the model precise instructions for a task. You can think of it as a prompt library, but as we’ll see, the reality goes much further. It’s useful as soon as you have recurring tasks, and as a developer that happens a lot:
- writing a commit with a concise, normalized message
- creating a PR with a proper description
- reviewing, simplifying, refactoring code
- resolving a ticket end-to-end
You invoke a skill with /skill-name, either explicitly, or auto-triggered by the model when it detects that a task matches the description.
Skills vs commands
For a long time, the only way to customize the agent was custom commands: a markdown file in ~/.claude/commands/, and /my-command did the trick. Skills came later, more powerful, and in January 2026 Anthropic settled the matter by merging commands into skills (official doc): a .claude/commands/deploy.md file and a .claude/skills/deploy/SKILL.md skill both create /deploy, but a command remains a limited format, with no support directory and no auto-invocation by the model.
Concretely, a command is now a single-file skill, with a stripped-down frontmatter. Existing .claude/commands/ files keep working for backward compatibility, but in the long run there’s no guarantee they’ll stay supported. If you’re starting today, go straight to skills.
A single-file skill, the minimal format
Before diving into the full format, let’s look at the simplest possible skeleton of a skill: a single SKILL.md file, a two-line frontmatter, a prompt. No steps, no orchestration. It’s the lowest bar to clear if you’re new to Claude Code customization.
The frontmatter is a YAML block delimited by two
---lines at the top of a markdown file. It contains metadata that the tool reads before processing the body of the file (description, configuration…).
Here’s my commit skill, for example:
---description: Create a commit with auto-generated concise message---
# Commit Workflow
Generate a commit with a clean, concise message following conventional commit style.
## Step 1: Analyze Changes1. Run `git status` to see modified/staged files2. Run `git diff` to understand what changed3. Identify the type: feature, fix, refactor, docs, test, chore...
## Step 2: Generate Commit Message
Use conventional commit format:
- First line: type + colon + space + short description (max 50 chars)- Use imperative mood ("Add", "Fix", "Update")- Empty line, then bullet points for details (only if helpful)
## Step 3: Stage and Commit1. Stage relevant changes: `git add <files>`2. Create the commit with the generated message3. Show the result
## Important- NEVER amend existing commits- Commit message in EnglishThree sections: explain what the skill does, list the steps to produce a quality message, restate the non-negotiable rules. That’s it. I invoke it with /commit and the model runs through it.
Passing arguments
You can also parameterize a skill. The argument-hint field in the frontmatter is shown during autocomplete to remind the user what to type, and the $ARGUMENTS placeholder lets you pull what was typed into the prompt:
---description: Fix a GitHub issueargument-hint: [issue-number]---
Fix GitHub issue $ARGUMENTS following our coding standards.
1. Read the issue description with `gh issue view $ARGUMENTS`2. Understand the requirements3. Implement the fix4. Write tests5. Create a commitWith /fix-issue 142, $ARGUMENTS is replaced by 142 at runtime. Handy for parameterized tasks. If you have multiple arguments to manipulate independently, the arguments field lets you name them (for example arguments: [component, framework] to access $component and $framework in the prompt).
The full format: what the skill brings on top
With what we’ve seen so far, you can already go pretty far. But we’ve stayed on the minimal skeleton. The full format (official documentation, complete Anthropic guide) brings three major things the single-file version can’t offer.
1. Auto-discovery by the model
Unlike legacy commands which could only be triggered manually, a skill is auto-invocable by the model by default: Claude Code reads the description from the frontmatter, and if it matches what you’re asking for, it triggers the skill on its own.
Concretely, if I have a commit skill with a description like “Stage and commit current changes following conventional commit style”, just saying “make a commit” is enough for Claude Code to trigger it, without me having to type /commit.
That’s also why the quality of the description is critical: it’s the only criterion the model uses to decide whether to invoke the skill or not. If you want to disable that behavior, the disable-model-invocation: true frontmatter brings the skill back to user-invocable only.
2. A complete folder, not just a single file
A skill is a folder with a SKILL.md as the entry point, and as many supporting files as you need: templates, scripts, references, sub-steps…
~/.claude/skills/resolve/├── SKILL.md # entry point, read on invocation├── steps/│ ├── 00-initialization.md│ ├── 01-fetch-ticket.md│ └── ...├── references/│ └── conventions.md└── scripts/ └── helper.shThe SKILL.md is read as soon as the skill is invoked. The other files are only loaded when the SKILL.md references them explicitly, at the moment the model needs them.
That nuance changes everything. We’ve already seen it in the article on context engineering: context is the key. The bigger it gets, the less effective the model becomes, drowning under information.
With a skill, you build complex workflows without bloating the initial context. The SKILL.md orchestrates, sequences the steps, and progressively loads the details as execution unfolds. That’s exactly what we want: a detailed workflow without smothering the model under context.
By the way, Anthropic explicitly recommends keeping the SKILL.md under 500 lines and moving all detailed material (references, examples, long steps) into supporting files. The entry point should stay a table of contents, not an encyclopedia.
3. A much richer frontmatter
Beyond the description we saw on the single-file skill, the full format exposes a whole set of options to fine-tune behavior:
| Field | What it does |
|---|---|
description | What the skill does and when to use it. Critical for model auto-invocation. Keep it concise but explicit: it’s permanently loaded into context, and automatically truncated at 1,536 characters |
disable-model-invocation | When true, only the user can invoke the skill (disables model auto-discovery) |
user-invocable | When false, the skill doesn’t appear in the / menu: only the model can trigger it automatically when the description matches (the inverse of disable-model-invocation) |
allowed-tools | List of tools the model can use without asking permission while the skill is active |
model | Forces a specific model for the skill’s execution (e.g. opus for heavy tasks) |
effort | Effort level (low, medium, high, xhigh, max) during the skill |
context | When set to fork, runs the skill in an isolated subagent with its own context |
agent | Subagent type to use when context: fork is set (e.g. Explore, Plan) |
hooks | Hooks scoped to the skill’s lifecycle |
paths | Glob patterns that restrict auto-trigger to certain files |
arguments | Declares named arguments for $name substitutions in the content |
With all that, you go far beyond the simple prompt library. You build real orchestrated workflows, where each skill can decide its tools, its isolation, or even the model it uses.
My /resolve skill: orchestrating ticket resolution
To illustrate the power of the format, let me detail the skill I use every day: /resolve. The full code is available on GitHub if you want to dig into it.
The idea: take a YouTrack or GitHub ticket, and resolve it from A to Z. I transcribed my usual method into a series of steps, each in its own markdown file:
00-initialization.md # initial config, working folder creation01-fetch-ticket.md # fetch the ticket via the YouTrack/GitHub MCP02-analyze-complexity.md # complexity estimation, risk detection03-exploration.md # exploration of the code involved by the ticket04-create-plan.md # create a detailed implementation plan05-plan-validation.md # validate the plan with me before writing a single line06-implement.md # step-by-step implementation, following the plan07-simplify.md # simplification of the code produced (DRY, clarity, naming)08-review.md # automatic code review by a subagent09-finalize.md # commit, push, create the PRThe SKILL.md is an orchestrator: it decides which step to load based on the mode and current state, executes the instructions, then chains to the next one. Each step file is only read when needed.
Modes via arguments
Depending on how I invoke the skill, the workflow behaves differently:
/resolve PROJ-42: interactive mode, the skill prompts me to validate each key step/resolve PROJ-42 --plan-only: stop after plan validation, so I can read it again with fresh eyes/resolve PROJ-42 --auto: autonomous mode, no prompts, runs all the way to the PR/resolve --continue: resume where we left off
That last one deserves a word. The skill persists a state machine in a JSON file (.claude-work/{ticket-id}/status.json) that tracks progress step by step. If I close Claude Code in the middle, I can restart it the next day, the skill reads the state and resumes where we stopped. In practice that’s what makes the difference between a long prompt and a workflow that survives interruptions.
Why it works
The ingredients that make this skill hold up:
- Each step is focused: the model only reads instructions relevant to the current step, not the full orchestrator
- Tools are declared:
allowed-toolslists everything the skill needs to run uninterrupted. Internal tools (Read,Bash,Edit…), subagents, MCPs. No permission prompt that breaks the flow in the middle of a workflow - State is externalized: the JSON status file serves as memory between sessions
- Control stays with the user:
disable-model-invocation: true, I’m the one who decides when to launch this skill
For scale, the /resolve skill totals about 3,000 lines, but the entry point (SKILL.md) is only ~300 lines: that’s all that gets loaded by default when the skill starts. The rest (the 10 step files, the references file) only joins the context when the orchestrator actually needs it. Cramming all of that into a single file would blow up the context at invocation, and the model would get lost in a mass of instructions where 90% don’t concern the current step. So a workflow this size could never have been usable in the single-file format.
What I’m not showing you
To be honest: the version I’m describing here is a snapshot taken after several months of iteration. The first version of the skill didn’t hold up. It crashed in the middle, skipped steps, forgot decisions taken earlier, and the produced code didn’t match the ticket 100%. I had to rework the orchestrator dozens of times, re-split steps, move rules from one file to another, add guardrails, remove others that were getting in the way more than helping.
On the result side, I use it on all my tickets, important or not. That said, I never ship the first output as-is: I read it, I iterate several passes on the produced code, I poke at where it drifts, I ask for adjustments. Nothing new about that, by the way, it’s what I was already doing before AI when I wrote code by hand: no first draft ever ships to prod as-is.
Skill-creator: the skill that creates skills
Last point, and not the least: building a well-designed skill isn’t trivial. There’s the description to polish, the file split to anticipate, and all the frontmatter fields to arbitrate.
Luckily, Anthropic published an official plugin containing a skill dedicated to creating skills: /skill-creator.
The principle: you describe what you want to automate, and it generates the skill skeleton with best practices baked-in. Coherent frontmatter, description calibrated for auto-invocation, and a clean folder split. With examples when the topic warrants it.
Build your own skills, don’t copy others’
One last word, because it’s probably the most common trap. Since skills exist, ready-made packs have been popping up: plugins shared on GitHub, collections sold by AI influencers, “ultimate starter packs” promising to transform your workflow in two clicks. It’s tempting. But in my opinion it’s a bad idea.
A skill, by construction, encodes a way of working. Mine, with my Docker worktrees, my custom YouTrack MCPs and my multi-pass plan validation process, isn’t yours. If you blindly install a skill designed for someone else, at best it’ll never trigger because the description doesn’t match your vocabulary, at worst it’ll force you into a method that isn’t yours, and you’ll end up fighting the agent instead of steering it.
That said, watch out for the all-or-nothing trap. Between blindly installing someone else’s skill and rewriting everything from scratch, there’s a third path, more efficient: fork, understand, adapt. Read public skills, take the ones that come close to your need, but open them up, edit the description to match your vocabulary, rework the steps to fit your process.
And if you’re starting from a blank page, pick a task you do three times a week, open a SKILL.md, write the steps as you’d explain them to a new colleague. That’s already a skill. The rest will come through iteration.