Skill: oki3505f/remote-skill-engine (version 1.0.0)

--- name: remote-skill-engine description: Cache and use skills from ClawHub and GitHub as if locally installed. Stores remote skills in local cache folder for offline use. --- # Remote Skill Engine This skill enables you to discover, cache, and use skills directly from remote registries (ClawHub, GitHub, etc.). Skills are stored in `~/.openclaw/workspace/remote-skills-cache/` and work exactly like locally installed skills. ## Core Capabilities ### 1. Skill Discovery & Search Search across multiple registries: ```bash # ClawHub search clawhub search "<query>" --limit <n> # GitHub skill search (via gh CLI) gh search repos --language=markdown "skill openclaw" "<topic>" ``` **When to use:** User needs a skill for a specific task, wants to explore available skills, or needs to compare options. ### 2. Remote Skill Caching (KEY FEATURE) Cache any remote skill to work EXACTLY like installed skills: ```bash # Cache a skill from ClawHub ./scripts/cache-skill.sh clawhub://security-auditor # Cache from GitHub ./scripts/cache-skill.sh github://owner/repo/branch # Cache from direct URL ./scripts/cache-skill.sh https://raw.githubusercontent.com/.../SKILL.md # List cached skills clawhub list --cache # Use cached skill (works like installed!) # Just trigger it normally - it's in your skills path! ``` **Cache Location:** `~/.openclaw/workspace/remote-skills-cache/<skill-name>/` **Cached skills are symlinked** to `skills/` folder so they work identically to installed skills. ### 3. Batch Cache Management ```bash # Cache multiple skills at once ./scripts/cache-skills-batch.json skills-list.json # Update all cached skills ./scripts/update-cached-skills.sh # Remove cached skill ./scripts/uncache-skill.sh <skill-name> # Show cache stats ./scripts/cache-stats.sh ``` ### 4. Smart Sync & Updates ```bash # Check for updates to cached skills ./scripts/check-updates.sh # Sync specific skill to latest ./scripts/sync-skill.sh <skill-name> # Auto-sync on skill trigger (configurable) # Set in config.json: {"autoSync": true} ``` ### 5. Offline Mode Once cached, skills work WITHOUT internet: - All scripts available locally - References cached alongside SKILL.md - Assets downloaded and stored - No network calls needed ## Workflows ### NEW: Workflow 1 - Cache Remote Skill **Trigger:** User says "Install/caching <skill-name>" or "Get <skill> from web" ```bash # Step 1: Find the skill clawhub search "<skill-name>" --limit 1 # Step 2: Cache it locally python scripts/cache-skill.py <skill-name> <source-url> # Step 3: Verify cache ls ~/.openclaw/workspace/remote-skills-cache/<skill-name>/ # Step 4: Use it (works like installed!) # Just use the skill normally - it auto-triggers from skills/ ``` ### NEW: Workflow 2 - Batch Cache Skills List **Trigger:** User wants multiple skills cached ```json // skills-to-cache.json { "skills": [ {"name": "security-auditor", "source": "clawhub://security-auditor"}, {"name": "coding-agent", "source": "github://user/repo/main"} ] } ``` ```bash ./scripts/batch-cache.sh skills-to-cache.json ``` ### Workflow 3: Compare Multiple Skills (Enhanced) **Trigger:** User says "Which skill is better for X?" 1. Fetch metadata for each skill 2. Check if cached, cache if not 3. Create comparison with SOURCE + CACHE STATUS: ```markdown | Skill | Version | Source | Cached | Last Sync | |-------|---------|--------|--------|-----------| | skill-a | 1.0.0 | ClawHub | ✅ | 2h ago | | skill-b | 2.1.0 | GitHub | ❌ | - | ``` 4. Recommend + offer to cache ### Workflow 4: Sync Cached Skills **Trigger:** Heartbeat or user says "Update cached skills" ```bash # Check all cached skills for updates ./scripts/check-updates.sh # Update outdated skills ./scripts/update-cached-skills.sh --auto # Update specific skill ./scripts/sync-skill.sh <skill-name> ``` ### Workflow 5: Use Cached Skill (Transparent) **Trigger:** User triggers any cached skill normally - Skill is already in `skills/` folder (symlinked) - Works IDENTICALLY to installed skills - No special handling needed - All scripts/refs/assets available locally ## Scripts ### `scripts/fetch-skill.py` Fetch a skill's SKILL.md and parse frontmatter without full download. ```python #!/usr/bin/env python3 """Fetch remote skill metadata without full download.""" import requests import yaml import sys def fetch_skill_frontmatter(skill_url): """Fetch only YAML frontmatter from SKILL.md""" resp = requests.get(skill_url, stream=True) content = "" in_frontmatter = False for line in resp.iter_lines(): line = line.decode('utf-8') if line == '---': if not in_frontmatter: in_frontmatter = True continue else: break if in_frontmatter: content += line + '\n' try: return yaml.safe_load(content) except yaml.YAMLError as e: print(f"Parse error: {e}", file=sys.stderr) return None if __name__ == "__main__": if len(sys.argv) != 2: print("Usage: fetch-skill.py <skill-url>") sys.exit(1) metadata = fetch_skill_frontmatter(sys.argv[1]) if metadata: print(f"Name: {metadata.get('name', 'N/A')}") print(f"Description: {metadata.get('description', 'N/A')}") ``` **Usage:** ```bash python scripts/fetch-skill.py "https://raw.githubusercontent.com/repo/SKILL.md" ``` ### `scripts/compare-skills.py` Compare multiple skills side-by-side. ```python #!/usr/bin/env python3 """Compare multiple remote skills.""" import json import sys def compare_skills(skill_data_list): """Generate comparison table""" print("| Skill | Version | Description | Requirements |") print("|-------|---------|-------------|--------------|") for skill in skill_data_list: name = skill.get('name', 'N/A') version = skill.get('version', 'N/A') desc = skill.get('description', '')[:80] + '...' if len(skill.get('description', '')) > 80 else skill.get('description', 'N/A') reqs = skill.get('requires', 'None') print(f"| {name} | {version} | {desc} | {reqs} |") if __name__ == "__main__": # Read JSON from stdin skills = json.load(sys.stdin) compare_skills(skills) ``` ## References ### `references/registry-urls.md` Known skill registry endpoints: #### ClawHub - Search: `https://clawhub.com/api/skills/search?q=<query>` - Skill detail: `https://clawhub.com/api/skills/<name>` - Raw SKILL.md: `https://raw.githubusercontent.com/<owner>/<repo>/SKILL.md` #### GitHub - Search API: `https://api.github.com/search/code?q=SKILL.md+openclaw` - Raw file: `https://raw.githubusercontent.com/<owner>/<repo>/<branch>/SKILL.md` #### Awesome Lists - `https://raw.githubusercontent.com/openclaw/awesome-openclaw/main/README.md` ### `references/skill-patterns.md` Common skill patterns to recognize: **Skill Types:** - **Tool wrappers** - Provide CLI access to external tools (nmap, sqlmap, etc.) - **Workflow engines** - Multi-step processes (security audits, deployments) - **Knowledge bases** - Domain expertise + reference docs - **Automation scripts** - Repetitive task automation - **Integration layers** - MCP servers, API connectors **Metadata Patterns:** ```yaml metadata: openclaw: requires: bins: ["tool-name"] # Required binaries install: - kind: node # or brew, apt, pip package: package-name ``` ## Usage Examples ### Example 1: Find and Use Security Skill ``` User: "Find me a skill for web security scanning" You: 1. clawhub search "web security" --limit 5 2. Present results 3. User picks "security-audit-toolkit" 4. Fetch SKILL.md, read workflows 5. Follow the skill's security audit process ``` ### Example 2: Temporary Skill Usage ``` User: "Use the coding agent to review my repo, but don't install it" You: 1. Fetch coding agent SKILL.md from ClawHub 2. Read its code review workflow 3. Execute the workflow steps manually 4. Report findings ``` ### Example 3: Batch Discovery ``` User: "Show me all available skills" You: 1. clawhub search "" --limit 100 2. Categorize by keywords 3. Present organized list: - Security (12 skills) - Coding (8 skills) - Automation (6 skills) - etc. ``` ## Best Practices 1. **Cache metadata locally** - Store skill descriptions in `memory/skills-cached.md` to avoid repeated fetches 2. **Respect rate limits** - Don't hammer ClawHub/GitHub APIs 3. **Verify skill sources** - Only fetch from trusted registries 4. **Clean up temp files** - Remove `/tmp/remote-skill-*` after use 5. **Track skill usage** - Log which remote skills work well for future reference ## Trigger Examples When this skill activates: - "Find a skill for X" - "What skills do Y?" - "Use <skill-name> without installing" - "Compare skills for X" - "Show me available skills" - "What does <skill> do?" - "Is there a skill for X?" --- **Remote Skill Engine** - Stream skills, don't install them. 🌐⚡

