# Teach Claude your workflow: build an Agent Skill with SKILL.md

> Source: <https://dev.to/aifrontierpost/teach-claude-your-workflow-build-an-agent-skill-with-skillmd-2c28>
> Published: 2026-09-25 21:21:33+00:00

*Originally published at [AI Frontier Post](https://aifrontierpost.com/articles/build-agent-skill-claude-skill-md-tutorial/).*

Every regular Claude user eventually hits the same wall: a workflow they repeat every week — the way they want release notes written, code reviewed, or research briefs formatted — that they have to re-explain in every single session. Paste the checklist again. Correct the format again. Watch the agent improvise the steps you already decided. The knowledge exists, but it lives nowhere the agent can reliably find it.

**Agent Skills** are the industry's answer, and they are having a moment: Anthropic launched them as a Claude feature in October 2025, published the format as an open standard on December 18, 2025 at **agentskills.io**, and by mid-2026 roughly forty products read the same files — Claude Code, OpenAI's Codex, Cursor, GitHub Copilot, Google's Gemini CLI among them. The whole standard is almost absurdly small: **a folder containing a single Markdown file, SKILL.md**, with a few lines of YAML metadata on top and instructions below.

This tutorial builds one from scratch, end to end. You will pick a workflow worth teaching, write the frontmatter and instructions, validate the result with a real script, and install it so it triggers automatically in your own sessions. No API keys, no servers, no cost — and the skill you build works in every agent that speaks the open standard, not just Claude.

`pip install pyyaml` if you don't have it).
Strip away the announcements and a skill is a directory with one required file:

```
changelog-writer/
└── SKILL.md        # YAML frontmatter + Markdown instructions
```

The **frontmatter** — just `name` and `description` are required — tells the agent *what* the skill does and *when* to use it. The **body** tells it *how*: the workflow, the rules, the output contract, the edge cases. Optionally, the folder grows three sub-directories: `scripts/` for executable code, `references/` for docs loaded on demand, and `assets/` for templates and static files.

The clever part is **progressive disclosure** — how the agent loads the skill in three stages instead of all at once:

*AI-generated illustration: progressive disclosure — metadata always loaded, instructions on trigger, resources on demand.*

`name` and `description` into context. You can have fifty skills installed and barely feel it.
This is why skills scale where giant system prompts don't. Your changelog conventions cost you ~100 tokens every session; the full procedure costs nothing until someone actually asks for a changelog.

The most common failure in skill authoring is teaching the agent something that shouldn't be a skill. Use this filter — a job earns a skill when all three are true:

Good candidates: a **changelog writer** (our worked example), a **code reviewer** that enforces your team's conventions, a **research briefer** that always cites sources the same way, a **release checklist runner**. Bad candidates: single shell commands (make them aliases), pure reference facts (make them docs), anything you've only done once.

One skill, one job. If your "skill" needs a table of contents, it's two skills.

Create the directory where your agent looks for personal skills — `~/.claude/skills/` for Claude Code — and the one file that makes it a skill:

```
mkdir -p ~/.claude/skills/changelog-writer
touch ~/.claude/skills/changelog-writer/SKILL.md
```

The directory name **must match the skill's `name`** in the frontmatter, and the spec is strict about that name:

Prefer the gerund form for names — `writing-changelogs` rather than `changelog-writer` is the spec's own recommendation, though noun forms like ours are widespread in the ecosystem. Either is valid; pick one convention and keep it.

Everything above the skill's usefulness flows through the `description`. It is the *only* thing the agent sees before deciding to load your skill — so it must say what the skill does **and when to use it**, with the concrete keywords a user would actually type. Compare:

```
# Weak — the agent can't tell when this applies
description: "Helps with changelogs."

# Strong — what it does, when to trigger, user's own vocabulary
description: "Writes and updates project changelogs in Keep-a-Changelog format."
  Use when the user asks to draft a changelog, add release notes, or summarize
  recent commits for a release. Also use proactively before a version tag is created.
```

The spec caps descriptions at 1,024 characters and, notably, agents tend to *under*-trigger skills — so be explicit about trigger contexts rather than subtle. Name the situations: "when the user asks to…", "before a version tag is created". Write in the third person, pack in the keywords.

Here is the complete frontmatter for our worked example:

```
---
name: changelog-writer
description: Writes and updates project changelogs in Keep-a-Changelog format.
  Use when the user asks to draft a changelog, add release notes, or summarize
  recent commits for a release. Also use proactively before a version tag is created.
license: MIT
---
```

Four more fields are defined by the open spec, all optional:

| Field | What it's for | Constraint | 
|---|---|---|
| `license` | License name, or a reference to a bundled license file | Keep it short | 
| `compatibility` | Environment requirements — intended product, system packages, network access | Max 500 chars; omit if you don't need it | 
| `metadata` | Arbitrary key-value map for anything the spec doesn't define (author, version, tags) | Use unique-ish key names to avoid collisions | 
| `allowed-tools` | Space-separated pre-approved tools, e.g. `Bash(git:*) Read` — least privilege at the skill level | Experimental; support varies by client | 

Claude Code adds its own non-portable extensions on top — `disable-model-inviction: true` (only the user may invoke it; use for anything with side effects like `/deploy`) and `user-invocable: false` (only the agent may invoke it; use for background knowledge). If you want the skill to travel to other agents unchanged, stick to the spec fields.

The body has no format restrictions — write whatever helps the agent perform the task — but the shape that works follows a consistent skeleton: **Purpose, Workflow, Output Contract, Operating Rules**. Tell the model what to *do next*, not the history of the domain. Keep the file under 500 lines; the spec recommends under 5,000 tokens for the instructions level.

Here is the complete worked example — the exact file I validated in Step 6:

```
# Changelog Writer

## Purpose
Produce a CHANGELOG.md entry or full file that follows the Keep-a-Changelog
format (Added / Changed / Deprecated / Removed / Fixed / Security), written
from git history or from user-supplied notes.

## Workflow
1. If no date or version is given, ask — never invent the release date.
2. Inspect recent history: `git log --oneline -20`.
3. Draft the entry under an `## [Unreleased]` heading (or the given version).
4. Sort entries: Added, Changed, Deprecated, Removed, Fixed, Security.
5. Write facts only. Do not embellish commit messages into features that
   were not shipped.

## Output Contract
- Output Markdown only, no commentary around it.
- Never claim a bug is fixed unless a commit touching it exists.
- Keep entries one line each, present tense, no trailing period.
```

Notice what this does and doesn't contain. It names the format and the category order. It gives the exact command to inspect history. It pins down the failure modes that matter — inventing dates, embellishing commits, wrapping output in chatty commentary. It does *not* explain what a changelog is, what git log does, or the philosophy of release notes. The agent already knows those things; the skill supplies *your* decisions.

Two more authoring rules that separate working skills from dead ones:

`references/` and point at it. The skill's instructions stay lean; the detail loads only when needed.
Most first skills need nothing beyond SKILL.md, and that's fine — the spec's three optional directories exist for when instructions alone aren't the right tool:

*AI-generated illustration: the full skill layout — SKILL.md plus the three optional directories.*

`scripts/` — executable code, for anything deterministic.`REFERENCE.md`), form templates (` FORMS.md`), domain files (` finance.md`, `legal.md`). Keep each file focused and small — agents load them individually.`assets/` — static resources.
The judgment call: **flexibility goes in instructions, reliability goes in scripts, factual lookup goes in references.** A changelog checker that verifies every entry ends without a period? That's deterministic — a script. The Keep-a-Changelog category definitions? A reference file. File references use paths relative to the skill root, and the spec asks you to keep reference chains one level deep — SKILL.md points at a reference file, not at a file that points at another file.

Where you put the folder decides who gets the skill:

`~/.claude/skills/``.claude/skills/`
After installing, start a new session (or run the client's skill-reload command). Triggering happens two ways: **automatically**, when your request matches the description — "draft the changelog for the 2.4 release" should now wake the skill on its own; or **manually** as a slash command — `/changelog-writer` in Claude Code. Test the automatic path with the exact phrasing from your description; if it doesn't trigger, your description needs more of the user's vocabulary, not less.

Now validate. Anthropic publishes a reference validation library (`skills-ref`), but the spec's naming rules are simple enough to check directly. This script enforces the constraints from the spec — name format, description length, directory match, the 500-line limit — and I ran it against the skill from Step 4:

``` python
import re, yaml
from pathlib import Path

def validate_skill(skill_dir):
    root = Path(skill_dir)
    errors = []
    text = (root / "SKILL.md").read_text()
    _, front, body = text.split("---", 2)
    fm = yaml.safe_load(front)          # pip install pyyaml
    name, desc = fm.get("name", ""), str(fm.get("description", ""))
    if not re.fullmatch(r"[a-z0-9]+(?:-[a-z0-9]+)*", name):
        errors.append("name violates naming rules (lowercase, hyphens only)")
    if name != root.name:
        errors.append(f"name '{name}' does not match directory '{root.name}'")
    if not (1 <= len(desc) <= 1024):
        errors.append("description length out of range (1-1024 chars)")
    if len(body.splitlines()) > 500:
        errors.append("SKILL.md exceeds 500 lines")
    allowed = {"name", "description", "license", "compatibility",
               "metadata", "allowed-tools"}
    if unknown := set(fm) - allowed:
        errors.append(f"unknown frontmatter fields: {unknown}")
    return errors

print(validate_skill("~/.claude/skills/changelog-writer".replace("~", str(Path.home()))))
```

Against our changelog-writer: `[]` — valid. Against a deliberately broken skill (missing `name`, 1,100-character description), it reported exactly the two violations: `name is required; description length out of range (1-1024)`. Validation is not a substitute for testing the behavior — ask the agent to use the skill on a real task and read what comes out — but it catches the structural mistakes that silently prevent loading.

One safety note before you go further: a skill is instructions plus, optionally, code that runs with your agent's permissions. **Install skills only from sources you trust, and read SKILL.md before installing** — the same review you'd give any script before running it. Community marketplaces now list thousands of skills; treat an unknown skill the way you'd treat an unknown npm package.

Skills overlap with half a dozen other mechanisms. Here's the map, in plain terms:

`.claude/skills/deploy/SKILL.md` creates `/deploy`, and adds auto-triggering on top.
Mental model: **MCP is the nervous system, skills are the handbook, projects are the memory, subagents are the work crew.**

A skill is the smallest unit of reusable agent expertise: a folder, a SKILL.md, a name and description that cost ~100 tokens per session. Pick one repeated workflow, write the frontmatter as the trigger contract it is, keep the body to decisions and failure modes, split detail into references, validate the structure, and install it where the team can inherit it. Do that three or four times and your agent stops being a brilliant stranger every morning — it starts being the colleague who already knows how you work.
