> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/nearai/ironclaw/llms.txt
> Use this file to discover all available pages before exploring further.

# Skills System

> Extend agent behavior with prompt-level instructions, activation criteria, and trust-based authority

## Overview

Skills are SKILL.md files (YAML frontmatter + markdown prompt) that extend the agent's behavior through prompt-level instructions. Unlike code-level tools (WASM/MCP), skills operate in the LLM context and are subject to trust-based authority attenuation.

## Trust Model

Skills have two trust states that determine their authority:

* **Trusted**: User-placed skills (local/workspace) with full tool access
* **Installed**: Registry/external skills, restricted to read-only tools

The effective tool ceiling is determined by the **lowest-trust** active skill, preventing privilege escalation through skill mixing.

### Trust Assignment

Skill trust is determined by location:

```text theme={null}
┌─────────────────────────────────────┬─────────┐
│ Location                            │ Trust   │
├─────────────────────────────────────┼─────────┤
│ <workspace>/skills/                 │ Trusted │
│ ~/.ironclaw/skills/                 │ Trusted │
│ ~/.ironclaw/installed_skills/       │ Installed│
│ Bundled with application            │ Trusted │
└─────────────────────────────────────┴─────────┘
```

## SKILL.md Format

### Structure

```yaml theme={null}
---
name: writing-assistant
version: "1.0.0"
description: Professional writing and editing
activation:
  keywords: ["write", "edit", "proofread"]
  patterns: ["(?i)\\b(write|draft)\\b.*\\b(email|letter)\\b"]
  tags: ["writing", "editing"]
  max_context_tokens: 2000
metadata:
  openclaw:
    requires:
      bins: ["vale"]       # Required binaries
      env: ["VALE_CONFIG"]  # Required env vars
      config: ["/etc/vale.ini"]  # Required files
---

# Skill Prompt

You are a professional writing assistant. Help the user with:

- Writing and editing professional documents
- Grammar and style improvements
- Tone and clarity adjustments

Use the `vale` tool for automated style checking when available.
```

### Frontmatter Fields

#### Required

* `name`: Alphanumeric, hyphens, underscores, dots. Max 64 chars. Pattern: `^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$`

#### Optional

* `version`: Semantic version (default: `"0.0.0"`)
* `description`: Short summary
* `activation`: When to activate this skill
* `metadata`: OpenClaw-specific metadata (gating requirements)

### Activation Criteria

```yaml theme={null}
activation:
  keywords: ["write", "edit"]  # Max 20, min 3 chars each
  patterns: ["(?i)write.*email"]  # Max 5 regex patterns
  tags: ["writing", "email"]  # Max 10, min 3 chars each
  max_context_tokens: 2000  # Token budget
```

**Limits** (enforced at load time):

* Keywords: Max 20, min length 3 chars
* Patterns: Max 5 regex patterns
* Tags: Max 10, min length 3 chars
* Token budget: Max 2x declared `max_context_tokens`

### Gating Requirements

Skills can declare dependencies:

```yaml theme={null}
metadata:
  openclaw:
    requires:
      bins: ["vale", "aspell"]  # Must be on PATH
      env: ["VALE_CONFIG"]      # Must be set
      config: ["/etc/vale.ini"]  # Must exist
```

Skills failing gating checks are skipped during discovery.

## Discovery and Loading

### Directory Layouts

Two layouts are supported:

#### Flat Layout

```
skills/
  SKILL.md
```

#### Subdirectory Layout

```
skills/
  writing-assistant/
    SKILL.md
  code-review/
    SKILL.md
```

### Discovery Order

Earlier locations win on name collision:

1. Workspace skills (`<workspace>/skills/`) - Trusted
2. User skills (`~/.ironclaw/skills/`) - Trusted
3. Installed skills (`~/.ironclaw/installed_skills/`) - Installed

### Load-Time Validation

1. **File size**: Max 64 KiB
2. **Name validation**: Matches `^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$`
3. **Frontmatter parsing**: Valid YAML
4. **Activation limits**: Keywords/patterns/tags within caps
5. **Token budget**: Prompt size \< 2x declared `max_context_tokens`
6. **Gating checks**: Required bins/env/config present
7. **Symlink rejection**: No symlinks allowed
8. **Line ending normalization**: CRLF → LF

### Loading Pipeline

