Skip to content

How Do OpenClaw Skills Work: Discovery, Injection, and Reading Flow

Problem

When I first looked at OpenClaw’s skills system, I expected to find tool-like function calls. Instead, I found a document-based architecture where skills are instruction files read by the model on demand.

This confused me at first. How does the model know which skill to use? When does skill content actually enter the conversation? Why aren’t skills just regular tools?

The Three-Stage Flow

OpenClaw skills work through a three-stage process:

Discovery Injection Reading
│ │ │
▼ ▼ ▼
Scan dirs → Build catalog → Read SKILL.md
Priority <available_ via Read tool
hierarchy skills> tag

Let me break down each stage.

Stage 1: Discovery

The discovery phase scans multiple directories for SKILL.md files. The key insight is that OpenClaw uses an explicit priority hierarchy to handle skill name conflicts.

Priority Hierarchy

extra < bundled < managed < agents-personal < agents-project < workspace

When two skills have the same name, the higher priority one wins. This means a workspace skill overrides a bundled skill with the same name.

Discovery Implementation

The discovery process walks through each root directory in priority order:

skillDiscovery.js
const priorityOrder = [
'extra',
'bundled',
'managed',
'agents-personal',
'agents-project',
'workspace'
];
const discoveredSkills = new Map();
for (const root of priorityOrder) {
const skillFiles = findSkillFiles(root);
for (const skillFile of skillFiles) {
const skillName = extractSkillName(skillFile);
// Higher priority overrides lower priority
discoveredSkills.set(skillName, skillFile);
}
}

Size Enforcement

OpenClaw rejects skills over 256KB. This enforces documentation discipline - skills should be focused instruction sets, not comprehensive manuals.

sizeCheck.js
const MAX_SKILL_SIZE = 256 * 1024; // 256KB
if (skillSize > MAX_SKILL_SIZE) {
console.warn(`Skill ${skillName} exceeds 256KB, skipping`);
continue;
}

This size limit caught me off guard initially. I had written a 400KB skill file with extensive examples. The fix was extracting examples into separate files and keeping the core skill focused.

Frontmatter Parsing

SKILL.md files use YAML frontmatter, but OpenClaw enforces it weakly. Missing or broken frontmatter is tolerated.

SKILL.md frontmatter example
---
name: my-skill
description: Brief description for catalog
requires:
- nodejs
- AWS_CLI
---
# My Skill
Instructions go here...

If the frontmatter is missing, OpenClaw falls back to the filename as the skill name. If parsing fails, it extracts what it can and continues.

frontmatterParser.js
function parseFrontmatter(content) {
const match = content.match(/^---\n([\s\S]*?)\n---/);
if (!match) {
// Missing frontmatter - use defaults
return { name: null, description: '' };
}
try {
return yaml.parse(match[1]);
} catch (e) {
// Broken frontmatter - extract what we can
console.warn(`Frontmatter parse error: ${e.message}`);
return { name: null, description: '' };
}
}

Stage 2: Injection

The injection phase builds an <available_skills> catalog that goes into the system prompt. The key insight is that only skill metadata is injected, not the full skill content.

Available Skills Catalog

The catalog looks like this in the system prompt:

<available_skills>
- commit: Create well-formatted git commits
- review-pr: Review pull requests for issues
- planner: Create implementation plans for features
- tdd-guide: Guide test-driven development workflow
</available_skills>

Each line shows the skill name and a brief description. This is enough for the model to know what skills exist without loading all skill content.

Injection Process

skillInjection.js
function buildAvailableSkillsTag(skills) {
const lines = [];
for (const [name, skill] of skills) {
const description = skill.description || 'No description';
lines.push(`- ${name}: ${description}`);
}
return `<available_skills>\n${lines.join('\n')}\n</available_skills>`;
}

System Prompt Rules

The system prompt includes strict rules about skill usage:

When using skills:
- Explicitly match one skill before reading it
- Maximum one skill can be loaded at a time
- Never assume a skill exists without checking the catalog
- Use the Read tool to access skill content

These rules prevent the model from claiming to use skills it hasn’t actually read.

Stage 3: Reading

The reading phase is where skill content actually enters the conversation. The model uses the standard Read tool to access SKILL.md files.

How Reading Works

When the model decides to use a skill:

  1. It calls the Read tool with the skill file path
  2. The skill content enters as a toolResult in the message flow
  3. The model now has the full skill instructions in context
Model: "I need to create a commit. Let me read the commit skill."
Tool Call: Read(file="/path/to/commit/SKILL.md")
Tool Result: "# Commit Skill\n\nCreate well-formatted commits...\n\n1. Run git status\n2. Run git diff..."
Model: "I have the commit skill. Following its instructions..."

Why Not Tool Calls?

I initially wondered why skills aren’t just function calls. The answer lies in flexibility.

Tool calls have rigid schemas - predefined parameters, return types, and behaviors. Skills are freeform instruction documents. They can contain:

  • Procedural steps
  • Decision trees
  • Examples and templates
  • Context-specific guidance
Tool approach:
commit(message: string) → Result
Skill approach:
1. Run git status to see changes
2. Run git diff to understand changes
3. Draft message following format
4. Verify message meets standards
5. Create commit

The skill approach handles the messy reality of software development. A commit skill can adapt its behavior based on the type of changes, the project context, and the user’s preferences.

Reading Constraints

The system prompt constrains reading in important ways:

1. "Explicit match one skill" - The model must identify the specific skill it needs
2. "Maximum one skill" - Only one skill can be active at a time
3. Skills are read on-demand, not pre-loaded

These constraints prevent context bloat. Instead of loading all skills upfront, the model reads only what it needs.

Common Misconceptions

Misconception 1: Skills Are Tools

Skills are not tools. Tools have executable implementations. Skills are documents the model reads and interprets.

Tool: function call with parameters
Skill: document with instructions

Misconception 2: Skills Are Auto-Executed

Skills are not automatically executed. The model decides when to read a skill based on the available skills catalog and the task at hand.

Misconception 3: All Skills Load at Once

Only the skill metadata loads into the system prompt. Full skill content loads on demand through the Read tool.

Practical Example

Let me trace through a real example. I ask OpenClaw to commit my changes.

Step 1: Model checks available skills

The model sees:

<available_skills>
- commit: Create well-formatted git commits
- review-pr: Review pull requests
...
</available_skills>

Step 2: Model matches the task to a skill

Model: “The user wants to commit. The ‘commit’ skill matches this task.”

Step 3: Model reads the skill

Tool Call: Read("/workspace/.claude/skills/commit/SKILL.md")

Step 4: Skill content enters conversation

# Commit Skill
Create well-formatted git commits following conventional commits format.
## Steps
1. Run `git status` to see all changes
2. Run `git diff` to understand changes
3. Draft commit message
4. Verify message format
...

Step 5: Model follows skill instructions

The model now executes the commit workflow as described in the skill.

Summary

In this post, I explained the three-stage flow of OpenClaw skills: discovery, injection, and reading. The key point is that skills are instruction documents, not tools. They’re discovered by scanning directories with priority hierarchy, injected as a lightweight catalog in the system prompt, and read on-demand through the standard Read tool.

The priority hierarchy (extra < bundled < managed < agents-personal < agents-project < workspace) determines which skill wins when names conflict. The 256KB size limit enforces focused documentation. Frontmatter parsing is weak - missing or broken frontmatter is tolerated.

Understanding this flow helped me write better skills and debug when skills weren’t being used. The document-based approach gives flexibility that rigid tool schemas cannot match.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments