Skills
Skills are reusable units of procedural knowledge — a name, a one-line description, and a body of instructions (plus optional bundled resources). The agent sees only the names and descriptions up front and pulls a skill’s full body on demand when the task matches. This keeps the prompt small no matter how many skills you attach.
npm install @kuralle-agents/skillsQuick start
Section titled “Quick start”import { defineSkill } from '@kuralle-agents/skills';import { defineAgent } from '@kuralle-agents/core';
const returnsPolicy = defineSkill({ name: 'returns-policy', description: 'Returns, refunds, exchanges, and the 30-day window. Use when a customer asks about returning an order.', allowedTools: ['lookup_order'], body: [ '# Returns', '1. Confirm the order id, then run `lookup_order`.', '2. If the order is under 30 days old, it is returnable.', '3. Refunds take 5-7 business days to the original method.', ].join('\n'), resources: { 'exceptions.md': '# Non-returnable\n- Gift cards\n- Final-sale items' },});
const agent = defineAgent({ id: 'support', model, instructions: 'You are a support agent. Load the matching skill before answering policy questions; never guess.', tools: { lookup_order: lookupOrder }, skills: [returnsPolicy, shippingInfo, warrantyClaims /* …attach as many as you like */],});Progressive disclosure (the 3 levels)
Section titled “Progressive disclosure (the 3 levels)”- Level 1 — always in context (cheap). Every turn, Kuralle injects an
## Available skillsblock listing each skill’sname+description. That’s all the model sees by default — N skills cost only N description lines, not N full bodies. - Level 2 — on demand. When a task matches a description, the model calls the
load_skill(name)tool and gets that skill’s fullbodyback as a tool result. - Level 3 — bundled resources. A skill can ship reference files in
resources; the model reads one withread_skill_resource(name, path)only when it needs it.
system prompt ──▶ ## Available skills (names + descriptions — every turn) model ──▶ load_skill("returns-policy") ──▶ full SKILL.md body (tool result) model ──▶ read_skill_resource("returns-policy", "exceptions.md") ──▶ file contentMany skills, multi-turn
Section titled “Many skills, multi-turn”- Attach as many skills as you want — only descriptions are ever in the base prompt, so the menu scales. The model selects by matching the task to a description, and can load more than one skill in a single turn (e.g. answer a gift-card question and a 2FA question together).
- Loaded skills persist across turns. A
load_skillresult stays in the conversation transcript, which Kuralle restores on each turn. So a follow-up question about an already-loaded skill is answered without reloading — the body is still in context.
defineSkill options
Section titled “defineSkill options”| Field | Type | Notes |
|-------|------|-------|
| name | string | Stable identifier the model passes to load_skill. |
| description | string | The Level-1 selector — write it so the model knows when to load this skill. |
| body | string | The full instructions returned by load_skill (your SKILL.md). |
| allowedTools? | string[] | Tools this skill is allowed to drive (advisory scoping). |
| resources? | Record<string, string> | Bundled files exposed via read_skill_resource. |
You can also load skills from SKILL.md folders or a custom SkillStore instead of inline
defineSkill objects — see the @kuralle-agents/skills README for store backends.
Skills on a filesystem
Section titled “Skills on a filesystem”defineSkill builds skills in code. You can also keep them as SKILL.md folders on a
workspace filesystem — the same
agentskills.io layout Claude and other agents use — and load them with
fsSkillStore:
import { InMemoryFs, fsSkillStore } from '@kuralle-agents/fs';
const fs = new InMemoryFs({ '/skills/refunds/SKILL.md': '---\nname: refunds\ndescription: Handle refunds.\n---\n\n# Refunds\n...',});const agent = defineAgent({ id: 'support', model, workspace: fs, skills: fsSkillStore(fs) });fsSkillStore implements the same store interface as an array of defineSkill objects, so
progressive disclosure works identically. On a persistent
SqlFileSystem, skills survive restarts.
Where it runs
Section titled “Where it runs”Skills are pure data + two tools, so they run identically on Node and Cloudflare Workers. The
@kuralle-agents/skills workerd parity test verifies a skill body loads byte-identically inside the
Cloudflare runtime, and a KuralleAgent Durable Object does load_skill over a deployed Worker with
no changes (see examples-deploy/kuralle-skill-smoke).