claude-mpm 3.1.3__py3-none-any.whl → 3.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/__init__.py +3 -3
 - claude_mpm/__main__.py +0 -17
 - claude_mpm/agents/INSTRUCTIONS.md +81 -18
 - claude_mpm/agents/backups/INSTRUCTIONS.md +238 -0
 - claude_mpm/agents/base_agent.json +1 -1
 - claude_mpm/agents/templates/pm.json +25 -0
 - claude_mpm/agents/templates/research.json +2 -1
 - claude_mpm/cli/__init__.py +19 -23
 - claude_mpm/cli/commands/__init__.py +3 -1
 - claude_mpm/cli/commands/agents.py +7 -18
 - claude_mpm/cli/commands/info.py +5 -10
 - claude_mpm/cli/commands/memory.py +232 -0
 - claude_mpm/cli/commands/run.py +501 -28
 - claude_mpm/cli/commands/tickets.py +10 -17
 - claude_mpm/cli/commands/ui.py +15 -37
 - claude_mpm/cli/parser.py +91 -1
 - claude_mpm/cli/utils.py +9 -28
 - claude_mpm/config/socketio_config.py +256 -0
 - claude_mpm/constants.py +9 -0
 - claude_mpm/core/__init__.py +2 -2
 - claude_mpm/core/agent_registry.py +4 -4
 - claude_mpm/core/claude_runner.py +919 -0
 - claude_mpm/core/config.py +21 -1
 - claude_mpm/core/factories.py +1 -1
 - claude_mpm/core/hook_manager.py +196 -0
 - claude_mpm/core/pm_hook_interceptor.py +205 -0
 - claude_mpm/core/service_registry.py +1 -1
 - claude_mpm/core/simple_runner.py +323 -33
 - claude_mpm/core/socketio_pool.py +582 -0
 - claude_mpm/core/websocket_handler.py +233 -0
 - claude_mpm/deployment_paths.py +261 -0
 - claude_mpm/hooks/builtin/memory_hooks_example.py +67 -0
 - claude_mpm/hooks/claude_hooks/hook_handler.py +667 -679
 - claude_mpm/hooks/claude_hooks/hook_wrapper.sh +9 -4
 - claude_mpm/hooks/memory_integration_hook.py +312 -0
 - claude_mpm/models/__init__.py +9 -91
 - claude_mpm/orchestration/__init__.py +1 -1
 - claude_mpm/scripts/claude-mpm-socketio +32 -0
 - claude_mpm/scripts/claude_mpm_monitor.html +567 -0
 - claude_mpm/scripts/install_socketio_server.py +407 -0
 - claude_mpm/scripts/launch_monitor.py +132 -0
 - claude_mpm/scripts/manage_version.py +479 -0
 - claude_mpm/scripts/socketio_daemon.py +181 -0
 - claude_mpm/scripts/socketio_server_manager.py +428 -0
 - claude_mpm/services/__init__.py +5 -0
 - claude_mpm/services/agent_lifecycle_manager.py +76 -25
 - claude_mpm/services/agent_memory_manager.py +684 -0
 - claude_mpm/services/agent_modification_tracker.py +98 -17
 - claude_mpm/services/agent_persistence_service.py +33 -13
 - claude_mpm/services/agent_registry.py +82 -43
 - claude_mpm/services/hook_service.py +362 -0
 - claude_mpm/services/socketio_client_manager.py +474 -0
 - claude_mpm/services/socketio_server.py +698 -0
 - claude_mpm/services/standalone_socketio_server.py +631 -0
 - claude_mpm/services/ticket_manager.py +4 -5
 - claude_mpm/services/{ticket_manager_dependency_injection.py → ticket_manager_di.py} +12 -39
 - claude_mpm/services/{legacy_ticketing_service.py → ticketing_service_original.py} +9 -16
 - claude_mpm/services/version_control/semantic_versioning.py +9 -10
 - claude_mpm/services/websocket_server.py +376 -0
 - claude_mpm/utils/dependency_manager.py +211 -0
 - claude_mpm/utils/import_migration_example.py +80 -0
 - claude_mpm/utils/path_operations.py +0 -20
 - claude_mpm/web/open_dashboard.py +34 -0
 - {claude_mpm-3.1.3.dist-info → claude_mpm-3.2.1.dist-info}/METADATA +20 -9
 - {claude_mpm-3.1.3.dist-info → claude_mpm-3.2.1.dist-info}/RECORD +70 -50
 - claude_mpm-3.2.1.dist-info/entry_points.txt +7 -0
 - claude_mpm/cli_old.py +0 -728
 - claude_mpm/models/common.py +0 -41
 - claude_mpm/models/lifecycle.py +0 -97
 - claude_mpm/models/modification.py +0 -126
 - claude_mpm/models/persistence.py +0 -57
 - claude_mpm/models/registry.py +0 -91
 - claude_mpm/security/__init__.py +0 -8
 - claude_mpm/security/bash_validator.py +0 -393
 - claude_mpm-3.1.3.dist-info/entry_points.txt +0 -4
 - /claude_mpm/{cli_enhancements.py → experimental/cli_enhancements.py} +0 -0
 - {claude_mpm-3.1.3.dist-info → claude_mpm-3.2.1.dist-info}/WHEEL +0 -0
 - {claude_mpm-3.1.3.dist-info → claude_mpm-3.2.1.dist-info}/licenses/LICENSE +0 -0
 - {claude_mpm-3.1.3.dist-info → claude_mpm-3.2.1.dist-info}/top_level.txt +0 -0
 
| 
         @@ -0,0 +1,428 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env python3
         
     | 
| 
      
 2 
     | 
    
         
            +
            """Socket.IO Server Manager - Deployment-agnostic server management.
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            This script provides unified management for Socket.IO servers across different deployment scenarios:
         
     | 
| 
      
 5 
     | 
    
         
            +
            - Local development
         
     | 
| 
      
 6 
     | 
    
         
            +
            - PyPI installation
         
     | 
| 
      
 7 
     | 
    
         
            +
            - Docker containers
         
     | 
| 
      
 8 
     | 
    
         
            +
            - System service installation
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            Features:
         
     | 
| 
      
 11 
     | 
    
         
            +
            - Start/stop/restart standalone servers
         
     | 
| 
      
 12 
     | 
    
         
            +
            - Version compatibility checking
         
     | 
| 
      
 13 
     | 
    
         
            +
            - Health monitoring and diagnostics
         
     | 
| 
      
 14 
     | 
    
         
            +
            - Multi-instance management
         
     | 
| 
      
 15 
     | 
    
         
            +
            - Automatic dependency installation
         
     | 
| 
      
 16 
     | 
    
         
            +
            """
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            import argparse
         
     | 
| 
      
 19 
     | 
    
         
            +
            import json
         
     | 
| 
      
 20 
     | 
    
         
            +
            import os
         
     | 
| 
      
 21 
     | 
    
         
            +
            import signal
         
     | 
| 
      
 22 
     | 
    
         
            +
            import subprocess
         
     | 
