Articles / AI

Skills: I Know Kung Fu!

Nico Devs·
Skills: I Know Kung Fu!

There's this famous scene in The Matrix where Neo gets a software update that instantly teaches him Kung Fu. I'd love that in real life! Maybe then I could finally play the keyboard without sounding like a cat walked across it.

Unfortunately, we don't have that. But we do have skills for our agents, which, while not nearly as cool, can still save you a big chunk of time.

Think about everything you asked Claude to do this week. Was there one request you found yourself repeating over and over, using almost the exact same prompt?

For me, it's creating GitHub PRs.

Open a PR. Keep the description short. No emojis. Don't list yourself as a co-author. Don't use the word seamlessly...

I'd type it, the agent would do a decent job, and a couple of hours later I'd find myself typing the exact same thing again for the next branch.

Open a PR. Short description. No emojis...

You know the saying, "This meeting could have been an email"? That's how I feel every time I catch myself repeating a prompt: "This prompt should have been a skill."

What Is a Skill?

A skill is a recipe you write once so the agent can perform the same task over and over without you narrating every step again.

Unlike a prompt, a skill can bring along everything the task needs: scripts, references, templates, rules, etc.

You create a folder under .claude/skills/. Call it whatever you like, for example joke. Inside it, create a SKILL.md file.

SKILL.md should have two parts:

  • A frontmatter block with a name and a description.
  • A markdown body with the actual instructions, written in natural language.

Here's an example:

.claude/skills/joke/SKILL.md
---
name: joke
description: Tells a funny joke.
---

Tell me a funny joke. Extra points if it is about programmers!

To use a skill, you can either:

  • Invoke it as a command: type a slash followed by the skill's name, e.g. /joke, and press enter.
  • Ask in natural language: if the skill's description matches what you asked the agent to do, Claude will automatically pick it up. Here, something like "tell me a joke" will do.

If you'd rather invoke the skill explicitly every time, add disable-model-invocation: true to the frontmatter.

The description is more important than it looks. Whenever you ask Claude to do something, it uses the descriptions of your available skills to decide whether one matches the request.

If the description is vague, the skill rarely gets used. If it's specific, Claude starts reaching for it automatically.

A Skill Is a Folder, Not Just a File

So far we've only seen a single markdown file, but a skill is really the entire folder around the markdown file. This folder can contain scripts, reference documents, templates, and anything else the workflow needs.

Tip 1: Skills Can Execute Scripts

If you describe an action in natural language, the model is usually smart enough to figure out how to do it and write code on the fly to make it happen. Sometimes, though, you want something more predictable.

You can include commands and code snippets in your skills. So instead of saying "Clear the logs" and hoping the model does the right thing, you can simply tell it what to run:

SKILL.md
- Clear the logs.
+ Run `rm -rf ./logs` to clear the logs.

Once those scripts get more than a few lines long, you can move them into their own files.

Say I want a video-to-gif skill that turns a screen recording into a small GIF I can drop into an issue. To keep the colors from looking terrible, that's really a two-pass job: first build a color palette tailored to the video, then render the GIF using it. That's fiddly enough that I don't want the agent reconstructing the exact ffmpeg filters every time, so I drop a script.sh into the skill folder:

📂 video-to-gif/
├── 📄 SKILL.md
└── ▶️ script.sh

script.sh scales the video down to 420p, drops it to 12 frames per second, and squeezes it into a 64-color palette:

script.sh
#!/usr/bin/env bash
set -euo pipefail
INPUT="${1:?usage: script.sh <input-video>}"
OUTPUT="${2:-output.gif}"

# 1. build an optimized palette from the video
ffmpeg -i "$INPUT" -vf "fps=12,scale=-1:420:flags=lanczos,palettegen=max_colors=64" -y palette.png

# 2. render the gif using that palette
ffmpeg -i "$INPUT" -i palette.png -lavfi "fps=12,scale=-1:420:flags=lanczos[x];[x][1:v]paletteuse" -y "$OUTPUT"

# 3. clean up
rm -f palette.png

Then inside SKILL.md, I simply reference it with ${CLAUDE_SKILL_DIR} so the path works no matter where the skill is invoked from:

Run `${CLAUDE_SKILL_DIR}/script.sh <recording.mov>` to produce the GIF.

The script gets executed, not pasted into the conversation, so it doesn't consume context. The agent simply runs it, captures the result, and moves on.

Anything mechanical and deterministic belongs in a script anyway. It's faster, cheaper, and usually more reliable than asking the model to work out the same commands from scratch every single time.

Tip 2: Skills Can Load Reference Files

The same idea works for knowledge.

If a skill depends on a long reference—API documentation, a style guide, naming conventions, or some giant formatting spec—you don't have to cram it all into SKILL.md.

Instead, drop it into a dedicated file and reference it:

SKILL.md
- If the project uses React, follow [react_rules.md](react_rules.md).
- If the project uses Vue, use [vue_rules.md](vue_rules.md) instead.

Claude only reads the file it actually needs.

This pattern is called progressive disclosure. The body of SKILL.md is loaded when the skill is invoked, and any referenced files are read only if the task actually requires them.

Anthropic recommends keeping SKILL.md under 500 lines and pushing heavier details into supporting files. After using skills for a while, that advice makes a lot of sense.

Tip 3: Skills Can Pre-Approve Tools

You can add an allowed-tools line in the frontmatter to pre-approve specific tools, so a mostly mechanical skill doesn't keep stopping to ask for permission.

This is especially useful for skills that always perform the same safe operations, like running checks, formatting code, or committing.

---
name: commit
description: Stage and commit the current changes with a concise message.
allowed-tools: Bash(git add *) Bash(git commit *)
---

The opposite works too: use the disallowed-tools field to block specific tools.


Turning a Repetitive Workflow Into a Skill

Now, we can put all these tips into action to create our full /github-pr skill.

As I said at the beginning, the routine I got tired of looked something like this:

  • Branch off the default branch.
  • Stage the changes.
  • Read the full diff instead of trusting the commit messages.
  • With that information, write a descriptive title and short description.
  • Open the PR in GitHub.
  • Request a review from Copilot, GitHub's automated code review bot.

So let's turn that workflow into something the agent can execute.

Instead of cramming everything into one giant SKILL.md, let's split it into different files:

📂 github-pr/
├── 📄 SKILL.md
├── 📄 pull_request_description_template.md
└── ▶️ rebase.sh
  • SKILL.md is the main recipe.
  • pull_request_description_template.md is the PR description base.
  • rebase.sh handles the one mechanical step that's worth scripting.

Let's review each, starting with the skill file:

SKILL.md
---
name: github-pr
description: Create a GitHub PR with a concise description.
allowed-tools: Bash(git *) Bash(gh pr create *)
---

# GitHub PR Skill

Open a pull request with a short, descriptive title and a clear description.

## Usage

`/github-pr <target-branch>`

## Steps

1. Find the default branch if no target was given:

`git remote show origin | sed -n 's/.*HEAD branch: //p'`

2. Move the current changes onto a fresh feature branch, on top of the latest target. Name the branch after the change (e.g. "add-user-profile", "fix-cart-rounding"):

`${CLAUDE_SKILL_DIR}/rebase.sh <target> <feature-branch>`

3. Read the full diff so the description reflects what actually changed:

`git diff <target>...HEAD`

4. Draft the PR body from [pull_request_description_template.md](pull_request_description_template.md), filling it in from the diff. Keep the title concise and descriptive ("Add user profile page").

5. Push the branch and open the PR:

```sh
git push --set-upstream origin "$(git branch --show-current)"
gh pr create --base <target> --title "<title>" --body-file - <<'EOF'
<the filled-in PR body>
EOF
```

## Tone

Write plainly. No bold labels on bullets. No AI tells ("seamlessly", "leverage", "robust", "ensures that"). No hyphens or em dashes as sentence connectors.

## Rules

- No co-authors.
- No screenshots.
- Descriptive title required.

Notice how the skill itself stays small. The real work lives in the pieces around it, which are much easier to maintain on their own: the script and the template.

Here's the script:

rebase.sh
#!/usr/bin/env bash
# Move the current local changes onto a fresh feature branch,
# on top of the latest version of the target branch.
# Usage: rebase.sh <target-branch> <feature-branch>
set -euo pipefail

TARGET="${1:?usage: rebase.sh <target-branch> <feature-branch>}"
BRANCH="${2:?usage: rebase.sh <target-branch> <feature-branch>}"

git stash push --include-untracked --message "github-pr wip" || true
git checkout "$TARGET"
git pull --ff-only
git checkout -b "$BRANCH"
git stash pop || true

And here's the template all PR descriptions must follow:

pull_request_description_template.md
## What

[One sentence: what this PR does.]

## Changes

- [One bullet per meaningful behavior change]

## How to test

1. [Step by step, so a reviewer can verify it]

Now I just type /github-pr main once my code is ready for review. And boom! The PR gets created.

The skill rebases the work, reads the diff, fills in the template, and opens the PR using the description format I like. The skill knows how I like my branches named, how I want PRs written, and what I don't want in the final output. All those little preferences I used to retype, now baked into something the agent just follows.


In Closing

Skills can really make your day (and your clanker's!) way easier. I've been trying to lean into skills more and more as I find parts of my workflow that can be automated.

The more I use them, the less they feel like time-saving shortcuts and the more they feel like teaching the agent how I work.

To learn more about skills, visit the Agent Skills website.

In the next article, we'll look at a more advanced way of working with skills: how to stack and chain them together to take our automation to the next level.

Until next time!

More in AI