Skills are modular knowledge or capability packs that an LLM can load and unload mid-conversation. Think of them as roles' lightweight cousins: where a Role defines the assistant's identity (single-active, switched explicitly by the user), a skill is a capability the model layers onto whatever role/agent/session is already active.
Multiple skills can be loaded at once. They compose, meaning their instructions stack, their tool whitelists union, and their declared MCP servers get acquired automatically.
Skills are static curated knowledge packs. For evolving facts the LLM curates over time across sessions, see Memory.
Common uses:
- Methodology overlays ("how to do git surgery", "how to review code").
- Toolkit unlocks that grant a small bundle of tools or MCP servers without changing the active role.
- One-shot helpers that auto-unload after the model finishes a task (see auto_unload).
Prerequisite: function calling must be enabled. Skills are built on top of Coyote's function calling system so as to not overwhelm the context with skill descriptions.
skill__list,skill__load, andskill__unloadmeta-tools are themselves function calls, and most skills grant tools the model needs to invoke. If your global config hasfunction_calling_support: false, the entire skills system is disabled: the meta-tools are not registered, the REPL.skill loadcommand refuses, andcoyote --skill <name>exits with a clear error. See Function Calling to enable it.
Skill Definition
Skills in Coyote are directories under your Coyote configuration's skills/ directory, each containing a SKILL.md
file. To find your skills directory:
coyote --info | grep 'skills_dir' | awk '{print $2}'
The on-disk layout:
<coyote-config-dir>/skills/
└── <skill-name>/
└── SKILL.md # YAML frontmatter + body
Example: A skill that teaches the model git conventions and grants shell access for running git commands.
skills/git-master/SKILL.md:
---
description: Atomic commits, rebase methodology, conflict resolution.
enabled_tools: execute_command
---
You are operating on a git repository. Apply these conventions strictly. Use the `execute_command` tool to run git
commands.
## Atomic commits
Each commit represents one logical change. If the commit message needs the word "and," the change is too large; split
it.
## Commit messages
- Subject line: imperative mood, ≤50 characters, no trailing period.
- Blank line.
- Body: explain WHY, not WHAT.
To see complete examples, look at the bundled built-in skills.
Frontmatter
The YAML frontmatter at the top of SKILL.md is where you declare the skill's metadata and what extra capabilities it
grants when loaded. All fields are optional.
| Field | Default | Description |
|---|---|---|
description |
empty | Short one-line description shown to the model when it lists available skills. Make it specific: this is what helps the model decide when to load. |
enabled_tools |
none | Comma-separated tool names that become available while the skill is loaded. Union with the active role/agent/session's tools. Tools must exist in visible tools. |
enabled_mcp_servers |
none | MCP server names the skill needs. Accepts a YAML list (preferred) or a comma-separated string. Skills can reference servers from your mcp.json; those servers are auto-acquired on load and released on unload via reference counting. |
auto_unload |
false |
If true, the skill is automatically removed from the registry at the end of every turn where the model produced a final response (no more tool calls). |
Body
Everything after the frontmatter is the skill body; i.e. the instructional content that gets injected into the model's system prompt while the skill is loaded. This is where the methodology, conventions, or workflow guidance lives.
Bodies stack in load order: if you load git-master, then ai-slop-remover, the system prompt contains the active
role's prompt followed by git-master's body followed by ai-slop-remover's body, separated by blank lines.
There is no length limit, but keep skill bodies focused. If you find yourself writing a manual, split it into multiple skills the model can load in combination.
Skills vs. Roles
| Skill | Role | |
|---|---|---|
| Activation | Model-driven (via the skill__load tool) or user-driven (via .skill load). |
User-driven (via .role <name> or --role <name>). |
| Cardinality | Multiple loaded at once; bodies stack, tools/MCPs union. | One active at a time; switching exits the previous role. |
| Lifecycle | Loaded mid-conversation; can auto-unload at turn end. | Active for the whole session until explicitly exited. |
| Mental model | "Add this capability to my context." | "Be this kind of assistant." |
Use a role when you want to be a SQL expert or a resume builder. Use a skill when you want to temporarily augment your current context with git knowledge, frontend conventions, or any other modular capability.
Managing Skills
REPL commands
| Command | Effect |
|---|---|
.skill loaded |
List currently-loaded skills in this session. |
.skill load <name> |
Load a skill. Validates policy + compatibility, then refreshes the tool scope. |
.skill unload <name> |
Unload a loaded skill. Releases any MCP servers it pulled in. |
.edit skill <name> |
Open an existing skill in $EDITOR (fails if the skill doesn't exist). |
.skill <name> |
Open the skill in $EDITOR. Creates a scaffolded SKILL.md if missing. |
.delete skill |
Interactive prompt to choose installed skills to delete. |
.install skills |
Reinstall all bundled built-in skills, overwriting existing copies after confirmation. |
.install remote <git-url> --filter skills |
Install only skills/ from a remote repo. See Sharing Configurations. |
.set skills_enabled <true|false|null> |
Flip the master switch at runtime. Inside a session this sets the session override; otherwise it updates the global default (null restores the default true). |
.set enabled_skills <csv|null> |
Replace the global default-active skill list at runtime, e.g. .set enabled_skills git-master,ai-slop-remover. Use null to clear. |
CLI flags
| Flag | Effect |
|---|---|
coyote --list-skills |
Print installed skill names, one per line. |
coyote --skill <NAME> |
If the skill exists, load it and proceed to REPL or one-shot. If missing, scaffold + edit. |
coyote --skill <A> --skill <B> [...] |
Pre-load multiple skills in argument order. All must exist; fails fast if any is missing. |
coyote --install skills |
Reinstall all bundled built-in skills. |
coyote --install-from <url> --filter skills |
Install only skills/ from a remote repo. |
coyote --skill <name> composes naturally with --role, --agent, and a one-shot prompt:
coyote --role frontend --skill git-master "rebase main into this branch and resolve conflicts"
This activates the frontend role, loads the git-master skill on top, and runs the prompt with both in effect.
Visibility and Enforcement
Skills respect a layered visibility/enforcement model. You can scope which skills are available globally and per-context.
Global config
In your config.yaml:
skills_enabled: true # master switch (default true)
visible_skills: [git-master, code-review, ai-slop-remover] # the universe; omit for "all installed"
enabled_skills: [git-master, ai-slop-remover] # default-active subset in the blank context
inject_skill_instructions: true # inject the skill-discovery hint into the system prompt (default true)
skill_instructions: null # override the built-in hint text (default null = use built-in)
skills_enabled: falseat the global level turns skills off entirely. Theskill__list/skill__load/skill__unloadtools are not exposed to the model. Equivalent to skills not existing.visible_skillsdeclares the universe — the set of skills allowed to be referenced by any context. If omitted, all installed skills are eligible.enabled_skillsis the default-active subset in the no-role/no-agent/no-session context. Skills outside this list are not loadable in the default REPL.inject_skill_instructionscontrols whether a short hint is appended to the system prompt telling the model that specialized skills may apply and to callskill__listearly in a task. The hint is gated by the same predicate that exposes theskill__*meta-tools (function_calling_support+ effectiveskills_enabled) and is auto-suppressed when the effectiveenabled_skillsset is empty (i.e.skill__listwould return nothing). Defaulttrue.skill_instructionslets you replace the built-in hint text with custom wording. Leavenullto use the default.
Role, agent, and session overrides
Each of these contexts can independently set skills_enabled, enabled_skills, inject_skill_instructions, and
skill_instructions. Precedence:
skills_enabled: Any level settingfalsewins. Once disabled at any level, the master switch is off for that context.enabled_skills: Most-specific-wins:session > agent > role > global. The first level that setsenabled_skillstoSome([...])is the effective list for that turn.inject_skill_instructions/skill_instructions: Follow the sameinject_todo_instructions/continuation_promptcascade. When an agent is active its value is used directly; otherwise the resolver picks the first defined value fromsession > role > global. The bool defaults totrue; the string defaults tonull(built-in text). Both can be flipped at runtime via.set inject_skill_instructions <bool>and.set skill_instructions <text|null>.
Example role that opts into a different skill set than the global default:
---
name: frontend-dev
enabled_skills:
- git-master
- ai-slop-remover
- frontend-ui-ux
---
You are a frontend specialist.
enabled_skills accepts either a YAML list (as shown above) or a comma-separated string
(enabled_skills: git-master,ai-slop-remover,frontend-ui-ux). Both forms parse to the same value.
When this role is active, the model sees exactly those three skills in skill__list. Any other installed skill is
filtered out of the response and cannot be loaded. skill__load rejects it with "Skill 'X' is not enabled in this context".
Validation
Four validation points enforce these rules:
| Where | What is checked | Severity |
|---|---|---|
| Global feature gate | function_calling_support: true is required for skills to exist at all (registry/tools/REPL/CLI). |
Skills are silently disabled. No skill__* tools, .skill load and --skill refuse. |
| Config load | Every name in any enabled_skills exists on disk and is in global visible_skills (when visible_skills is set). |
Hard error. Refuses to start with the offending name + level. |
| Runtime list/build | For each enabled skill: declared MCPs require MCP support. | Filter + warn. Incompatible skills are dropped from skill__list. |
skill__load call |
Skill is in the effective enabled set and its declared MCPs are usable. | Tool error to model with a distinct message per failure mode. |
Skills require function calling. Skills are useless without it. The meta-tools (
skill__list,skill__load,skill__unload) are themselves function calls, and most skills grant tools the model needs to invoke. Iffunction_calling_support: falsein your global config, the entire skills feature is off. The REPL.skill loadcommand, the CLI--skill <name>flag, and the model's ownskill__loadinvocation all refuse with a clear error.
Skills in Graph Agents
Graph agents integrate with the skill system at two levels: a graph-wide universe set at the top of
graph.yaml, and a per-node auto-load set on llm nodes. Other node types (script, approval, input, rag, map, end)
have no skill surface. Skills only apply where a model call happens.
Graph level — the universe
The top-level skills_enabled, enabled_skills, inject_skill_instructions, and skill_instructions fields in
graph.yaml work just like they do on a normal agent's config.yaml: they declare the policy ceiling and the
skill-hint behavior for the whole graph.
name: coder
skills_enabled: true
enabled_skills:
- code-review
- git-master
- verification-gates
inject_skill_instructions: true # Inject the skill-discovery hint into every `llm` node's system prompt
skill_instructions: null # Override the built-in hint text (null uses the default)
skills_enabled: falseat the graph level turns skills off for everyllmnode in the graph regardless of node-level fields. Equivalent to "skills don't exist for this graph."enabled_skillsat the graph level is the universe: the set of skill names anyllmnode in the graph is allowed to reference in its ownenabled_skills. The validator rejects per-node entries that aren't in this set at load time.inject_skill_instructionsat the graph level is the agent-layer toggle for the skill-discovery hint. Defaults totrue. Suppressed automatically when the effectiveenabled_skillsset is empty.skill_instructionsat the graph level replaces the built-in hint text with your own wording. Leavenullto use the default.- Omitting
enabled_skillsinherits whatever the role / global cascade resolves to (typically "all visible").
Per-node: discovery-only on llm nodes
llm nodes can independently declare skills_enabled, enabled_skills, inject_skill_instructions, and
skill_instructions. The graph-level fields gate what's allowed in the graph at all; the node-level fields
narrow that universe to what this node's model can see and load, and let you customize the skill-discovery hint
for just that node. Nothing is auto-loaded. The model uses skill__list and skill__load to bring skills in as
it needs them, matching the rest of Loki's skill model.
nodes:
implement:
type: llm
prompt: "{{plan_summary}}"
tools: [fs_write, fs_patch, fs_cat]
enabled_skills: # must be a subset of graph-level enabled_skills
- code-review
- verification-gates
# skills_enabled: false # would disable skills for this node only
# inject_skill_instructions: false # would suppress the skill-discovery hint for this node only
# skill_instructions: "..." # would substitue custom hint text for this node only
Semantics:
- Discovery, not auto-load. When the node runs,
skill__listis exposed to the model along withskill__loadandskill__unload. The model decides which skills (if any) to load based on the task at hand. Loaded skills compose into the system prompt and union tools/MCP servers via the standardeffective_rolepipeline; the loading itself goes through the samerefresh_tool_scopepath as.skill loadin the REPL. - Per-node policy. When
enabled_skillsis set on the node, the graph's effective skill policy is temporarily narrowed to that subset for the duration of the node.skill__listwill only show those skills andskill__loadwill only accept those names. When the node finishes, the original policy is restored so the next node sees its own. skills_enabled: falseat the node level is an off-switch: noskill__*meta-tools are exposed and no skills can be loaded from this node. Useful when one node in an otherwise skill-enabled graph should run with a strict, narrow context (for example a structured-output extraction step that shouldn't load editorial conventions).- Injection cascade.
inject_skill_instructionsandskill_instructionsresolvenode > graph > app(most specific wins). Settinginject_skill_instructions: falseon a node suppresses the skill-discovery hint for just that node; settingskill_instructions: "..."substitutes custom hint text for just that node. Both are optional and inherit the graph-level value when omitted. - Skill state carry-over. Skills the model loads during a node persist into subsequent nodes (they're real
registry insertions, not node-scoped state). If you want a skill to clean up automatically at turn end, mark it
auto_unload: truein the skill's own frontmatter.
Agent nodes have no skill surface
agent nodes spawn a full child agent (graph or normal). That child agent declares its own skill policy in its own
config.yaml / graph.yaml. Adding a skill field on the agent node would be redundant at best and surprising at
worst. The child author already controls what skills the child uses. So agent nodes intentionally don't accept
enabled_skills / skills_enabled.
Validation
| Where | Check | Severity |
|---|---|---|
| Graph load | Per-node enabled_skills entries are non-empty strings |
Hard error |
| Graph load | Per-node enabled_skills are a subset of graph-level enabled_skills (when set) |
Hard error |
| Node execution | Each auto-loaded skill resolves to a real installed skill (otherwise that skill is skipped with a warning) | Warn |
| Node execution | Each auto-loaded skill's MCP / function-calling requirements are satisfied | Warn |
The validator only runs validate_before_run (the default); inspecting a graph via coyote --info -a <name> does
not trigger validation, so subset-violation errors surface on the first real run.
Auto-unload
Skills with auto_unload: true in their frontmatter are removed from the registry at the end of each turn. Specifically,
after the model produces a final response with no further tool calls. They survive through multi-step tool-using rounds
within a single turn, but the next user message starts with the registry cleaned.
Use this for one-shot capabilities the model needs for a focused task and shouldn't carry into unrelated next turns.
---
description: Untangle a complex git rebase.
enabled_tools: execute_command
auto_unload: true
---
After the model finishes a rebase request, the skill quietly removes itself. The user's next question won't have rebase context in its system prompt.
MCP server caching. When an auto_unload skill declared MCP servers in its enabled_mcp_servers, those server
processes are kept running until the next role/agent/session switch (when the full tool scope rebuilds). This is
intentional: if the skill is reloaded later in the same session, the MCP server is already warm and the reload is
instantaneous. The model can't invoke those servers in subsequent turns because they're no longer in the effective MCP
whitelist — but their processes stay cached for fast re-load.
Built-in Skills
Coyote ships with four built-in skills, installed automatically on first run:
| Skill | Granted tools | Purpose |
|---|---|---|
git-master |
execute_command |
Atomic commits, rebase methodology, conflict resolution, investigation. |
code-review |
fs_read, fs_grep, fs_glob, fs_cat, fs_ls |
Correctness/tests/clarity/coupling/footguns review checklist. |
ai-slop-remover |
none (knowledge-only) | Detect and remove AI slop from code and prose. |
frontend-ui-ux |
fs_read, fs_write, fs_patch, fs_grep, fs_glob, fs_cat, fs_ls, fs_mkdir |
Designer-turned-developer crafting UI/UX even without mockups. |
Each is intentionally short — they're starter templates. Fork them via .skill <name> to customize.
To reinstall the bundled built-ins (e.g., to restore a built-in you've modified):
coyote --install skills
# or in the REPL:
.install skills
This overwrites every bundled skill in your skills directory after a confirmation prompt. Local skills you created yourself are left untouched.
Sharing Skills
Skills install via the same Sharing Configurations flow as other Coyote assets. A remote repo laid out as:
<repo>/
└── skills/
└── <skill-name>/
└── SKILL.md
installs with:
coyote --install-from https://github.com/<owner>/<repo>
# or just the skills/:
coyote --install-from https://github.com/<owner>/<repo> --filter skills
See Sharing Configurations for the full layout, ref pinning, conflict resolution, and secrets handling. Skills follow the standard per-file conflict prompts.
Programmatic Tools
When skills are enabled, the model has three tools available for managing them itself:
| Tool | Parameters | Effect |
|---|---|---|
skill__list |
none | Returns each available skill's name, description, granted tools/MCPs, and loaded state. |
skill__load |
name: string |
Loads a skill after validating policy + compatibility. Refreshes the tool scope. |
skill__unload |
name: string |
Unloads a loaded skill. Releases its MCP server handles. |
These are how the model discovers and uses skills mid-conversation. You can disable this discovery channel by setting
skills_enabled: false at any level; the three tools then disappear from the model's function list entirely. The same
happens if function_calling_support: false is set globally. Without function calling, the skill system has no surface
to operate on, so it is silently disabled across REPL, CLI, agents, and the model's tool list.