```rust theme={null}
// Load and validate skill
let raw_bytes = tokio::fs::read(path).await?;
let raw_content = String::from_utf8(raw_bytes)?;
let normalized = normalize_line_endings(&raw_content);
let parsed = parse_skill_md(&normalized)?;

// Check gating
let result = check_requirements(&parsed.manifest.metadata).await;
if !result.passed {
    return Err(GatingFailed);
}

// Compute hash
let content_hash = compute_hash(&parsed.prompt_content);

// Compile patterns
let compiled_patterns = LoadedSkill::compile_patterns(
    &parsed.manifest.activation.patterns
);

// Build loaded skill
LoadedSkill {
    manifest: parsed.manifest,
    prompt_content: parsed.prompt_content,
    trust,
    source,
    content_hash,
    compiled_patterns,
    lowercased_keywords,
    lowercased_tags,
}
```

## Activation and Selection

### Scoring Algorithm

```rust theme={null}
pub fn score_skill(skill: &LoadedSkill, message: &str) -> f64 {
    let msg_lower = message.to_lowercase();
    let mut score = 0.0;
    
    // Exact keyword matches (weight: 10.0)
    for keyword in &skill.lowercased_keywords {
        if msg_lower == keyword {
            score += 10.0;
        } else if msg_lower.contains(keyword) {
            score += 5.0;  // Substring match
        }
    }
    
    // Regex pattern matches (weight: 15.0)
    for pattern in &skill.compiled_patterns {
        if pattern.is_match(message) {
            score += 15.0;
        }
    }
    
    // Tag matches (weight: 2.0)
    for tag in &skill.lowercased_tags {
        if msg_lower.contains(tag) {
            score += 2.0;
        }
    }
    
    score
}
```

### Selection Process

1. Score all skills against incoming message
2. Filter skills with score > 0
3. Sort by score (descending)
4. Take top N skills (default: 3)
5. Check total token budget
6. Return selected skills

### Tool Attenuation

When skills are active, tool access is restricted:

```rust theme={null}
pub fn attenuate_tools(
    tools: &[Tool],
    lowest_trust: SkillTrust,
) -> Vec<Tool> {
    match lowest_trust {
        SkillTrust::Trusted => tools.to_vec(),  // Full access
        SkillTrust::Installed => {
            // Only read-only tools
            tools.iter()
                .filter(|t| is_read_only_tool(t.name()))
                .cloned()
                .collect()
        }
    }
}
```

Read-only tools:

* `Read`
* `Glob`
* `Grep`
* `WebFetch`
* `SkillCatalog`

Write tools (blocked for Installed skills):

* `Write`
* `Edit`
* `Bash`
* `Task`
* All other tools

## Skill Injection

Skills are injected into the LLM context:

```xml theme={null}
<skill name="writing-assistant" version="1.0.0" trust="trusted">
  You are a professional writing assistant. Help the user with:
  
  - Writing and editing professional documents
  - Grammar and style improvements
  - Tone and clarity adjustments
</skill>

<skill name="code-review" version="2.3.1" trust="installed">
  You are a code reviewer. Analyze code for:
  
  - Style and best practices
  - Potential bugs
  - Performance issues
</skill>
```

### Security: Tag Breakout Prevention

Skill content is escaped to prevent tag injection:

```rust theme={null}
pub fn escape_skill_content(content: &str) -> String {
    static SKILL_TAG_RE: LazyLock<Regex> = LazyLock::new(|| {
        Regex::new(r"(?i)</?[\s\x00]*skill").unwrap()
    });
    
    SKILL_TAG_RE.replace_all(content, |caps: &Captures| {
        let matched = caps.get(0).unwrap().as_str();
        format!("&lt;{}", &matched[1..])
    }).into_owned()
}
```

This prevents skills from:

* Closing their own `</skill>` tag prematurely
* Injecting fake `<skill trust="trusted">` blocks
* Breaking out via mixed case, whitespace, or null bytes

## Managing Skills

### Via CLI

```bash theme={null}
# List loaded skills
ironclaw skill list

# Show skill details
ironclaw skill show writing-assistant

# Install from file
ironclaw skill install --file ~/Downloads/skill.md

# Install from ClawHub
ironclaw skill install writing-assistant

# Remove skill
ironclaw skill remove writing-assistant

# Reload all skills
ironclaw skill reload
```

### Via Tool Call

The agent can manage skills:

```xml theme={null}
<SkillInstall>
  <content>
  ---
  name: custom-skill
  description: My custom skill
  ---
  
  You are a helpful assistant.
  </content>
</SkillInstall>

<SkillRemove name="custom-skill" />
```

### Programmatic API