| 
      
 23 
     | 
    
         
            +
            import sys
         
     | 
| 
      
 24 
     | 
    
         
            +
            import time
         
     | 
| 
      
 25 
     | 
    
         
            +
            from pathlib import Path
         
     | 
| 
      
 26 
     | 
    
         
            +
            from typing import Dict, List, Optional
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            try:
         
     | 
| 
      
 29 
     | 
    
         
            +
                import requests
         
     | 
| 
      
 30 
     | 
    
         
            +
                REQUESTS_AVAILABLE = True
         
     | 
| 
      
 31 
     | 
    
         
            +
            except ImportError:
         
     | 
| 
      
 32 
     | 
    
         
            +
                REQUESTS_AVAILABLE = False
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            class ServerManager:
         
     | 
| 
      
 36 
     | 
    
         
            +
                """Manages Socket.IO server instances across different deployment modes."""
         
     | 
| 
      
 37 
     | 
    
         
            +
                
         
     | 
| 
      
 38 
     | 
    
         
            +
                def __init__(self):
         
     | 
| 
      
 39 
     | 
    
         
            +
                    self.base_port = 8765
         
     | 
| 
      
 40 
     | 
    
         
            +
                    self.max_instances = 5
         
     | 
| 
      
 41 
     | 
    
         
            +
                    self.script_dir = Path(__file__).parent
         
     | 
| 
      
 42 
     | 
    
         
            +
                    self.project_root = self.script_dir.parent
         
     | 
| 
      
 43 
     | 
    
         
            +
                    
         
     | 
| 
      
 44 
     | 
    
         
            +
                def get_server_info(self, port: int) -> Optional[Dict]:
         
     | 
| 
      
 45 
     | 
    
         
            +
                    """Get server information from a running instance."""
         
     | 
| 
      
 46 
     | 
    
         
            +
                    if not REQUESTS_AVAILABLE:
         
     | 
| 
      
 47 
     | 
    
         
            +
                        return None
         
     | 
| 
      
 48 
     | 
    
         
            +
                    
         
     | 
| 
      
 49 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 50 
     | 
    
         
            +
                        response = requests.get(f"http://localhost:{port}/health", timeout=2.0)
         
     | 
| 
      
 51 
     | 
    
         
            +
                        if response.status_code == 200:
         
     | 
| 
      
 52 
     | 
    
         
            +
                            return response.json()
         
     | 
| 
      
 53 
     | 
    
         
            +
                    except Exception:
         
     | 
| 
      
 54 
     | 
    
         
            +
                        pass
         
     | 
| 
      
 55 
     | 
    
         
            +
                    return None
         
     | 
| 
      
 56 
     | 
    
         
            +
                
         
     | 
| 
      
 57 
     | 
    
         
            +
                def list_running_servers(self) -> List[Dict]:
         
     | 
| 
      
 58 
     | 
    
         
            +
                    """List all running Socket.IO servers."""
         
     | 
| 
      
 59 
     | 
    
         
            +
                    running_servers = []
         
     | 
| 
      
 60 
     | 
    
         
            +
                    
         
     | 
| 
      
 61 
     | 
    
         
            +
                    for port in range(self.base_port, self.base_port + self.max_instances):
         
     | 
| 
      
 62 
     | 
    
         
            +
                        server_info = self.get_server_info(port)
         
     | 
| 
      
 63 
     | 
    
         
            +
                        if server_info:
         
     | 
| 
      
 64 
     | 
    
         
            +
                            server_info['port'] = port
         
     | 
| 
      
 65 
     | 
    
         
            +
                            running_servers.append(server_info)
         
     | 
| 
      
 66 
     | 
    
         
            +
                    
         
     | 
| 
      
 67 
     | 
    
         
            +
                    return running_servers
         
     | 
| 
      
 68 
     | 
    
         
            +
                
         
     | 
| 
      
 69 
     | 
    
         
            +
                def find_available_port(self, start_port: int = None) -> int:
         
     | 
| 
      
 70 
     | 
    
         
            +
                    """Find the next available port for a new server."""
         
     | 
| 
      
 71 
     | 
    
         
            +
                    start_port = start_port or self.base_port
         
     | 
| 
      
 72 
     | 
    
         
            +
                    
         
     | 
| 
      
 73 
     | 
    
         
            +
                    for port in range(start_port, start_port + self.max_instances):
         
     | 
| 
      
 74 
     | 
    
         
            +
                        if not self.get_server_info(port):
         
     | 
| 
      
 75 
     | 
    
         
            +
                            return port
         
     | 
| 
      
 76 
     | 
    
         
            +
                    
         
     | 
| 
      
 77 
     | 
    
         
            +
                    raise RuntimeError(f"No available ports found in range {start_port}-{start_port + self.max_instances}")
         
     | 
| 
      
 78 
     | 
    
         
            +
                
         
     | 
| 
      
 79 
     | 
    
         
            +
                def start_server(self, port: int = None, server_id: str = None, 
         
     | 
| 
      
 80 
     | 
    
         
            +
                                host: str = "localhost") -> bool:
         
     | 
| 
      
 81 
     | 
    
         
            +
                    """Start a standalone Socket.IO server."""
         
     | 
| 
      
 82 
     | 
    
         
            +
                    
         
     | 
| 
      
 83 
     | 
    
         
            +
                    # Find available port if not specified
         
     | 
| 
      
 84 
     | 
    
         
            +
                    if port is None:
         
     | 
| 
      
 85 
     | 
    
         
            +
                        try:
         
     | 
| 
      
 86 
     | 
    
         
            +
                            port = self.find_available_port()
         
     | 
| 
      
 87 
     | 
    
         
            +
                        except RuntimeError as e:
         
     | 
| 
      
 88 
     | 
    
         
            +
                            print(f"Error: {e}")
         
     | 
| 
      
 89 
     | 
    
         
            +
                            return False
         
     | 
| 
      
 90 
     | 
    
         
            +
                    
         
     | 
| 
      
 91 
     | 
    
         
            +
                    # Check if server is already running on this port
         
     | 
| 
      
 92 
     | 
    
         
            +
                    if self.get_server_info(port):
         
     | 
| 
      
 93 
     | 
    
         
            +
                        print(f"Server already running on port {port}")
         
     | 
| 
      
 94 
     | 
    
         
            +
                        return False
         
     | 
| 
      
 95 
     | 
    
         
            +
                    
         
     | 
| 
      
 96 
     | 
    
         
            +
                    # Try different ways to start the server based on deployment
         
     | 
| 
      
 97 
     | 
    
         
            +
                    success = False
         
     | 
| 
      
 98 
     | 
    
         
            +
                    
         
     | 
| 
      
 99 
     | 
    
         
            +
                    # Method 1: Try installed claude-mpm package
         
     | 