path: references/registry-urls.md
content: # Skill Registry URLs

## ClawHub

**Base URL:** `https://clawhub.com`

### Endpoints

- **Search API:** `https://clawhub.com/api/skills/search?q=<query>&limit=<n>`
- **Skill Detail:** `https://clawhub.com/api/skills/<skill-name>`
- **Skill Manifest:** `https://clawhub.com/api/skills/<skill-name>/manifest`
- **Raw SKILL.md:** Varies by repo (check skill metadata for source URL)

### CLI Commands

```bash
# Search
clawhub search "<query>" --limit <n>

# Get skill info
clawhub install <skill-name> --dry-run

# List installed
clawhub list
```

## GitHub

**Base URL:** `https://github.com`

### Search Patterns

- **Code Search:** `https://github.com/search?q=SKILL.md+openclaw&type=code`
- **Repo Search:** `https://github.com/search?q=openclaw-skill+in:name&type=repositories`

### Raw File URLs

```
https://raw.githubusercontent.com/<owner>/<repo>/<branch>/SKILL.md
https://raw.githubusercontent.com/<owner>/<repo>/<branch>/scripts/<script>.py
https://raw.githubusercontent.com/<owner>/<repo>/<branch>/references/<file>.md
```

### GitHub CLI

```bash
# Search repos
gh search repos --language=markdown "openclaw skill"

# Search code
gh search code "SKILL.md" --language=markdown

# View repo
gh repo view <owner>/<repo>
```

