#!/usr/bin/env python3 """ Migrate SuperClaude components to Skills-based architecture Converts always-loaded Markdown files to on-demand Skills loading for 97-98% token savings at Claude Code startup. Usage: python scripts/migrate_to_skills.py --dry-run # Preview changes python scripts/migrate_to_skills.py # Execute migration python scripts/migrate_to_skills.py --rollback # Undo migration """ import argparse import shutil from pathlib import Path import sys # Configuration CLAUDE_DIR = Path.home() / ".claude" SUPERCLAUDE_DIR = CLAUDE_DIR / "superclaude" SKILLS_DIR = CLAUDE_DIR / "skills" BACKUP_DIR = SUPERCLAUDE_DIR.parent / "superclaude.backup" # Component mapping: superclaude path → skill name COMPONENTS = { # Agents "agents/pm-agent.md": "pm", "agents/task-agent.md": "task", "agents/research-agent.md": "research", "agents/brainstorm-agent.md": "brainstorm", "agents/analyzer.md": "analyze", # Modes "modes/MODE_Orchestration.md": "orchestration-mode", "modes/MODE_Brainstorming.md": "brainstorming-mode", "modes/MODE_Introspection.md": "introspection-mode", "modes/MODE_Task_Management.md": "task-management-mode", "modes/MODE_Token_Efficiency.md": "token-efficiency-mode", "modes/MODE_DeepResearch.md": "deep-research-mode", "modes/MODE_Business_Panel.md": "business-panel-mode", } # Shared modules (copied to each skill that needs them) SHARED_MODULES = [ "modules/git-status.md", "modules/token-counter.md", "modules/pm-formatter.md", ] def create_skill_md(skill_name: str, original_file: Path) -> str: """Generate SKILL.md content from original file""" # Extract frontmatter if exists content = original_file.read_text() lines = content.split("\n") description = f"{skill_name.replace('-', ' ').title()} - Skills-based implementation" # Try to extract description from frontmatter if lines[0].strip() == "---": for line in lines[1:10]: if line.startswith("description:"): description = line.split(":", 1)[1].strip().strip('"') break return f"""--- name: {skill_name} description: {description} version: 1.0.0 author: SuperClaude migrated: true --- # {skill_name.replace('-', ' ').title()} Skills-based on-demand loading implementation. **Token Efficiency**: - Startup: 0 tokens (not loaded) - Description: ~50-100 tokens - Full load: ~2,500 tokens (when used) **Activation**: `/sc:{skill_name}` or auto-triggered by context **Implementation**: See `implementation.md` for full protocol **Modules**: Additional support files in `modules/` directory """ def migrate_component(source_path: Path, skill_name: str, dry_run: bool = False) -> dict: """Migrate a single component to Skills structure""" result = { "skill": skill_name, "source": str(source_path), "status": "skipped", "token_savings": 0, } if not source_path.exists(): result["status"] = "source_missing" return result # Calculate token savings word_count = len(source_path.read_text().split()) original_tokens = int(word_count * 1.3) skill_tokens = 70 # SKILL.md description only result["token_savings"] = original_tokens - skill_tokens skill_dir = SKILLS_DIR / skill_name if dry_run: result["status"] = "would_migrate" result["target"] = str(skill_dir) return result # Create skill directory skill_dir.mkdir(parents=True, exist_ok=True) # Create SKILL.md skill_md = skill_dir / "SKILL.md" skill_md.write_text(create_skill_md(skill_name, source_path)) # Copy implementation impl_md = skill_dir / "implementation.md" shutil.copy2(source_path, impl_md) # Copy modules if this is an agent if "agents" in str(source_path): modules_dir = skill_dir / "modules" modules_dir.mkdir(exist_ok=True) for module_path in SHARED_MODULES: module_file = SUPERCLAUDE_DIR / module_path if module_file.exists(): shutil.copy2(module_file, modules_dir / module_file.name) result["status"] = "migrated" result["target"] = str(skill_dir) return result def backup_superclaude(dry_run: bool = False) -> bool: """Create backup of current SuperClaude directory""" if not SUPERCLAUDE_DIR.exists(): print(f"❌ SuperClaude directory not found: {SUPERCLAUDE_DIR}") return False if BACKUP_DIR.exists(): print(f"⚠️ Backup already exists: {BACKUP_DIR}") print(" Skipping backup (use --force to overwrite)") return True if dry_run: print(f"Would create backup: {SUPERCLAUDE_DIR} → {BACKUP_DIR}") return True print(f"Creating backup: {BACKUP_DIR}") shutil.copytree(SUPERCLAUDE_DIR, BACKUP_DIR) print("✅ Backup created") return True def rollback_migration() -> bool: """Restore from backup""" if not BACKUP_DIR.exists(): print(f"❌ No backup found: {BACKUP_DIR}") return False print(f"Rolling back to backup...") # Remove skills directory if SKILLS_DIR.exists(): print(f"Removing skills: {SKILLS_DIR}") shutil.rmtree(SKILLS_DIR) # Restore superclaude if SUPERCLAUDE_DIR.exists(): print(f"Removing current: {SUPERCLAUDE_DIR}") shutil.rmtree(SUPERCLAUDE_DIR) print(f"Restoring from backup...") shutil.copytree(BACKUP_DIR, SUPERCLAUDE_DIR) print("✅ Rollback complete") return True def main(): parser = argparse.ArgumentParser( description="Migrate SuperClaude to Skills-based architecture" ) parser.add_argument( "--dry-run", action="store_true", help="Preview changes without executing" ) parser.add_argument( "--rollback", action="store_true", help="Restore from backup" ) parser.add_argument( "--no-backup", action="store_true", help="Skip backup creation (dangerous)" ) args = parser.parse_args() # Rollback mode if args.rollback: success = rollback_migration() sys.exit(0 if success else 1) # Migration mode print("=" * 60) print("SuperClaude → Skills Migration") print("=" * 60) if args.dry_run: print("🔍 DRY RUN MODE - No changes will be made\n") # Backup if not args.no_backup: if not backup_superclaude(args.dry_run): sys.exit(1) print(f"\nMigrating {len(COMPONENTS)} components...\n") # Migrate components results = [] total_savings = 0 for source_rel, skill_name in COMPONENTS.items(): source_path = SUPERCLAUDE_DIR / source_rel result = migrate_component(source_path, skill_name, args.dry_run) results.append(result) status_icon = { "migrated": "✅", "would_migrate": "📋", "source_missing": "⚠️", "skipped": "⏭️", }.get(result["status"], "❓") print(f"{status_icon} {skill_name:25} {result['status']:15} " f"(saves {result['token_savings']:,} tokens)") total_savings += result["token_savings"] # Summary print("\n" + "=" * 60) print("SUMMARY") print("=" * 60) migrated = sum(1 for r in results if r["status"] in ["migrated", "would_migrate"]) skipped = sum(1 for r in results if r["status"] in ["source_missing", "skipped"]) print(f"Migrated: {migrated}/{len(COMPONENTS)}") print(f"Skipped: {skipped}/{len(COMPONENTS)}") print(f"Total token savings: {total_savings:,} tokens") print(f"Savings percentage: {total_savings * 100 // (total_savings + 500):.0f}%") if args.dry_run: print("\n💡 Run without --dry-run to execute migration") else: print(f"\n✅ Migration complete!") print(f" Backup: {BACKUP_DIR}") print(f" Skills: {SKILLS_DIR}") print(f"\n Use --rollback to undo changes") return 0 if __name__ == "__main__": sys.exit(main())