| 
      
 100 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 101 
     | 
    
         
            +
                        cmd = [
         
     | 
| 
      
 102 
     | 
    
         
            +
                            sys.executable, "-m", "claude_mpm.services.standalone_socketio_server",
         
     | 
| 
      
 103 
     | 
    
         
            +
                            "--host", host,
         
     | 
| 
      
 104 
     | 
    
         
            +
                            "--port", str(port)
         
     | 
| 
      
 105 
     | 
    
         
            +
                        ]
         
     | 
| 
      
 106 
     | 
    
         
            +
                        if server_id:
         
     | 
| 
      
 107 
     | 
    
         
            +
                            cmd.extend(["--server-id", server_id])
         
     | 
| 
      
 108 
     | 
    
         
            +
                        
         
     | 
| 
      
 109 
     | 
    
         
            +
                        print(f"Starting server on {host}:{port} using installed package...")
         
     | 
| 
      
 110 
     | 
    
         
            +
                        
         
     | 
| 
      
 111 
     | 
    
         
            +
                        # Start in background
         
     | 
| 
      
 112 
     | 
    
         
            +
                        subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
         
     | 
| 
      
 113 
     | 
    
         
            +
                        
         
     | 
| 
      
 114 
     | 
    
         
            +
                        # Wait for server to start
         
     | 
| 
      
 115 
     | 
    
         
            +
                        for _ in range(10):  # Wait up to 10 seconds
         
     | 
| 
      
 116 
     | 
    
         
            +
                            time.sleep(1)
         
     | 
| 
      
 117 
     | 
    
         
            +
                            if self.get_server_info(port):
         
     | 
| 
      
 118 
     | 
    
         
            +
                                success = True
         
     | 
| 
      
 119 
     | 
    
         
            +
                                break
         
     | 
| 
      
 120 
     | 
    
         
            +
                                
         
     | 
| 
      
 121 
     | 
    
         
            +
                    except Exception as e:
         
     | 
| 
      
 122 
     | 
    
         
            +
                        print(f"Failed to start via installed package: {e}")
         
     | 
| 
      
 123 
     | 
    
         
            +
                    
         
     | 
| 
      
 124 
     | 
    
         
            +
                    # Method 2: Try local development mode
         
     | 
| 
      
 125 
     | 
    
         
            +
                    if not success:
         
     | 
| 
      
 126 
     | 
    
         
            +
                        try:
         
     | 
| 
      
 127 
     | 
    
         
            +
                            server_path = self.project_root / "src" / "claude_mpm" / "services" / "standalone_socketio_server.py"
         
     | 
| 
      
 128 
     | 
    
         
            +
                            if server_path.exists():
         
     | 
| 
      
 129 
     | 
    
         
            +
                                cmd = [
         
     | 
| 
      
 130 
     | 
    
         
            +
                                    sys.executable, str(server_path),
         
     | 
| 
      
 131 
     | 
    
         
            +
                                    "--host", host,
         
     | 
| 
      
 132 
     | 
    
         
            +
                                    "--port", str(port)
         
     | 
| 
      
 133 
     | 
    
         
            +
                                ]
         
     | 
| 
      
 134 
     | 
    
         
            +
                                if server_id:
         
     | 
| 
      
 135 
     | 
    
         
            +
                                    cmd.extend(["--server-id", server_id])
         
     | 
| 
      
 136 
     | 
    
         
            +
                                
         
     | 
| 
      
 137 
     | 
    
         
            +
                                print(f"Starting server using local development mode...")
         
     | 
| 
      
 138 
     | 
    
         
            +
                                
         
     | 
| 
      
 139 
     | 
    
         
            +
                                # Set PYTHONPATH for local development
         
     | 
| 
      
 140 
     | 
    
         
            +
                                env = os.environ.copy()
         
     | 
| 
      
 141 
     | 
    
         
            +
                                src_path = str(self.project_root / "src")
         
     | 
| 
      
 142 
     | 
    
         
            +
                                if "PYTHONPATH" in env:
         
     | 
| 
      
 143 
     | 
    
         
            +
                                    env["PYTHONPATH"] = f"{src_path}:{env['PYTHONPATH']}"
         
     | 
| 
      
 144 
     | 
    
         
            +
                                else:
         
     | 
| 
      
 145 
     | 
    
         
            +
                                    env["PYTHONPATH"] = src_path
         
     | 
| 
      
 146 
     | 
    
         
            +
                                
         
     | 
| 
      
 147 
     | 
    
         
            +
                                subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, env=env)
         
     | 
| 
      
 148 
     | 
    
         
            +
                                
         
     | 
| 
      
 149 
     | 
    
         
            +
                                # Wait for server to start
         
     | 
| 
      
 150 
     | 
    
         
            +
                                for _ in range(10):
         
     | 
| 
      
 151 
     | 
    
         
            +
                                    time.sleep(1)
         
     | 
| 
      
 152 
     | 
    
         
            +
                                    if self.get_server_info(port):
         
     | 
| 
      
 153 
     | 
    
         
            +
                                        success = True
         
     | 
| 
      
 154 
     | 
    
         
            +
                                        break
         
     | 
| 
      
 155 
     | 
    
         
            +
                                        
         
     | 
| 
      
 156 
     | 
    
         
            +
                        except Exception as e:
         
     | 
| 
      
 157 
     | 
    
         
            +
                            print(f"Failed to start in development mode: {e}")
         
     | 
| 
      
 158 
     | 
    
         
            +
                    
         
     | 
| 
      
 159 
     | 
    
         
            +
                    if success:
         
     | 
| 
      
 160 
     | 
    
         
            +
                        print(f"✅ Server started successfully on {host}:{port}")
         
     | 
| 
      
 161 
     | 
    
         
            +
                        return True
         
     | 
| 
      
 162 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 163 
     | 
    
         
            +
                        print(f"❌ Failed to start server on {host}:{port}")
         
     | 
| 
      
 164 
     | 
    
         
            +
                        return False
         
     | 
| 
      
 165 
     | 
    
         
            +
                
         
     | 
| 
      
 166 
     | 
    
         
            +
                def stop_server(self, port: int = None, server_id: str = None) -> bool:
         
     | 
| 
      
 167 
     | 
    
         
            +
                    """Stop a running Socket.IO server."""
         
     | 
| 
      
 168 
     | 
    
         
            +
                    
         
     | 
| 
      
 169 
     | 
    
         
            +
                    if port is None and server_id is None:
         
     | 
| 
      
 170 
     | 
    
         
            +
                        print("Must specify either port or server_id")
         
     | 
| 
      
 171 
     | 
    
         
            +
                        return False
         
     | 
| 
      
 172 
     | 
    
         
            +
                    
         
     | 
| 
      
 173 
     | 
    
         
            +
                    # Find server by ID if port not specified
         
     | 
| 
      
 174 
     | 
    
         
            +
                    if port is None:
         
     | 
| 
      
 175 
     | 
    
         
            +
                        running_servers = self.list_running_servers()
         
     | 
| 
      
 176 
     | 
    
         
            +
                        for server in running_servers:
         
     | 
| 
      
 177 
     | 
    
         
            +
                            if server.get('server_id') == server_id:
         
     | 