```rust theme={null}
use ironclaw::skills::SkillRegistry;

// Create registry
let mut registry = SkillRegistry::new(user_dir)
    .with_workspace_dir(workspace_dir)
    .with_installed_dir(installed_dir);

// Discover all skills
let loaded = registry.discover_all().await;
println!("Loaded {} skills", loaded.len());

// Install skill
let content = std::fs::read_to_string("skill.md")?;
registry.install_skill(&content).await?;

// Remove skill
registry.remove_skill("skill-name").await?;

// Find skill
if let Some(skill) = registry.find_by_name("writing-assistant") {
    println!("Found: {} v{}", skill.name(), skill.version());
}
```

## Creating Skills

### Simple Skill

```yaml theme={null}
---
name: my-skill
description: A simple skill
activation:
  keywords: ["help", "assist"]
---

You are a helpful assistant.
```

### Advanced Skill

```yaml theme={null}
---
name: deployment-helper
version: "1.2.0"
description: Deployment planning and execution
activation:
  keywords: ["deploy", "release", "ship"]
  patterns:
    - "(?i)\\bdeploy\\b.*\\b(production|staging)\\b"
    - "(?i)\\brelease\\b.*\\bv?\\d+"
  tags: ["devops", "deployment"]
  max_context_tokens: 3000
metadata:
  openclaw:
    requires:
      bins: ["kubectl", "helm"]
      env: ["KUBECONFIG"]
---

# Deployment Helper

You are a DevOps expert specializing in Kubernetes deployments.

## Capabilities

- Plan deployment strategies
- Review manifests for issues
- Generate helm commands
- Troubleshoot deployment failures

## Guidelines

1. Always check `kubectl` version compatibility
2. Verify namespace before deploying
3. Use `--dry-run=client` for validation
4. Include rollback instructions

## Tools

Use `Bash` to run kubectl/helm commands when needed.
```

## ClawHub Integration

Skills can be published to and installed from ClawHub:

```bash theme={null}
# Search for skills
ironclaw skill search writing

# Install from ClawHub
ironclaw skill install writing-assistant

# Publish skill
ironclaw skill publish --file my-skill.md
```

Published skills are installed to `~/.ironclaw/installed_skills/` with `Installed` trust.

## Best Practices

### Skill Design

1. **Focused scope**: One skill per domain (writing, code review, etc.)
2. **Clear activation**: Use specific keywords and patterns
3. **Token budget**: Keep prompts under 2000 tokens
4. **Gating**: Declare binary/config dependencies
5. **Examples**: Include example interactions in the prompt

### Security

1. **Trust isolation**: Don't mix trusted and installed skills for sensitive tasks
2. **Review installed skills**: Always review skill content before installing
3. **Workspace override**: Place custom versions in workspace to override installed skills
4. **Symlink prohibition**: Never use symlinks in skills directories

### Performance

1. **Keyword limits**: Use \< 10 keywords per skill
2. **Pattern complexity**: Avoid complex regex (max 64 KiB compiled size)
3. **Token budget**: Larger prompts consume more context window
4. **Selective activation**: Use precise patterns to avoid activating unnecessarily

## Troubleshooting

### Skill Not Loading

```bash theme={null}
# Check logs
export RUST_LOG=ironclaw::skills=debug
ironclaw run

# Common issues:
# 1. Invalid YAML frontmatter
# 2. Name doesn't match pattern
# 3. File too large (> 64 KiB)
# 4. Failed gating checks
# 5. Symlink detected
```

### Skill Not Activating

```bash theme={null}
# Check activation criteria
ironclaw skill show my-skill

# Test scoring
export RUST_LOG=ironclaw::skills::selector=debug
ironclaw run

# Common issues:
# 1. Keywords too short (< 3 chars)
# 2. Pattern doesn't match message
# 3. Score too low (< threshold)
```

### Tool Access Denied

```
Error: Tool 'Bash' not available (installed skill active)
```

**Cause**: Installed skill is active, restricting tools to read-only.

**Solution**:

1. Override the skill by placing a trusted version in workspace/user dir
2. Remove the installed skill
3. Use read-only tools only

## Source Code

Key files:

* `src/skills/mod.rs` - Module overview, types, escaping
* `src/skills/registry.rs` - Discovery, loading, management
* `src/skills/parser.rs` - SKILL.md parsing
* `src/skills/selector.rs` - Activation scoring and selection
* `src/skills/attenuation.rs` - Tool restriction based on trust
* `src/skills/gating.rs` - Dependency checks (bins/env/config)
* `src/skills/catalog.rs` - ClawHub integration
