Skill
A persona the model becomes — a system prompt plus optional examples, tools, and delegates. Pure declarative configuration.
A Skill is a packaged behavior. A system prompt that turns a generic LLM into a domain-specialized agent: a researcher, a critic, a code reviewer, a customer support triager. Optional examples make it few-shot. Optional tool and delegate references make it composable.
Skills are not tools. A tool is a function the model calls. A skill is a persona the model becomes. Confusing the two produces the LangChain "agents" mess where you can't tell what .invoke() versus .run() versus .stream() does.
The interface
import type { SkillDefinition } from '@agentskit/core'
export interface SkillDefinition {
name: string
description: string
systemPrompt: string
examples?: Array<{ input: string; output: string }>
tools?: string[] // tool names, resolved at activation
delegates?: string[] // skill names, resolved at activation
temperature?: number
metadata?: Record<string, unknown>
onActivate?: () => MaybePromise<{ tools?: ToolDefinition[] }>
}Notice: no run() method. A skill is not invokable. The runtime activates it and proceeds with normal model calls plus the skill's prompt, tools, and temperature.
Defining a skill
import type { SkillDefinition } from '@agentskit/core'
export const codeReviewer: SkillDefinition = {
name: 'code_reviewer',
description: 'Reviews TypeScript pull requests for bugs, missing tests, and style.',
systemPrompt: `You are a meticulous senior TypeScript engineer reviewing a PR.
Always:
- Read the diff fully before commenting
- Flag missing tests as severity:high
- Flag any 'any' usage with a concrete suggestion
- Approve only after you've checked the test runner output
- End with a one-line summary verdict`,
tools: ['filesystem_read', 'shell'],
examples: [
{
input: 'Review the diff in PR #42',
output: '...comment-by-comment review with a verdict at the end...',
},
],
temperature: 0.2,
}The skill is just data. It can be serialized, versioned, traded in a marketplace, or generated by another agent.
Using a built-in skill
import { researcher, critic, planner } from '@agentskit/skills'
const result = await runtime.run('What changed in JavaScript runtimes in the last year?', {
skill: researcher,
})Tools by reference
Skills don't define tools inline. They reference tool names, and the runtime resolves them against the registry at activation:
const supportTriager: SkillDefinition = {
name: 'support_triager',
description: 'Routes tickets to the right team.',
systemPrompt: '...',
tools: ['lookup_customer', 'list_open_tickets', 'tag_ticket'],
}
createRuntime({
adapter,
tools: [lookupCustomer, listOpenTickets, tagTicket],
})
await runtime.run('Triage all unassigned tickets', { skill: supportTriager })This is what enables tool reuse across skills, and what makes the marketplace possible.
Delegation: skills calling skills
import { planner, researcher, coder } from '@agentskit/skills'
await runtime.run('Build a landing page about quantum computing', {
skill: planner,
delegates: {
researcher: { skill: researcher, tools: [webSearch()], maxSteps: 3 },
coder: { skill: coder, tools: [...filesystem({ basePath: './src' })], maxSteps: 8 },
},
})The runtime materializes each delegate as a tool the planner can call (delegate_researcher, delegate_coder). To the model, delegation is just another tool call. To the runtime, it's a recursive run() with depth tracking.
Dynamic per-user tools (onActivate)
The one and only escape hatch in an otherwise-pure-data contract:
const supportAgent: SkillDefinition = {
name: 'support_agent',
description: 'Acts on behalf of the current user.',
systemPrompt: '...',
async onActivate() {
const userToken = await getCurrentUserToken()
return {
tools: [createGmailTool(userToken), createCalendarTool(userToken)],
}
},
}Use this only for credential-bound tools that can't be constructed at the registry level. General initialization belongs elsewhere.
When to write your own
Almost always, if the built-in skills don't cover your domain. A skill is a system prompt plus a few fields — it's the cheapest concept in AgentsKit to author. The hard part is the prompt itself.
Read existing skills (packages/skills/src/researcher.ts, critic.ts, coder.ts, summarizer.ts, planner.ts) to see the prompt patterns we use. They're not magic; they're disciplined.
Common pitfalls
| Pitfall | What to do instead |
|---|---|
| Putting tool implementations inside the skill | Reference by name; register the tool with the runtime |
| Doing I/O when defining the skill (top-level await) | Move it into onActivate or out of the skill entirely |
Trying to call skill.run() | Skills aren't invokable — pass them to runtime.run({ skill }) |
| Multi-turn examples encoded as one input/output | Encode the demonstration in systemPrompt prose for v1 |
| Skill versioning via filename | Use metadata.version until the manifest spec lands |
Going deeper
The full list of invariants (twelve of them, S1–S12) is in ADR 0005 — Skill contract.