Example

Advertise a skill by description, load the body on demand

Write a SKILL.md, confirm the system prompt carries its name, description and path but not its body, and force it with /skill:name.

A prompt template waits until you type it. A skill begins when Pi recognises the kind of task. The whole mechanism turns on one field: the system prompt carries every skill’s name, description and path, never the body, so the description is the entire routing surface. This page writes a SKILL.md and shows exactly what the model is told.

Write the skill

A skill is a directory containing SKILL.md:

review-guard/
└── SKILL.md
---
name: review-guard
description: Reviews changed code against this repository's conventions and runs its own checks. Use when reviewing a diff, a pull request, or uncommitted changes.
---

# Review guard

1. Run `scripts/run-checks.sh` and read the output before reviewing anything.
2. Read `references/checklist.md` and apply it to the diff.
3. Report findings ordered by severity, each with a file path.

Place it in your user or project skills directory and run Pi from a location where it is discoverable. The description states both what the skill does and when it applies; “Reviews code” gives the model nothing to match against “implement this”, while the text above does.

What the model is told

At startup After the task matches, or /skill:name
name, description and path, in the system prompt the SKILL.md body
not the body arguments appended as a user request

That split is why an irrelevant skill costs almost nothing, and why the body loads whole every time it fires. A body that has outgrown a screen wants a file beside it (chapter 14), not a longer SKILL.md.

Expected outcome

The backing test creates skills in a user directory and inspects the request the provider receives. It asserts, at Pi 1.0.4:

  • the system prompt matches the skill name, its description and SKILL.md, and does not match a marker placed in the body;
  • a skill with no description is absent from the prompt entirely — a silent disappearance, not an error;
  • /skill:review-guard src/auth only puts the body and the appended argument in the user message;
  • disable-model-invocation: true removes the description from what the model is told but leaves /skill:review-guard working;
  • a skill under skills/group/inner/ is discovered recursively;
  • of two skills named review, exactly one is advertised.

These are observed assertions from a real session with a scripted provider, not a transcript of a real model deciding to route.

Mechanism and limitations

Documented: the pinned docs/skills.md defines the format and frontmatter fields, says startup adds each skill’s name, description and path to the system prompt but not the full instructions, states that arguments after /skill:name are appended, documents disable-model-invocation, and notes that directories containing SKILL.md are discovered recursively and that skills without descriptions are not loaded. Observed: skills.test.ts checks every bullet above against the prompt and messages a scripted provider received. It never exercises routing itself.

Routing is a convention, not a guarantee: docs/skills.md says a model might fail to load a relevant skill, which is why /skill:name exists. A skill is still text — it can say “never force push”, but nothing enforces it. If you reach for /skill:name routinely, the description needs work or the skill is really a template.

Full source and test .

Understand this example

Copy this prompt into your AI tool. No code runs here.

Apply this example

Copy this prompt into your AI tool. No code runs here.