claude-mpm 3.7.4__py3-none-any.whl → 3.8.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/VERSION +1 -1
- claude_mpm/agents/BASE_PM.md +0 -106
- claude_mpm/agents/INSTRUCTIONS.md +0 -78
- claude_mpm/agents/MEMORY.md +88 -0
- claude_mpm/agents/WORKFLOW.md +86 -0
- claude_mpm/agents/schema/agent_schema.json +1 -1
- claude_mpm/agents/templates/code_analyzer.json +26 -11
- claude_mpm/agents/templates/data_engineer.json +4 -7
- claude_mpm/agents/templates/documentation.json +2 -2
- claude_mpm/agents/templates/engineer.json +2 -2
- claude_mpm/agents/templates/ops.json +3 -8
- claude_mpm/agents/templates/qa.json +2 -3
- claude_mpm/agents/templates/research.json +2 -3
- claude_mpm/agents/templates/security.json +3 -6
- claude_mpm/agents/templates/ticketing.json +4 -9
- claude_mpm/agents/templates/version_control.json +3 -3
- claude_mpm/agents/templates/web_qa.json +4 -4
- claude_mpm/agents/templates/web_ui.json +4 -4
- claude_mpm/cli/__init__.py +2 -2
- claude_mpm/cli/commands/__init__.py +2 -1
- claude_mpm/cli/commands/agents.py +118 -1
- claude_mpm/cli/commands/tickets.py +596 -19
- claude_mpm/cli/parser.py +228 -5
- claude_mpm/config/__init__.py +30 -39
- claude_mpm/config/socketio_config.py +8 -5
- claude_mpm/constants.py +13 -0
- claude_mpm/core/__init__.py +8 -18
- claude_mpm/core/cache.py +596 -0
- claude_mpm/core/claude_runner.py +166 -622
- claude_mpm/core/config.py +5 -1
- claude_mpm/core/constants.py +339 -0
- claude_mpm/core/container.py +461 -22
- claude_mpm/core/exceptions.py +392 -0
- claude_mpm/core/framework_loader.py +208 -93
- claude_mpm/core/interactive_session.py +432 -0
- claude_mpm/core/interfaces.py +424 -0
- claude_mpm/core/lazy.py +467 -0
- claude_mpm/core/logging_config.py +444 -0
- claude_mpm/core/oneshot_session.py +465 -0
- claude_mpm/core/optimized_agent_loader.py +485 -0
- claude_mpm/core/optimized_startup.py +490 -0
- claude_mpm/core/service_registry.py +52 -26
- claude_mpm/core/socketio_pool.py +162 -5
- claude_mpm/core/types.py +292 -0
- claude_mpm/core/typing_utils.py +477 -0
- claude_mpm/dashboard/static/js/components/file-tool-tracker.js +46 -2
- claude_mpm/dashboard/templates/index.html +5 -5
- claude_mpm/hooks/claude_hooks/hook_handler.py +213 -99
- claude_mpm/init.py +2 -1
- claude_mpm/services/__init__.py +78 -14
- claude_mpm/services/agent/__init__.py +24 -0
- claude_mpm/services/agent/deployment.py +2548 -0
- claude_mpm/services/agent/management.py +598 -0
- claude_mpm/services/agent/registry.py +813 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +592 -269
- claude_mpm/services/agents/deployment/async_agent_deployment.py +5 -1
- claude_mpm/services/agents/management/agent_capabilities_generator.py +21 -11
- claude_mpm/services/agents/memory/agent_memory_manager.py +156 -1
- claude_mpm/services/async_session_logger.py +8 -3
- claude_mpm/services/communication/__init__.py +21 -0
- claude_mpm/services/communication/socketio.py +1933 -0
- claude_mpm/services/communication/websocket.py +479 -0
- claude_mpm/services/core/__init__.py +123 -0
- claude_mpm/services/core/base.py +247 -0
- claude_mpm/services/core/interfaces.py +951 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +23 -23
- claude_mpm/services/framework_claude_md_generator.py +3 -2
- claude_mpm/services/health_monitor.py +4 -3
- claude_mpm/services/hook_service.py +64 -4
- claude_mpm/services/infrastructure/__init__.py +21 -0
- claude_mpm/services/infrastructure/logging.py +202 -0
- claude_mpm/services/infrastructure/monitoring.py +893 -0
- claude_mpm/services/memory/indexed_memory.py +648 -0
- claude_mpm/services/project/__init__.py +21 -0
- claude_mpm/services/project/analyzer.py +864 -0
- claude_mpm/services/project/registry.py +608 -0
- claude_mpm/services/project_analyzer.py +95 -2
- claude_mpm/services/recovery_manager.py +15 -9
- claude_mpm/services/socketio/__init__.py +25 -0
- claude_mpm/services/socketio/handlers/__init__.py +25 -0
- claude_mpm/services/socketio/handlers/base.py +121 -0
- claude_mpm/services/socketio/handlers/connection.py +198 -0
- claude_mpm/services/socketio/handlers/file.py +213 -0
- claude_mpm/services/socketio/handlers/git.py +723 -0
- claude_mpm/services/socketio/handlers/memory.py +27 -0
- claude_mpm/services/socketio/handlers/project.py +25 -0
- claude_mpm/services/socketio/handlers/registry.py +145 -0
- claude_mpm/services/socketio_client_manager.py +12 -7
- claude_mpm/services/socketio_server.py +156 -30
- claude_mpm/services/ticket_manager.py +377 -51
- claude_mpm/utils/agent_dependency_loader.py +66 -15
- claude_mpm/utils/error_handler.py +1 -1
- claude_mpm/utils/robust_installer.py +587 -0
- claude_mpm/validation/agent_validator.py +27 -14
- claude_mpm/validation/frontmatter_validator.py +231 -0
- {claude_mpm-3.7.4.dist-info → claude_mpm-3.8.1.dist-info}/METADATA +74 -41
- {claude_mpm-3.7.4.dist-info → claude_mpm-3.8.1.dist-info}/RECORD +101 -76
- claude_mpm/.claude-mpm/logs/hooks_20250728.log +0 -10
- claude_mpm/agents/agent-template.yaml +0 -83
- claude_mpm/cli/README.md +0 -108
- claude_mpm/cli_module/refactoring_guide.md +0 -253
- claude_mpm/config/async_logging_config.yaml +0 -145
- claude_mpm/core/.claude-mpm/logs/hooks_20250730.log +0 -34
- claude_mpm/dashboard/.claude-mpm/memories/README.md +0 -36
- claude_mpm/dashboard/README.md +0 -121
- claude_mpm/dashboard/static/js/dashboard.js.backup +0 -1973
- claude_mpm/dashboard/templates/.claude-mpm/memories/README.md +0 -36
- claude_mpm/dashboard/templates/.claude-mpm/memories/engineer_agent.md +0 -39
- claude_mpm/dashboard/templates/.claude-mpm/memories/version_control_agent.md +0 -38
- claude_mpm/hooks/README.md +0 -96
- claude_mpm/schemas/agent_schema.json +0 -435
- claude_mpm/services/framework_claude_md_generator/README.md +0 -92
- claude_mpm/services/version_control/VERSION +0 -1
- {claude_mpm-3.7.4.dist-info → claude_mpm-3.8.1.dist-info}/WHEEL +0 -0
- {claude_mpm-3.7.4.dist-info → claude_mpm-3.8.1.dist-info}/entry_points.txt +0 -0
- {claude_mpm-3.7.4.dist-info → claude_mpm-3.8.1.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-3.7.4.dist-info → claude_mpm-3.8.1.dist-info}/top_level.txt +0 -0
    
        claude_mpm/core/container.py
    CHANGED
    
    | @@ -28,7 +28,7 @@ class ServiceLifetime(Enum): | |
| 28 28 | 
             
                """Service lifetime options."""
         | 
| 29 29 | 
             
                SINGLETON = "singleton"  # One instance per container
         | 
| 30 30 | 
             
                TRANSIENT = "transient"  # New instance per request
         | 
| 31 | 
            -
                SCOPED = "scoped"       # One instance per scope | 
| 31 | 
            +
                SCOPED = "scoped"       # One instance per scope
         | 
| 32 32 |  | 
| 33 33 |  | 
| 34 34 | 
             
            class ServiceRegistration:
         | 
| @@ -88,19 +88,90 @@ class ServiceNotFoundError(Exception): | |
| 88 88 | 
             
                pass
         | 
| 89 89 |  | 
| 90 90 |  | 
| 91 | 
            +
            class ServiceScope:
         | 
| 92 | 
            +
                """
         | 
| 93 | 
            +
                Represents a service scope for scoped lifetime services.
         | 
| 94 | 
            +
                
         | 
| 95 | 
            +
                Scoped services live for the duration of the scope and are shared
         | 
| 96 | 
            +
                within that scope.
         | 
| 97 | 
            +
                """
         | 
| 98 | 
            +
                
         | 
| 99 | 
            +
                def __init__(self, container: 'DIContainer'):
         | 
| 100 | 
            +
                    """Initialize service scope."""
         | 
| 101 | 
            +
                    self._container = container
         | 
| 102 | 
            +
                    self._scoped_instances: Dict[Type, Any] = {}
         | 
| 103 | 
            +
                    self._disposed = False
         | 
| 104 | 
            +
                    self._lock = threading.Lock()
         | 
| 105 | 
            +
                    
         | 
| 106 | 
            +
                def get_scoped_instance(self, service_type: Type) -> Optional[Any]:
         | 
| 107 | 
            +
                    """Get scoped instance if exists."""
         | 
| 108 | 
            +
                    with self._lock:
         | 
| 109 | 
            +
                        return self._scoped_instances.get(service_type)
         | 
| 110 | 
            +
                        
         | 
| 111 | 
            +
                def set_scoped_instance(self, service_type: Type, instance: Any) -> None:
         | 
| 112 | 
            +
                    """Store scoped instance."""
         | 
| 113 | 
            +
                    with self._lock:
         | 
| 114 | 
            +
                        if not self._disposed:
         | 
| 115 | 
            +
                            self._scoped_instances[service_type] = instance
         | 
| 116 | 
            +
                            
         | 
| 117 | 
            +
                def dispose(self) -> None:
         | 
| 118 | 
            +
                    """
         | 
| 119 | 
            +
                    Dispose of the scope and all scoped instances.
         | 
| 120 | 
            +
                    
         | 
| 121 | 
            +
                    Calls dispose() on instances that implement IDisposable.
         | 
| 122 | 
            +
                    """
         | 
| 123 | 
            +
                    with self._lock:
         | 
| 124 | 
            +
                        if self._disposed:
         | 
| 125 | 
            +
                            return
         | 
| 126 | 
            +
                            
         | 
| 127 | 
            +
                        for service_type, instance in self._scoped_instances.items():
         | 
| 128 | 
            +
                            # Call dispose if available
         | 
| 129 | 
            +
                            if hasattr(instance, 'dispose'):
         | 
| 130 | 
            +
                                try:
         | 
| 131 | 
            +
                                    instance.dispose()
         | 
| 132 | 
            +
                                except Exception as e:
         | 
| 133 | 
            +
                                    logger.error(f"Error disposing scoped service {service_type}: {e}")
         | 
| 134 | 
            +
                                    
         | 
| 135 | 
            +
                        self._scoped_instances.clear()
         | 
| 136 | 
            +
                        self._disposed = True
         | 
| 137 | 
            +
                        
         | 
| 138 | 
            +
                def __enter__(self):
         | 
| 139 | 
            +
                    """Context manager entry."""
         | 
| 140 | 
            +
                    return self
         | 
| 141 | 
            +
                    
         | 
| 142 | 
            +
                def __exit__(self, exc_type, exc_val, exc_tb):
         | 
| 143 | 
            +
                    """Context manager exit - dispose scope."""
         | 
| 144 | 
            +
                    self.dispose()
         | 
| 145 | 
            +
             | 
| 146 | 
            +
             | 
| 91 147 | 
             
            class DIContainer:
         | 
| 92 148 | 
             
                """
         | 
| 93 | 
            -
                 | 
| 149 | 
            +
                Enhanced Dependency Injection Container.
         | 
| 94 150 |  | 
| 95 | 
            -
                Provides | 
| 151 | 
            +
                Provides:
         | 
| 152 | 
            +
                - Service registration with multiple lifetime options
         | 
| 153 | 
            +
                - Automatic constructor injection
         | 
| 154 | 
            +
                - Interface to implementation mapping
         | 
| 155 | 
            +
                - Circular dependency detection
         | 
| 156 | 
            +
                - Lazy loading support
         | 
| 157 | 
            +
                - Scoped service support
         | 
| 158 | 
            +
                - Service disposal lifecycle
         | 
| 159 | 
            +
                - Named registrations
         | 
| 160 | 
            +
                - Configuration injection
         | 
| 96 161 | 
             
                """
         | 
| 97 162 |  | 
| 98 163 | 
             
                def __init__(self):
         | 
| 99 164 | 
             
                    """Initialize the DI container."""
         | 
| 100 165 | 
             
                    self._registrations: Dict[Type, ServiceRegistration] = {}
         | 
| 166 | 
            +
                    self._named_registrations: Dict[str, ServiceRegistration] = {}
         | 
| 167 | 
            +
                    self._factories: Dict[Type, Callable] = {}
         | 
| 101 168 | 
             
                    self._singletons: Dict[Type, Any] = {}
         | 
| 169 | 
            +
                    self._scopes: List[ServiceScope] = []
         | 
| 170 | 
            +
                    self._initialization_order: List[Type] = []
         | 
| 171 | 
            +
                    self._disposal_handlers: Dict[Type, Callable] = {}
         | 
| 102 172 | 
             
                    self._lock = threading.RLock()
         | 
| 103 173 | 
             
                    self._resolving: Set[Type] = set()
         | 
| 174 | 
            +
                    self._current_scope: Optional[ServiceScope] = None
         | 
| 104 175 |  | 
| 105 176 | 
             
                def register(
         | 
| 106 177 | 
             
                    self,
         | 
| @@ -152,20 +223,108 @@ class DIContainer: | |
| 152 223 |  | 
| 153 224 | 
             
                def register_singleton(
         | 
| 154 225 | 
             
                    self,
         | 
| 155 | 
            -
                     | 
| 156 | 
            -
                    implementation: Optional[Union[Type[T], T]] = None
         | 
| 226 | 
            +
                    interface: Type[T],
         | 
| 227 | 
            +
                    implementation: Optional[Union[Type[T], T]] = None,
         | 
| 228 | 
            +
                    instance: Optional[T] = None,
         | 
| 229 | 
            +
                    name: Optional[str] = None,
         | 
| 230 | 
            +
                    dispose_handler: Optional[Callable[[T], None]] = None
         | 
| 157 231 | 
             
                ) -> None:
         | 
| 158 232 | 
             
                    """
         | 
| 159 233 | 
             
                    Register a singleton service.
         | 
| 160 234 |  | 
| 161 | 
            -
                     | 
| 235 | 
            +
                    Args:
         | 
| 236 | 
            +
                        interface: The interface/base type to register
         | 
| 237 | 
            +
                        implementation: The concrete implementation class
         | 
| 238 | 
            +
                        instance: Pre-created instance (alternative to implementation)
         | 
| 239 | 
            +
                        name: Optional name for named registration
         | 
| 240 | 
            +
                        dispose_handler: Optional handler called when disposing service
         | 
| 241 | 
            +
                    
         | 
| 242 | 
            +
                    Examples:
         | 
| 243 | 
            +
                        # Register with implementation class
         | 
| 244 | 
            +
                        container.register_singleton(ILogger, ConsoleLogger)
         | 
| 245 | 
            +
                        
         | 
| 246 | 
            +
                        # Register with instance
         | 
| 247 | 
            +
                        container.register_singleton(IConfig, instance=Config())
         | 
| 248 | 
            +
                        
         | 
| 249 | 
            +
                        # Register with disposal handler
         | 
| 250 | 
            +
                        container.register_singleton(
         | 
| 251 | 
            +
                            IDatabase,
         | 
| 252 | 
            +
                            DatabaseConnection,
         | 
| 253 | 
            +
                            dispose_handler=lambda db: db.close()
         | 
| 254 | 
            +
                        )
         | 
| 162 255 | 
             
                    """
         | 
| 163 | 
            -
                     | 
| 164 | 
            -
             | 
| 165 | 
            -
                         | 
| 256 | 
            +
                    # For named registrations, create a unique registration
         | 
| 257 | 
            +
                    if name:
         | 
| 258 | 
            +
                        # Create named registration directly
         | 
| 259 | 
            +
                        registration = ServiceRegistration(
         | 
| 260 | 
            +
                            service_type=interface,
         | 
| 261 | 
            +
                            implementation=implementation,
         | 
| 262 | 
            +
                            instance=instance,
         | 
| 263 | 
            +
                            lifetime=ServiceLifetime.SINGLETON
         | 
| 264 | 
            +
                        )
         | 
| 265 | 
            +
                        self._named_registrations[name] = registration
         | 
| 266 | 
            +
                        
         | 
| 267 | 
            +
                        # Also store the instance if provided
         | 
| 268 | 
            +
                        if instance is not None:
         | 
| 269 | 
            +
                            # Store with a composite key for retrieval
         | 
| 270 | 
            +
                            named_key = (interface, name)
         | 
| 271 | 
            +
                            if not hasattr(self, '_named_singletons'):
         | 
| 272 | 
            +
                                self._named_singletons = {}
         | 
| 273 | 
            +
                            self._named_singletons[named_key] = instance
         | 
| 166 274 | 
             
                    else:
         | 
| 167 | 
            -
                         | 
| 275 | 
            +
                        # Normal registration without name
         | 
| 276 | 
            +
                        if instance is not None:
         | 
| 277 | 
            +
                            self.register(interface, instance=instance)
         | 
| 278 | 
            +
                        elif implementation is not None and not inspect.isclass(implementation):
         | 
| 279 | 
            +
                            # It's an instance passed as implementation (backward compatibility)
         | 
| 280 | 
            +
                            self.register(interface, instance=implementation)
         | 
| 281 | 
            +
                        else:
         | 
| 282 | 
            +
                            self.register(interface, implementation, lifetime=ServiceLifetime.SINGLETON)
         | 
| 283 | 
            +
                            
         | 
| 284 | 
            +
                    # Handle disposal handler
         | 
| 285 | 
            +
                    if dispose_handler:
         | 
| 286 | 
            +
                        if name:
         | 
| 287 | 
            +
                            # Store with composite key for named services
         | 
| 288 | 
            +
                            if not hasattr(self, '_named_disposal_handlers'):
         | 
| 289 | 
            +
                                self._named_disposal_handlers = {}
         | 
| 290 | 
            +
                            self._named_disposal_handlers[(interface, name)] = dispose_handler
         | 
| 291 | 
            +
                        else:
         | 
| 292 | 
            +
                            self._disposal_handlers[interface] = dispose_handler
         | 
| 293 | 
            +
                        
         | 
| 294 | 
            +
                    # Track initialization order for proper disposal
         | 
| 295 | 
            +
                    key = (interface, name) if name else interface
         | 
| 296 | 
            +
                    if key not in self._initialization_order:
         | 
| 297 | 
            +
                        self._initialization_order.append(key)
         | 
| 298 | 
            +
                        
         | 
| 299 | 
            +
                def register_scoped(
         | 
| 300 | 
            +
                    self,
         | 
| 301 | 
            +
                    interface: Type[T],
         | 
| 302 | 
            +
                    implementation: Optional[Type[T]] = None,
         | 
| 303 | 
            +
                    name: Optional[str] = None
         | 
| 304 | 
            +
                ) -> None:
         | 
| 305 | 
            +
                    """
         | 
| 306 | 
            +
                    Register a scoped service.
         | 
| 307 | 
            +
                    
         | 
| 308 | 
            +
                    Scoped services are created once per scope and shared within that scope.
         | 
| 309 | 
            +
                    
         | 
| 310 | 
            +
                    Args:
         | 
| 311 | 
            +
                        interface: The interface/base type to register
         | 
| 312 | 
            +
                        implementation: The concrete implementation
         | 
| 313 | 
            +
                        name: Optional name for named registration
         | 
| 168 314 |  | 
| 315 | 
            +
                    Examples:
         | 
| 316 | 
            +
                        # Register scoped service
         | 
| 317 | 
            +
                        container.register_scoped(IRequestContext, RequestContext)
         | 
| 318 | 
            +
                        
         | 
| 319 | 
            +
                        # Use in scope
         | 
| 320 | 
            +
                        with container.create_scope() as scope:
         | 
| 321 | 
            +
                            context = container.get(IRequestContext)  # Created
         | 
| 322 | 
            +
                            context2 = container.get(IRequestContext)  # Same instance
         | 
| 323 | 
            +
                    """
         | 
| 324 | 
            +
                    self.register(interface, implementation, lifetime=ServiceLifetime.SCOPED)
         | 
| 325 | 
            +
                    if name:
         | 
| 326 | 
            +
                        self._named_registrations[name] = self._registrations[interface]
         | 
| 327 | 
            +
                
         | 
| 169 328 | 
             
                def register_transient(
         | 
| 170 329 | 
             
                    self,
         | 
| 171 330 | 
             
                    service_type: Type[T],
         | 
| @@ -180,20 +339,90 @@ class DIContainer: | |
| 180 339 |  | 
| 181 340 | 
             
                def register_factory(
         | 
| 182 341 | 
             
                    self,
         | 
| 183 | 
            -
                     | 
| 342 | 
            +
                    interface: Type[T],
         | 
| 184 343 | 
             
                    factory: Callable[['DIContainer'], T],
         | 
| 185 | 
            -
                    lifetime: ServiceLifetime = ServiceLifetime. | 
| 344 | 
            +
                    lifetime: ServiceLifetime = ServiceLifetime.TRANSIENT,
         | 
| 345 | 
            +
                    name: Optional[str] = None
         | 
| 186 346 | 
             
                ) -> None:
         | 
| 187 347 | 
             
                    """
         | 
| 188 348 | 
             
                    Register a service with a factory function.
         | 
| 189 349 |  | 
| 190 350 | 
             
                    The factory receives the container as parameter for resolving dependencies.
         | 
| 351 | 
            +
                    
         | 
| 352 | 
            +
                    Args:
         | 
| 353 | 
            +
                        interface: The interface/base type to register
         | 
| 354 | 
            +
                        factory: Factory function that creates instances
         | 
| 355 | 
            +
                        lifetime: Service lifetime (default: TRANSIENT for factories)
         | 
| 356 | 
            +
                        name: Optional name for named registration
         | 
| 357 | 
            +
                        
         | 
| 358 | 
            +
                    Examples:
         | 
| 359 | 
            +
                        # Register with factory
         | 
| 360 | 
            +
                        container.register_factory(
         | 
| 361 | 
            +
                            IService,
         | 
| 362 | 
            +
                            lambda c: Service(c.get(ILogger), c.get(IConfig)),
         | 
| 363 | 
            +
                            lifetime=ServiceLifetime.SINGLETON
         | 
| 364 | 
            +
                        )
         | 
| 365 | 
            +
                    """
         | 
| 366 | 
            +
                    self.register(interface, factory=factory, lifetime=lifetime)
         | 
| 367 | 
            +
                    self._factories[interface] = factory
         | 
| 368 | 
            +
                    if name:
         | 
| 369 | 
            +
                        self._named_registrations[name] = self._registrations[interface]
         | 
| 370 | 
            +
                    
         | 
| 371 | 
            +
                def get(self, interface: Type[T], name: Optional[str] = None) -> T:
         | 
| 372 | 
            +
                    """
         | 
| 373 | 
            +
                    Get service instance with dependency resolution.
         | 
| 374 | 
            +
                    
         | 
| 375 | 
            +
                    This is the primary method for retrieving services from the container.
         | 
| 376 | 
            +
                    It handles all lifetime management and dependency resolution.
         | 
| 377 | 
            +
                    
         | 
| 378 | 
            +
                    Args:
         | 
| 379 | 
            +
                        interface: The type to resolve
         | 
| 380 | 
            +
                        name: Optional name for named registration lookup
         | 
| 381 | 
            +
                        
         | 
| 382 | 
            +
                    Returns:
         | 
| 383 | 
            +
                        Instance of the requested service
         | 
| 384 | 
            +
                        
         | 
| 385 | 
            +
                    Raises:
         | 
| 386 | 
            +
                        ServiceNotFoundError: If service is not registered
         | 
| 387 | 
            +
                        CircularDependencyError: If circular dependencies detected
         | 
| 388 | 
            +
                        
         | 
| 389 | 
            +
                    Examples:
         | 
| 390 | 
            +
                        # Get by interface
         | 
| 391 | 
            +
                        logger = container.get(ILogger)
         | 
| 392 | 
            +
                        
         | 
| 393 | 
            +
                        # Get named service
         | 
| 394 | 
            +
                        primary_db = container.get(IDatabase, name="primary")
         | 
| 191 395 | 
             
                    """
         | 
| 192 | 
            -
                     | 
| 396 | 
            +
                    if name:
         | 
| 397 | 
            +
                        if name not in self._named_registrations:
         | 
| 398 | 
            +
                            suggestions = self._get_similar_names(name)
         | 
| 399 | 
            +
                            raise ServiceNotFoundError(
         | 
| 400 | 
            +
                                f"Named service '{name}' is not registered. "
         | 
| 401 | 
            +
                                f"Did you mean: {', '.join(suggestions)}?" if suggestions else ""
         | 
| 402 | 
            +
                            )
         | 
| 403 | 
            +
                        
         | 
| 404 | 
            +
                        # Check if we have a pre-stored instance for this named service
         | 
| 405 | 
            +
                        named_key = (interface, name)
         | 
| 406 | 
            +
                        if hasattr(self, '_named_singletons') and named_key in self._named_singletons:
         | 
| 407 | 
            +
                            return self._named_singletons[named_key]
         | 
| 408 | 
            +
                            
         | 
| 409 | 
            +
                        # Otherwise resolve from named registration
         | 
| 410 | 
            +
                        registration = self._named_registrations[name]
         | 
| 411 | 
            +
                        if registration.instance is not None:
         | 
| 412 | 
            +
                            return registration.instance
         | 
| 413 | 
            +
                        elif registration.factory:
         | 
| 414 | 
            +
                            return registration.factory(self)
         | 
| 415 | 
            +
                        else:
         | 
| 416 | 
            +
                            return self.create_instance(registration.implementation, registration.dependencies)
         | 
| 417 | 
            +
                                
         | 
| 418 | 
            +
                    return self._resolve_internal(interface)
         | 
| 193 419 |  | 
| 194 420 | 
             
                def resolve(self, service_type: Type[T]) -> T:
         | 
| 195 421 | 
             
                    """
         | 
| 196 | 
            -
                    Resolve a service from the container.
         | 
| 422 | 
            +
                    Resolve a service from the container (legacy method).
         | 
| 423 | 
            +
                    
         | 
| 424 | 
            +
                    This method is maintained for backward compatibility.
         | 
| 425 | 
            +
                    New code should use get() instead.
         | 
| 197 426 |  | 
| 198 427 | 
             
                    Args:
         | 
| 199 428 | 
             
                        service_type: The type to resolve
         | 
| @@ -205,23 +434,43 @@ class DIContainer: | |
| 205 434 | 
             
                        ServiceNotFoundError: If service is not registered
         | 
| 206 435 | 
             
                        CircularDependencyError: If circular dependencies detected
         | 
| 207 436 | 
             
                    """
         | 
| 437 | 
            +
                    return self.get(service_type)
         | 
| 438 | 
            +
                    
         | 
| 439 | 
            +
                def _resolve_internal(self, service_type: Type[T]) -> T:
         | 
| 440 | 
            +
                    """
         | 
| 441 | 
            +
                    Internal method to resolve a service.
         | 
| 442 | 
            +
                    
         | 
| 443 | 
            +
                    Handles the actual resolution logic with proper locking and lifecycle management.
         | 
| 444 | 
            +
                    """
         | 
| 208 445 | 
             
                    with self._lock:
         | 
| 209 446 | 
             
                        # Check for circular dependencies
         | 
| 210 447 | 
             
                        if service_type in self._resolving:
         | 
| 211 | 
            -
                            cycle = " -> ".join(str(t) for t in self._resolving) + f" -> {service_type}"
         | 
| 448 | 
            +
                            cycle = " -> ".join(str(t.__name__) for t in self._resolving) + f" -> {service_type.__name__}"
         | 
| 212 449 | 
             
                            raise CircularDependencyError(f"Circular dependency detected: {cycle}")
         | 
| 213 450 |  | 
| 214 451 | 
             
                        # Check if registered
         | 
| 215 452 | 
             
                        if service_type not in self._registrations:
         | 
| 216 | 
            -
                             | 
| 453 | 
            +
                            suggestions = self._get_similar_types(service_type)
         | 
| 454 | 
            +
                            error_msg = f"Service {service_type.__name__} is not registered."
         | 
| 455 | 
            +
                            if suggestions:
         | 
| 456 | 
            +
                                error_msg += f" Did you mean: {', '.join(suggestions)}?"
         | 
| 457 | 
            +
                            raise ServiceNotFoundError(error_msg)
         | 
| 217 458 |  | 
| 218 459 | 
             
                        registration = self._registrations[service_type]
         | 
| 219 460 |  | 
| 220 | 
            -
                        #  | 
| 461 | 
            +
                        # Handle different lifetimes
         | 
| 221 462 | 
             
                        if registration.lifetime == ServiceLifetime.SINGLETON:
         | 
| 463 | 
            +
                            # Return existing singleton if available
         | 
| 222 464 | 
             
                            if service_type in self._singletons:
         | 
| 223 465 | 
             
                                return self._singletons[service_type]
         | 
| 224 466 |  | 
| 467 | 
            +
                        elif registration.lifetime == ServiceLifetime.SCOPED:
         | 
| 468 | 
            +
                            # Check current scope
         | 
| 469 | 
            +
                            if self._current_scope:
         | 
| 470 | 
            +
                                instance = self._current_scope.get_scoped_instance(service_type)
         | 
| 471 | 
            +
                                if instance is not None:
         | 
| 472 | 
            +
                                    return instance
         | 
| 473 | 
            +
                                    
         | 
| 225 474 | 
             
                        # Mark as resolving
         | 
| 226 475 | 
             
                        self._resolving.add(service_type)
         | 
| 227 476 |  | 
| @@ -229,10 +478,24 @@ class DIContainer: | |
| 229 478 | 
             
                            # Create instance
         | 
| 230 479 | 
             
                            instance = registration.create_instance(self)
         | 
| 231 480 |  | 
| 232 | 
            -
                            # Store  | 
| 481 | 
            +
                            # Store based on lifetime
         | 
| 233 482 | 
             
                            if registration.lifetime == ServiceLifetime.SINGLETON:
         | 
| 234 483 | 
             
                                self._singletons[service_type] = instance
         | 
| 235 | 
            -
                                
         | 
| 484 | 
            +
                                if service_type not in self._initialization_order:
         | 
| 485 | 
            +
                                    self._initialization_order.append(service_type)
         | 
| 486 | 
            +
                                    
         | 
| 487 | 
            +
                            elif registration.lifetime == ServiceLifetime.SCOPED:
         | 
| 488 | 
            +
                                if self._current_scope:
         | 
| 489 | 
            +
                                    self._current_scope.set_scoped_instance(service_type, instance)
         | 
| 490 | 
            +
                                    
         | 
| 491 | 
            +
                            # Call initialization hook if available
         | 
| 492 | 
            +
                            if hasattr(instance, 'initialize'):
         | 
| 493 | 
            +
                                try:
         | 
| 494 | 
            +
                                    instance.initialize()
         | 
| 495 | 
            +
                                except Exception as e:
         | 
| 496 | 
            +
                                    logger.error(f"Failed to initialize service {service_type.__name__}: {e}")
         | 
| 497 | 
            +
                                    raise
         | 
| 498 | 
            +
                                    
         | 
| 236 499 | 
             
                            return instance
         | 
| 237 500 |  | 
| 238 501 | 
             
                        finally:
         | 
| @@ -278,6 +541,22 @@ class DIContainer: | |
| 278 541 | 
             
                        if param.annotation != param.empty:
         | 
| 279 542 | 
             
                            param_type = param.annotation
         | 
| 280 543 |  | 
| 544 | 
            +
                            # Handle string annotations (forward references)
         | 
| 545 | 
            +
                            if isinstance(param_type, str):
         | 
| 546 | 
            +
                                # Try to resolve forward reference
         | 
| 547 | 
            +
                                try:
         | 
| 548 | 
            +
                                    # Get the module where the class is defined
         | 
| 549 | 
            +
                                    import sys
         | 
| 550 | 
            +
                                    frame = sys._getframe(1)
         | 
| 551 | 
            +
                                    module = frame.f_globals
         | 
| 552 | 
            +
                                    if param_type in module:
         | 
| 553 | 
            +
                                        param_type = module[param_type]
         | 
| 554 | 
            +
                                except:
         | 
| 555 | 
            +
                                    # If we can't resolve, skip this parameter
         | 
| 556 | 
            +
                                    if param.default != param.empty:
         | 
| 557 | 
            +
                                        kwargs[param_name] = param.default
         | 
| 558 | 
            +
                                    continue
         | 
| 559 | 
            +
                            
         | 
| 281 560 | 
             
                            # Handle Optional types
         | 
| 282 561 | 
             
                            if hasattr(param_type, '__origin__') and param_type.__origin__ is Union:
         | 
| 283 562 | 
             
                                # Get the non-None type from Optional
         | 
| @@ -285,7 +564,11 @@ class DIContainer: | |
| 285 564 | 
             
                                param_type = next((arg for arg in args if arg is not type(None)), None)
         | 
| 286 565 |  | 
| 287 566 | 
             
                            if param_type and param_type in self._registrations:
         | 
| 288 | 
            -
                                 | 
| 567 | 
            +
                                # Avoid circular dependency by checking if we're already resolving this type
         | 
| 568 | 
            +
                                if param_type not in self._resolving:
         | 
| 569 | 
            +
                                    kwargs[param_name] = self.resolve(param_type)
         | 
| 570 | 
            +
                                elif param.default != param.empty:
         | 
| 571 | 
            +
                                    kwargs[param_name] = param.default
         | 
| 289 572 | 
             
                            elif param.default != param.empty:
         | 
| 290 573 | 
             
                                # Use default value
         | 
| 291 574 | 
             
                                kwargs[param_name] = param.default
         | 
| @@ -301,25 +584,181 @@ class DIContainer: | |
| 301 584 | 
             
                    with self._lock:
         | 
| 302 585 | 
             
                        return self._registrations.copy()
         | 
| 303 586 |  | 
| 587 | 
            +
                def create_scope(self) -> ServiceScope:
         | 
| 588 | 
            +
                    """
         | 
| 589 | 
            +
                    Create a new service scope.
         | 
| 590 | 
            +
                    
         | 
| 591 | 
            +
                    Scoped services will be created once per scope and shared within that scope.
         | 
| 592 | 
            +
                    
         | 
| 593 | 
            +
                    Returns:
         | 
| 594 | 
            +
                        New ServiceScope instance
         | 
| 595 | 
            +
                        
         | 
| 596 | 
            +
                    Examples:
         | 
| 597 | 
            +
                        # Use scope with context manager
         | 
| 598 | 
            +
                        with container.create_scope() as scope:
         | 
| 599 | 
            +
                            # Services with SCOPED lifetime are shared within this scope
         | 
| 600 | 
            +
                            service1 = container.get(IScopedService)
         | 
| 601 | 
            +
                            service2 = container.get(IScopedService)
         | 
| 602 | 
            +
                            assert service1 is service2  # Same instance
         | 
| 603 | 
            +
                            
         | 
| 604 | 
            +
                        # Scope is disposed, scoped instances are cleaned up
         | 
| 605 | 
            +
                    """
         | 
| 606 | 
            +
                    scope = ServiceScope(self)
         | 
| 607 | 
            +
                    with self._lock:
         | 
| 608 | 
            +
                        self._scopes.append(scope)
         | 
| 609 | 
            +
                        # Set as current scope for resolution
         | 
| 610 | 
            +
                        old_scope = self._current_scope
         | 
| 611 | 
            +
                        self._current_scope = scope
         | 
| 612 | 
            +
                        
         | 
| 613 | 
            +
                    # Return a context manager that restores old scope
         | 
| 614 | 
            +
                    class ScopeContext:
         | 
| 615 | 
            +
                        def __init__(self, container, new_scope, old_scope):
         | 
| 616 | 
            +
                            self.container = container
         | 
| 617 | 
            +
                            self.new_scope = new_scope
         | 
| 618 | 
            +
                            self.old_scope = old_scope
         | 
| 619 | 
            +
                            
         | 
| 620 | 
            +
                        def __enter__(self):
         | 
| 621 | 
            +
                            return self.new_scope
         | 
| 622 | 
            +
                            
         | 
| 623 | 
            +
                        def __exit__(self, exc_type, exc_val, exc_tb):
         | 
| 624 | 
            +
                            with self.container._lock:
         | 
| 625 | 
            +
                                self.container._current_scope = self.old_scope
         | 
| 626 | 
            +
                                if self.new_scope in self.container._scopes:
         | 
| 627 | 
            +
                                    self.container._scopes.remove(self.new_scope)
         | 
| 628 | 
            +
                            self.new_scope.dispose()
         | 
| 629 | 
            +
                            
         | 
| 630 | 
            +
                    return ScopeContext(self, scope, old_scope)
         | 
| 631 | 
            +
                    
         | 
| 304 632 | 
             
                def create_child_container(self) -> 'DIContainer':
         | 
| 305 633 | 
             
                    """
         | 
| 306 634 | 
             
                    Create a child container that inherits registrations.
         | 
| 307 635 |  | 
| 308 | 
            -
                    Useful for  | 
| 636 | 
            +
                    Useful for isolated scenarios where you want separate singleton instances.
         | 
| 309 637 | 
             
                    """
         | 
| 310 638 | 
             
                    child = DIContainer()
         | 
| 311 639 | 
             
                    with self._lock:
         | 
| 312 640 | 
             
                        # Copy registrations but not singleton instances
         | 
| 313 641 | 
             
                        for service_type, registration in self._registrations.items():
         | 
| 314 642 | 
             
                            child._registrations[service_type] = registration
         | 
| 643 | 
            +
                        # Copy named registrations
         | 
| 644 | 
            +
                        child._named_registrations = self._named_registrations.copy()
         | 
| 645 | 
            +
                        # Copy factories
         | 
| 646 | 
            +
                        child._factories = self._factories.copy()
         | 
| 647 | 
            +
                        # Copy disposal handlers
         | 
| 648 | 
            +
                        child._disposal_handlers = self._disposal_handlers.copy()
         | 
| 315 649 | 
             
                    return child
         | 
| 316 650 |  | 
| 651 | 
            +
                def dispose(self) -> None:
         | 
| 652 | 
            +
                    """
         | 
| 653 | 
            +
                    Dispose of the container and all managed services.
         | 
| 654 | 
            +
                    
         | 
| 655 | 
            +
                    Calls disposal handlers for singleton services in reverse initialization order.
         | 
| 656 | 
            +
                    Also disposes any active scopes.
         | 
| 657 | 
            +
                    """
         | 
| 658 | 
            +
                    with self._lock:
         | 
| 659 | 
            +
                        # Dispose scopes first
         | 
| 660 | 
            +
                        for scope in reversed(self._scopes):
         | 
| 661 | 
            +
                            scope.dispose()
         | 
| 662 | 
            +
                        self._scopes.clear()
         | 
| 663 | 
            +
                        
         | 
| 664 | 
            +
                        # Dispose singletons in reverse initialization order
         | 
| 665 | 
            +
                        for service_type in reversed(self._initialization_order):
         | 
| 666 | 
            +
                            if service_type in self._singletons:
         | 
| 667 | 
            +
                                instance = self._singletons[service_type]
         | 
| 668 | 
            +
                                
         | 
| 669 | 
            +
                                # Call disposal handler if registered
         | 
| 670 | 
            +
                                if service_type in self._disposal_handlers:
         | 
| 671 | 
            +
                                    try:
         | 
| 672 | 
            +
                                        self._disposal_handlers[service_type](instance)
         | 
| 673 | 
            +
                                    except Exception as e:
         | 
| 674 | 
            +
                                        logger.error(f"Error in disposal handler for {service_type.__name__}: {e}")
         | 
| 675 | 
            +
                                        
         | 
| 676 | 
            +
                                # Call dispose method if available
         | 
| 677 | 
            +
                                elif hasattr(instance, 'dispose'):
         | 
| 678 | 
            +
                                    try:
         | 
| 679 | 
            +
                                        instance.dispose()
         | 
| 680 | 
            +
                                    except Exception as e:
         | 
| 681 | 
            +
                                        logger.error(f"Error disposing service {service_type.__name__}: {e}")
         | 
| 682 | 
            +
                                        
         | 
| 683 | 
            +
                        # Clear everything
         | 
| 684 | 
            +
                        self._singletons.clear()
         | 
| 685 | 
            +
                        self._initialization_order.clear()
         | 
| 686 | 
            +
                        self._current_scope = None
         | 
| 687 | 
            +
                        
         | 
| 317 688 | 
             
                def clear(self) -> None:
         | 
| 318 689 | 
             
                    """Clear all registrations and instances."""
         | 
| 690 | 
            +
                    self.dispose()
         | 
| 319 691 | 
             
                    with self._lock:
         | 
| 320 692 | 
             
                        self._registrations.clear()
         | 
| 321 | 
            -
                        self. | 
| 693 | 
            +
                        self._named_registrations.clear()
         | 
| 694 | 
            +
                        self._factories.clear()
         | 
| 695 | 
            +
                        self._disposal_handlers.clear()
         | 
| 322 696 | 
             
                        self._resolving.clear()
         | 
| 697 | 
            +
                        
         | 
| 698 | 
            +
                def _get_similar_types(self, service_type: Type) -> List[str]:
         | 
| 699 | 
            +
                    """
         | 
| 700 | 
            +
                    Get similar registered type names for better error messages.
         | 
| 701 | 
            +
                    
         | 
| 702 | 
            +
                    Uses simple string similarity to suggest possible alternatives.
         | 
| 703 | 
            +
                    """
         | 
| 704 | 
            +
                    if not self._registrations:
         | 
| 705 | 
            +
                        return []
         | 
| 706 | 
            +
                        
         | 
| 707 | 
            +
                    type_name = service_type.__name__.lower()
         | 
| 708 | 
            +
                    similar = []
         | 
| 709 | 
            +
                    
         | 
| 710 | 
            +
                    for registered_type in self._registrations.keys():
         | 
| 711 | 
            +
                        registered_name = registered_type.__name__
         | 
| 712 | 
            +
                        registered_lower = registered_name.lower()
         | 
| 713 | 
            +
                        
         | 
| 714 | 
            +
                        # Check for substring match
         | 
| 715 | 
            +
                        if type_name in registered_lower or registered_lower in type_name:
         | 
| 716 | 
            +
                            similar.append(registered_name)
         | 
| 717 | 
            +
                            continue
         | 
| 718 | 
            +
                            
         | 
| 719 | 
            +
                        # Check for common prefix
         | 
| 720 | 
            +
                        common_prefix_len = 0
         | 
| 721 | 
            +
                        for i, (a, b) in enumerate(zip(type_name, registered_lower)):
         | 
| 722 | 
            +
                            if a == b:
         | 
| 723 | 
            +
                                common_prefix_len = i + 1
         | 
| 724 | 
            +
                            else:
         | 
| 725 | 
            +
                                break
         | 
| 726 | 
            +
                                
         | 
| 727 | 
            +
                        if common_prefix_len >= min(3, len(type_name) // 2):
         | 
| 728 | 
            +
                            similar.append(registered_name)
         | 
| 729 | 
            +
                            
         | 
| 730 | 
            +
                    return similar[:3]  # Return top 3 suggestions
         | 
| 731 | 
            +
                    
         | 
| 732 | 
            +
                def _get_similar_names(self, name: str) -> List[str]:
         | 
| 733 | 
            +
                    """
         | 
| 734 | 
            +
                    Get similar registered names for better error messages.
         | 
| 735 | 
            +
                    """
         | 
| 736 | 
            +
                    if not self._named_registrations:
         | 
| 737 | 
            +
                        return []
         | 
| 738 | 
            +
                        
         | 
| 739 | 
            +
                    name_lower = name.lower()
         | 
| 740 | 
            +
                    similar = []
         | 
| 741 | 
            +
                    
         | 
| 742 | 
            +
                    for registered_name in self._named_registrations.keys():
         | 
| 743 | 
            +
                        registered_lower = registered_name.lower()
         | 
| 744 | 
            +
                        
         | 
| 745 | 
            +
                        # Check for substring match
         | 
| 746 | 
            +
                        if name_lower in registered_lower or registered_lower in name_lower:
         | 
| 747 | 
            +
                            similar.append(registered_name)
         | 
| 748 | 
            +
                            continue
         | 
| 749 | 
            +
                            
         | 
| 750 | 
            +
                        # Check for common prefix
         | 
| 751 | 
            +
                        common_prefix_len = 0
         | 
| 752 | 
            +
                        for i, (a, b) in enumerate(zip(name_lower, registered_lower)):
         | 
| 753 | 
            +
                            if a == b:
         | 
| 754 | 
            +
                                common_prefix_len = i + 1
         | 
| 755 | 
            +
                            else:
         | 
| 756 | 
            +
                                break
         | 
| 757 | 
            +
                                
         | 
| 758 | 
            +
                        if common_prefix_len >= min(3, len(name_lower) // 2):
         | 
| 759 | 
            +
                            similar.append(registered_name)
         | 
| 760 | 
            +
                            
         | 
| 761 | 
            +
                    return similar[:3]  # Return top 3 suggestions
         | 
| 323 762 |  | 
| 324 763 |  | 
| 325 764 | 
             
            # Global container instance (optional, for convenience)
         |