| 
      
 178 
     | 
    
         
            +
                                port = server['port']
         
     | 
| 
      
 179 
     | 
    
         
            +
                                break
         
     | 
| 
      
 180 
     | 
    
         
            +
                        
         
     | 
| 
      
 181 
     | 
    
         
            +
                        if port is None:
         
     | 
| 
      
 182 
     | 
    
         
            +
                            print(f"Server with ID '{server_id}' not found")
         
     | 
| 
      
 183 
     | 
    
         
            +
                            return False
         
     | 
| 
      
 184 
     | 
    
         
            +
                    
         
     | 
| 
      
 185 
     | 
    
         
            +
                    # Get server info
         
     | 
| 
      
 186 
     | 
    
         
            +
                    server_info = self.get_server_info(port)
         
     | 
| 
      
 187 
     | 
    
         
            +
                    if not server_info:
         
     | 
| 
      
 188 
     | 
    
         
            +
                        print(f"No server running on port {port}")
         
     | 
| 
      
 189 
     | 
    
         
            +
                        return False
         
     | 
| 
      
 190 
     | 
    
         
            +
                    
         
     | 
| 
      
 191 
     | 
    
         
            +
                    # Try to get PID and send termination signal
         
     | 
| 
      
 192 
     | 
    
         
            +
                    pid = server_info.get('pid')
         
     | 
| 
      
 193 
     | 
    
         
            +
                    if pid:
         
     | 
| 
      
 194 
     | 
    
         
            +
                        try:
         
     | 
| 
      
 195 
     | 
    
         
            +
                            os.kill(pid, signal.SIGTERM)
         
     | 
| 
      
 196 
     | 
    
         
            +
                            print(f"✅ Sent termination signal to server (PID: {pid})")
         
     | 
| 
      
 197 
     | 
    
         
            +
                            
         
     | 
| 
      
 198 
     | 
    
         
            +
                            # Wait for server to stop
         
     | 
| 
      
 199 
     | 
    
         
            +
                            for _ in range(10):
         
     | 
| 
      
 200 
     | 
    
         
            +
                                time.sleep(1)
         
     | 
| 
      
 201 
     | 
    
         
            +
                                if not self.get_server_info(port):
         
     | 
| 
      
 202 
     | 
    
         
            +
                                    print(f"✅ Server stopped successfully")
         
     | 
| 
      
 203 
     | 
    
         
            +
                                    return True
         
     | 
| 
      
 204 
     | 
    
         
            +
                            
         
     | 
| 
      
 205 
     | 
    
         
            +
                            # Force kill if still running
         
     | 
| 
      
 206 
     | 
    
         
            +
                            try:
         
     | 
| 
      
 207 
     | 
    
         
            +
                                os.kill(pid, signal.SIGKILL)
         
     | 
| 
      
 208 
     | 
    
         
            +
                                print(f"⚠️ Force killed server (PID: {pid})")
         
     | 
| 
      
 209 
     | 
    
         
            +
                                return True
         
     | 
| 
      
 210 
     | 
    
         
            +
                            except:
         
     | 
| 
      
 211 
     | 
    
         
            +
                                pass
         
     | 
| 
      
 212 
     | 
    
         
            +
                                
         
     | 
| 
      
 213 
     | 
    
         
            +
                        except OSError as e:
         
     | 
| 
      
 214 
     | 
    
         
            +
                            print(f"Error stopping server: {e}")
         
     | 
| 
      
 215 
     | 
    
         
            +
                    
         
     | 
| 
      
 216 
     | 
    
         
            +
                    print(f"❌ Failed to stop server on port {port}")
         
     | 
| 
      
 217 
     | 
    
         
            +
                    return False
         
     | 
| 
      
 218 
     | 
    
         
            +
                
         
     | 
| 
      
 219 
     | 
    
         
            +
                def restart_server(self, port: int = None, server_id: str = None) -> bool:
         
     | 
| 
      
 220 
     | 
    
         
            +
                    """Restart a Socket.IO server."""
         
     | 
| 
      
 221 
     | 
    
         
            +
                    
         
     | 
| 
      
 222 
     | 
    
         
            +
                    # Stop the server first
         
     | 
| 
      
 223 
     | 
    
         
            +
                    if self.stop_server(port, server_id):
         
     | 
| 
      
 224 
     | 
    
         
            +
                        time.sleep(2)  # Give it time to fully stop
         
     | 
| 
      
 225 
     | 
    
         
            +
                        
         
     | 
| 
      
 226 
     | 
    
         
            +
                        # Start it again
         
     | 
| 
      
 227 
     | 
    
         
            +
                        if port is None:
         
     | 
| 
      
 228 
     | 
    
         
            +
                            port = self.find_available_port()
         
     | 
| 
      
 229 
     | 
    
         
            +
                        
         
     | 
| 
      
 230 
     | 
    
         
            +
                        return self.start_server(port)
         
     | 
| 
      
 231 
     | 
    
         
            +
                    
         
     | 
| 
      
 232 
     | 
    
         
            +
                    return False
         
     | 
| 
      
 233 
     | 
    
         
            +
                
         
     | 
| 
      
 234 
     | 
    
         
            +
                def status(self, verbose: bool = False) -> None:
         
     | 
| 
      
 235 
     | 
    
         
            +
                    """Show status of all Socket.IO servers."""
         
     | 
| 
      
 236 
     | 
    
         
            +
                    running_servers = self.list_running_servers()
         
     | 
| 
      
 237 
     | 
    
         
            +
                    
         
     | 
| 
      
 238 
     | 
    
         
            +
                    if not running_servers:
         
     | 
| 
      
 239 
     | 
    
         
            +
                        print("No Socket.IO servers currently running")
         
     | 
| 
      
 240 
     | 
    
         
            +
                        return
         
     | 
| 
      
 241 
     | 
    
         
            +
                    
         
     | 
| 
      
 242 
     | 
    
         
            +
                    print(f"Found {len(running_servers)} running server(s):")
         
     | 
| 
      
 243 
     | 
    
         
            +
                    print()
         
     | 
| 
      
 244 
     | 
    
         
            +
                    
         
     | 
| 
      
 245 
     | 
    
         
            +
                    for server in running_servers:
         
     | 
| 
      
 246 
     | 
    
         
            +
                        port = server['port']
         
     | 
| 
      
 247 
     | 
    
         
            +
                        server_id = server.get('server_id', 'unknown')
         
     | 
| 
      
 248 
     | 
    
         
            +
                        version = server.get('server_version', 'unknown')
         
     | 
| 
      
 249 
     | 
    
         
            +
                        uptime = server.get('uptime_seconds', 0)
         
     | 
| 
      
 250 
     | 
    
         
            +
                        clients = server.get('clients_connected', 0)
         
     | 
| 
      
 251 
     | 
    
         
            +
                        
         
     | 
| 
      
 252 
     | 
    
         
            +
                        print(f"🖥️  Server ID: {server_id}")
         
     | 
| 
      
 253 
     | 
    
         
            +
                        print(f"   Port: {port}")
         
     | 
| 
      
 254 
     | 
    
         
            +
                        print(f"   Version: {version}")
         
     | 
| 
      
 255 
     | 
    
         
            +
                        print(f"   Uptime: {self._format_uptime(uptime)}")
         
     | 
| 
      
 256 
     | 
    
         
            +
                        print(f"   Clients: {clients}")
         
     | 
| 
      
 257 
     | 
    
         
            +
                        
         
     | 
| 
      
 258 
     | 
    
         
            +
                        if verbose:
         
     | 
| 
      
 259 
     | 
    
         
            +
                            print(f"   PID: {server.get('pid', 'unknown')}")
         
     | 
| 
      
 260 
     | 
    
         
            +
                            print(f"   Host: {server.get('host', 'unknown')}")
         
     | 
| 
      
 261 
     | 
    
         
            +
                            
         
     | 
| 
      
 262 
     | 
    
         
            +
                            # Get additional stats
         
     | 
| 
      
 263 
     | 
    
         
            +
                            stats = self._get_server_stats(port)
         
     | 
| 
      
 264 
     | 
    
         
            +
                            if stats:
         
     | 
| 
      
 265 
     | 
    
         
            +
                                events_processed = stats.get('events', {}).get('total_processed', 0)
         
     | 
| 
      
 266 
     | 
    
         
            +
                                clients_served = stats.get('connections', {}).get('total_served', 0)
         
     | 
| 
      
 267 
     | 
    
         
            +
                                print(f"   Events processed: {events_processed}")
         
     | 
| 
      
 268 
     | 
    
         
            +
                                print(f"   Total clients served: {clients_served}")
         
     | 
| 
      
 269 
     | 
    
         
            +
                        
         
     | 
| 
      
 270 
     | 
    
         
            +
                        print()
         
     | 
| 
      
 271 
     | 
    
         
            +
                
         
     | 
| 
      
 272 
     | 
    
         
            +
                def health_check(self, port: int = None) -> bool:
         
     | 
| 
      
 273 
     | 
    
         
            +
                    """Perform health check on server(s)."""
         
     | 
| 
      
 274 
     | 
    
         
            +
                    
         
     | 
| 
      
 275 
     | 
    
         
            +
                    if port:
         
     | 
| 
      
 276 
     | 
    
         
            +
                        # Check specific server
         
     | 
| 
      
 277 
     | 
    
         
            +
                        server_info = self.get_server_info(port)
         
     | 
| 
      
 278 
     | 
    
         
            +
                        if server_info:
         
     | 
| 
      
 279 
     | 
    
         
            +
                            status = server_info.get('status', 'unknown')
         
     | 
| 
      
 280 
     | 
    
         
            +
                            print(f"Server on port {port}: {status}")
         
     | 
| 
      
 281 
     | 
    
         
            +
                            return status == 'healthy'
         
     | 
| 
      
 282 
     | 
    
         
            +
                        else:
         
     | 
| 
      
 283 
     | 
    
         
            +
                            print(f"No server found on port {port}")
         
     | 
| 
      
 284 
     | 
    
         
            +
                            return False
         
     | 
| 
      
 285 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 286 
     | 
    
         
            +
                        # Check all servers
         
     | 
| 
      
 287 
     | 
    
         
            +
                        running_servers = self.list_running_servers()
         
     | 
| 
      
 288 
     | 
    
         
            +
                        if not running_servers:
         
     | 
| 
      
 289 
     | 
    
         
            +
                            print("No servers running")
         
     | 
| 
      
 290 
     | 
    
         
            +
                            return False
         
     | 
| 
      
 291 
     | 
    
         
            +
                        
         
     | 
| 
      
 292 
     | 
    
         
            +
                        all_healthy = True
         
     | 
| 
      
 293 
     | 
    
         
            +
                        for server in running_servers:
         
     | 
| 
      
 294 
     | 
    
         
            +
                            port = server['port']
         
     | 
| 
      
 295 
     | 
    
         
            +
                            status = server.get('status', 'unknown')
         
     | 
| 
      
 296 
     | 
    
         
            +
                            server_id = server.get('server_id', 'unknown')
         
     | 
| 
      
 297 
     | 
    
         
            +
                            print(f"Server {server_id} (port {port}): {status}")
         
     | 
| 
      
 298 
     | 
    
         
            +
                            if status != 'healthy':
         
     | 
| 
      
 299 
     | 
    
         
            +
                                all_healthy = False
         
     | 
| 
      
 300 
     | 
    
         
            +
                        
         
     | 
| 
      
 301 
     | 
    
         
            +
                        return all_healthy
         
     | 
| 
      
 302 
     | 
    
         
            +
                
         
     | 
| 
      
 303 
     | 
    
         
            +
                def install_dependencies(self) -> bool:
         
     | 
| 
      
 304 
     | 
    
         
            +
                    """Install required dependencies for Socket.IO server."""
         
     | 
| 
      
 305 
     | 
    
         
            +
                    dependencies = ['python-socketio>=5.11.0', 'aiohttp>=3.9.0', 'requests>=2.25.0']
         
     | 
| 
      
 306 
     | 
    
         
            +
                    
         
     | 
| 
      
 307 
     | 
    
         
            +
                    print("Installing Socket.IO server dependencies...")
         
     | 
| 
      
 308 
     | 
    
         
            +
                    
         
     | 
| 
      
 309 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 310 
     | 
    
         
            +
                        cmd = [sys.executable, '-m', 'pip', 'install'] + dependencies
         
     | 
| 
      
 311 
     | 
    
         
            +
                        result = subprocess.run(cmd, capture_output=True, text=True)
         
     | 
| 
      
 312 
     | 
    
         
            +
                        
         
     | 
| 
      
 313 
     | 
    
         
            +
                        if result.returncode == 0:
         
     | 
| 
      
 314 
     | 
    
         
            +
                            print("✅ Dependencies installed successfully")
         
     | 
| 
      
 315 
     | 
    
         
            +
                            return True
         
     | 
| 
      
 316 
     | 
    
         
            +
                        else:
         
     | 
| 
      
 317 
     | 
    
         
            +
                            print(f"❌ Failed to install dependencies: {result.stderr}")
         
     | 
| 
      
 318 
     | 
    
         
            +
                            return False
         
     | 
| 
      
 319 
     | 
    
         
            +
                            
         
     | 
| 
      
 320 
     | 
    
         
            +
                    except Exception as e:
         
     | 
| 
      
 321 
     | 
    
         
            +
                        print(f"❌ Error installing dependencies: {e}")
         
     | 
| 
      
 322 
     | 
    
         
            +
                        return False
         
     | 
| 
      
 323 
     | 
    
         
            +
                
         
     | 
| 
      
 324 
     | 
    
         
            +
                def _format_uptime(self, seconds: float) -> str:
         
     | 
| 
      
 325 
     | 
    
         
            +
                    """Format uptime in a human-readable way."""
         
     | 
| 
      
 326 
     | 
    
         
            +
                    if seconds < 60:
         
     | 
