Book a call
RECIPE20mVERIFIED 2026-08-02 · CLAUDE CODE 2.1.220 · CODEX CLI 0.146.0 · GITHUB COPILOT CLI 1.0.77 · CURSOR 2026.07.23-e383d2b · KIMI CODE CLI 0.31.1

Writing a skill that triggers when it should — and not when it shouldn't

Write a skill's description so it fires on the right requests and stays quiet on the rest — the one field selection turns on, and how to test both directions.

Two ways a skill goes wrong

2 potential problems after installing a skill:

  • It doesn't work — you do it manually but the skill just sits there

  • It works too much — the skill is doing its thing on every occasion and is taking the context you were focusing on for the task

The description is read before the body

These are all connected with the same thing — the description of the skill. Let's think how the selection works — it sees the name and the description, that's it. It doesn't read the body at this stage. What's more, in terms of every harness the body of the skill is always kept on disk until you choose a skill to work with. The app fetches all the names and descriptions on startup so the body doesn't cost anything until it's actually used.

That means that if your description is not very precise, your perfect instructions aren't being used as they should. Treat the description like a classifier for the requests that come to the skill. Describe it accordingly.

In terms of all the other harnesses this thing is crucial as the model decides to choose a skill based on the description (in case of Gemini, a user types a command so it's more about the search feature here, but we'll provide an alternative for this case below).

Write it as a trigger: what it does, then when to use it

There are 2 parts to every proper description:

  • what it does

  • when to use it

Often people forget about the second part. Make sure you describe it from the third-person perspective as it will appear in the system prompt and first-person phrasing is just not good here. Use language a real user would use in the when part.

Bad version:

YAML
description: Helps with database migrations

Good version:

YAML
description: Write and review reversible database schema migrations. Use when the user adds, alters, or drops a table or column, writes a migration file, or changes the schema.

The thing is that the model decides which skill to pick based on the description so it needs to find certain keywords like "migration", "alter table", "add a column" etc there. In other words — it needs to find words that you'd use if you were to ask about sth that should fire this skill.

Also, it's good to list the most common scenario on top as in many cases the list of skills is truncated to a certain amount, so it shows only the "budget" and the rest goes into ellipsis. For that reason the beginning of the description is crucial as the trailing text is the first thing cut.

Bound it: say when not to fire

The other thing is about boundaries. If a skill doesn't fire when it should, it's probably because there are no keywords the model can find there. If it fires too often, then the description is too general and you need to introduce an explicit boundary (or negative scope) for it.

Bad version — just database, which wakes up for connection errors and slow-query questions too:

YAML
description: database

Good version:

YAML
description: …changes the schema. Not for query tuning, connection errors, or seed data.

That negative-scope part of the description is basically a single line and it eliminates an entire category of false positives, so it's really powerful.

Test both directions, and tune the description — not the body

To make sure your description is right you can do the following after installing the skill:

  • Ask about sth that this skill should address but using natural language — if it doesn't respond, there are probably some keywords missing

  • Ask about sth that this skill shouldn't address but using natural language — if it responds, the description is too general

If you face any of these problems make sure to edit the description and not the body, as the body is responsible for what happens after the selection whereas the description determines if a skill will be selected at all.

The two symptoms, and their fixes

So:

  • If the skill doesn't fire — include the words you'd naturally use, and try rephrasing your request in a way that you think will make it work; also check if the skill has been installed properly (if you have messed up the frontmatter, it loads with empty metadata, so you can still call it manually but the model has nothing to match against)

  • If the skill fires when it shouldn't — make it more specific or introduce a negative scope

If you've created a side-effecting skill like deploy or sending messages, you can't secure it with a description, so if you don't want it fired automatically, make it manual-only as shown in the previous recipe.

In your harness

The key thing is that the process of writing a good description is the same for every harness, and all that changes is the name of the mechanism.

IN YOUR HARNESS

In Claude Code

Skills live at .claude/skills/<name>/SKILL.md in the project, or ~/.claude/skills/<name>/SKILL.md for every project. Claude loads one automatically when it's relevant, and you can also run /name yourself. The description is what the automatic choice turns on; an optional when_to_use field holds the trigger phrases and example requests and is appended to it:

YAML
---
name: migrations
description: Write and review reversible database schema migrations. Use when the user adds, alters, or drops a table or column, writes a migration file, or changes the schema.
when_to_use: Fires on "migration", "alter table", "add a column", schema changes. Not query tuning, connection errors, or seed data.
---

Keep it tight. The combined description and when_to_use is truncated at 1,536 characters in the skill listing, and the whole listing shares a budget of roughly 1% of the context window — so with a lot of skills installed, descriptions get shortened and the least-used ones lose theirs first. /doctor estimates what the listing is costing you.

