Skip to content
Malik Hamza Shabbir
AI Agentsagent skillsskill.mdclaude codecursor

Write Once, Run Everywhere: Building Agent Skills (SKILL.md) That Work in Claude, Cursor, Copilot and Codex

HSMalik Hamza ShabbirUpdated 9 min read

In short

An Agent Skill is a portable SKILL.md folder that loads the same workflow into Claude Code, Cursor, Copilot, Codex, OpenCode, and Kiro without rewriting it per tool. The key is progressive disclosure: the description matches at discovery, the body loads at activation, and scripts or reference files load only at execution. Author to the portable subset and one skill serves every agent. See how I package this in AI agents and automation.

Write Once, Run Everywhere: Building Agent Skills (SKILL.md) That Work in Claude, Cursor, Copilot and Codex
On this page

An Agent Skill is a folder with a SKILL.md file at its root: a Markdown document with YAML frontmatter that tells any compatible coding agent what the skill does, when to use it, and how to run it. The reason it matters in 2026 is that SKILL.md has become a shared open format, so the same skill folder loads in Claude Code, GitHub Copilot, Cursor, OpenCode, Codex, and Kiro without rewriting it per tool. In this post I walk through how I author one portable skill with progressive disclosure, show a real reusable skill I ship to clients, and explain why I stopped rebuilding the same workflow three times for three different agents.

What is a SKILL.md Agent Skill, exactly?

A SKILL.md skill is a self-contained folder that packages instructions, optional scripts, and reference files so an agent can load a procedure on demand instead of you re-explaining it every session. Think of it as a function the agent can call in plain language, except the "function body" is Markdown plus whatever files you bundle next to it.

The minimum is one file. A skill named pr-review lives at pr-review/SKILL.md, and that file has two parts: YAML frontmatter and a Markdown body.

YAML
---
name: changelog-entry
description: >
  Generate a changelog entry from staged git changes following
  Keep a Changelog format. Use when the user asks to update the
  changelog, write release notes, or document changes for a release.
---

## Changelog Entry

When the user wants a changelog entry:

1. Run `git diff --staged --stat` to see what changed.
2. Group changes into Added, Changed, Fixed, Removed.
3. Write entries in past tense, user-facing language, one line each.
4. Insert them under the `## [Unreleased]` heading in CHANGELOG.md.

Skip internal refactors that have no user-visible effect.

That is a complete, working skill. The agent reads the description, decides whether the current request matches, and if it does, pulls the body into context and follows it. Everything else in this post is about making that loading smart instead of dumping everything at once.

How does progressive disclosure actually work in a skill?

Progressive disclosure means the agent only loads what it needs, in three stages: discovery, activation, and execution. This is the single most important idea in skill authoring, because context windows are finite and a skill that front-loads 4,000 tokens of instructions will get ignored or will crowd out the actual task.

Here is how the three stages map to what lives where:





At discovery, the agent has read only your frontmatter for every installed skill. That is why the description is the highest-leverage text in the whole skill: it is the matcher. If it is vague, the skill never fires. If it lists concrete trigger phrases, it fires reliably.

At activation, the body loads. Keep it to the procedure and the decisions. Do not paste a 600-line API reference here.

At execution, the agent reads sibling files only when the body tells it to. So a skill that needs a long style guide references it instead of inlining it:

MARKDOWN
For the full tone rules, read `reference/voice-guide.md` before drafting.
Run `scripts/validate.py` against the output and fix any errors it reports.

Those files cost zero tokens until the moment they are opened. That is the whole trick: a skill can carry megabytes of supporting material and still have a tiny activation footprint.

What does a real reusable client skill look like?

Here is a skill I actually ship to clients: a release-notes generator that turns merged PRs into customer-facing notes in their house voice. I reuse the same folder across every client and only swap the reference file. This is the kind of reusable asset that earns its keep, because I write the logic once and it runs in whatever agent the client's team already uses.

The folder:

TEXT
release-notes/
  SKILL.md
  reference/
    voice-guide.md
  scripts/
    fetch-merged-prs.sh

