Skip to content
ivralo
Sections

Docs / Skills

Writing a skill

How to write a skill of your own: the SKILL.md format, the YAML rule that matters most, scripts and references, and the lint that checks it all.

Start from the scaffold

ivralo skills create deploy-checks --agent coder

writes a skill on the local shelf that already validates, and assigns it to the coder preset in the same step. Leave out --agent and the skill rides a specialist of its own (see How skills work).

OptionWhat it adds
--agent, -athe preset key that owns it; repeatable
--scriptsa scripts/ folder with example.py
--referencesa references/ folder with notes.md
--shippedwrites to the shipped shelf instead; for work on ivralo itself

A name is lowercase letters, digits and single hyphens, at most 64 characters, and must not exist on either shelf. --shipped cannot be combined with --agent: a shipped skill is owned through a preset's skills= list in the roster, and the command prints that edit for you.

The SKILL.md file

A SKILL.md is YAML frontmatter between two --- lines, then a markdown body. This is what the scaffold writes:

---
name: "deploy-checks"
description: "TODO: what this skill teaches and when to load it. State the trigger ('Load when the user ...') and draw the boundary: Do NOT load it for jobs another skill owns - name that skill instead."
metadata:
  ivralo:
    capabilities: []
---

# deploy-checks

TODO: the technique this skill teaches - when to use it, the hard rules,
worked code the agent can adapt.

## Steps

1. TODO

The frontmatter keys ivralo knows are name, description, metadata, license, compatibility and allowed-tools. Any other key is a lint warning. name must equal the directory's name.

Quote the description

The frontmatter is YAML. A description almost always contains a colon, and an unquoted value with a colon in it is not valid YAML. When that happens the skill does not load, and nothing else tells you. So always put the description in double quotes.

Write the description for the index

The description is the only part an agent sees until it opens the skill. It should say three things:

  • what the skill teaches, in its first sentence (that sentence is all other specialists see);
  • when to load it: "Load when the user asks ...";
  • when not to: "Do NOT load for ...", naming the skill that owns that job instead.

Lint warns when a description is shorter than 120 characters or has no "do not load" boundary.

The body

The body is read once the agent decides the skill applies. Keep it to the technique: the steps, the rules that are easy to get wrong, and short worked code the agent can adapt. A body that alone would take more than a quarter of the model's context window is a lint warning. Move long material into references/ and say in the body when to read it.

Scripts

A script is a Python file in scripts/. The agent runs it from its own Python, inside the worker, with run_skill_script from ivralo.engine:

from ivralo.engine import run_skill_script

run_skill_script("deploy-checks", "check_manifest.py", "deploy/manifest.yaml")

run_skill_script finds the script on whichever shelf serves the skill, runs it with the runtime's interpreter, and prints what it wrote, which is how the output reaches the model. Arguments are the script's command line; file names in them are workspace names. A script that runs longer than 120 seconds is stopped, and a non-zero exit raises after its output is printed.

Two rules follow from that:

  • print what the caller needs to see; printed output is the only thing that comes back;
  • never tell the agent to run a script by a relative path; the shelf is not in the workspace.

Scripts run inside the worker under its write guard, not in ivralo's own process. Read a script before you install a skill that ships one.

References

A file in references/ is longer documentation the agent reads only when the body sends it there. It costs nothing until then.

Grants for the skill's own specialist

metadata.ivralo.capabilities lists the grants the skill asks for when it rides a specialist of its own. The scaffold writes it empty, because most skills need nothing beyond the baseline: Python in the worker, the workspace's files and asking you a question. Names come from Capabilities; an unknown name is a lint error.

Lint

ivralo skills lint deploy-checks

reads skills for the mistakes that each cost someone a debugging session. With no name it reads both shelves.

LevelRuleWhat it catches
errordescription-unquoteda description without quotes
errorcapabilities-declaredan unknown grant name, or not a list
errorscript-missingthe body names a script the skill does not ship
errorscript-syntaxa script that does not compile
warningdescription-shortunder 120 characters
warningdescription-no-negative-guidanceno "do not load" boundary
warningfrontmatter-unknown-keysa key ivralo does not know
warningsubprocess-no-capturesubprocess without capture_output=True
warningos-systema call to os.system()
warningabsolute-path-writea write to an absolute or .. path
warningstdout-not-printeda worked example that computes and never prints
warningbody-oversizeda body over a quarter of the context window

Findings never block. An install shows them before it asks, ivralo doctor lists them, and the options below change the rest:

OptionEffect
--strictexit 1 when an error is found, for CI
--shelf shipped or --shelf localread one shelf only
--fixquote an unreadable name: or description: on the local shelf

Check it loads

ivralo skills

shows the skill on its shelf and whether it validates. Start a new session, or type /new, and the team carries it.