| 
      
 327 
     | 
    
         
            +
                        return f"{seconds:.1f}s"
         
     | 
| 
      
 328 
     | 
    
         
            +
                    elif seconds < 3600:
         
     | 
| 
      
 329 
     | 
    
         
            +
                        return f"{seconds/60:.1f}m"
         
     | 
| 
      
 330 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 331 
     | 
    
         
            +
                        return f"{seconds/3600:.1f}h"
         
     | 
| 
      
 332 
     | 
    
         
            +
                
         
     | 
| 
      
 333 
     | 
    
         
            +
                def _get_server_stats(self, port: int) -> Optional[Dict]:
         
     | 
| 
      
 334 
     | 
    
         
            +
                    """Get detailed server statistics."""
         
     | 
| 
      
 335 
     | 
    
         
            +
                    if not REQUESTS_AVAILABLE:
         
     | 
| 
      
 336 
     | 
    
         
            +
                        return None
         
     | 
| 
      
 337 
     | 
    
         
            +
                    
         
     | 
| 
      
 338 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 339 
     | 
    
         
            +
                        response = requests.get(f"http://localhost:{port}/stats", timeout=2.0)
         
     | 
| 
      
 340 
     | 
    
         
            +
                        if response.status_code == 200:
         
     | 
| 
      
 341 
     | 
    
         
            +
                            return response.json()
         
     | 
| 
      
 342 
     | 
    
         
            +
                    except Exception:
         
     | 
| 
      
 343 
     | 
    
         
            +
                        pass
         
     | 
| 
      
 344 
     | 
    
         
            +
                    return None
         
     | 
| 
      
 345 
     | 
    
         
            +
             
     | 
| 
      
 346 
     | 
    
         
            +
             
     | 
| 
      
 347 
     | 
    
         
            +
            def main():
         
     | 
| 
      
 348 
     | 
    
         
            +
                """Main CLI entry point."""
         
     | 
| 
      
 349 
     | 
    
         
            +
                parser = argparse.ArgumentParser(description="Socket.IO Server Manager")
         
     | 
| 
      
 350 
     | 
    
         
            +
                subparsers = parser.add_subparsers(dest='command', help='Available commands')
         
     | 
| 
      
 351 
     | 
    
         
            +
                
         
     | 
| 
      
 352 
     | 
    
         
            +
                # Start command
         
     | 
| 
      
 353 
     | 
    
         
            +
                start_parser = subparsers.add_parser('start', help='Start a Socket.IO server')
         
     | 
| 
      
 354 
     | 
    
         
            +
                start_parser.add_argument('--port', type=int, help='Port to bind to (auto-detect if not specified)')
         
     | 
| 
      
 355 
     | 
    
         
            +
                start_parser.add_argument('--host', default='localhost', help='Host to bind to')
         
     | 
| 
      
 356 
     | 
    
         
            +
                start_parser.add_argument('--server-id', help='Custom server ID')
         
     | 
| 
      
 357 
     | 
    
         
            +
                
         
     | 
| 
      
 358 
     | 
    
         
            +
                # Stop command
         
     | 
| 
      
 359 
     | 
    
         
            +
                stop_parser = subparsers.add_parser('stop', help='Stop a Socket.IO server')
         
     | 
| 
      
 360 
     | 
    
         
            +
                stop_parser.add_argument('--port', type=int, help='Port of server to stop')
         
     | 
| 
      
 361 
     | 
    
         
            +
                stop_parser.add_argument('--server-id', help='Server ID to stop')
         
     | 
| 
      
 362 
     | 
    
         
            +
                
         
     | 
| 
      
 363 
     | 
    
         
            +
                # Restart command
         
     | 
| 
      
 364 
     | 
    
         
            +
                restart_parser = subparsers.add_parser('restart', help='Restart a Socket.IO server')
         
     | 
| 
      
 365 
     | 
    
         
            +
                restart_parser.add_argument('--port', type=int, help='Port of server to restart')
         
     | 
| 
      
 366 
     | 
    
         
            +
                restart_parser.add_argument('--server-id', help='Server ID to restart')
         
     | 
| 
      
 367 
     | 
    
         
            +
                
         
     | 
| 
      
 368 
     | 
    
         
            +
                # Status command
         
     | 
| 
      
 369 
     | 
    
         
            +
                status_parser = subparsers.add_parser('status', help='Show server status')
         
     | 
| 
      
 370 
     | 
    
         
            +
                status_parser.add_argument('-v', '--verbose', action='store_true', help='Show detailed information')
         
     | 
| 
      
 371 
     | 
    
         
            +
                
         
     | 
| 
      
 372 
     | 
    
         
            +
                # Health check command
         
     | 
| 
      
 373 
     | 
    
         
            +
                health_parser = subparsers.add_parser('health', help='Perform health check')
         
     | 
| 
      
 374 
     | 
    
         
            +
                health_parser.add_argument('--port', type=int, help='Port to check (all servers if not specified)')
         
     | 
| 
      
 375 
     | 
    
         
            +
                
         
     | 
| 
      
 376 
     | 
    
         
            +
                # Install dependencies command
         
     | 
| 
      
 377 
     | 
    
         
            +
                subparsers.add_parser('install-deps', help='Install required dependencies')
         
     | 
| 
      
 378 
     | 
    
         
            +
                
         
     | 
| 
      
 379 
     | 
    
         
            +
                # List command
         
     | 
| 
      
 380 
     | 
    
         
            +
                subparsers.add_parser('list', help='List running servers')
         
     | 
| 
      
 381 
     | 
    
         
            +
                
         
     | 
| 
      
 382 
     | 
    
         
            +
                args = parser.parse_args()
         
     | 
| 
      
 383 
     | 
    
         
            +
                
         
     | 
| 
      
 384 
     | 
    
         
            +
                if not args.command:
         
     | 
| 
      
 385 
     | 
    
         
            +
                    parser.print_help()
         
     | 
| 
      
 386 
     | 
    
         
            +
                    return
         
     | 
| 
      
 387 
     | 
    
         
            +
                
         
     | 
| 
      
 388 
     | 
    
         
            +
                manager = ServerManager()
         
     | 
| 
      
 389 
     | 
    
         
            +
                
         
     | 
| 
      
 390 
     | 
    
         
            +
                if args.command == 'start':
         
     | 
| 
      
 391 
     | 
    
         
            +
                    success = manager.start_server(
         
     | 
| 
      
 392 
     | 
    
         
            +
                        port=args.port,
         
     | 
| 
      
 393 
     | 
    
         
            +
                        server_id=args.server_id,
         
     | 
| 
      
 394 
     | 
    
         
            +
                        host=args.host
         
     | 
| 
      
 395 
     | 
    
         
            +
                    )
         
     | 
| 
      
 396 
     | 
    
         
            +
                    sys.exit(0 if success else 1)
         
     | 
| 
      
 397 
     | 
    
         
            +
                
         
     | 
| 
      
 398 
     | 
    
         
            +
                elif args.command == 'stop':
         
     | 
