Skip to main content

Skills

Skills are reusable workflow commands packaged as markdown files. The agent loads a skill on demand via the use_skill tool; the file's content is returned and the agent follows the instructions inside. Skills also surface in the client as /command entries in the slash palette.

Anatomy​

text
my-app/
├── app.yaml
└── skills/
├── commit.md # skill body (markdown)
├── review.md
└── security-audit.md

Declare the skills under dev.skills :

yaml
dev:
skills:
- command: /commit
description: "Stage + commit + push the current diff"
path: skills/commit.md

- command: /review
description: "Adversarial code review with focus on security"
path: skills/review.md

- command: /security-audit
description: "Run the standard 6-step security audit"
path: skills/security-audit.md

Skill entry reference​

FieldTypeRequiredDescription
commandstring (min 1)yesSlash command id (e.g. /commit).
descriptionstringno (default "")One-line description shown in the slash palette and the use_skill catalog.
pathstring (min 1)yesPath to the .md file relative to the bundle directory.

The compiler checks that path: exists (a missing file is a real compile error - "the command fails the first time it is typed") but does not read the content into the compiled app: at runtime, use_skill re-reads the file live from the bundle directory on every call, not from anything embedded at compile time. In practice this only matters if the file is deleted or moved after a successful compile without recompiling - the compile-time check doesn't protect against that.

A skill referenced only through an agent's capabilities: list (not through dev.skills) has no compile-time existence check at all - a typo'd capability name compiles clean and silently loads nothing at runtime (see below).

How an agent uses a skill​

use_skill is one of the always-available primitives - see Built-in Tools → use_skill.

When the LLM calls use_skill(command='/commit'), the runtime looks up the matching skill entry, returns the file content as the action result, and adds dir (the skill file's own directory, relative to which any files the skill references should be read or executed) plus a "Follow these instructions" note.

jsonc
// LLM call
{"name": "use_skill", "arguments": {"command": "/commit"}}

// Result (returned to the LLM)
{
"success": true,
"data": {
"command": "/commit",
"description": "Stage + commit + push the current diff",
"content": "# Commit workflow\n\n1. Run `git status`...\n",
"dir": "skills",
"note": "Follow these instructions to complete the task. Files they reference are relative to `dir` - read or execute them from there."
}
}

The leading / is optional in the command parameter - the runtime adds it automatically.

Two complementary surfaces​

Don't confuse dev.skills with ui.slash_commands:

BlockPurposeLoaded byReach
dev.skillsReusable workflow MD files the agent loads via use_skill.Read live from the bundle on each call. The agent calls use_skill('/cmd').Server-side. The agent reads the markdown and follows the steps.
ui.slash_commandsPure client-side / palette entries.the chat client / web client renders the palette; the chosen command's template: becomes the message sent to the agent.Client-side. The agent never knows the slash palette existed - it just sees a normal user message.

A typical app declares both: the slash command exposes a typed form to the user; the resulting message tells the agent to invoke use_skill with the right command.

Auto-loading per agent​

An agent's capabilities: field lists skill names to auto-load into the agent's system prompt. At the start of each turn the runtime resolves each name (live from the bundle, same as use_skill) and appends the content under an ## Available capabilities section. Skills loaded this way don't need to be invoked explicitly - they're already in the agent's context.

A capability name resolves in this order: a matching dev.skills[].command first, then skills/<name>.md, then skills/<name>/SKILL.md - so a capability can point at a plain skill file even if it has no dev.skills entry at all (the SKILL.md-in-its-own-folder form is useful when the skill needs supporting files alongside it - see dir in the use_skill result above).

yaml
agents:
- id: reviewer
role: specialist
capabilities:
- git_review # loads skills/git_review.md into the prompt
- security_audit # loads skills/security_audit.md into the prompt

instructions.file is a separate mechanism for keeping a long prompt in its own file: the compiler checks the file exists and merges its content directly into system_prompt at compile time - content is fixed at compile time, unlike a skill's, which is read live each time it's used.

yaml
agents:
- id: reviewer
role: specialist
specialty: "Adversarial code review"
capabilities: [git_review] # auto-loaded from skills/
instructions:
file: ./instructions/review.md # appended to system_prompt

Skill file format​

Skills are plain markdown. The compiler doesn't parse them - it inlines the raw text. Convention:

markdown
# /commit - Stage, commit, push

## Goal
Produce a clean conventional-commits message and push to the
current branch.

## Steps
1. Run `git status` to see the working tree.
2. Run `git diff --staged` for any already-staged changes;
`git diff` for unstaged.
3. Group changes by intent. One commit per intent.
4. For each commit:
- Pick a type (feat / fix / refactor / docs / test / chore).
- Stage the relevant files (`git add <paths>`).
- Commit with a clear subject + body explaining the WHY.
5. Push the branch (`git push`).

## Anti-patterns
- DO NOT mix unrelated changes in one commit.
- DO NOT use vague subjects like "WIP" or "fix stuff".

The agent reads this verbatim and follows it. The file is read raw

  • there is no template resolution inside it: {{prompt.X}} or {{include:path}} written inside a skill markdown file is not resolved and reaches the agent literally as that text. (The separate {{skill.X}} macro - using a skill's content to fill in another field - does go through compile-time resolution; that's a different use case from a skill loaded via use_skill or capabilities:.)

Bundle layout​

<bundle>/skills/<name>.md is the convention, not an enforced rule - path: can point anywhere relative to the bundle root, and nothing checks the file exists until it's actually loaded (see above).

For very large apps with many skills, consider grouping them in sub-folders (skills/git/commit.md, skills/security/audit.md) and listing each entry's full path.

Compile-time validation​

Unknown keys on a skill entry are rejected. What the compiler actually checks:

  • Every entry must declare command and path (both non-empty), and command must match the slash-command format.
  • path must point to a file that exists in the bundle - a missing file is a compile error.
  • Duplicate command values raise an error (skill resolution would be ambiguous at runtime).

What it does not check: a name used only through an agent's capabilities: list, with no matching dev.skills entry - that path has no compile-time safety net. Test every capability-loaded skill with a real turn before shipping - a typo'd capability name compiles clean and silently loads nothing.

User skills (/use_skill)​

Skills on this page are agent-facing: the LLM calls use_skill and the body comes back as a tool result. The runtime also supports user-facing skills the end user picks from the composer and that get injected as a forced role: system directive on the matching turn. Authored per-user, stored in the daemon database, gated by a single YAML flag:

yaml
dev:
allow_user_skills: true # default false

Full mechanics, CRUD endpoints, the /use_skill <name> <prompt> parser, the turn-scoped injection slot, and the web composer flow (including the .md file picker) are documented in:

Reference → Runtime → User Skills & /use_skill

Cross-references​