#!/usr/bin/env python3 """ Ralph Superpowers Integration Complete integration of oh-my-opencode and superpowers features. This module dynamically loads, configures, and makes available all skills, agents, hooks, and MCPs from both projects for use in Claude Code CLI. """ import os import sys import json import shutil import subprocess import logging from pathlib import Path from typing import Dict, List, Optional, Any, Callable from dataclasses import dataclass, field from enum import Enum import importlib.util logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger('ralph.superpowers') class IntegrationType(Enum): """Types of integrations""" SKILL = "skill" HOOK = "hook" AGENT = "agent" MCP = "mcp" COMMAND = "command" TOOL = "tool" @dataclass class IntegrationModule: """Represents an integrated module""" name: str type: IntegrationType source: str # oh-my-opencode, superpowers, contains-studio path: str enabled: bool = True config: Dict = field(default_factory=dict) dependencies: List[str] = field(default_factory=list) @dataclass class SuperpowersConfig: """Configuration for superpowers integration""" # Skills from superpowers brainstorming_enabled: bool = True writing_plans_enabled: bool = True executing_plans_enabled: bool = True subagent_driven_dev_enabled: bool = True test_driven_dev_enabled: bool = True systematic_debugging_enabled: bool = True verification_enabled: bool = True code_review_enabled: bool = True git_worktrees_enabled: bool = True # Hooks from oh-my-opencode atlas_enabled: bool = True claude_code_hooks_enabled: bool = True ralph_loop_enabled: bool = True todo_enforcer_enabled: bool = True # Agents from oh-my-opencode sisyphus_enabled: bool = True oracle_enabled: bool = True librarian_enabled: bool = True explore_enabled: bool = True prometheus_enabled: bool = True # MCPs from oh-my-opencode websearch_enabled: bool = True context7_enabled: bool = True grep_app_enabled: bool = True # Contains-studio agents contains_studio_enabled: bool = True auto_delegate_enabled: bool = True proactive_agents_enabled: bool = True class SuperpowersIntegration: """ Main integration class for all superpowers features Manages: - Dynamic loading of skills from superpowers - Dynamic loading of hooks from oh-my-opencode - Dynamic loading of agents from both projects - MCP configuration and management - Command registration - Tool integration """ def __init__(self, config: Optional[SuperpowersConfig] = None): """Initialize the integration""" self.config = config or SuperpowersConfig() self.modules: Dict[str, IntegrationModule] = {} self.skill_hooks: Dict[str, List[Callable]] = {} self.hook_registry: Dict[str, Callable] = {} # Paths self.ralph_dir = Path.home() / '.claude' / 'skills' / 'ralph' self.superpowers_dir = self.ralph_dir / 'superpowers' self.oh_my_opencode_dir = self.ralph_dir / 'oh-my-opencode' self.contains_studio_dir = Path.home() / '.claude' / 'agents' logger.info("Superpowers Integration initialized") def install_all(self) -> Dict[str, Any]: """ Install and integrate all features Returns: Installation summary """ summary = { 'skills': [], 'hooks': [], 'agents': [], 'mcps': [], 'commands': [], 'errors': [] } try: # 1. Install superpowers skills if self.config.brainstorming_enabled: self._install_superpowers_skills(summary) # 2. Install oh-my-opencode hooks if self.config.atlas_enabled: self._install_oh_my_opencode_hooks(summary) # 3. Install agents if self.config.sisyphus_enabled or self.config.contains_studio_enabled: self._install_agents(summary) # 4. Install MCPs if self.config.websearch_enabled: self._install_mcps(summary) # 5. Register commands self._register_commands(summary) # 6. Create configuration files self._create_config_files() logger.info(f"Installation complete: {summary}") return summary except Exception as e: logger.error(f"Installation failed: {e}") summary['errors'].append(str(e)) return summary def _install_superpowers_skills(self, summary: Dict): """Install skills from superpowers""" logger.info("Installing superpowers skills...") skills_dir = self.superpowers_dir / 'skills' skills_dir.mkdir(parents=True, exist_ok=True) # Define skills to install skills = [ 'brainstorming', 'writing-plans', 'executing-plans', 'subagent-driven-development', 'test-driven-development', 'systematic-debugging', 'verification-before-completion', 'requesting-code-review', 'receiving-code-review', 'using-git-worktrees', 'finishing-a-development-branch', 'dispatching-parallel-agents', 'using-superpowers', 'writing-skills' ] for skill in skills: try: # Copy skill from superpowers source source = Path('/tmp/superpowers/skills') / skill if source.exists(): dest = skills_dir / skill if dest.exists(): shutil.rmtree(dest) shutil.copytree(source, dest) module = IntegrationModule( name=skill, type=IntegrationType.SKILL, source='superpowers', path=str(dest), enabled=True ) self.modules[skill] = module summary['skills'].append(skill) logger.info(f" ✓ Installed skill: {skill}") except Exception as e: logger.warning(f" ✗ Failed to install skill {skill}: {e}") summary['errors'].append(f"skill:{skill} - {e}") def _install_oh_my_opencode_hooks(self, summary: Dict): """Install hooks from oh-my-opencode""" logger.info("Installing oh-my-opencode hooks...") hooks_dir = self.oh_my_opencode_dir / 'hooks' hooks_dir.mkdir(parents=True, exist_ok=True) # Key hooks to install hooks = [ 'atlas', # Main orchestrator 'claude-code-hooks', # Claude Code compatibility 'ralph-loop', # Autonomous iteration 'todo-continuation-enforcer', # Task completion 'thinking-block-validator', # Validate thinking 'session-recovery', # Recovery from errors 'edit-error-recovery', # Recovery from edit errors 'start-work', # Work initialization ] for hook in hooks: try: source = Path('/tmp/oh-my-opencode/src/hooks') / hook if source.exists(): dest = hooks_dir / hook if dest.exists(): shutil.rmtree(dest) shutil.copytree(source, dest) module = IntegrationModule( name=hook, type=IntegrationType.HOOK, source='oh-my-opencode', path=str(dest), enabled=True ) self.modules[hook] = module summary['hooks'].append(hook) logger.info(f" ✓ Installed hook: {hook}") except Exception as e: logger.warning(f" ✗ Failed to install hook {hook}: {e}") summary['errors'].append(f"hook:{hook} - {e}") def _install_agents(self, summary: Dict): """Install agents from both projects""" logger.info("Installing agents...") # oh-my-opencode agents if self.config.sisyphus_enabled: omo_agents_dir = self.oh_my_opencode_dir / 'agents' omo_agents_dir.mkdir(parents=True, exist_ok=True) agents = [ 'sisyphus', 'oracle', 'librarian', 'explore', 'prometheus' ] for agent in agents: try: source = Path('/tmp/oh-my-opencode/src/agents') / f"{agent}.ts" if source.exists(): dest = omo_agents_dir / f"{agent}.md" # Convert TypeScript agent to Markdown format for Claude Code self._convert_agent_to_md(source, dest) module = IntegrationModule( name=agent, type=IntegrationType.AGENT, source='oh-my-opencode', path=str(dest), enabled=True ) self.modules[agent] = module summary['agents'].append(agent) logger.info(f" ✓ Installed agent: {agent}") except Exception as e: logger.warning(f" ✗ Failed to install agent {agent}: {e}") summary['errors'].append(f"agent:{agent} - {e}") # contains-studio agents (already handled by contains-studio integration) if self.config.contains_studio_enabled: summary['agents'].append('contains-studio-agents (30+ agents)') logger.info(f" ✓ Contains-studio agents already integrated") def _install_mcps(self, summary: Dict): """Install MCPs from oh-my-opencode""" logger.info("Installing MCPs...") mcps_dir = self.oh_my_opencode_dir / 'mcps' mcps_dir.mkdir(parents=True, exist_ok=True) mcps = [ 'websearch', 'context7', 'grep_app' ] for mcp in mcps: try: source = Path('/tmp/oh-my-opencode/src/mcp') / f"{mcp}.ts" if source.exists(): dest = mcps_dir / f"{mcp}.json" # Create MCP config mcp_config = self._create_mcp_config(mcp, source) with open(dest, 'w') as f: json.dump(mcp_config, f, indent=2) module = IntegrationModule( name=mcp, type=IntegrationType.MCP, source='oh-my-opencode', path=str(dest), enabled=True ) self.modules[mcp] = module summary['mcps'].append(mcp) logger.info(f" ✓ Installed MCP: {mcp}") except Exception as e: logger.warning(f" ✗ Failed to install MCP {mcp}: {e}") summary['errors'].append(f"mcp:{mcp} - {e}") def _register_commands(self, summary: Dict): """Register commands from both projects""" logger.info("Registering commands...") commands_dir = Path.home() / '.claude' / 'commands' # Ralph sub-commands ralph_commands = [ ('brainstorm', 'Interactive design refinement'), ('write-plan', 'Create implementation plan'), ('execute-plan', 'Execute plan in batches'), ('debug', 'Systematic debugging'), ('review', 'Code review'), ('status', 'Show Ralph status'), ('list-agents', 'List all available agents'), ('list-skills', 'List all available skills') ] for cmd_name, description in ralph_commands: try: cmd_file = commands_dir / f'ralph-{cmd_name}.md' content = f"""--- description: "{description}" disable-model-invocation: true --- Invoke ralph:{cmd_name} via the ralph skill """ with open(cmd_file, 'w') as f: f.write(content) summary['commands'].append(f'ralph-{cmd_name}') logger.info(f" ✓ Registered command: /ralph-{cmd_name}") except Exception as e: logger.warning(f" ✗ Failed to register command {cmd_name}: {e}") summary['errors'].append(f"command:{cmd_name} - {e}") def _create_config_files(self): """Create configuration files""" logger.info("Creating configuration files...") config_dir = Path.home() / '.claude' / 'config' config_dir.mkdir(parents=True, exist_ok=True) # Main Ralph config config_file = config_dir / 'ralph.json' config = { 'superpowers': { 'enabled': True, 'skills': { 'brainstorming': self.config.brainstorming_enabled, 'writing-plans': self.config.writing_plans_enabled, 'executing-plans': self.config.executing_plans_enabled, 'subagent-driven-development': self.config.subagent_driven_dev_enabled, 'test-driven-development': self.config.test_driven_dev_enabled, 'systematic-debugging': self.config.systematic_debugging_enabled, 'verification-before-completion': self.config.verification_enabled, 'requesting-code-review': self.config.code_review_enabled, 'receiving-code-review': self.config.code_review_enabled, 'using-git-worktrees': self.config.git_worktrees_enabled, 'finishing-a-development-branch': True, 'dispatching-parallel-agents': True }, 'hooks': { 'atlas': self.config.atlas_enabled, 'claude-code-hooks': self.config.claude_code_hooks_enabled, 'ralph-loop': self.config.ralph_loop_enabled, 'todo-continuation-enforcer': self.config.todo_enforcer_enabled }, 'agents': { 'sisyphus': self.config.sisyphus_enabled, 'oracle': self.config.oracle_enabled, 'librarian': self.config.librarian_enabled, 'explore': self.config.explore_enabled, 'prometheus': self.config.prometheus_enabled, 'contains-studio': self.config.contains_studio_enabled }, 'mcps': { 'websearch': self.config.websearch_enabled, 'context7': self.config.context7_enabled, 'grep_app': self.config.grep_app_enabled }, 'auto_delegate': self.config.auto_delegate_enabled, 'proactive_agents': self.config.proactive_agents_enabled }, 'multi_agent': { 'enabled': os.getenv('RALPH_MULTI_AGENT', '').lower() == 'true', 'max_workers': int(os.getenv('RALPH_MAX_WORKERS', 12)), 'min_workers': int(os.getenv('RALPH_MIN_WORKERS', 2)) } } with open(config_file, 'w') as f: json.dump(config, f, indent=2) logger.info(f" ✓ Created config: {config_file}") def _convert_agent_to_md(self, source_ts: Path, dest_md: Path): """Convert TypeScript agent to Markdown format for Claude Code""" # Read TypeScript source with open(source_ts, 'r') as f: content = f.read() # Extract key information # This is a simplified conversion - real implementation would parse TS properly md_content = f"""--- name: {source_ts.stem} description: "Agent from oh-my-opencode: {source_ts.stem}" color: blue tools: Read, Write, Edit, Bash, Grep, Glob --- # {source_ts.stem.title()} Agent This agent was imported from oh-my-opencode. ## Purpose {self._extract_purpose(content)} ## Capabilities - Multi-model orchestration - Specialized tool usage - Background task management - Advanced code analysis ## Integration This agent integrates with Ralph's multi-agent system for coordinated task execution. """ with open(dest_md, 'w') as f: f.write(md_content) def _extract_purpose(self, ts_content: str) -> str: """Extract purpose description from TypeScript content""" # Simplified extraction if 'orchestrat' in ts_content.lower(): return "Orchestrates multiple agents and coordinates complex workflows" elif 'oracle' in ts_content.lower() or 'consult' in ts_content.lower(): return "Provides consultation and debugging expertise" elif 'librarian' in ts_content.lower() or 'docs' in ts_content.lower(): return "Searches documentation and codebases" elif 'explore' in ts_content.lower() or 'grep' in ts_content.lower(): return "Fast codebase exploration and search" elif 'prometheus' in ts_content.lower() or 'plan' in ts_content.lower(): return "Strategic planning and task breakdown" else: return "Specialized AI agent for specific tasks" def _create_mcp_config(self, mcp_name: str, source_file: Path) -> Dict: """Create MCP configuration""" # Base MCP config configs = { 'websearch': { 'name': 'websearch', 'command': 'npx', 'args': ['-y', '@modelcontextprotocol/server-exa'], 'env': { 'EXA_API_KEY': '${EXA_API_KEY}' } }, 'context7': { 'name': 'context7', 'command': 'npx', 'args': ['-y', '@context7/mcp-server-docs'], 'env': {} }, 'grep_app': { 'name': 'grep_app', 'command': 'npx', 'args': ['-y', '@modelcontextprotocol/server-github'], 'env': { 'GITHUB_TOKEN': '${GITHUB_TOKEN}' } } } return configs.get(mcp_name, { 'name': mcp_name, 'command': 'echo', 'args': ['MCP not configured'] }) def load_skill(self, skill_name: str) -> Optional[Any]: """Dynamically load a skill""" skill_key = f"skills.{skill_name}" if skill_key not in self.modules: logger.warning(f"Skill not found: {skill_name}") return None module = self.modules[skill_key] try: # Load the skill module spec = importlib.util.spec_from_file_location( f"ralph.skills.{skill_name}", os.path.join(module.path, 'SKILL.md') ) if spec and spec.loader: skill_module = importlib.util.module_from_spec(spec) spec.loader.exec_module(skill_module) return skill_module except Exception as e: logger.error(f"Failed to load skill {skill_name}: {e}") return None def invoke_hook(self, hook_name: str, context: Dict) -> Any: """Invoke a registered hook""" if hook_name not in self.hook_registry: logger.debug(f"Hook not registered: {hook_name}") return None try: hook_func = self.hook_registry[hook_name] return hook_func(context) except Exception as e: logger.error(f"Hook {hook_name} failed: {e}") return None def register_hook(self, hook_name: str, hook_func: Callable): """Register a hook function""" self.hook_registry[hook_name] = hook_func logger.info(f"Registered hook: {hook_name}") def get_status(self) -> Dict: """Get integration status""" return { 'modules': { name: { 'type': module.type.value, 'source': module.source, 'enabled': module.enabled, 'path': module.path } for name, module in self.modules.items() }, 'config': { 'superpowers': { 'skills_enabled': sum(1 for m in self.modules.values() if m.type == IntegrationType.SKILL and m.enabled), 'hooks_enabled': sum(1 for m in self.modules.values() if m.type == IntegrationType.HOOK and m.enabled), 'agents_enabled': sum(1 for m in self.modules.values() if m.type == IntegrationType.AGENT and m.enabled), 'mcps_enabled': sum(1 for m in self.modules.values() if m.type == IntegrationType.MCP and m.enabled) } }, 'hooks_registered': list(self.hook_registry.keys()) } def main(): """Main entry point for CLI usage""" import argparse parser = argparse.ArgumentParser(description='Ralph Superpowers Integration') parser.add_argument('--install', action='store_true', help='Install all superpowers') parser.add_argument('--status', action='store_true', help='Show integration status') parser.add_argument('--config', help='Path to config file') args = parser.parse_args() # Load config config = SuperpowersConfig() if args.config: with open(args.config) as f: config_data = json.load(f) # Apply config... # Create integration integration = SuperpowersIntegration(config) if args.install: summary = integration.install_all() print("\n=== Installation Summary ===") print(f"Skills: {len(summary['skills'])}") print(f"Hooks: {len(summary['hooks'])}") print(f"Agents: {len(summary['agents'])}") print(f"MCPs: {len(summary['mcps'])}") print(f"Commands: {len(summary['commands'])}") if summary['errors']: print(f"\nErrors: {len(summary['errors'])}") for error in summary['errors']: print(f" - {error}") elif args.status: status = integration.get_status() print(json.dumps(status, indent=2)) if __name__ == '__main__': main()