The SKILL.md:

YAML
---
name: release-notes
description: >
  Draft customer-facing release notes from merged GitHub PRs since the
  last tag. Use when asked to write release notes, prep a release
  announcement, or summarize what shipped this sprint.
---

## Release Notes

## Steps

1. Run `scripts/fetch-merged-prs.sh` to list PRs merged since the last
   git tag. It prints title, number, and labels as TSV.
2. Drop PRs labeled `internal`, `chore`, or `dependencies`.
3. Group the rest under: New, Improved, Fixed.
4. Read `reference/voice-guide.md` and rewrite each line in that voice.
5. Output Markdown only. No preamble. Lead with the single biggest
   user-visible change.

## Rules

- One sentence per item. Active voice. Name the user benefit, not the
  code change.
- If a PR has no user-visible effect, leave it out even if it is large.
- Never invent a feature that is not backed by a PR in the list.

The script stays boring on purpose so it is portable:

BASH
#!/usr/bin/env bash
set -euo pipefail
last_tag=$(git describe --tags --abbrev=0)
gh pr list --state merged --search "merged:>$(git log -1 --format=%cs "$last_tag")" \
  --json number,title,labels \
  --template '{{range .}}{{.number}}\t{{.title}}\t{{range .labels}}{{.name}} {{end}}{{"\n"}}{{end}}'

The voice-guide.md is the only file that changes per client. One client wants dry and factual, another wants warm and exclamation-friendly. The procedure does not change, so I do not touch SKILL.md. That separation is the reason this scales: logic in the body, taste in a reference file, plumbing in a script.

This pattern lives at the center of how I build AI agents and automation for small teams, because a skill is a deliverable a client keeps and reuses long after the engagement ends.

Will the same SKILL.md really run in Claude, Cursor, Copilot, and Codex?

Yes, with caveats. The core SKILL.md shape, a folder with a frontmatter name and description plus a Markdown body, is recognized across the tools that adopted the open Agent Skills format in early-to-mid 2026, including Claude Code, GitHub Copilot, Cursor, OpenCode, Codex, and Kiro. What differs is the install location and a few optional fields, not the format itself.

The portable subset is small and stable. Stick to it and your skill loads everywhere:








StageWhat loadsToken costLives in
Discoveryname + description only~30-80 tokensYAML frontmatter
ActivationFull SKILL.md bodya few hundred to ~2k tokensMarkdown body
ExecutionReferenced files, scripts, templatesloaded only when read/runsibling files
ConcernPortable choiceTool-specific detail
File nameSKILL.md at folder rootuniversal
Required fieldsname, descriptionuniversal
Trigger logicnatural-language phrases in descriptionuniversal
Bundled scriptsplain shell or Python, called by relative pathruntime must be present
Install paththe skills directory the tool scansdiffers per tool
Extra frontmatteravoid relying on itsome tools add their own keys

My rule: write to the lowest common denominator. Put all trigger logic in the description as plain phrases, reference scripts by relative path, and never depend on a frontmatter key that only one tool reads. If a tool needs a vendor-specific field, I add it as an extra key the others will ignore, rather than restructuring the skill.

The one real portability risk is scripts. A skill that shells out to gh or python assumes that runtime exists wherever the skill runs. I keep scripts dependency-light and document the requirement in the body so the agent can degrade gracefully if a tool is missing.

Why is the open SKILL.md standard better than per-tool config?

Because you author the workflow once and it follows the developer instead of the tool. Before this format settled, the same "write release notes our way" workflow meant a Claude project instruction, a Cursor rule file, and a Copilot custom instruction, three copies that drifted out of sync the moment one changed. A single SKILL.md collapses that into one source of truth.

Three concrete payoffs I see in client work:

  • Versioning. A skill is a folder. It goes in git, gets reviewed in a PR, and ships with the repo. Per-tool config usually lives in scattered settings that no one reviews.

  • Onboarding. A new hire clones the repo and inherits every team skill automatically. There is no "and now configure your editor" step.

  • No vendor lock. If a team moves from one agent to another, the skills come along. I have migrated clients between tools without rewriting a single skill body.


