Skip to main content
A Ralphy skill is one folder under .agents/skills/<name>/ containing a single SKILL.md. Skills follow the agentskills.io convention so they install cleanly into Claude Code, Cursor, Codex, and Copilot through ralphy skill install. This page is the wire-format spec: frontmatter fields, body sections, the lint that enforces both, and the description discipline that keeps the slash-command menu scannable.

Full example

A valid .agents/skills/evaluator/SKILL.md:
That file passes bun run lint:skills and renders correctly in every supported agent.

Where skills live

One folder per skill. The folder name must match the name: field — the lint rejects a mismatch. The folder is yours; drop scripts, templates, or worked-example files alongside SKILL.md and they ship with the skill on install.

Frontmatter contract

The frontmatter is a YAML block between --- fences. scripts/lint-skills.ts enforces: The lint parser handles two YAML shapes per field: inline key: value and the folded key: >- block used for multi-line description.

The name rule, in source

Lower-case letters, digits, and hyphens. No underscores. No leading hyphen. No uppercase.

The description cap, in source

1536 is the agentskills.io hard cap. The lint hard-fails above it. Soft ceiling is ~1500: Claude Code budgets roughly 1% of context for skill discovery across every installed skill, so two bloated descriptions push siblings out of the menu without warning.

The namespace whitelist

Any other value is rejected. The split: ralphy skill install installs only user skills by default. --dev opts into the maintainer set. The agents-md lint (scripts/lint-agents-md.ts) reads namespace: and skips the “is this skill referenced from the routing table” check for maintainer skills — maintainer skills are explicit-invocation-only.

Body structure

The lint warns (does not error) when section headings are missing. Every non-trivial skill should hit this order:
The lint walks for any ## heading starting with Trigger, Hard invariants, Workflow, Outputs, or Cookbook (case-insensitive prefix match). It only warns when none of them are present, so an intentionally minimal skill-creator-style body does not generate noise.

Writing a great description

The description is the user-facing summary of the skill, not an auto-route trigger phrase list. Claude Code renders it in the /<skill> slash-command menu, the “suggest this skill” surface, and the ralphy skill list output. A scannable paragraph beats a wall of synonyms.

Good: ~300 chars

What works: leads with the verb, names the output, says when the user reaches for it. No synonym lists.

Bad: ~1800 chars (rejected by the cap)

What fails: every trigger phrase, every synonym, every example utterance crammed into one field. The lint rejects this for length, but even at 1535 chars it would be a defect — the menu becomes unreadable and sibling skills get squeezed out of the discovery budget.

Structure that works

One paragraph, optionally broken into “what / when” sentences:
  1. First sentence. What the skill does and what it produces.
  2. Second sentence. When the user reaches for it.
  3. (Optional) Third sentence. What it does not do — the closest adjacent skill the user might confuse it with.
If you need to spell out trigger phrases for routing fidelity, put them in the body’s ## Trigger section. The body is where the agent looks once the skill fires; the description is where the user looks before invoking it. The agentskills.io cap (1536) and the menu-budget reality (~1500 soft) are not the same number on purpose. Hard cap stops the worst case; soft ceiling is the discipline.

ALSO FIRE / DO NOT FIRE / HARD INVARIANTS

For longer skills (the existing evaluator and researcher), the description ends with See body for ALSO FIRE / DO NOT FIRE / HARD INVARIANTS. and those clauses move into the body under ## Trigger refinements and ## Hard invariants. This pattern keeps the description scannable while preserving the routing-fidelity detail the agent needs once the skill fires. The rule of thumb: if a clause is longer than half a sentence, move it from the description into the body. The description’s job is “should this skill fire?”; the body’s job is “now that it fired, what do I do?”

Lint

The script walks .agents/skills/*/SKILL.md and checks:
  • Frontmatter parses cleanly (a ------ block with valid YAML).
  • name matches /^[a-z][a-z0-9-]*$/ and equals the folder name.
  • description is present and ≤ 1536 chars.
  • namespace, if set, is user or maintainer.
  • (Warning) The body has at least one ## section heading.
CI runs lint:skills on every PR. A failing lint blocks merge.

Installing skills

ralphy skill install is the cross-agent installer in cli/lib/skill/installer.ts. It supports four targets: The sentinel block (<!-- ralphy:start v=1 --><!-- ralphy:end -->) makes re-runs idempotent. A second ralphy skill install swaps the inner content without duplicating the block.