## Awesome Lists

- **Awesome OpenClaw:** `https://raw.githubusercontent.com/openclaw/awesome-openclaw/main/README.md`
- **Community Skills:** Check ClawHub search for community-curated lists

## Known Skill Repositories

| Repository | Type | Notes |
|------------|------|-------|
| `openclaw/openclaw` | Core | Official OpenClaw skills |
| `clawhub/registry` | Registry | ClawHub public skills |
| Community repos | Various | Search via ClawHub |

## Rate Limits

- **ClawHub:** No documented limits (be reasonable)
- **GitHub API:** 60 requests/hour unauthenticated, 5000/hour with token
- **Raw GitHub:** Higher limits but don't abuse

## Authentication

- **ClawHub:** Not required for read/search
- **GitHub:** Token optional for public repos (use `gh auth login` for higher limits)


path: scripts/batch-cache.sh
content: #!/bin/bash
# Batch cache multiple skills from a JSON file

if [ -z "$1" ]; then
    echo "Usage: batch-cache.sh <skills-file.json>"
    echo ""
    echo "JSON format:"
    echo '{'
    echo '  "skills": ['
    echo '    {"name": "skill-a", "source": "clawhub://skill-a"},'
    echo '    {"name": "skill-b", "source": "github://user/repo/main"}'
    echo '  ]'
    echo '}'
    exit 1
fi

SKILLS_FILE="$1"
SCRIPT_DIR="$(dirname "$0")"

if [ ! -f "$SKILLS_FILE" ]; then
    echo "❌ File not found: $SKILLS_FILE"
    exit 1
fi

echo "📦 Batch caching skills from: $SKILLS_FILE"
echo ""

# Parse JSON and cache each skill
python3 -c "
import json
import sys
import subprocess

with open('$SKILLS_FILE', 'r') as f:
    data = json.load(f)

skills = data.get('skills', [])
print(f'Found {len(skills)} skills to cache')
print('=' * 60)

success_count = 0
fail_count = 0

for skill in skills:
    name = skill.get('name')
    source = skill.get('source')
    
    if not name or not source:
        print(f'⚠️  Skipping invalid entry: {skill}')
        fail_count += 1
        continue
    
    print(f'\\n[{success_count + fail_count + 1}/{len(skills)}] Caching: {name}')
    result = subprocess.run(['python3', '$SCRIPT_DIR/cache-skill.py', name, source])
    
    if result.returncode == 0:
        success_count += 1
    else:
        fail_count += 1

