import os
import sys
import json
import logging
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# Add the ai-hub to sys.path
sys.path.insert(0, '/app')
from app.db.models import Skill, SkillFile
from app.config import settings
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# The new unified skills directory
BASE_DATA_DIR = "/app/data"
SKILLS_DIR = os.path.join(BASE_DATA_DIR, "skills")
logger.info(f"Target Skills Directory: {SKILLS_DIR}")
def migrate():
# Setup Database Connection
db_url = settings.DATABASE_URL
if not db_url:
logger.error("DATABASE_URL is not set!")
sys.exit(1)
engine = create_engine(db_url)
Session = sessionmaker(bind=engine)
session = Session()
try:
skills = session.query(Skill).all()
logger.info(f"Found {len(skills)} skills in the database. Starting migration...")
os.makedirs(SKILLS_DIR, exist_ok=True)
for skill in skills:
# 1. Determine Feature Parent Directory (default to 'swarm_control' if none)
feature = "swarm_control"
if skill.features and isinstance(skill.features, list) and len(skill.features) > 0:
feature = skill.features[0]
elif isinstance(skill.features, str):
try:
features_list = json.loads(skill.features)
if features_list: feature = features_list[0]
except:
feature = skill.features
# Slugify the skill name for the folder
slug = skill.name.replace(" ", "_").lower()
# Target path: /app/data/skills/swarm_control/get_weather/
target_path = os.path.join(SKILLS_DIR, feature, slug)
os.makedirs(target_path, exist_ok=True)
logger.info(f"Migrating Skill '{skill.name}' -> {target_path}")
# 2. Write Virtual Files
skill_md_content = None
has_files = False
for sf in skill.files:
has_files = True
file_abs_path = os.path.join(target_path, sf.file_path)
os.makedirs(os.path.dirname(file_abs_path), exist_ok=True)
with open(file_abs_path, "w") as f:
f.write(sf.content or "")
if sf.file_path == "SKILL.md":
skill_md_content = sf.content
# If it didn't have a SKILL.md for some reason but has an old-style description:
if not skill_md_content:
logger.warning(f"Skill '{skill.name}' had no SKILL.md. Generating one...")
with open(os.path.join(target_path, "SKILL.md"), "w") as f:
f.write(f"### Skill Name: {skill.name}\n\n{skill.description or 'No description provided.'}\n")
# 3. Write Invisible Metadata File (.metadata.json)
# This handles access control and legacy UI metadata properties
metadata = {
"owner_id": skill.owner_id,
"is_system": skill.is_system,
"id": skill.id, # keep track just in case
"extra_metadata": skill.extra_metadata or {"emoji": "🛠️"}
}
with open(os.path.join(target_path, ".metadata.json"), "w") as f:
json.dump(metadata, f, indent=4)
logger.info("Migration strictly to File System completed perfectly!")
except Exception as e:
logger.error(f"Migration Failed: {e}")
finally:
session.close()
if __name__ == "__main__":
migrate()