Skills - Extensible Domain Knowledge

Level: Enhancement (beyond minimal agent) Prerequisites: Tool system, memory system


What This Adds

A markdown-based extensibility system for adding domain-specific instructions, workflows, and tools to the agent. Skills are discovered from multiple locations, loaded on-demand, and can bundle MCP servers, tools, and executable scripts.


Key Concept

Question Answer
What is a skill? Markdown file with YAML frontmatter + instructions
When loaded? On-demand via skill tool call
Where discovered? 9 locations in priority order
Token cost? Level 1: ~100 tokens at startup, Level 2: <5k when loaded

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                    SKILL DISCOVERY                               │
├─────────────────────────────────────────────────────────────────┤
│  .agents/skills/          →  Workspace-local (highest priority) │
│  ~/.config/agents/skills/ →  Global user skills                 │
│  ~/.config/amp/skills/    →  Global amp skills                  │
│  .claude/skills/          →  Claude-compatible (local)          │
│  ~/.claude/skills/        →  Claude-compatible (global)         │
│  ~/.claude/plugins/cache/ →  Plugin cache                       │
│  AMP_TOOLBOX / tools/     →  Toolbox (deprecated)               │
│  skills.path setting      →  Custom paths                       │
│  Built-in skills          →  5 system skills (lowest priority)  │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                    SKILL SERVICE                                 │
├─────────────────────────────────────────────────────────────────┤
│  • File watching with 200ms debounce                            │
│  • Deduplication by name (first location wins)                  │
│  • MCP server derivation from bundled mcp.json                  │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                    SKILL TOOL                                    │
├─────────────────────────────────────────────────────────────────┤
│  name: "skill"                                                  │
│  input: { name: string, arguments?: string }                    │
│  output: <loaded_skill> XML with instructions                   │
└─────────────────────────────────────────────────────────────────┘

Discovery Order

Skills are discovered in priority order. First match wins for duplicate names.

Priority Path Type
1 .agents/skills/ Workspace local
2 ~/.config/agents/skills/ Global agents
3 ~/.config/amp/skills/ Global amp
4 .claude/skills/ Local Claude compat
5 ~/.claude/skills/ Global Claude compat
6 ~/.claude/plugins/cache/ Plugin cache
7 AMP_TOOLBOX env / ~/.config/amp/tools Toolbox
8 skills.path setting Custom paths
9 Built-in System default

Skill Structure

Minimal Skill (Instructions Only)

.agents/skills/my-skill/
└── SKILL.md

Skill with Scripts

.agents/skills/my-skill/
├── SKILL.md
└── scripts/
    └── my-script.sh

Complex Skill (Full)

.agents/skills/my-skill/
├── SKILL.md           # Required: frontmatter + instructions
├── mcp.json           # Optional: bundled MCP servers
├── scripts/           # Optional: executable scripts
│   └── run.sh
├── toolbox/           # Optional: auto-registered tools
│   └── my-tool
└── reference/         # Optional: additional docs
    └── api.md

SKILL.md Format

Required Structure

---
name: my-skill-name
description: Does X when Y happens. Use for Z tasks.
---

# Instructions

The content here is injected into the conversation when loaded.

Frontmatter Fields

Field Type Required Description
name string Yes Unique skill identifier
description string Yes When to use this skill
isolatedContext boolean No Run with isolated context
disable-model-invocation boolean No Hide from model's skill list
argument-hint string No Hint shown in available skills
builtin-tools string[] No Tools to enable when loaded

Example with All Options

---
name: web-scraper
description: Scrapes web pages and extracts structured data. Use for data extraction tasks.
argument-hint: <url>
builtin-tools:
  - web_search
  - read_web_page
---

# Web Scraper

Instructions for scraping web pages...

Token Budget (Progressive Disclosure)

Level When Loaded Token Cost Content
Level 1 Startup ~100 tokens Name + description only
Level 2 Tool invoked <5k tokens SKILL.md body
Level 3 On demand Variable Reference files, scripts

Keep SKILL.md under 500 lines. Split large content into separate files.


Built-in Skills

Five skills are always available:

Skill Purpose
building-skills Guide for creating new skills
bookkeeper Summarizes threads for handoff
painter Image generation guidance
code-review Automated code review
migrating-to-skills Migrating from toolbox

Built-in skills have lowest priority - workspace skills with the same name override them.


Skill Tool Schema

const skillTool = {
  name: "skill",
  description: "Load a skill to get specialized instructions for a task.",
  inputSchema: {
    type: "object",
    properties: {
      name: {
        type: "string",
        description: "The name of the skill to load"
      },
      arguments: {
        type: "string",
        description: "Optional arguments to pass to the skill"
      }
    },
    required: ["name"]
  }
};

Implementation

Discovery Function

interface Skill {
  name: string;
  description: string;
  frontmatter: Record<string, unknown>;
  content: string;
  baseDir: string;
  mcpServers?: MCPServerConfig[];
  builtinTools?: string[];
  files?: string[];
}