print('')
print('=' * 60)
print(f'✅ Success: {success_count}')
print(f'❌ Failed: {fail_count}')
"

echo ""
echo "🎉 Batch caching complete!"


path: scripts/cache-skill.py
content: #!/usr/bin/env python3
"""Cache a remote skill locally so it works like an installed skill."""
import sys
import os
import urllib.request
import ssl
import json
import subprocess
from pathlib import Path

CACHE_DIR = Path.home() / ".openclaw" / "workspace" / "remote-skills-cache"
SKILLS_DIR = Path.home() / ".openclaw" / "workspace" / "skills"

def parse_source(source):
    """Parse source URL into components."""
    if source.startswith("clawhub://"):
        skill_name = source.replace("clawhub://", "")
        return {
            "type": "clawhub",
            "name": skill_name,
            "url": None
        }
    elif source.startswith("github://"):
        parts = source.replace("github://", "").split("/")
        if len(parts) >= 3:
            return {
                "type": "github",
                "owner": parts[0],
                "repo": parts[1],
                "branch": parts[2] if len(parts) > 2 else "main",
                "path": "/".join(parts[3:]) if len(parts) > 3 else ""
            }
    elif source.startswith("http"):
        return {
            "type": "direct",
            "url": source
        }
    return None

def fetch_skill_metadata(skill_name):
    """Fetch skill metadata from ClawHub."""
    try:
        result = subprocess.run(
            ["clawhub", "search", skill_name, "--limit", "1"],
            capture_output=True,
            text=True
        )
        return result.stdout
    except Exception as e:
        print(f"⚠️  Warning: Could not fetch metadata: {e}")
        return None

def download_file(url, dest_path):
    """Download a file from URL."""
    ctx = ssl.create_default_context()
    ctx.check_hostname = False
    ctx.verify_mode = ssl.CERT_NONE
    
    try:
        req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0 RemoteSkillEngine/1.0'})
        with urllib.request.urlopen(req, context=ctx, timeout=30) as response:
            with open(dest_path, 'wb') as f:
                f.write(response.read())
        return True
    except Exception as e:
        print(f"❌ Failed to download {url}: {e}")
        return False

