> ## Documentation Index
> Fetch the complete documentation index at: https://docs.praison.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Importing Hermes/OpenClaw Skills

> Guide for importing Hermes/OpenClaw skills into PraisonAI discovery roots and activation UX

Teams adopting Hermes/OpenClaw bring skills as directories and need a migration map into PraisonAI's discovery roots and activation UX.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Skill Import Pipeline"
        A[📋 Hermes/OpenClaw] --> B[🔍 Validate]
        B --> C[📁 Place in Discovery Root]
        C --> D[⚡ Activate]
    end
    
    classDef input fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef process fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff
    
    class A input
    class B,C process
    class D output
```

## Quick Start

<Steps>
  <Step title="Simple Import">
    Import an existing Hermes/OpenClaw skill into your PraisonAI project:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import Agent
    from praisonaiagents.skills import discover_skills

    # Discover skills from custom directory
    skills = discover_skills(["/path/to/hermes-skills"])

    agent = Agent(
        name="SkillAgent",
        instructions="Use imported skills to help users",
        skills=skills
    )

    agent.start("Process this data using available skills")
    ```
  </Step>

  <Step title="With Validation">
    Validate and safely import skills with error handling:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import Agent
    from praisonaiagents.skills import validate, load_skill

    # Validate skill before import
    skill_path = "/path/to/hermes-skills/data-processor"
    errors = validate(skill_path)

    if not errors:
        skill = load_skill("data-processor", ["/path/to/hermes-skills"])
        agent = Agent(
            name="ValidatedAgent",
            instructions="Use validated skills only",
            skills=[skill] if skill else []
        )
    else:
        print(f"Skill validation failed: {errors}")
    ```
  </Step>
</Steps>

***

## How It Works

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant PraisonAI
    participant SkillManager
    participant HermesSkill
    
    User->>PraisonAI: Import Hermes skill
    PraisonAI->>SkillManager: Discover skills
    SkillManager->>HermesSkill: Validate SKILL.md
    HermesSkill-->>SkillManager: Validation result
    SkillManager-->>PraisonAI: Skill loaded
    PraisonAI-->>User: Ready for use
```

| Phase           | Process                              | Output            |
| --------------- | ------------------------------------ | ----------------- |
| **Discovery**   | Scan directories for SKILL.md files  | Found skills list |
| **Validation**  | Check format, dependencies, security | Validation report |
| **Integration** | Load into PraisonAI discovery roots  | Active skills     |

***

## Migration Dimensions

### Artifact Inventory

Hermes/OpenClaw skills typically follow this structure:

```
skill-folder/
├── SKILL.md            ← frontmatter + body
├── scripts/            ← executable helpers (risk: platform-specific)  
└── references/         ← supplementary docs/data
```

PraisonAI handles these through its skill discovery system:

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    subgraph "Migration Decisions"
        A{Where to copy?} -->|Project-level| B[.praisonai/skills/...]
        A -->|User-global| C[~/.praisonai/skills/...]
        
        D{Name collisions?} -->|Rename| E[skill-name-v2]
        D -->|Namespace| F[hermes/skill-name]
        
        G{Script execution?} -->|Sandbox| H[Restricted execution]
        G -->|Allowlist| I[Pre-approved shells]
    end
    
    classDef decision fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef option fill:#189AB4,stroke:#7C90A0,color:#fff
    
    class A,D,G decision
    class B,C,E,F,H,I option
