Newer
Older
cortex-hub / ai-hub / app / core / skills / loader.py
import os
import yaml
from typing import List, Dict

def load_skills_from_directory(directory: str) -> List[Dict]:
    skills = []
    if not os.path.exists(directory):
        return skills

    for item in os.listdir(directory):
        item_path = os.path.join(directory, item)
        if os.path.isdir(item_path):
            skill_md_path = os.path.join(item_path, "SKILL.md")
            if os.path.exists(skill_md_path):
                with open(skill_md_path, "r") as f:
                    content = f.read()
                
                # Simple frontmatter parser
                if content.startswith("---"):
                    parts = content.split("---", 2)
                    if len(parts) >= 3:
                        try:
                            frontmatter = yaml.safe_load(parts[1])
                            body = parts[2].strip()
                            
                            skill_def = frontmatter
                            
                            vfs_files = []
                            for root, _, files in os.walk(item_path):
                                for file in files:
                                    if file.startswith('.'): continue
                                    file_abs = os.path.join(root, file)
                                    file_rel = os.path.relpath(file_abs, item_path).replace('\\', '/')
                                    try:
                                        with open(file_abs, "r") as ff:
                                            vfs_files.append({"path": file_rel, "content": ff.read()})
                                    except Exception:
                                        pass
                            
                            skill_def["_vfs_files"] = vfs_files
                            
                            # Ensure metadata exists
                            if "extra_metadata" not in skill_def:
                                skill_def["extra_metadata"] = {}
                            
                            # Handle common OpenClaw metadata locations
                            if "emoji" in frontmatter and "emoji" not in skill_def["extra_metadata"]:
                                skill_def["extra_metadata"]["emoji"] = frontmatter["emoji"]
                            
                            # Validation: Ensure features match platform profiles
                            from app.core.orchestration.profiles import get_allowed_features
                            allowed = get_allowed_features()
                            skill_features = skill_def.get("features", [])
                            if not all(f in allowed for f in skill_features):
                                invalid = [f for f in skill_features if f not in allowed]
                                # Only log warning, don't crash, to remain developer-friendly
                                print(f"⚠️ Warning in {skill_def.get('name')}: features {invalid} are not registered in profiles.py. They will use the default 'chat' behavior.")

                            skills.append(skill_def)
                        except Exception as e:
                            print(f"Error parsing {skill_md_path}: {e}")
    return skills