def cache_skill(skill_name, source_url):
    """Cache a skill from remote source."""
    print(f"🔄 Caching skill: {skill_name}")
    print(f"📍 Source: {source_url}")
    print("")
    
    # Create cache directory
    cache_path = CACHE_DIR / skill_name
    cache_path.mkdir(parents=True, exist_ok=True)
    
    scripts_dir = cache_path / "scripts"
    refs_dir = cache_path / "references"
    assets_dir = cache_path / "assets"
    
    scripts_dir.mkdir(exist_ok=True)
    refs_dir.mkdir(exist_ok=True)
    assets_dir.mkdir(exist_ok=True)
    
    # Parse source
    parsed = parse_source(source_url)
    if not parsed:
        print(f"❌ Invalid source format: {source_url}")
        return False
    
    skill_url = None
    
    if parsed["type"] == "clawhub":
        # Search ClawHub to get actual repo URL
        print("🔍 Searching ClawHub for skill info...")
        result = subprocess.run(
            ["clawhub", "search", skill_name, "--limit", "1"],
            capture_output=True,
            text=True
        )
        # Parse output to get skill name/version
        lines = result.stdout.strip().split('\n')
        for line in lines:
            if skill_name in line.lower():
                print(f"✅ Found: {line}")
                break
        
        # Try to install temporarily to get files, or fetch from known patterns
        print("📥 Attempting to fetch SKILL.md...")
        
        # Common raw URL patterns for ClawHub skills
        possible_urls = [
            f"https://raw.githubusercontent.com/openclaw/{skill_name.replace('-skill', '')}/main/SKILL.md",
            f"https://raw.githubusercontent.com/openclaw/{skill_name}/main/SKILL.md",
            f"https://raw.githubusercontent.com/clawhub/{skill_name}/main/SKILL.md",
        ]
        
        for url in possible_urls:
            print(f"  Trying: {url}")
            dest = cache_path / "SKILL.md"
            if download_file(url, dest):
                skill_url = url
                print(f"  ✅ Success!")
                break
            dest.unlink(miss_ok=True)
        
        if not skill_url:
            print("❌ Could not find SKILL.md from known patterns")
            print("💡 Try providing a direct GitHub URL instead")
            return False
    
    elif parsed["type"] == "github":
        base_url = f"https://raw.githubusercontent.com/{parsed['owner']}/{parsed['repo']}/{parsed['branch']}"
        skill_url = f"{base_url}/{parsed['path']}/SKILL.md" if parsed['path'] else f"{base_url}/SKILL.md"
        
        print(f"📥 Downloading from GitHub...")
        dest = cache_path / "SKILL.md"
        if not download_file(skill_url, dest):
            print("❌ Failed to download SKILL.md")
            return False
        print("✅ SKILL.md downloaded")
    
    elif parsed["type"] == "direct":
        skill_url = source_url
        dest = cache_path / "SKILL.md"
        if not download_file(skill_url, dest):
            print("❌ Failed to download SKILL.md")
            return False
        print("✅ SKILL.md downloaded")
    
    # Try to fetch common additional files
    if skill_url:
        base_url = skill_url.replace("/SKILL.md", "")
        
        # Fetch scripts
        print("📥 Checking for scripts...")
        common_scripts = ["fetch-skill.py", "compare-skills.py", "main.py", "run.py"]
        for script in common_scripts:
            url = f"{base_url}/scripts/{script}"
            dest = scripts_dir / script
            if download_file(url, dest):
                print(f"  ✅ Downloaded: scripts/{script}")
                os.chmod(dest, 0o755)
            else:
                dest.unlink(miss_ok=True)
        
        # Fetch references
        print("📥 Checking for references...")
        common_refs = ["README.md", "API.md", "GUIDE.md"]
        for ref in common_refs:
            url = f"{base_url}/references/{ref}"
            dest = refs_dir / ref
            if download_file(url, dest):
                print(f"  ✅ Downloaded: references/{ref}")
            else:
                dest.unlink(miss_ok=True)
    
    # Save metadata
    metadata = {
        "name": skill_name,
        "source": source_url,
        "cached_at": subprocess.run(["date", "-Iseconds"], capture_output=True, text=True).stdout.strip(),
        "skill_url": skill_url,
        "version": "cached"
    }
    
    with open(cache_path / "cache-metadata.json", 'w') as f:
        json.dump(metadata, f, indent=2)
    
    # Create symlink in skills/ folder
    skills_symlink = SKILLS_DIR / skill_name
    if skills_symlink.exists() or skills_symlink.is_symlink():
        skills_symlink.unlink()
    
    skills_symlink.symlink_to(cache_path)
    print(f"🔗 Created symlink: {skills_symlink} -> {cache_path}")
    
    print("")
    print("=" * 60)
    print(f"✅ Skill '{skill_name}' cached successfully!")
    print(f"📂 Location: {cache_path}")
    print(f"🔗 Symlinked to: {skills_symlink}")
    print(f"🎯 Status: READY TO USE (works like installed skill)")
    print("=" * 60)
    
    return True

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("Usage: cache-skill.py <skill-name> <source-url>")
        print("")
        print("Examples:")
        print("  cache-skill.py security-auditor clawhub://security-auditor")
        print("  cache-skill.py my-skill github://owner/repo/main")
        print("  cache-skill.py my-skill https://raw.githubusercontent.com/.../SKILL.md")
        sys.exit(1)
    
    skill_name = sys.argv[1]
    source_url = sys.argv[2]
    
    success = cache_skill(skill_name, source_url)
    sys.exit(0 if success else 1)


path: scripts/compare-skills.py
content: #!/usr/bin/env python3
"""Compare multiple remote skills side-by-side."""
import sys
import json

def compare_skills(skill_data_list):
    """Generate comparison table for skills"""
    print("\n### Skill Comparison\n")
    print("| Skill | Version | Description | Requirements |")
    print("|-------|---------|-------------|--------------|")
    
    for skill in skill_data_list:
        name = skill.get('name', 'N/A')
        version = skill.get('version', 'N/A')
        desc = skill.get('description', 'N/A')
        
        # Truncate description if too long
        if len(desc) > 60:
            desc = desc[:57] + '...'
        
        # Extract requirements if available
        reqs = 'None'
        if 'metadata' in skill:
            meta = skill['metadata']
            if 'openclaw' in meta and 'requires' in meta['openclaw']:
                bins = meta['openclaw']['requires'].get('bins', [])
                if bins:
                    reqs = ', '.join(bins)
        
        print(f"| {name} | {version} | {desc} | {reqs} |")
    
    print("\n")

