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
                            
                            # Split body into Documentation and AI Instructions
                            # Convention: Use common headers or tags to denote the start of AI instructions
                            human_docs = body
                            ai_instructions = ""
                            
                            separators = ["# AI Instructions", "# Intelligence Protocol", "<!-- ai-instructions -->"]
                            for sep in separators:
                                if sep in body:
                                    split_parts = body.split(sep, 1)
                                    human_docs = split_parts[0].strip()
                                    ai_instructions = split_parts[1].strip()
                                    break
                            
                            skill_def["preview_markdown"] = human_docs
                            skill_def["system_prompt"] = ai_instructions
                            
                            # 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