A skill is a named chunk of instructions (a markdown document) that the model loads only when it needs it. This is progressive disclosure: instead of pasting every playbook into the system prompt, you list skills by name and one-line description, and the model pulls in the full text on demand.
How it works when an agent has skills:
- The system prompt gets a compact Available skills block: one line per
skill (
- name: description) plus one sentence telling the model to callload_skillbefore doing a task a skill covers. - A
load_skilltool (input{ name }) is registered automatically. It returns the skill's full content as the tool result. - Skill bodies are not in the prompt until loaded. An agent with twenty long skills pays roughly one line per skill on every request, and the body cost only on the runs that use it.
An unknown name is a tool error that lists the valid skill names, so the model can correct itself.
import { createAgent, defineSkill } from '@lousho/build-ai-agent';
const agent = createAgent({
instructions: 'You are a release engineer.',
provider,
skills: [
defineSkill({
name: 'changelog',
description: 'How to write a changelog entry',
content: '# Changelog entries\n\nUse the imperative mood and link the PR.',
}),
],
});defineSkill({ name, description, content }) validates its input. name must
match ^[a-z0-9][a-z0-9-_]{0,63}$; description and content must be
non-empty. Errors say what is wrong and how to fix it.
AgentExecutor.execute() takes the same skills option; see
the executor API.
If you already registered a tool named load_skill, passing skills throws:
rename your tool or drop the option.
loadSkills(dir) reads skill files, so non-developers can edit them without
touching code. Two layouts are supported and can be mixed in one directory:
skills/
changelog/SKILL.md # <name>/SKILL.md (Anthropic / open-harness convention)
code-review.md # <name>.md (eve convention)
Each file starts with YAML frontmatter; the rest is the skill's content:
---
description: How to write a changelog entry
---
# Changelog entries
Use the imperative mood and link the PR.descriptionis required.nameis optional and defaults to the folder name (SKILL.mdlayout) or the file name without.md.- Folders without a
SKILL.mdare ignored. So is a loose.mdfile with no frontmatter (aREADME.md): it is skipped with a console warning instead of failing the whole load. A file with frontmatter but nodescriptionis still an error. - A
<name>/SKILL.mdskill records its folder asskill.directory.load_skillthen appends the folder path and lists the files next toSKILL.md(up to 50), and aread_skill_filetool (input{ skill, path }) is registered to read them. The path is relative to the skill's folder and cannot leave it:.., absolute paths and symlinks out are refused. Text files up to 256 KB can be read.defineSkill({ directory })sets the same field for a skill you build in code. A tool of your own namedread_skill_fileclashes the same wayload_skilldoes. - Skills come back sorted by name, so prompts are deterministic.
- Errors are
LOUSHO_SKILL_INVALIDand name the file and the problem: missing description, invalid YAML, invalid name, an unreadable directory, or two skills with the same name (both paths are listed).
import { createAgent, loadSkills } from '@lousho/build-ai-agent';
const agent = createAgent({
instructions: 'You are a release engineer.',
provider,
skills: [...(await loadSkills('./skills'))],
});Use mockModel to assert the model sees descriptions but not
bodies until it loads one: the first request's system prompt lists the skills,
and the request after a load_skill call contains the body in the tool result.