if __name__ == "__main__":
    if len(sys.argv) > 1:
        # Read from file
        try:
            with open(sys.argv[1], 'r') as f:
                skills = json.load(f)
            compare_skills(skills)
        except Exception as e:
            print(f"Error: {e}", file=sys.stderr)
            sys.exit(1)
    else:
        # Read from stdin
        try:
            skills = json.load(sys.stdin)
            compare_skills(skills)
        except json.JSONDecodeError:
            print("Error: Invalid JSON input", file=sys.stderr)
            sys.exit(1)


path: scripts/execute-remote-skill.py
content: #!/usr/bin/env python3
"""Execute a remote skill directly without downloading."""
import sys
import urllib.request
import ssl
import tempfile
import os
import subprocess

def fetch_and_execute_skill(skill_url, command):
    """Fetch SKILL.md and execute a command using the skill's instructions."""
    ctx = ssl.create_default_context()
    ctx.check_hostname = False
    ctx.verify_mode = ssl.CERT_NONE
    
    try:
        # Fetch SKILL.md
        req = urllib.request.Request(
            skill_url,
            headers={'User-Agent': 'Mozilla/5.0 RemoteSkillEngine/1.0'}
        )
        
        with urllib.request.urlopen(req, context=ctx, timeout=30) as response:
            skill_content = response.read().decode('utf-8')
        
        # Create temporary directory for skill
        with tempfile.TemporaryDirectory() as tmpdir:
            skill_file = os.path.join(tmpdir, 'SKILL.md')
            with open(skill_file, 'w') as f:
                f.write(skill_content)
            
            print(f"📦 Loaded skill from: {skill_url}")
            print(f"📄 Skill size: {len(skill_content)} bytes")
            print(f"🔧 Ready to execute: {command}")
            print("")
            print("=" * 60)
            print("SKILL LOADED - Following instructions:")
            print("=" * 60)
            
            # Display key sections (first 2000 chars for context)
            print(skill_content[:2000])
            if len(skill_content) > 2000:
                print("\n... [skill continues]")
            
            print("")
            print("=" * 60)
            print("To use this skill, follow the instructions in SKILL.md above")
            print("or execute specific commands/scripts from the skill's scripts/")
            print("=" * 60)
            
            return True
            
    except Exception as e:
        print(f"❌ Error: {e}", file=sys.stderr)
        return False

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: execute-remote-skill.py <skill-url> [command]")
        print("Example: execute-remote-skill.py https://raw.githubusercontent.com/user/repo/SKILL.md 'run security scan'")
        sys.exit(1)
    
    skill_url = sys.argv[1]
    command = sys.argv[2] if len(sys.argv) > 2 else ""
    
    success = fetch_and_execute_skill(skill_url, command)
    sys.exit(0 if success else 1)


path: scripts/fetch-skill.py
content: #!/usr/bin/env python3
"""Fetch remote skill metadata without full download."""
import sys
import urllib.request
import ssl

def fetch_skill_frontmatter(skill_url):
    """Fetch only YAML frontmatter from SKILL.md"""
    try:
        # Handle SSL certificate verification
        ctx = ssl.create_default_context()
        ctx.check_hostname = False
        ctx.verify_mode = ssl.CERT_NONE
        
        req = urllib.request.Request(
            skill_url,
            headers={'User-Agent': 'Mozilla/5.0 RemoteSkillEngine/1.0'}
        )
        
        with urllib.request.urlopen(req, context=ctx, timeout=10) as response:
            content = ""
            in_frontmatter = False
            frontmatter_lines = []
            
            for line in response:
                line = line.decode('utf-8').rstrip()
                
                if line == '---':
                    if not in_frontmatter:
                        in_frontmatter = True
                        continue
                    else:
                        # End of frontmatter
                        break
                
                if in_frontmatter:
                    frontmatter_lines.append(line)
            
            # Parse YAML manually (simple parser)
            metadata = {}
            current_key = None
            
            for line in frontmatter_lines:
                if ':' in line and not line.startswith(' ') and not line.startswith('#'):
                    key, _, value = line.partition(':')
                    key = key.strip()
                    value = value.strip()
                    metadata[key] = value
                    current_key = key
                elif line.startswith('  ') and current_key:
                    # Handle nested values (simple case)
                    if current_key in metadata:
                        if isinstance(metadata[current_key], str):
                            metadata[current_key] = [metadata[current_key]]
                        metadata[current_key].append(line.strip())
            
            return metadata
            
    except Exception as e:
        print(f"Error fetching skill: {e}", file=sys.stderr)
        return None

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Usage: fetch-skill.py <skill-url>")
        print("Example: fetch-skill.py https://raw.githubusercontent.com/user/repo/SKILL.md")
        sys.exit(1)
    
    metadata = fetch_skill_frontmatter(sys.argv[1])
    
    if metadata:
        print(f"Name: {metadata.get('name', 'N/A')}")
        print(f"Description: {metadata.get('description', 'N/A')}")
        if 'metadata' in metadata:
            print(f"Metadata: {metadata['metadata']}")
    else:
        sys.exit(1)