| 
      
 399 
     | 
    
         
            +
                    success = manager.stop_server(
         
     | 
| 
      
 400 
     | 
    
         
            +
                        port=args.port,
         
     | 
| 
      
 401 
     | 
    
         
            +
                        server_id=args.server_id
         
     | 
| 
      
 402 
     | 
    
         
            +
                    )
         
     | 
| 
      
 403 
     | 
    
         
            +
                    sys.exit(0 if success else 1)
         
     | 
| 
      
 404 
     | 
    
         
            +
                
         
     | 
| 
      
 405 
     | 
    
         
            +
                elif args.command == 'restart':
         
     | 
| 
      
 406 
     | 
    
         
            +
                    success = manager.restart_server(
         
     | 
| 
      
 407 
     | 
    
         
            +
                        port=args.port,
         
     | 
| 
      
 408 
     | 
    
         
            +
                        server_id=args.server_id
         
     | 
| 
      
 409 
     | 
    
         
            +
                    )
         
     | 
| 
      
 410 
     | 
    
         
            +
                    sys.exit(0 if success else 1)
         
     | 
| 
      
 411 
     | 
    
         
            +
                
         
     | 
| 
      
 412 
     | 
    
         
            +
                elif args.command == 'status':
         
     | 
| 
      
 413 
     | 
    
         
            +
                    manager.status(verbose=args.verbose)
         
     | 
| 
      
 414 
     | 
    
         
            +
                
         
     | 
| 
      
 415 
     | 
    
         
            +
                elif args.command == 'health':
         
     | 
| 
      
 416 
     | 
    
         
            +
                    healthy = manager.health_check(port=args.port)
         
     | 
| 
      
 417 
     | 
    
         
            +
                    sys.exit(0 if healthy else 1)
         
     | 
| 
      
 418 
     | 
    
         
            +
                
         
     | 
| 
      
 419 
     | 
    
         
            +
                elif args.command == 'install-deps':
         
     | 
| 
      
 420 
     | 
    
         
            +
                    success = manager.install_dependencies()
         
     | 
| 
      
 421 
     | 
    
         
            +
                    sys.exit(0 if success else 1)
         
     | 
| 
      
 422 
     | 
    
         
            +
                
         
     | 
| 
      
 423 
     | 
    
         
            +
                elif args.command == 'list':
         
     | 
| 
      
 424 
     | 
    
         
            +
                    manager.status(verbose=False)
         
     | 
| 
      
 425 
     | 
    
         
            +
             
     | 
| 
      
 426 
     | 
    
         
            +
             
     | 
| 
      
 427 
     | 
    
         
            +
            if __name__ == "__main__":
         
     | 
| 
      
 428 
     | 
    
         
            +
                main()
         
     | 
    
        claude_mpm/services/__init__.py
    CHANGED
    
    | 
         @@ -2,9 +2,14 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            from .ticket_manager import TicketManager
         
     | 
| 
       4 
4 
     | 
    
         
             
            from .agent_deployment import AgentDeploymentService
         
     | 
| 
      
 5 
     | 
    
         
            +
            from .agent_memory_manager import AgentMemoryManager, get_memory_manager
         
     | 
| 
      
 6 
     | 
    
         
            +
            from .hook_service import HookService
         
     | 
| 
       5 
7 
     | 
    
         | 
| 
       6 
8 
     | 
    
         
             
            # Import other services as needed
         
     | 
| 
       7 
9 
     | 
    
         
             
            __all__ = [
         
     | 
| 
       8 
10 
     | 
    
         
             
                "TicketManager",
         
     | 
| 
       9 
11 
     | 
    
         
             
                "AgentDeploymentService",
         
     | 
| 
      
 12 
     | 
    
         
            +
                "AgentMemoryManager",
         
     | 
| 
      
 13 
     | 
    
         
            +
                "get_memory_manager",
         
     | 
| 
      
 14 
     | 
    
         
            +
                "HookService",
         
     | 
| 
       10 
15 
     | 
    
         
             
            ]
         
     | 
| 
         @@ -27,46 +27,97 @@ Created for ISS-0118: Agent Registry and Hierarchical Discovery System 
     | 
|
| 
       27 
27 
     | 
    
         
             
            import asyncio
         
     | 
| 
       28 
28 
     | 
    
         
             
            import logging
         
     | 
| 
       29 
29 
     | 
    
         
             
            import time
         
     | 
| 
      
 30 
     | 
    
         
            +
            from dataclasses import dataclass, field
         
     | 
| 
       30 
31 
     | 
    
         
             
            from datetime import datetime, timedelta
         
     | 
| 
      
 32 
     | 
    
         
            +
            from enum import Enum
         
     | 
| 
       31 
33 
     | 
    
         
             
            from pathlib import Path
         
     | 
| 
       32 
34 
     | 
    
         
             
            from typing import Dict, List, Optional, Set, Any, Tuple, Union
         
     | 
| 
       33 
35 
     | 
    
         | 
| 
       34 
36 
     | 
    
         
             
            from claude_mpm.services.shared_prompt_cache import SharedPromptCache
         
     | 
| 
       35 
     | 
    
         
            -
            from claude_mpm.services.agent_registry import AgentRegistry
         
     | 
| 
       36 
     | 
    
         
            -
            from claude_mpm.services.agent_modification_tracker import  
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
            from claude_mpm.models.lifecycle import (
         
     | 
| 
       41 
     | 
    
         
            -
                LifecycleOperation,
         
     | 
| 
       42 
     | 
    
         
            -
                LifecycleState,
         
     | 
| 
       43 
     | 
    
         
            -
                AgentLifecycleRecord,
         
     | 
| 
       44 
     | 
    
         
            -
                LifecycleOperationResult
         
     | 
| 
       45 
     | 
    
         
            -
            )
         
     | 