```

### Configuration Options

| Option                | Type        | Default | Description                               |
| --------------------- | ----------- | ------- | ----------------------------------------- |
| `skill_dirs`          | `List[str]` | `None`  | Directories to scan for skills            |
| `include_defaults`    | `bool`      | `True`  | Include built-in skill directories        |
| `validate_scripts`    | `bool`      | `True`  | Validate script security before execution |
| `namespace_conflicts` | `bool`      | `False` | Auto-namespace conflicting skill names    |

***

## Step-by-Step Migration

<Steps>
  <Step title="Validate UTF-8 Encoding">
    Ensure all skill files use proper encoding (especially on Windows):

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from pathlib import Path

    def validate_encoding(skill_path: str) -> bool:
        """Validate all files in skill directory are UTF-8 encoded."""
        skill_dir = Path(skill_path)
        
        for file_path in skill_dir.rglob("*.md"):
            try:
                file_path.read_text(encoding='utf-8')
            except UnicodeDecodeError:
                print(f"Invalid encoding in {file_path}")
                return False
        
        return True
    ```
  </Step>

  <Step title="Lint Frontmatter">
    Check SKILL.md frontmatter against supported keys:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    import yaml
    from pathlib import Path

    def lint_frontmatter(skill_path: str) -> list:
        """Lint SKILL.md frontmatter for compatibility."""
        skill_md = Path(skill_path) / "SKILL.md"
        content = skill_md.read_text()
        
        if not content.startswith('---'):
            return ["Missing YAML frontmatter"]
        
        # Extract frontmatter
        parts = content.split('---', 2)
        if len(parts) < 3:
            return ["Malformed frontmatter"]
        
        try:
            frontmatter = yaml.safe_load(parts[1])
            required_keys = ['name', 'description']
            missing = [key for key in required_keys if key not in frontmatter]
            return [f"Missing required key: {key}" for key in missing]
        except yaml.YAMLError as e:
            return [f"Invalid YAML: {e}"]
    ```
  </Step>

  <Step title="Place in Discovery Root">
    Copy to appropriate discovery directory:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    import shutil
    from pathlib import Path

    def import_skill(source_path: str, target_dir: str = None) -> str:
        """Import Hermes/OpenClaw skill to PraisonAI discovery root."""
        source = Path(source_path)
        
        # Default to project-level skills directory
        if target_dir is None:
            target_dir = Path.cwd() / ".praisonai" / "skills"
        else:
            target_dir = Path(target_dir)
        
        target_dir.mkdir(parents=True, exist_ok=True)
        destination = target_dir / source.name
        
        # Handle name conflicts
        counter = 1
        while destination.exists():
            destination = target_dir / f"{source.name}-{counter}"
            counter += 1
        
        shutil.copytree(source, destination)
        return str(destination)
    ```
  </Step>

  <Step title="Test Discovery">
    Verify the skill is discoverable by PraisonAI:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents.skills import discover_skills

    def test_discovery(skills_dir: str) -> bool:
        """Test if imported skills are discoverable."""
        skills = discover_skills([skills_dir])
        
        if not skills:
            print("No skills discovered")
            return False
        
        for skill in skills:
            print(f"✓ Discovered: {skill.name} - {skill.description}")
        
        return True
    ```
  </Step>

  <Step title="Security Review">
    Strip secrets and validate script safety:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    import re
    from pathlib import Path

    def security_review(skill_path: str) -> list:
        """Review skill for security issues."""
        issues = []
        skill_dir = Path(skill_path)
        
        # Check for hardcoded secrets
        secret_patterns = [
            r'api[_-]?key\s*=\s*["\'][^"\']+["\']',
            r'password\s*=\s*["\'][^"\']+["\']',
            r'token\s*=\s*["\'][^"\']+["\']'
        ]
        
        for file_path in skill_dir.rglob("*"):
            if file_path.is_file() and file_path.suffix in ['.md', '.py', '.txt']:
                content = file_path.read_text()
                for pattern in secret_patterns:
                    if re.search(pattern, content, re.IGNORECASE):
                        issues.append(f"Potential secret in {file_path}")
        
        return issues
    ```
  </Step>
</Steps>

***

## Runtime Bridges

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Skill Integration Layers"
        A[Skills] --> B[Instructions + Policy]
        C[Tools] --> D[Mechanisms + APIs]
        E[MCP] --> F[Host Capabilities]
    end
    
    subgraph "Activation Flow"
        B --> G[Agent Context]
        D --> G
        F --> G
        G --> H[Unified Interface]
    end
    
    classDef skill fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef tool fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff
    
    class A,B skill
    class C,D,E,F tool
    class G,H output
```

Skills coexist with tools through layered integration:

1. **Skills** provide instructions and safety policy text
2. **Tools** provide mechanisms (API keys, quotas, auditing)
3. **MCP** bridges host capabilities and external services

### Tool Integration Pattern

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent
from praisonaiagents.tools import tool
from praisonaiagents.skills import load_skill

# Load Hermes skill
hermes_skill = load_skill("data-processor", ["/path/to/skills"])

# Create complementary tool
@tool
def process_data_api(data: str) -> str:
    """Process data using external API with proper auth."""
    # Tool handles API keys, rate limiting, etc.
    return api_call(data)

agent = Agent(
    name="HybridAgent", 
    skills=[hermes_skill],
    tools=[process_data_api]
)
```

***

## Best Practices

