22
Skills
Alex Clarke edited this page 2026-08-28 14:58:31 -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. For always-on, human-curated project rules injected into every prompt, see Workspace Instructions.

Common uses:

  • Methodology overlays ("how to do git surgery", "how to review code", "how to review state-changing code for transactional integrity"; see the bundled transactional-integrity skill).
  • 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.

Workspace-Local Skills

In addition to global skills, Coyote discovers skills local to the current working directory. Place a skill under .coyote/skills/ in your project root and it becomes available whenever you run Coyote from that directory (or any subdirectory):

<project-root>/
└── .coyote/
    └── skills/
        └── <skill-name>/
            └── SKILL.md

Workspace skills work identically to global skills; same SKILL.md format, same frontmatter fields, same composition rules. The only differences are:

  • Discovery: Coyote scans .coyote/skills/ in addition to the global skills directory.
  • Precedence: If a workspace skill and a global skill share the same name, the workspace version wins.
  • Scope: Workspace skills are only visible when running from that directory tree; they do not appear in other projects.

This is useful for project-specific workflows: a db-migration skill with your team's migration conventions, a deploy skill that loads the project's deployment MCP server, or any other task-specific overlay that doesn't belong in your global configuration.

Workspace skills can reference MCP servers from both the global mcp.json and the workspace .coyote/mcp.json (see Workspace-Local MCP Servers).

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.
mcp_tools none Per-server MCP tool whitelists applied while the skill is loaded, as one extra filter layer. A YAML map of server name -> tool-name patterns; each value accepts a YAML list or a comma-separated string, and a bare null value blocks all of that server's tools. Entries only apply to servers the skill enables. See Restricting MCP Tools.
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
.list skills List skills available in this context. Applies the same visibility and policy filters as the model-facing skill__list tool; loaded skills are marked (loaded).
.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 <git-url> --filter skills Install only skills/ from a remote repo. See Bundles.
.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-builtins skills Reinstall all bundled built-in skills.
coyote --install <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 thirty-five built-in skills, installed automatically on first run:

Skill Granted tools / MCP servers 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.
security-review fs_read, fs_grep, fs_glob, fs_cat, fs_ls Security analysis of a code change: traces untrusted data to dangerous sinks (injection, committed secrets, authz gaps, path traversal, SSRF, unsafe deserialization, supply chain); posture-gated PASS/FAIL verdict.
transactional-integrity fs_read, fs_grep, fs_glob, fs_cat, fs_ls Review state-changing code for atomicity gaps, read-modify-write races, non-idempotent handlers of at-least-once inputs, and dual-writes with no reconciliation.
logging-discipline none (knowledge-only) Calibrate log output to the repo's existing logging conventions; review diffs for under- and over-logging (silent new failure paths, log-and-rethrow duplication, register mismatches).
comment-discipline none (knowledge-only) Calibrate comment density and style to the repo's existing comment register (self-documenting / api-documented / comment-heavy) before writing code.
observability-review fs_read, fs_grep, fs_glob, fs_cat, fs_ls Post-implementation observability analysis: decide what monitoring, metrics, and alerts the new code needs (accounting for what exists) and produce alert-as-code changes or a concrete monitoring TODO.
incident-prior-art execute_command Check a code change against operational history (i.e. git archaeology on deleted/weakened guards plus incident-record sweeps) so reviews catch regressions of hard-won production lessons.
rest-api-review fs_read, fs_grep, fs_glob, fs_cat, fs_ls Surface-contract review of REST/HTTP routes and handlers (plus gRPC/GraphQL sections): pagination, per-route auth decisions, PUT/DELETE idempotency, error shape, breaking published shapes. Routed by the plan's declared surfaces.
cli-review fs_read, fs_grep, fs_glob, fs_cat, fs_ls Surface-contract review of command-line changes: exit codes, stdout/stderr channel discipline, help text, non-interactive escape hatches, signal/cleanup behavior, config precedence.
library-review fs_read, fs_grep, fs_glob, fs_cat, fs_ls Surface-contract review of a library's public API: semver discipline against the manifest version, panic reachability from public entry points, doc coverage, error-type information, dependency weight.
worker-review fs_read, fs_grep, fs_glob, fs_cat, fs_ls Surface-contract review of queue/stream consumers and scheduled jobs: retry/backoff policy, DLQ/poison-message routing, graceful-shutdown drain, concurrency bounds.
iac-review fs_read, fs_grep, fs_glob, fs_cat, fs_ls Surface-contract review of Terraform/Helm/K8s manifests/Dockerfiles/compose: pinning, plaintext secret material, IAM/RBAC scope, resource limits, mutable tags, destructive operations.
migration-review fs_read, fs_grep, fs_glob, fs_cat, fs_ls Review of DB schema migrations: expand/contract compatibility with running code, reversibility, online/concurrent index creation, backfills mixed into DDL transactions.
cicd-review fs_read, fs_grep, fs_glob, fs_cat, fs_ls Review of CI/CD pipeline definitions: SHA-pinned actions, token/credential permission scoping, secret exposure to fork-PR triggers, cross-branch cache poisoning.
ai-slop-remover none (knowledge-only) Detect and remove AI slop from code and prose.
diagnostics execute_command Systematic troubleshooting of technical issues (services, networking, containers, OS) by running diagnostic commands directly.
diagnosing-bugs execute_command Feedback-loop-first debugging for hard bugs and performance regressions: build a tight, red-capable reproduction loop BEFORE forming hypotheses, minimise it, then test falsifiable hypotheses.
codebase-design none (knowledge-only) Shared vocabulary and principles for designing deep modules (depth, seams, adapters, leverage, locality) plus the deletion test and design-it-twice.
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.
delegation-protocol none (knowledge-only) Structured 6-section delegation template and session-continuity rules for sub-agents.
parallel-research none (knowledge-only) Fan-out exploration protocol; parallel research agents without duplicated work.
oracle-protocol none (knowledge-only) Discipline for when and how to consult Oracle.
grilling none (knowledge-only) Interview the user relentlessly about a plan, decision, or design until shared understanding is reached, and structured as a design tree worked in frontier rounds.
verification-gates execute_command Evidence requirements (diagnostics, builds, tests) before claiming completion.
plan-authoring fs_read, fs_grep, fs_glob, fs_ls, fs_cat, fs_write Author executable high-level plans and per-step implementation plans for phased work; defines the plan repo layout and step-plan schema.
plan-review fs_read, fs_grep, fs_glob, fs_ls, fs_cat Adversarial review of implementation plans against executability, verifiability, and completeness standards.
adversarial-review fs_read, fs_grep, fs_glob, fs_cat, fs_ls Adversarial plan-conformance review of an implementation against the plan it should satisfy (skipped criteria, scope drift, interface substitution); verdict CONFORMS/DIVERGES. Complements code-review (quality) and is the post-implementation counterpart to plan-review.
plan-gatekeeping fs_read, fs_grep, fs_glob, fs_cat, fs_ls Gatekeep a plan for self-containedness before finalization: the "sealed container" test — every implementer question answered inline or via a verified pointer; verdict PLAN_GATE: SEALED/LEAKY with the missing questions.
usage-pattern-testing fs_read, fs_cat, fs_grep, fs_glob, fs_ls, fs_write, fs_patch, execute_command Black-box verification of a change's consumer-facing surface (HTTP APIs, RPCs, CLIs) against a locally running instance with clean state: run the repo's existing usage suites first (regression check, whatever their format, e.g. Hurl files, curl scripts, collections), then author spec-first tests for the uncovered delta in the repo's suite conventions (tools like Hurl/grpcurl are examples, not requirements); every failure classified BUG / EXPECTED-CHANGE / ENV; verdict USAGE_PROBE: PASS/FAIL/INCONCLUSIVE. The only review gate that tests behavior by using the thing, not reading it.
design-session fs_read, fs_grep, fs_glob, fs_ls, fs_cat, fs_write AI-first design decomposition: ground a design doc in the actual codebase, produce a PLAN file with approach, alternatives, and a ~1-engineer-day task breakdown with measurable acceptance criteria. Includes a quality-bar round declaring rigor (poc/prototype/production) and surfaces, which calibrate every downstream review gate.
task-tracking fs_read, fs_grep, fs_glob, fs_ls, fs_cat, fs_write, fs_patch, fs_mkdir File-based task tracking for plan-driven runs: TASK-NNN directory schema (index.md + append-only log.md), frontmatter lifecycle, numbering, follow-up tasks, and end-of-run consistency checks.
step-implementation execute_command End-to-end protocol for executing one step of a phased implementation plan.
handoff-protocol fs_read, fs_cat, fs_ls, fs_write Schema and discipline for writing and reading step handoff documents between implementation steps.
iwe-knowledge-base MCP server: iwe Navigate and curate large markdown knowledgebases (plans, specs, notes) via IWE graph tools. Requires the iwec binary.

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 Bundles flow as other Coyote assets. A remote repo laid out as:

<repo>/
└── skills/
    └── <skill-name>/
        └── SKILL.md

installs with:

coyote --install <owner>/<repo>
# or just the skills/:
coyote --install <owner>/<repo> --filter skills

See Bundles 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.