| 
       46 
     | 
    
         
            -
            from claude_mpm.models.modification import (
         
     | 
| 
       47 
     | 
    
         
            -
                AgentModification,
         
     | 
| 
       48 
     | 
    
         
            -
                ModificationType,
         
     | 
| 
      
 37 
     | 
    
         
            +
            from claude_mpm.services.agent_registry import AgentRegistry, AgentMetadata
         
     | 
| 
      
 38 
     | 
    
         
            +
            from claude_mpm.services.agent_modification_tracker import (
         
     | 
| 
      
 39 
     | 
    
         
            +
                AgentModificationTracker, 
         
     | 
| 
      
 40 
     | 
    
         
            +
                AgentModification, 
         
     | 
| 
      
 41 
     | 
    
         
            +
                ModificationType, 
         
     | 
| 
       49 
42 
     | 
    
         
             
                ModificationTier
         
     | 
| 
       50 
43 
     | 
    
         
             
            )
         
     | 
| 
       51 
     | 
    
         
            -
            from claude_mpm. 
     | 
| 
      
 44 
     | 
    
         
            +
            from claude_mpm.services.agent_persistence_service import (
         
     | 
| 
      
 45 
     | 
    
         
            +
                AgentPersistenceService,
         
     | 
| 
       52 
46 
     | 
    
         
             
                PersistenceStrategy,
         
     | 
| 
       53 
47 
     | 
    
         
             
                PersistenceRecord,
         
     | 
| 
       54 
48 
     | 
    
         
             
                PersistenceOperation
         
     | 
| 
       55 
49 
     | 
    
         
             
            )
         
     | 
| 
       56 
     | 
    
         
            -
            from claude_mpm. 
     | 
| 
      
 50 
     | 
    
         
            +
            from claude_mpm.services.agent_management_service import AgentManager
         
     | 
| 
      
 51 
     | 
    
         
            +
            from claude_mpm.models.agent_definition import AgentDefinition, AgentType
         
     | 
| 
       57 
52 
     | 
    
         
             
            from claude_mpm.core.base_service import BaseService
         
     | 
| 
       58 
53 
     | 
    
         
             
            from claude_mpm.utils.path_operations import path_ops
         
     | 
| 
       59 
54 
     | 
    
         
             
            from claude_mpm.utils.config_manager import ConfigurationManager
         
     | 
| 
       60 
55 
     | 
    
         | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
                 
     | 
| 
       65 
     | 
    
         
            -
                 
     | 
| 
       66 
     | 
    
         
            -
                 
     | 
| 
       67 
     | 
    
         
            -
                 
     | 
| 
       68 
     | 
    
         
            -
                 
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
            class LifecycleOperation(Enum):
         
     | 
| 
      
 58 
     | 
    
         
            +
                """Agent lifecycle operations."""
         
     | 
| 
      
 59 
     | 
    
         
            +
                CREATE = "create"
         
     | 
| 
      
 60 
     | 
    
         
            +
                UPDATE = "update"
         
     | 
| 
      
 61 
     | 
    
         
            +
                DELETE = "delete"
         
     | 
| 
      
 62 
     | 
    
         
            +
                RESTORE = "restore"
         
     | 
| 
      
 63 
     | 
    
         
            +
                MIGRATE = "migrate"
         
     | 
| 
      
 64 
     | 
    
         
            +
                REPLICATE = "replicate"
         
     | 
| 
      
 65 
     | 
    
         
            +
                VALIDATE = "validate"
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
            class LifecycleState(Enum):
         
     | 
| 
      
 69 
     | 
    
         
            +
                """Agent lifecycle states."""
         
     | 
| 
      
 70 
     | 
    
         
            +
                ACTIVE = "active"
         
     | 
| 
      
 71 
     | 
    
         
            +
                MODIFIED = "modified"
         
     | 
| 
      
 72 
     | 
    
         
            +
                DELETED = "deleted"
         
     | 
| 
      
 73 
     | 
    
         
            +
                CONFLICTED = "conflicted"
         
     | 
| 
      
 74 
     | 
    
         
            +
                MIGRATING = "migrating"
         
     | 
| 
      
 75 
     | 
    
         
            +
                VALIDATING = "validating"
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
            @dataclass
         
     | 
| 
      
 79 
     | 
    
         
            +
            class AgentLifecycleRecord:
         
     | 
| 
      
 80 
     | 
    
         
            +
                """Complete lifecycle record for an agent."""
         
     | 
| 
      
 81 
     | 
    
         
            +
                
         
     | 
| 
      
 82 
     | 
    
         
            +
                agent_name: str
         
     | 
| 
      
 83 
     | 
    
         
            +
                current_state: LifecycleState
         
     | 
| 
      
 84 
     | 
    
         
            +
                tier: ModificationTier
         
     | 
| 
      
 85 
     | 
    
         
            +
                file_path: str
         
     | 
| 
      
 86 
     | 
    
         
            +
                created_at: float
         
     | 
| 
      
 87 
     | 
    
         
            +
                last_modified: float
         
     | 
| 
      
 88 
     | 
    
         
            +
                version: str
         
     | 
| 
      
 89 
     | 
    
         
            +
                modifications: List[str] = field(default_factory=list)  # Modification IDs
         
     | 
| 
      
 90 
     | 
    
         
            +
                persistence_operations: List[str] = field(default_factory=list)  # Operation IDs
         
     | 
| 
      
 91 
     | 
    
         
            +
                backup_paths: List[str] = field(default_factory=list)
         
     | 
| 
      
 92 
     | 
    
         
            +
                validation_status: str = "valid"
         
     | 
| 
      
 93 
     | 
    
         
            +
                validation_errors: List[str] = field(default_factory=list)
         
     | 
| 
      
 94 
     | 
    
         
            +
                metadata: Dict[str, Any] = field(default_factory=dict)
         
     | 
| 
      
 95 
     | 
    
         
            +
                
         
     | 
| 
      
 96 
     | 
    
         
            +
                @property
         
     | 
| 
      
 97 
     | 
    
         
            +
                def age_days(self) -> float:
         
     | 
| 
      
 98 
     | 
    
         
            +
                    """Get age in days."""
         
     | 
| 
      
 99 
     | 
    
         
            +
                    return (time.time() - self.created_at) / (24 * 3600)
         
     | 
| 
      
 100 
     | 
    
         
            +
                
         
     | 
| 
      
 101 
     | 
    
         
            +
                @property
         
     | 
| 
      
 102 
     | 
    
         
            +
                def last_modified_datetime(self) -> datetime:
         
     | 
| 
      
 103 
     | 
    
         
            +
                    """Get last modified as datetime."""
         
     | 
| 
      
 104 
     | 
    
         
            +
                    return datetime.fromtimestamp(self.last_modified)
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
            @dataclass
         
     | 
| 
      
 108 
     | 
    
         
            +
            class LifecycleOperationResult:
         
     | 
| 
      
 109 
     | 
    
         
            +
                """Result of a lifecycle operation."""
         
     | 
| 
      
 110 
     | 
    
         
            +
                
         
     | 
| 
      
 111 
     | 
    
         
            +
                operation: LifecycleOperation
         
     | 
| 
      
 112 
     | 
    
         
            +
                agent_name: str
         
     | 
| 
      
 113 
     | 
    
         
            +
                success: bool
         
     | 
| 
      
 114 
     | 
    
         
            +
                duration_ms: float
         
     | 
| 
      
 115 
     | 
    
         
            +
                error_message: Optional[str] = None
         
     | 
| 
      
 116 
     | 
    
         
            +
                modification_id: Optional[str] = None
         
     | 
| 
      
 117 
     | 
    
         
            +
                persistence_id: Optional[str] = None
         
     | 
| 
      
 118 
     | 
    
         
            +
                cache_invalidated: bool = False
         
     | 
| 
      
 119 
     | 
    
         
            +
                registry_updated: bool = False
         
     | 
| 
      
 120 
     | 
    
         
            +
                metadata: Dict[str, Any] = field(default_factory=dict)
         
     | 
| 
       70 
121 
     | 
    
         | 
| 
       71 
122 
     | 
    
         | 
| 
       72 
123 
     | 
    
         
             
            class AgentLifecycleManager(BaseService):
         
     |