Two levers for the over-firing case:

  • paths: takes globs and limits automatic loading to when Claude is working on matching files — a clean way to scope a skill to, say, db/migrations/**.

  • disable-model-invocation: true makes it manual-only, which also drops its description from context. That's the setting-goals case, not this one.

If a skill never fires, run claude --debug — a frontmatter parse error means Claude loaded the body with empty metadata and there was nothing to match on. One naming rule that bites: name is lowercase letters, numbers and hyphens, 64 characters max, and can't contain "claude" or "anthropic".

In Codex CLI

Project skills go in .agents/skills/<name>/SKILL.md; Codex scans .agents/skills in every directory from your working directory up to the repo root. Personal skills live in ~/.agents/skills/. name and description are both required, and Codex "can choose a skill when your task matches the skill description" — same progressive disclosure as the rest, name and description first, the full SKILL.md only once it commits.

YAML
---
name: migrations
description: Write and review reversible database schema migrations. Use when adding, altering, or dropping a table or column, writing a migration file, or changing the schema. Not for query tuning, connection errors, or seed data.
---

Codex has no separate when_to_use field, so the trigger words and the out-of-scope clause both go in the description. OpenAI's own guidance is to "front-load the key use case and trigger words so a host can still match the skill if descriptions are shortened." To invoke one by hand, run /skills for the picker or type $ to mention a skill — there's no /name form.

In GitHub Copilot CLI

Copilot reads SKILL.md files from .github/skills/, .agents/skills/ and .claude/skills/ in the project, and from ~/.copilot/skills/ or ~/.agents/skills/ for you personally. name (lowercase, hyphens for spaces) and description are required, and "Copilot will decide when to use your skills based on your prompt and the skill's description."

The useful part is that you can see the exact text it matches on without starting a session:

BASH
copilot skill list

That prints every discovered skill next to its description — the string the model actually reads. If your skill isn't in the list, that's a discovery or frontmatter problem, not a trigger one; fix that first. copilot skill add <file-or-dir-or-url> pulls one in from elsewhere. Since the file format is the shared SKILL.md standard, a description you tuned for Copilot is the same one Claude Code and Codex will match on.

In Cursor

Cursor splits this into two mechanisms, and only one of them is description-triggered. Commands in .cursor/commands/ are manual — you invoke them by hand. The half that fires on a description is a rule. Put it in .cursor/rules/<name>.mdc:

MARKDOWN
---
description: Reversible database schema migrations — adding, altering or dropping tables or columns, writing a migration file, changing the schema
alwaysApply: false
---

- Make every migration reversible, with a matching down-migration.
- Separate the structural change from any data backfill.

With alwaysApply: false, a description, and no globs, this is the "Apply Intelligently" type: "Agent reads the description and pulls the rule in when relevant." The description is your trigger, and the craft is identical to a skill's — what it covers, and the words that should pull it in.

Two neighbouring types are worth knowing for the over-firing case: set globs instead of a description and you get "Apply to Specific Files", which fires on file matches rather than on the request; set alwaysApply: true and it's in every chat. cursor-agent rule scaffolds one. Watch two traps — Ask mode is read-only Q&A and unrelated to this, and a rule with neither a description nor globs won't auto-apply at all.

In Gemini CLI

Gemini is the exception. Custom commands live in ~/.gemini/commands/<name>.toml, or .gemini/commands/ inside the project, and they're user-invoked — you type /name. The description is the label shown next to the command in the /help menu; it does not let the model pick the command for you.

TOML
description = "Reversible schema migrations: adding, altering or dropping tables, writing a migration file"

prompt = """
Follow this whenever the change touches the schema:
- Make every migration reversible, with a matching down-migration.
- Separate the structural change from any data backfill.

The change: {{args}}
"""

So "triggers when it should" means two different things here. First, write the description so you spot the right command in a crowded /help list. Second, if you want the model to reach for the procedure on its own, the place to say so is GEMINI.md — the memory file the model actually reads — not the command's description. A subdirectory namespaces the command, so commands/db/migrate.toml becomes /db:migrate.

In Kimi Code CLI

Project skills go in .agents/skills/<name>/SKILL.md or .kimi-code/skills/<name>/SKILL.md; personal ones in ~/.kimi-code/skills/ or ~/.agents/skills/. On start, Kimi Code discovers every skill and reads its description, so that's what a match is made against. It gives you a dedicated field for the trigger detail:

YAML
---
name: migrations
description: Write and review reversible database schema migrations.
whenToUse: Adding, altering or dropping a table or column; writing a migration file; changing the schema. Not query tuning, connection errors, or seed data.
---

"The model can also invoke a Skill automatically based on description and whenToUse (unless disableModelInvocation is true or type is flow)." For the manual-only case, set disableModelInvocation: true; to invoke by hand, it's /skill:migrations. --skills-dir points Kimi at a directory of your choosing instead of the auto-discovered ones.

After it's wired in

As you can see, it's all about the description and putting some effort into it really pays off. If it doesn't work or works too often, treat it as feedback and improve the description further. You're basically working on the target for the model to aim at.

THE FILE.claude/skills/migrations/SKILL.md
MARKDOWN
---
name: migrations
description: Write and review reversible database schema migrations. Use when the user adds, alters, or drops a table or column, writes a migration file, or changes the schema. Not for query tuning, connection errors, or seed data.
---

Follow this whenever the change touches the schema.

1. **Make it reversible.** Every migration has a matching down-migration that
   restores the previous schema. If a change can't be undone, say so before writing it.
2. **Separate schema from data.** Structural change in one migration, backfill in
   another. A single migration that alters a column and rewrites every row can hold a lock
   for the length of the backfill.
3. **Add before you remove.** Ship the new column, move reads and writes over, then drop
   the old one in a later migration — never in the same deploy.
4. **Name the lock.** State which tables the migration locks, and for how long on a
   production-sized table. If you don't know, say so rather than guessing.
5. **Test the down path.** Run up, then down, then up again against a scratch database
   before proposing the change.

Report the plan first — the checks above, filled in for this change — and wait for a go
before writing the migration.
j / k to move between lessons