async function discoverSkills(
  workspaceRoots: string[],
  settings: Settings
): Promise<{ skills: Skill[]; errors: Error[] }> {
  const skills: Skill[] = [];
  const errors: Error[] = [];
  const seenNames = new Set<string>();
  const homedir = os.homedir();

  // Discovery order (first match wins)
  const paths = [
    // 1. Workspace .agents/skills/
    ...workspaceRoots.map(r => path.join(r, ".agents", "skills")),
    // 2. Global ~/.config/agents/skills/
    path.join(homedir, ".config", "agents", "skills"),
    // 3. Global ~/.config/amp/skills/
    path.join(homedir, ".config", "amp", "skills"),
    // 4. Local .claude/skills/
    ...workspaceRoots.map(r => path.join(r, ".claude", "skills")),
    // 5. Global ~/.claude/skills/
    path.join(homedir, ".claude", "skills"),
    // 6. Plugin cache
    path.join(homedir, ".claude", "plugins", "cache"),
    // 7. Toolbox
    process.env.AMP_TOOLBOX || path.join(homedir, ".config", "amp", "tools"),
    // 8. Custom paths
    ...(settings["skills.path"] || [])
  ];

  for (const skillPath of paths) {
    const found = await scanSkillDirectory(skillPath);
    for (const skill of found) {
      if (!seenNames.has(skill.name)) {
        seenNames.add(skill.name);
        skills.push(skill);
      }
    }
  }

  // 9. Built-in skills (lowest priority)
  for (const builtin of getBuiltinSkills()) {
    if (!seenNames.has(builtin.name)) {
      seenNames.add(builtin.name);
      skills.push(builtin);
    }
  }

  return { skills, errors };
}

Parsing SKILL.md

function parseSkillFile(content: string): { frontmatter: object; content: string } {
  const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
  if (!match || !match[1] || !match[2]) {
    throw new Error("SKILL.md must have YAML frontmatter");
  }

  const frontmatter = yaml.parse(match[1]);
  if (!frontmatter.name || !frontmatter.description) {
    throw new Error("SKILL.md frontmatter must include name and description");
  }

  return { frontmatter, content: match[2] };
}

Skill Loading

async function loadSkill(
  skillName: string,
  args: string | undefined,
  availableSkills: Skill[]
): Promise<ToolResult> {
  const skill = availableSkills.find(s => s.name === skillName);
  if (!skill) {
    return {
      error: `Skill "${skillName}" not found. Available: ${availableSkills.map(s => s.name).join(", ")}`
    };
  }

  // Build loaded skill XML
  const output = [
    `<loaded_skill name="${skill.name}">`,
    skill.content,
    args ? `\nArguments: ${args}` : "",
    `</loaded_skill>`
  ].join("\n");

  return { output };
}

Available Skills Prompt

function formatAvailableSkills(skills: Skill[]): string | null {
  const visible = skills.filter(s => !s.frontmatter["disable-model-invocation"]);
  if (visible.length === 0) return null;

  const list = visible.map(s => {
    const hint = s.frontmatter["argument-hint"] ? ` ${s.frontmatter["argument-hint"]}` : "";
    return `  <skill>
    <name>${s.name}</name>
    <description>${s.description}</description>
  </skill>`;
  }).join("\n");

  return `The following skills provide specialized instructions for specific tasks.
Use the skill tool to load a skill when the task matches its description.

<available_skills>
${list}
</available_skills>`;
}

File Watching

Skills support hot-reload with file watching:

const SKILL_WATCH_DEBOUNCE = 200; // ms

function watchSkills(
  skillPaths: string[],
  onChange: (skills: Skill[]) => void
): void {
  const watcher = chokidar.watch(skillPaths, {
    ignoreInitial: true,
    depth: 2  // Only watch SKILL.md level
  });

  const debouncedReload = debounce(async () => {
    const { skills } = await discoverSkills(workspaceRoots, settings);
    onChange(skills);
  }, SKILL_WATCH_DEBOUNCE);

  watcher.on("change", debouncedReload);
  watcher.on("add", debouncedReload);
  watcher.on("unlink", debouncedReload);
}

Constants

Constant Value Purpose
Max skill name 64 chars Name length limit
Max description 1024 chars Description length limit
Max files per skill 100 File count limit
Max single file 512 KB Single file size limit
Max total size 2 MB Total skill size limit
Watch debounce 200 ms File change debounce

MCP Server Bundling

Skills can bundle MCP servers via mcp.json:

{
  "mcpServers": {
    "my-server": {
      "command": "node",
      "args": ["./server.js"],
      "env": {}
    }
  }
}

When the skill is loaded, its MCP servers are started and their tools become available.


When to Add Skills

Add skill system when:

  1. Domain expertise - Specialized knowledge for specific domains
  2. Workflow automation - Common multi-step processes
  3. Tool bundles - Groups of MCP tools that work together
  4. Project customization - Project-specific instructions

Enhancement based on Amp Code v0.0.1769212917 patterns