<AccordionGroup>
  <Accordion title="Namespace Management">
    Use consistent naming to avoid conflicts:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    # Good: Namespaced skill names
    "hermes/data-processor"
    "openclaw/file-manager" 
    "custom/my-skill"

    # Bad: Generic names that conflict
    "processor"
    "manager"
    "skill"
    ```
  </Accordion>

  <Accordion title="Security Isolation">
    Review and sanitize script execution:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents.skills import validate

    # Always validate before importing
    def safe_import(skill_path: str) -> bool:
        errors = validate(skill_path)
        if errors:
            print(f"Security issues: {errors}")
            return False
        
        # Additional security checks
        security_issues = security_review(skill_path)
        if security_issues:
            print(f"Security review failed: {security_issues}")
            return False
        
        return True
    ```
  </Accordion>

  <Accordion title="Version Management">
    Track skill versions and compatibility:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    # Include version in skill metadata
    metadata = {
        "source": "hermes",
        "version": "1.2.0", 
        "compatibility": "praisonai>=0.1.0",
        "imported_at": "2024-01-15T10:30:00Z"
    }
    ```
  </Accordion>

  <Accordion title="Testing Integration">
    Test skills in isolation before deployment:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    def test_imported_skill(skill_name: str) -> bool:
        """Test imported skill functionality."""
        skill = load_skill(skill_name)
        
        if not skill:
            return False
        
        # Create test agent
        test_agent = Agent(
            name="TestAgent",
            skills=[skill],
            instructions="Test the imported skill"
        )
        
        # Run test cases
        test_result = test_agent.start("Execute test scenario")
        return "error" not in test_result.lower()
    ```
  </Accordion>
</AccordionGroup>

***

## Troubleshooting

### Common Issues

| Problem                | Symptoms                | Solution                                        |
| ---------------------- | ----------------------- | ----------------------------------------------- |
| **Skill not detected** | Empty discovery results | Check path, YAML format, UTF-8 encoding         |
| **Import errors**      | Module not found        | Verify script dependencies, update requirements |
| **Permission denied**  | Script execution fails  | Review file permissions, security settings      |
| **Name conflicts**     | Skill override warnings | Use namespacing or rename conflicting skills    |

### Validation Checklist

* [ ] SKILL.md exists and has valid YAML frontmatter
* [ ] Required fields: `name`, `description`
* [ ] UTF-8 encoding throughout
* [ ] No hardcoded secrets in files
* [ ] Scripts have proper dependencies
* [ ] File permissions allow execution

### Debug Commands

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Test skill discovery
praisonai skills list --dirs /path/to/hermes-skills

# Validate specific skill
praisonai skills validate --path /path/to/skill-folder

# Generate skills prompt XML
praisonai skills prompt --dirs /path/to/hermes-skills
```

***

## Capability Requirements

Hermes/OpenClaw skills can now declare capability requirements that are **first-class parsed** into `SkillRequirements` and enforced via PraisonAI's capability gates system.

### Supported Frontmatter Keys

All these frontmatter keys from Hermes/OpenClaw skills are now parsed and enforced:

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Hermes/OpenClaw Frontmatter"
        A[requires_tools] --> D[SkillRequirements.tools]
        B[requires_servers] --> E[SkillRequirements.servers]
        C[requires_env] --> F[SkillRequirements.env_vars]
        G[openclaw] --> H[SkillRequirements.openclaw_hints]
    end
    
    classDef input fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff
    
    class A,B,C,G input
    class D,E,F,H output
```

| Hermes/OpenClaw Key | PraisonAI Field                    | Validation Result                      |
| ------------------- | ---------------------------------- | -------------------------------------- |
| `requires_tools`    | `SkillRequirements.tools`          | Missing tools → DEGRADED/UNAVAILABLE   |
| `requires_servers`  | `SkillRequirements.servers`        | Missing servers → DEGRADED/UNAVAILABLE |
| `requires_env`      | `SkillRequirements.env_vars`       | Missing env vars → DEGRADED            |
| `openclaw`          | `SkillRequirements.openclaw_hints` | Passthrough metadata                   |

### Updated Linting

Update your frontmatter linting to accept these new capability requirement keys:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
def lint_frontmatter(skill_path: str) -> list:
    """Lint SKILL.md frontmatter for compatibility."""
    skill_md = Path(skill_path) / "SKILL.md"
    content = skill_md.read_text()
    
    if not content.startswith('---'):
        return ["Missing YAML frontmatter"]
    
    parts = content.split('---', 2)
    if len(parts) < 3:
        return ["Malformed frontmatter"]
    
    try:
        frontmatter = yaml.safe_load(parts[1])
        
        # Required keys
        required_keys = ['name', 'description']
        missing = [key for key in required_keys if key not in frontmatter]
        
        # Known capability keys (now accepted)
        capability_keys = [
            'requires_tools', 'requires-tools',
            'requires_servers', 'requires-servers', 
            'requires_env', 'requires-env',
            'openclaw'
        ]
        
        return [f"Missing required key: {key}" for key in missing]
    except yaml.YAMLError as e:
        return [f"Invalid YAML: {e}"]
```

For complete capability gates documentation, see [Skill Capability Gates](/features/skill-capability-gates).

***

## Related

<CardGroup cols={2}>
  <Card title="Skills Overview" icon="puzzle-piece" href="/docs/concepts/skills">
    Learn about PraisonAI's skill system architecture
  </Card>

  <Card title="Tool Integration" icon="wrench" href="/docs/concepts/tools">
    Understand tools vs skills differences
  </Card>
</CardGroup>
