Skills

SKILL.md files agents load on demand via skill_load.

drover implements the Agent Skills specification. A skill is a directory containing a SKILL.md file plus optional scripts/, references/, and assets/ subdirectories. The agent’s system prompt advertises name + description; the body loads on demand via skill_load; supporting files are pulled lazily via skill_resource. Progressive disclosure keeps the system prompt small.

Directory layout

my-skill/
├── SKILL.md          # required: metadata + instructions
├── scripts/          # optional: executable code agents can run
├── references/       # optional: extended documentation
└── assets/           # optional: templates, schemas, images

SKILL.md format

mdx
---
name: pdf-processing
description: Extract text and tables from PDFs, fill forms, merge files. Use when working with PDF documents.
license: Apache-2.0
compatibility: Requires poppler-utils
metadata:
  author: example-org
  version: "1.0"
allowed-tools: Bash(jq:*) Read Edit
---

# pdf-processing

Step-by-step instructions go here. See [references/REFERENCE.md](references/REFERENCE.md)
for the full API.

Frontmatter fields

fieldrequiredconstraints
nameyes1-64 chars, [a-z0-9-]+, no leading/trailing/consecutive hyphens. Must equal the parent directory name.
descriptionyes1-1024 chars. Describes what the skill does and when to use it.
licensenoLicense name or reference to a bundled file.
compatibilityno≤500 chars. Environment requirements (system packages, network access, etc.).
metadatanoMap of string keys to string values for clients to use.
allowed-toolsnoSpace-separated (or list) of pre-approved tools. Experimental.

Any other frontmatter keys are preserved verbatim under spec.extra.

Build a registry

ts
import { createSkillRegistry, scanSkillDirs } from "@drover/skills";

const specs = await scanSkillDirs(["./skills", "./node_modules/@my-org/shared-skills"]);
const skills = createSkillRegistry(specs);

scanSkillDirs walks each root recursively (max depth 3 by default), finds SKILL.md files, dedups by name with first-wins. Pass agent- local dirs ahead of shared library dirs so local skills shadow.

Strict vs lenient parsing

By default, any spec violation throws and stops the scan. Use mode: "lenient" plus an onIssue callback to load best-effort and surface warnings:

ts
const specs = await scanSkillDirs([root], {
  mode: "lenient",
  onIssue: (file, issues) => {
    for (const i of issues) console.warn(`${file} — ${i.field}: ${i.message}`);
  },
});

Declare in a spec

ts
const spec = defineAgent({
  /* ... */
  skills: ["pdf-processing", "factcheck"],
});

spec.skills is the allowlist. Skills outside it are unreachable for this agent even if they’re in the registry — progressive disclosure shouldn’t bypass least-privilege intent.

Wire on the run

ts
runAgent(spec, input, { skills });

The harness:

  1. Auto-injects skill_load and skill_resource, both gated by spec.skills.
  2. Appends an “Available skills” section to the system prompt listing only allowlisted name + one-line description (plus a compatibility hint when set).

Progressive disclosure in practice

  1. Startup (~100 tokens per skill): name + description in the system prompt.
  2. Activation (full body): the model calls skill_load(name="<x>") when it decides the skill is relevant.
  3. Resources (on demand): if the body references scripts/extract.py or references/REFERENCE.md, the model calls skill_resource(name="<x>", resource="<relative-path>").

skill_resource denies absolute paths and any .. segments — reads stay inside the skill directory.

Inspect a registry

ts
skills.has("pdf-processing");  // boolean
skills.get("pdf-processing");  // SkillSpec | undefined
skills.list();                  // SkillSpec[]
ts
import { listSkillResources, readSkillResource } from "@drover/skills";

const spec = skills.get("pdf-processing")!;
const res = await listSkillResources(spec);
// { scripts: ["extract.py"], references: ["REFERENCE.md"], assets: [], other: [...] }

const text = await readSkillResource(spec, "references/REFERENCE.md");

Alternative layouts

drover supports the canonical skills/<name>/SKILL.md layout out of the box. For flat layouts (skills/<name>.md) or any other on-disk shape, write a custom loader that produces SkillSpec[] and pass to createSkillRegistry — the registry is decoupled from the on-disk layout.

Authoring guidance

  • Keep the SKILL.md body under 500 lines / ~5000 tokens.
  • Push detailed reference material to references/ so the model only loads it when explicitly needed.
  • Use relative paths from the skill root for file references; keep them one level deep.
  • Validate with the skills-ref reference linter before committing.

Type to search…

↑↓ navigate open esc close