This is the same instinct behind treating agent infrastructure as portable assets rather than disposable config. If you are working at the protocol layer too, it pairs naturally with migrating a remote MCP server to the stateless spec and with auditing a third-party MCP server before you trust it , since skills and MCP servers are the two halves of how I package reusable agent capability for clients.

How do I test a skill so it fires when I want it to?

Test the description first, because a skill that never activates is worse than no skill. The fastest check is to open a fresh session and phrase a request the way a real user would, then see if the agent picks the skill up without you naming it.

My checklist before I hand a skill to a client:

  1. Trigger test. Ask for the task in three different phrasings. If it only fires when I say the skill name, the description is too narrow. Add the phrasings that failed.

  2. Anti-trigger test. Ask for something adjacent but wrong. If a release-notes skill fires when I ask for a commit message, the description is too greedy. Tighten it.

  3. Cold execution. Run the skill in a session with no prior context. The body must stand alone, since the agent has no memory of how I built it. This is the same constraint a sibling agent faces, which is why I write bodies as if explaining to someone who just walked in.

  4. Missing-runtime test. Run it where the bundled script's runtime is absent and confirm the body tells the agent what to do instead of failing silently.


I keep descriptions in a "Use when..." form because it reads as the matcher it actually is. "Use when the user asks to write release notes, prep a release announcement, or summarize what shipped this sprint" beats "Generates release notes" every time, because it hands the agent the exact phrases to match against.

If you want to render skill output inside a chat UI rather than just dropping Markdown, that overlaps with shipping an MCP app with interactive UI , which is the path I take when a client wants the result to look like a product feature instead of a transcript.

What is the fastest way to start?

Make a folder, add one SKILL.md with a sharp description and a five-step body, and test it in a real session before adding anything else. Resist the urge to bundle scripts and reference files on day one. Most of my skills started as a single file and only grew a reference/ folder once the body got long enough to hurt activation.

The progression I recommend:

  1. One file, plain body, sharp description.

  2. Move long reference material to a sibling file the body reads on demand.

  3. Add a script only when the agent is doing repetitive deterministic work better handled by code.

  4. Commit the folder to the repo so the whole team and every tool inherits it.


If you are sitting on a workflow you explain to your agent the same way every week, that is a skill waiting to be written. I help teams turn those repeated explanations into portable, versioned skills and the agent infrastructure around them, and you can reach me through my contact page if you want a hand scoping the first few.

The short version: author to the portable subset, lean on progressive disclosure so activation stays cheap, and keep taste in reference files and logic in the body. Do that and one skill folder serves every agent your clients will ever use.

FAQ

What is a SKILL.md Agent Skill?

It is a self-contained folder with a SKILL.md file (YAML frontmatter plus a Markdown body) that tells any compatible agent what a workflow does, when to use it, and how to run it.

What is progressive disclosure in a skill?

It is loading only what the agent needs in three stages: the name and description at discovery, the full body at activation, and bundled files or scripts only at execution when the body tells the agent to read them.

Will the same SKILL.md run in Claude, Cursor, Copilot, and Codex?

Yes for the core format, since those tools adopted the open Agent Skills standard in early-to-mid 2026, though the install location and a few optional frontmatter fields differ per tool.

Why use the open SKILL.md standard instead of per-tool config?

Because you author the workflow once as a versioned git folder that follows the developer across tools, instead of maintaining drifting copies as Claude, Cursor, and Copilot config separately.

What is the most important part of a skill to get right?

The description field, because it is the matcher the agent reads at discovery, so it should list concrete trigger phrases in a Use-when form rather than a vague summary.

Working on something like this?

I build web apps, AI features, and mobile products for clients. If this article matches a problem you have, tell me about it.

Start a conversation
HS

Malik Hamza Shabbir · Full-Stack & AI Engineer

I build full-stack and AI products solo: a reputation SaaS in production, RAG pipelines, and React Native apps. I write from what I ship, not from documentation summaries.

Related articles