path: scripts/list-cached-skills.py
content: #!/usr/bin/env python3
"""List all cached remote skills with their status."""
import json
from pathlib import Path
from datetime import datetime

CACHE_DIR = Path.home() / ".openclaw" / "workspace" / "remote-skills-cache"

def get_cache_stats(skill_path):
    """Get stats for a cached skill."""
    metadata_file = skill_path / "cache-metadata.json"
    
    if metadata_file.exists():
        with open(metadata_file, 'r') as f:
            metadata = json.load(f)
        return {
            "name": metadata.get('name', skill_path.name),
            "source": metadata.get('source', 'Unknown'),
            "cached_at": metadata.get('cached_at', 'Unknown'),
            "version": metadata.get('version', 'N/A')
        }
    else:
        return {
            "name": skill_path.name,
            "source": "Unknown",
            "cached_at": "Unknown",
            "version": "N/A"
        }

def main():
    if not CACHE_DIR.exists():
        print("📭 No cached skills found.")
        print(f"Cache directory: {CACHE_DIR}")
        return
    
    cached_skills = list(CACHE_DIR.iterdir())
    cached_skills = [d for d in cached_skills if d.is_dir()]
    
    if not cached_skills:
        print("📭 No cached skills found.")
        return
    
    print("=" * 80)
    print("💾 CACHED REMOTE SKILLS")
    print("=" * 80)
    print("")
    print(f"{'Name':<30} {'Source':<30} {'Cached At':<25} {'Version':<10}")
    print("-" * 80)
    
    for skill_path in sorted(cached_skills):
        stats = get_cache_stats(skill_path)
        name = stats['name'][:28]
        source = stats['source'][:28]
        cached_at = stats['cached_at'][:23] if stats['cached_at'] != 'Unknown' else 'Unknown'
        version = stats['version'][:8]
        
        print(f"{name:<30} {source:<30} {cached_at:<25} {version:<10}")
    
    print("-" * 80)
    print(f"Total: {len(cached_skills)} skill(s) cached")
    print("")
    print(f"Cache location: {CACHE_DIR}")
    print("Tip: Cached skills work like installed skills (symlinked to skills/ folder)")
    print("=" * 80)

if __name__ == "__main__":
    main()


path: scripts/skill-finder.sh
content: #!/bin/bash
# Advanced skill discovery across multiple sources

QUERY="$1"
LIMIT="${2:-10}"

echo "🔍 Searching for skills: $QUERY"
echo ""

# Search ClawHub
echo "=== ClawHub ==="
clawhub search "$QUERY" --limit "$LIMIT" 2>/dev/null || echo "ClawHub search failed"

echo ""

# Search GitHub for OpenClaw skills
echo "=== GitHub (OpenClaw Skills) ==="
gh search repos --language=markdown -q ".skill OR SKILL.md" "openclaw $QUERY" --limit "$LIMIT" 2>/dev/null || echo "GitHub search unavailable"

echo ""

# Search GitHub for generic agent skills
echo "=== GitHub (Agent Skills) ==="
gh search code --language=markdown "SKILL.md" "$QUERY" --limit "$LIMIT" 2>/dev/null || echo "GitHub code search unavailable"

echo ""
echo "✅ Search complete!"


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (CRITICAL)