128 lines
5.8 KiB
Markdown
128 lines
5.8 KiB
Markdown
# Skill mechanics
|
|
|
|
The skill-specific branch of [`writing-for-agents`](SKILL.md): what changes when the document is a
|
|
skill. Everything else about writing it is the universal reference in `SKILL.md`.
|
|
|
|
## Where a skill lives
|
|
|
|
A personal skill lives **once**, at `~/.agents/skills/<name>/`, with Claude Code pointed at it by a
|
|
symlink:
|
|
|
|
```bash
|
|
ln -s ../../.agents/skills/<name> ~/.claude/skills/<name>
|
|
```
|
|
|
|
`~/.agents/skills/` is the cross-runtime home: Codex, Copilot CLI, and Gemini CLI read it directly.
|
|
Writing a second copy into `~/.claude/skills/` gives you two files that drift, and the drift is
|
|
silent because each harness only ever reads its own. One home, one symlink per harness that needs it.
|
|
|
|
A skill that only makes sense inside one repo lives once at
|
|
`<repo>/.agents/skills/<name>/`. Point Claude Code at that source with a
|
|
`<repo>/.claude/skills/<name>` symlink.
|
|
|
|
## Frontmatter
|
|
|
|
```yaml
|
|
---
|
|
name: writing-for-agents # letters, numbers, hyphens only; matches the directory name
|
|
description: Use when ... # the skill's top-level context pointer
|
|
disable-model-invocation: true # Claude Code only; omit unless the skill is user-invoked
|
|
---
|
|
```
|
|
|
|
The whole frontmatter block caps at 1024 characters, so the description is the budget. Write it in
|
|
the third person: it is injected into the system prompt, not read as a reply.
|
|
|
|
The description is a context pointer, so every pointer rule in `SKILL.md` applies to it in full: name
|
|
the material and its trigger branches, front-load the leading word, one trigger per branch, and never
|
|
summarize the process.
|
|
|
|
## Codex metadata
|
|
|
|
Each skill also carries `agents/openai.yaml` beside its `SKILL.md`, holding Codex's skill-picker
|
|
metadata and, for a user-invoked skill, its half of the invocation setting:
|
|
|
|
```yaml
|
|
interface:
|
|
display_name: Writing for Agents
|
|
short_description: Reference for writing skills, AGENTS.md/CLAUDE.md, and pointed-at docs.
|
|
policy:
|
|
allow_implicit_invocation: false # user-invoked only; omit for model-invoked skills
|
|
```
|
|
|
|
**Invocation is set in two places, and they must agree.** `disable-model-invocation: true` governs
|
|
Claude Code; `policy.allow_implicit_invocation: false` governs Codex. A skill is user-invoked in both
|
|
harnesses or neither. Setting only the Claude Code half leaves the skill quietly model-invocable in
|
|
Codex, which is the same drift as two copies of the file, one field wide.
|
|
|
|
## Invocation
|
|
|
|
Two choices, trading the two loads:
|
|
|
|
| | Model-invoked | User-invoked |
|
|
|---|---|---|
|
|
| Who can fire it | The agent, you, or another skill | Only you, typing `/name` |
|
|
| Claude Code | Omit `disable-model-invocation` | `disable-model-invocation: true` |
|
|
| Codex | Omit the `policy` block | `policy.allow_implicit_invocation: false` |
|
|
| Description | Model-facing, carries trigger branches | Human-facing one-liner, triggers stripped |
|
|
| Cost | Permanent context load | Zero context load, paid in cognitive load |
|
|
|
|
Model-invocation always *includes* your reach: a description only ever adds agent discovery, it never
|
|
removes yours. So the question is only whether the agent must reach the skill on its own, or another
|
|
skill must. If it only ever fires by hand, make it user-invoked and pay no context load.
|
|
|
|
A model-invoked skill whose content is all reference is also the one home for shared reference:
|
|
several skills can invoke it, so the material lives in one place.
|
|
|
|
Shared reference that two *user-invoked* skills both need can live in neither, since with no
|
|
descriptions neither can fire the other. Push it to a plain file outside the skill system and point
|
|
at it from both.
|
|
|
|
## Calling one skill from another
|
|
|
|
Write the dependency as an explicit instruction to call the tool, not a bare slash-name left for the
|
|
model to interpret:
|
|
|
|
> Call the Skill tool with "grilling".
|
|
|
|
The tool takes one skill per call. A step needing two is two calls, and saying "call it with X and Y"
|
|
reads as a single call taking both. Say instead:
|
|
|
|
> Call the Skill tool twice, for "grilling" and "domain-modeling".
|
|
|
|
This only works on **model-invoked** skills. A user-invoked skill can never be reached this way. When
|
|
a step's precondition is a user-invoked skill, phrase it as an instruction for the human: "tell the
|
|
user to run `/ticket`", never as a tool call.
|
|
|
|
Router prose that just names skills for a human to pick from is not invoking anything, so it keeps
|
|
`/name` labels as plain text.
|
|
|
|
## Splitting by invocation
|
|
|
|
The invocation cut, alongside the branch and sequence cuts in `SKILL.md`: split off a model-invoked
|
|
skill when you have a distinct leading word that should trigger it on its own, a word you actually
|
|
use in your prompts, or when another skill must reach it. You pay context load for the new
|
|
always-loaded description, so that independent reach has to be worth it.
|
|
|
|
## Router skills
|
|
|
|
When user-invoked skills multiply past what you can remember, that piled-up cognitive load is cured
|
|
by a **router skill**: one user-invoked skill naming the others and when to reach for each, so you
|
|
have one name to remember instead of many. It can only hint, never fire them, since user-invoked
|
|
skills have no description for anything but you to reach.
|
|
|
|
Write the router when you notice yourself forgetting a skill exists, not before. A router over three
|
|
skills is furniture.
|
|
|
|
## Done when
|
|
|
|
- The skill exists once under `~/.agents/skills/`, with a symlink from `~/.claude/skills/`.
|
|
- `name` matches the directory and uses only letters, numbers, and hyphens.
|
|
- The description names the material and its triggers, in the third person, under the 1024-character
|
|
frontmatter cap.
|
|
- The invocation choice is deliberate: model-invoked because the agent or another skill must reach
|
|
it, user-invoked otherwise.
|
|
- The invocation choice is written in both harnesses' fields, or in neither.
|
|
- Every cross-skill dependency is a named Skill tool call to a model-invoked skill, or an instruction
|
|
for the human where the target is user-invoked.
|