8
Skills
Alex Clarke edited this page 2026-06-11 16:00:33 -06:00

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, and skill__unload meta-tools are themselves function calls, and most skills grant tools the model needs to invoke. If your global config has function_calling_support: false, the entire skills system is disabled: the meta-tools are not registered, the REPL .skill load command refuses, and coyote --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: false at the global level turns skills off entirely. The skill__list/skill__load/skill__unload tools are not exposed to the model. Equivalent to skills not existing.
  • visible_skills declares the universe — the set of skills allowed to be referenced by any context. If omitted, all installed skills are eligible.
  • enabled_skills is 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_instructions controls whether a short hint is appended to the system prompt telling the model that specialized skills may apply and to call skill__list early in a task. The hint is gated by the same predicate that exposes the skill__* meta-tools (function_calling_support + effective skills_enabled) and is auto-suppressed when the effective enabled_skills set is empty (i.e. skill__list would return nothing). Default true.
  • skill_instructions lets you replace the built-in hint text with custom wording. Leave null to 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 setting false wins. 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 sets enabled_skills to Some([...]) is the effective list for that turn.
  • inject_skill_instructions / skill_instructions: Follow the same inject_todo_instructions / continuation_prompt cascade. When an agent is active its value is used directly; otherwise the resolver picks the first defined value from session > role > global. The bool defaults to true; the string defaults to null (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. If function_calling_support: false in your global config, the entire skills feature is off. The REPL .skill load command, the CLI --skill <name> flag, and the model's own skill__load invocation 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: false at the graph level turns skills off for every llm node in the graph regardless of node-level fields. Equivalent to "skills don't exist for this graph."
  • enabled_skills at the graph level is the universe: the set of skill names any llm node in the graph is allowed to reference in its own enabled_skills. The validator rejects per-node entries that aren't in this set at load time.
  • inject_skill_instructions at the graph level is the agent-layer toggle for the skill-discovery hint. Defaults to true. Suppressed automatically when the effective enabled_skills set is empty.
  • skill_instructions at the graph level replaces the built-in hint text with your own wording. Leave null to use the default.
  • Omitting enabled_skills inherits 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__list is exposed to the model along with skill__load and skill__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 standard effective_role pipeline; the loading itself goes through the same refresh_tool_scope path as .skill load in the REPL.
  • Per-node policy. When enabled_skills is set on the node, the graph's effective skill policy is temporarily narrowed to that subset for the duration of the node. skill__list will only show those skills and skill__load will only accept those names. When the node finishes, the original policy is restored so the next node sees its own.
  • skills_enabled: false at the node level is an off-switch: no skill__* 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_instructions and skill_instructions resolve node > graph > app (most specific wins). Setting inject_skill_instructions: false on a node suppresses the skill-discovery hint for just that node; setting skill_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: true in 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.