claude-mpm 0.3.0__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.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/__init__.py +17 -0
- claude_mpm/__main__.py +14 -0
- claude_mpm/_version.py +32 -0
- claude_mpm/agents/BASE_AGENT_TEMPLATE.md +88 -0
- claude_mpm/agents/INSTRUCTIONS.md +375 -0
- claude_mpm/agents/__init__.py +118 -0
- claude_mpm/agents/agent_loader.py +621 -0
- claude_mpm/agents/agent_loader_integration.py +229 -0
- claude_mpm/agents/agents_metadata.py +204 -0
- claude_mpm/agents/base_agent.json +27 -0
- claude_mpm/agents/base_agent_loader.py +519 -0
- claude_mpm/agents/schema/agent_schema.json +160 -0
- claude_mpm/agents/system_agent_config.py +587 -0
- claude_mpm/agents/templates/__init__.py +101 -0
- claude_mpm/agents/templates/data_engineer_agent.json +46 -0
- claude_mpm/agents/templates/documentation_agent.json +45 -0
- claude_mpm/agents/templates/engineer_agent.json +49 -0
- claude_mpm/agents/templates/ops_agent.json +46 -0
- claude_mpm/agents/templates/qa_agent.json +45 -0
- claude_mpm/agents/templates/research_agent.json +49 -0
- claude_mpm/agents/templates/security_agent.json +46 -0
- claude_mpm/agents/templates/update-optimized-specialized-agents.json +374 -0
- claude_mpm/agents/templates/version_control_agent.json +46 -0
- claude_mpm/agents/test_fix_deployment/.claude-pm/config/project.json +6 -0
- claude_mpm/cli.py +655 -0
- claude_mpm/cli_main.py +13 -0
- claude_mpm/cli_module/__init__.py +15 -0
- claude_mpm/cli_module/args.py +222 -0
- claude_mpm/cli_module/commands.py +203 -0
- claude_mpm/cli_module/migration_example.py +183 -0
- claude_mpm/cli_module/refactoring_guide.md +253 -0
- claude_mpm/cli_old/__init__.py +1 -0
- claude_mpm/cli_old/ticket_cli.py +102 -0
- claude_mpm/config/__init__.py +5 -0
- claude_mpm/config/hook_config.py +42 -0
- claude_mpm/constants.py +150 -0
- claude_mpm/core/__init__.py +45 -0
- claude_mpm/core/agent_name_normalizer.py +248 -0
- claude_mpm/core/agent_registry.py +627 -0
- claude_mpm/core/agent_registry.py.bak +312 -0
- claude_mpm/core/agent_session_manager.py +273 -0
- claude_mpm/core/base_service.py +747 -0
- claude_mpm/core/base_service.py.bak +406 -0
- claude_mpm/core/config.py +334 -0
- claude_mpm/core/config_aliases.py +292 -0
- claude_mpm/core/container.py +347 -0
- claude_mpm/core/factories.py +281 -0
- claude_mpm/core/framework_loader.py +472 -0
- claude_mpm/core/injectable_service.py +206 -0
- claude_mpm/core/interfaces.py +539 -0
- claude_mpm/core/logger.py +468 -0
- claude_mpm/core/minimal_framework_loader.py +107 -0
- claude_mpm/core/mixins.py +150 -0
- claude_mpm/core/service_registry.py +299 -0
- claude_mpm/core/session_manager.py +190 -0
- claude_mpm/core/simple_runner.py +511 -0
- claude_mpm/core/tool_access_control.py +173 -0
- claude_mpm/hooks/README.md +243 -0
- claude_mpm/hooks/__init__.py +5 -0
- claude_mpm/hooks/base_hook.py +154 -0
- claude_mpm/hooks/builtin/__init__.py +1 -0
- claude_mpm/hooks/builtin/logging_hook_example.py +165 -0
- claude_mpm/hooks/builtin/post_delegation_hook_example.py +124 -0
- claude_mpm/hooks/builtin/pre_delegation_hook_example.py +125 -0
- claude_mpm/hooks/builtin/submit_hook_example.py +100 -0
- claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +237 -0
- claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +239 -0
- claude_mpm/hooks/builtin/workflow_start_hook.py +181 -0
- claude_mpm/hooks/hook_client.py +264 -0
- claude_mpm/hooks/hook_runner.py +370 -0
- claude_mpm/hooks/json_rpc_executor.py +259 -0
- claude_mpm/hooks/json_rpc_hook_client.py +319 -0
- claude_mpm/hooks/tool_call_interceptor.py +204 -0
- claude_mpm/init.py +246 -0
- claude_mpm/orchestration/SUBPROCESS_DESIGN.md +66 -0
- claude_mpm/orchestration/__init__.py +6 -0
- claude_mpm/orchestration/archive/direct_orchestrator.py +195 -0
- claude_mpm/orchestration/archive/factory.py +215 -0
- claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +188 -0
- claude_mpm/orchestration/archive/hook_integration_example.py +178 -0
- claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +826 -0
- claude_mpm/orchestration/archive/orchestrator.py +501 -0
- claude_mpm/orchestration/archive/pexpect_orchestrator.py +252 -0
- claude_mpm/orchestration/archive/pty_orchestrator.py +270 -0
- claude_mpm/orchestration/archive/simple_orchestrator.py +82 -0
- claude_mpm/orchestration/archive/subprocess_orchestrator.py +801 -0
- claude_mpm/orchestration/archive/system_prompt_orchestrator.py +278 -0
- claude_mpm/orchestration/archive/wrapper_orchestrator.py +187 -0
- claude_mpm/scripts/__init__.py +1 -0
- claude_mpm/scripts/ticket.py +269 -0
- claude_mpm/services/__init__.py +10 -0
- claude_mpm/services/agent_deployment.py +955 -0
- claude_mpm/services/agent_lifecycle_manager.py +948 -0
- claude_mpm/services/agent_management_service.py +596 -0
- claude_mpm/services/agent_modification_tracker.py +841 -0
- claude_mpm/services/agent_profile_loader.py +606 -0
- claude_mpm/services/agent_registry.py +677 -0
- claude_mpm/services/base_agent_manager.py +380 -0
- claude_mpm/services/framework_agent_loader.py +337 -0
- claude_mpm/services/framework_claude_md_generator/README.md +92 -0
- claude_mpm/services/framework_claude_md_generator/__init__.py +206 -0
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +151 -0
- claude_mpm/services/framework_claude_md_generator/content_validator.py +126 -0
- claude_mpm/services/framework_claude_md_generator/deployment_manager.py +137 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +106 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +582 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +97 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +27 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +23 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +23 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +20 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/header.py +26 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +30 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +37 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +111 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +89 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +39 -0
- claude_mpm/services/framework_claude_md_generator/section_manager.py +106 -0
- claude_mpm/services/framework_claude_md_generator/version_manager.py +121 -0
- claude_mpm/services/framework_claude_md_generator.py +621 -0
- claude_mpm/services/hook_service.py +388 -0
- claude_mpm/services/hook_service_manager.py +223 -0
- claude_mpm/services/json_rpc_hook_manager.py +92 -0
- claude_mpm/services/parent_directory_manager/README.md +83 -0
- claude_mpm/services/parent_directory_manager/__init__.py +577 -0
- claude_mpm/services/parent_directory_manager/backup_manager.py +258 -0
- claude_mpm/services/parent_directory_manager/config_manager.py +210 -0
- claude_mpm/services/parent_directory_manager/deduplication_manager.py +279 -0
- claude_mpm/services/parent_directory_manager/framework_protector.py +143 -0
- claude_mpm/services/parent_directory_manager/operations.py +186 -0
- claude_mpm/services/parent_directory_manager/state_manager.py +624 -0
- claude_mpm/services/parent_directory_manager/template_deployer.py +579 -0
- claude_mpm/services/parent_directory_manager/validation_manager.py +378 -0
- claude_mpm/services/parent_directory_manager/version_control_helper.py +339 -0
- claude_mpm/services/parent_directory_manager/version_manager.py +222 -0
- claude_mpm/services/shared_prompt_cache.py +819 -0
- claude_mpm/services/ticket_manager.py +213 -0
- claude_mpm/services/ticket_manager_di.py +318 -0
- claude_mpm/services/ticketing_service_original.py +508 -0
- claude_mpm/services/version_control/VERSION +1 -0
- claude_mpm/services/version_control/__init__.py +70 -0
- claude_mpm/services/version_control/branch_strategy.py +670 -0
- claude_mpm/services/version_control/conflict_resolution.py +744 -0
- claude_mpm/services/version_control/git_operations.py +784 -0
- claude_mpm/services/version_control/semantic_versioning.py +703 -0
- claude_mpm/ui/__init__.py +1 -0
- claude_mpm/ui/rich_terminal_ui.py +295 -0
- claude_mpm/ui/terminal_ui.py +328 -0
- claude_mpm/utils/__init__.py +16 -0
- claude_mpm/utils/config_manager.py +468 -0
- claude_mpm/utils/import_migration_example.py +80 -0
- claude_mpm/utils/imports.py +182 -0
- claude_mpm/utils/path_operations.py +357 -0
- claude_mpm/utils/paths.py +289 -0
- claude_mpm-0.3.0.dist-info/METADATA +290 -0
- claude_mpm-0.3.0.dist-info/RECORD +159 -0
- claude_mpm-0.3.0.dist-info/WHEEL +5 -0
- claude_mpm-0.3.0.dist-info/entry_points.txt +4 -0
- claude_mpm-0.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Lightweight Dependency Injection Container for Claude MPM.
|
|
3
|
+
|
|
4
|
+
This module provides a simple yet powerful dependency injection container
|
|
5
|
+
that supports:
|
|
6
|
+
- Service registration with interfaces
|
|
7
|
+
- Constructor injection
|
|
8
|
+
- Singleton and transient lifetimes
|
|
9
|
+
- Factory functions
|
|
10
|
+
- Lazy initialization
|
|
11
|
+
- Circular dependency detection
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import inspect
|
|
15
|
+
import threading
|
|
16
|
+
from abc import ABC
|
|
17
|
+
from enum import Enum
|
|
18
|
+
from typing import Any, Callable, Dict, List, Optional, Set, Type, TypeVar, Union
|
|
19
|
+
|
|
20
|
+
from .logger import get_logger
|
|
21
|
+
|
|
22
|
+
logger = get_logger(__name__)
|
|
23
|
+
|
|
24
|
+
T = TypeVar('T')
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ServiceLifetime(Enum):
|
|
28
|
+
"""Service lifetime options."""
|
|
29
|
+
SINGLETON = "singleton" # One instance per container
|
|
30
|
+
TRANSIENT = "transient" # New instance per request
|
|
31
|
+
SCOPED = "scoped" # One instance per scope (future enhancement)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ServiceRegistration:
|
|
35
|
+
"""Represents a service registration in the container."""
|
|
36
|
+
|
|
37
|
+
def __init__(
|
|
38
|
+
self,
|
|
39
|
+
service_type: Type,
|
|
40
|
+
implementation: Optional[Union[Type, Callable]] = None,
|
|
41
|
+
factory: Optional[Callable] = None,
|
|
42
|
+
instance: Optional[Any] = None,
|
|
43
|
+
lifetime: ServiceLifetime = ServiceLifetime.SINGLETON,
|
|
44
|
+
dependencies: Optional[Dict[str, Type]] = None
|
|
45
|
+
):
|
|
46
|
+
"""
|
|
47
|
+
Initialize service registration.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
service_type: The interface/base type being registered
|
|
51
|
+
implementation: The concrete implementation class
|
|
52
|
+
factory: Factory function to create instances
|
|
53
|
+
instance: Pre-created instance (for singleton registration)
|
|
54
|
+
lifetime: Service lifetime management
|
|
55
|
+
dependencies: Explicit dependency mapping
|
|
56
|
+
"""
|
|
57
|
+
self.service_type = service_type
|
|
58
|
+
self.implementation = implementation or service_type
|
|
59
|
+
self.factory = factory
|
|
60
|
+
self.instance = instance
|
|
61
|
+
self.lifetime = lifetime
|
|
62
|
+
self.dependencies = dependencies or {}
|
|
63
|
+
self._lock = threading.Lock()
|
|
64
|
+
|
|
65
|
+
def create_instance(self, container: 'DIContainer') -> Any:
|
|
66
|
+
"""Create an instance of the service."""
|
|
67
|
+
if self.instance is not None:
|
|
68
|
+
return self.instance
|
|
69
|
+
|
|
70
|
+
if self.factory:
|
|
71
|
+
return self.factory(container)
|
|
72
|
+
|
|
73
|
+
# Get constructor parameters
|
|
74
|
+
if inspect.isclass(self.implementation):
|
|
75
|
+
return container.create_instance(self.implementation, self.dependencies)
|
|
76
|
+
else:
|
|
77
|
+
# It's already an instance or callable
|
|
78
|
+
return self.implementation
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class CircularDependencyError(Exception):
|
|
82
|
+
"""Raised when circular dependencies are detected."""
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class ServiceNotFoundError(Exception):
|
|
87
|
+
"""Raised when a requested service is not registered."""
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class DIContainer:
|
|
92
|
+
"""
|
|
93
|
+
Lightweight Dependency Injection Container.
|
|
94
|
+
|
|
95
|
+
Provides service registration, resolution, and lifecycle management.
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
def __init__(self):
|
|
99
|
+
"""Initialize the DI container."""
|
|
100
|
+
self._registrations: Dict[Type, ServiceRegistration] = {}
|
|
101
|
+
self._singletons: Dict[Type, Any] = {}
|
|
102
|
+
self._lock = threading.RLock()
|
|
103
|
+
self._resolving: Set[Type] = set()
|
|
104
|
+
|
|
105
|
+
def register(
|
|
106
|
+
self,
|
|
107
|
+
service_type: Type[T],
|
|
108
|
+
implementation: Optional[Union[Type[T], Callable[..., T]]] = None,
|
|
109
|
+
lifetime: ServiceLifetime = ServiceLifetime.SINGLETON,
|
|
110
|
+
factory: Optional[Callable[['DIContainer'], T]] = None,
|
|
111
|
+
instance: Optional[T] = None,
|
|
112
|
+
dependencies: Optional[Dict[str, Type]] = None
|
|
113
|
+
) -> None:
|
|
114
|
+
"""
|
|
115
|
+
Register a service in the container.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
service_type: The interface/base type to register
|
|
119
|
+
implementation: The concrete implementation (class or factory)
|
|
120
|
+
lifetime: Service lifetime (singleton/transient)
|
|
121
|
+
factory: Optional factory function
|
|
122
|
+
instance: Pre-created instance (for singleton)
|
|
123
|
+
dependencies: Explicit dependency mapping for constructor params
|
|
124
|
+
|
|
125
|
+
Examples:
|
|
126
|
+
# Register interface with implementation
|
|
127
|
+
container.register(ILogger, ConsoleLogger)
|
|
128
|
+
|
|
129
|
+
# Register with factory
|
|
130
|
+
container.register(IDatabase, factory=lambda c: Database(c.resolve(IConfig)))
|
|
131
|
+
|
|
132
|
+
# Register singleton instance
|
|
133
|
+
container.register(IConfig, instance=Config())
|
|
134
|
+
|
|
135
|
+
# Register with explicit dependencies
|
|
136
|
+
container.register(IService, ServiceImpl, dependencies={'logger': ILogger})
|
|
137
|
+
"""
|
|
138
|
+
with self._lock:
|
|
139
|
+
registration = ServiceRegistration(
|
|
140
|
+
service_type=service_type,
|
|
141
|
+
implementation=implementation,
|
|
142
|
+
factory=factory,
|
|
143
|
+
instance=instance,
|
|
144
|
+
lifetime=lifetime,
|
|
145
|
+
dependencies=dependencies
|
|
146
|
+
)
|
|
147
|
+
self._registrations[service_type] = registration
|
|
148
|
+
|
|
149
|
+
# If instance provided, store as singleton
|
|
150
|
+
if instance is not None:
|
|
151
|
+
self._singletons[service_type] = instance
|
|
152
|
+
|
|
153
|
+
def register_singleton(
|
|
154
|
+
self,
|
|
155
|
+
service_type: Type[T],
|
|
156
|
+
implementation: Optional[Union[Type[T], T]] = None
|
|
157
|
+
) -> None:
|
|
158
|
+
"""
|
|
159
|
+
Register a singleton service.
|
|
160
|
+
|
|
161
|
+
Convenience method for registering singletons.
|
|
162
|
+
"""
|
|
163
|
+
if implementation is not None and not inspect.isclass(implementation):
|
|
164
|
+
# It's an instance
|
|
165
|
+
self.register(service_type, instance=implementation)
|
|
166
|
+
else:
|
|
167
|
+
self.register(service_type, implementation, lifetime=ServiceLifetime.SINGLETON)
|
|
168
|
+
|
|
169
|
+
def register_transient(
|
|
170
|
+
self,
|
|
171
|
+
service_type: Type[T],
|
|
172
|
+
implementation: Optional[Type[T]] = None
|
|
173
|
+
) -> None:
|
|
174
|
+
"""
|
|
175
|
+
Register a transient service.
|
|
176
|
+
|
|
177
|
+
Convenience method for registering transient services.
|
|
178
|
+
"""
|
|
179
|
+
self.register(service_type, implementation, lifetime=ServiceLifetime.TRANSIENT)
|
|
180
|
+
|
|
181
|
+
def register_factory(
|
|
182
|
+
self,
|
|
183
|
+
service_type: Type[T],
|
|
184
|
+
factory: Callable[['DIContainer'], T],
|
|
185
|
+
lifetime: ServiceLifetime = ServiceLifetime.SINGLETON
|
|
186
|
+
) -> None:
|
|
187
|
+
"""
|
|
188
|
+
Register a service with a factory function.
|
|
189
|
+
|
|
190
|
+
The factory receives the container as parameter for resolving dependencies.
|
|
191
|
+
"""
|
|
192
|
+
self.register(service_type, factory=factory, lifetime=lifetime)
|
|
193
|
+
|
|
194
|
+
def resolve(self, service_type: Type[T]) -> T:
|
|
195
|
+
"""
|
|
196
|
+
Resolve a service from the container.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
service_type: The type to resolve
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
Instance of the requested service
|
|
203
|
+
|
|
204
|
+
Raises:
|
|
205
|
+
ServiceNotFoundError: If service is not registered
|
|
206
|
+
CircularDependencyError: If circular dependencies detected
|
|
207
|
+
"""
|
|
208
|
+
with self._lock:
|
|
209
|
+
# Check for circular dependencies
|
|
210
|
+
if service_type in self._resolving:
|
|
211
|
+
cycle = " -> ".join(str(t) for t in self._resolving) + f" -> {service_type}"
|
|
212
|
+
raise CircularDependencyError(f"Circular dependency detected: {cycle}")
|
|
213
|
+
|
|
214
|
+
# Check if registered
|
|
215
|
+
if service_type not in self._registrations:
|
|
216
|
+
raise ServiceNotFoundError(f"Service {service_type} is not registered")
|
|
217
|
+
|
|
218
|
+
registration = self._registrations[service_type]
|
|
219
|
+
|
|
220
|
+
# Return existing singleton if available
|
|
221
|
+
if registration.lifetime == ServiceLifetime.SINGLETON:
|
|
222
|
+
if service_type in self._singletons:
|
|
223
|
+
return self._singletons[service_type]
|
|
224
|
+
|
|
225
|
+
# Mark as resolving
|
|
226
|
+
self._resolving.add(service_type)
|
|
227
|
+
|
|
228
|
+
try:
|
|
229
|
+
# Create instance
|
|
230
|
+
instance = registration.create_instance(self)
|
|
231
|
+
|
|
232
|
+
# Store singleton
|
|
233
|
+
if registration.lifetime == ServiceLifetime.SINGLETON:
|
|
234
|
+
self._singletons[service_type] = instance
|
|
235
|
+
|
|
236
|
+
return instance
|
|
237
|
+
|
|
238
|
+
finally:
|
|
239
|
+
self._resolving.remove(service_type)
|
|
240
|
+
|
|
241
|
+
def resolve_optional(self, service_type: Type[T], default: Optional[T] = None) -> Optional[T]:
|
|
242
|
+
"""
|
|
243
|
+
Resolve a service if registered, otherwise return default.
|
|
244
|
+
|
|
245
|
+
Useful for optional dependencies.
|
|
246
|
+
"""
|
|
247
|
+
try:
|
|
248
|
+
return self.resolve(service_type)
|
|
249
|
+
except ServiceNotFoundError:
|
|
250
|
+
return default
|
|
251
|
+
|
|
252
|
+
def create_instance(self, cls: Type[T], explicit_deps: Optional[Dict[str, Type]] = None) -> T:
|
|
253
|
+
"""
|
|
254
|
+
Create an instance of a class, resolving constructor dependencies.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
cls: The class to instantiate
|
|
258
|
+
explicit_deps: Explicit dependency mapping for constructor params
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
New instance with resolved dependencies
|
|
262
|
+
"""
|
|
263
|
+
# Get constructor signature
|
|
264
|
+
sig = inspect.signature(cls.__init__)
|
|
265
|
+
kwargs = {}
|
|
266
|
+
|
|
267
|
+
for param_name, param in sig.parameters.items():
|
|
268
|
+
if param_name == 'self':
|
|
269
|
+
continue
|
|
270
|
+
|
|
271
|
+
# Check explicit dependencies first
|
|
272
|
+
if explicit_deps and param_name in explicit_deps:
|
|
273
|
+
dep_type = explicit_deps[param_name]
|
|
274
|
+
kwargs[param_name] = self.resolve(dep_type)
|
|
275
|
+
continue
|
|
276
|
+
|
|
277
|
+
# Try to resolve by type annotation
|
|
278
|
+
if param.annotation != param.empty:
|
|
279
|
+
param_type = param.annotation
|
|
280
|
+
|
|
281
|
+
# Handle Optional types
|
|
282
|
+
if hasattr(param_type, '__origin__') and param_type.__origin__ is Union:
|
|
283
|
+
# Get the non-None type from Optional
|
|
284
|
+
args = param_type.__args__
|
|
285
|
+
param_type = next((arg for arg in args if arg is not type(None)), None)
|
|
286
|
+
|
|
287
|
+
if param_type and param_type in self._registrations:
|
|
288
|
+
kwargs[param_name] = self.resolve(param_type)
|
|
289
|
+
elif param.default != param.empty:
|
|
290
|
+
# Use default value
|
|
291
|
+
kwargs[param_name] = param.default
|
|
292
|
+
|
|
293
|
+
return cls(**kwargs)
|
|
294
|
+
|
|
295
|
+
def is_registered(self, service_type: Type) -> bool:
|
|
296
|
+
"""Check if a service type is registered."""
|
|
297
|
+
return service_type in self._registrations
|
|
298
|
+
|
|
299
|
+
def get_all_registrations(self) -> Dict[Type, ServiceRegistration]:
|
|
300
|
+
"""Get all service registrations."""
|
|
301
|
+
with self._lock:
|
|
302
|
+
return self._registrations.copy()
|
|
303
|
+
|
|
304
|
+
def create_child_container(self) -> 'DIContainer':
|
|
305
|
+
"""
|
|
306
|
+
Create a child container that inherits registrations.
|
|
307
|
+
|
|
308
|
+
Useful for scoped scenarios.
|
|
309
|
+
"""
|
|
310
|
+
child = DIContainer()
|
|
311
|
+
with self._lock:
|
|
312
|
+
# Copy registrations but not singleton instances
|
|
313
|
+
for service_type, registration in self._registrations.items():
|
|
314
|
+
child._registrations[service_type] = registration
|
|
315
|
+
return child
|
|
316
|
+
|
|
317
|
+
def clear(self) -> None:
|
|
318
|
+
"""Clear all registrations and instances."""
|
|
319
|
+
with self._lock:
|
|
320
|
+
self._registrations.clear()
|
|
321
|
+
self._singletons.clear()
|
|
322
|
+
self._resolving.clear()
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
# Global container instance (optional, for convenience)
|
|
326
|
+
_global_container: Optional[DIContainer] = None
|
|
327
|
+
_global_lock = threading.Lock()
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def get_container() -> DIContainer:
|
|
331
|
+
"""
|
|
332
|
+
Get the global DI container instance.
|
|
333
|
+
|
|
334
|
+
Creates one if it doesn't exist.
|
|
335
|
+
"""
|
|
336
|
+
global _global_container
|
|
337
|
+
with _global_lock:
|
|
338
|
+
if _global_container is None:
|
|
339
|
+
_global_container = DIContainer()
|
|
340
|
+
return _global_container
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def set_container(container: DIContainer) -> None:
|
|
344
|
+
"""Set the global DI container instance."""
|
|
345
|
+
global _global_container
|
|
346
|
+
with _global_lock:
|
|
347
|
+
_global_container = container
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Factory classes for creating complex dependencies in Claude MPM.
|
|
3
|
+
|
|
4
|
+
Provides factory patterns for services that require complex initialization
|
|
5
|
+
or have multiple configuration options.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Dict, Optional, Type, TypeVar
|
|
11
|
+
|
|
12
|
+
from .container import DIContainer
|
|
13
|
+
from .logger import get_logger
|
|
14
|
+
from .config import Config
|
|
15
|
+
from ..services.json_rpc_hook_manager import JSONRPCHookManager
|
|
16
|
+
from ..services.agent_deployment import AgentDeploymentService
|
|
17
|
+
from ..orchestration.factory import OrchestratorFactory
|
|
18
|
+
from ..orchestration.base import BaseOrchestrator
|
|
19
|
+
|
|
20
|
+
logger = get_logger(__name__)
|
|
21
|
+
|
|
22
|
+
T = TypeVar('T')
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ServiceFactory(ABC):
|
|
26
|
+
"""Base factory for creating services."""
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def create(self, container: DIContainer, **kwargs) -> Any:
|
|
30
|
+
"""Create a service instance."""
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class HookManagerFactory(ServiceFactory):
|
|
35
|
+
"""Factory for creating hook manager instances."""
|
|
36
|
+
|
|
37
|
+
def create(
|
|
38
|
+
self,
|
|
39
|
+
container: DIContainer,
|
|
40
|
+
log_dir: Optional[Path] = None,
|
|
41
|
+
enabled: bool = True
|
|
42
|
+
) -> Optional[JSONRPCHookManager]:
|
|
43
|
+
"""
|
|
44
|
+
Create a hook manager instance.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
container: DI container
|
|
48
|
+
log_dir: Log directory path
|
|
49
|
+
enabled: Whether hooks are enabled
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Hook manager instance or None if disabled
|
|
53
|
+
"""
|
|
54
|
+
config = container.resolve(Config)
|
|
55
|
+
|
|
56
|
+
# Check if hooks are enabled
|
|
57
|
+
if not enabled or config.get('hooks.enabled', True) is False:
|
|
58
|
+
logger.info("Hooks are disabled")
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
# Get log directory from config if not provided
|
|
62
|
+
if log_dir is None:
|
|
63
|
+
log_dir = Path(config.get('log_dir', '.claude-mpm/logs'))
|
|
64
|
+
|
|
65
|
+
# Create hook manager
|
|
66
|
+
hook_manager = JSONRPCHookManager(log_dir=log_dir)
|
|
67
|
+
|
|
68
|
+
# Start the service
|
|
69
|
+
if hook_manager.start_service():
|
|
70
|
+
logger.info("Hook manager initialized successfully")
|
|
71
|
+
return hook_manager
|
|
72
|
+
else:
|
|
73
|
+
logger.warning("Failed to start hook manager")
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class OrchestratorFactoryWrapper(ServiceFactory):
|
|
78
|
+
"""Factory wrapper for creating orchestrator instances."""
|
|
79
|
+
|
|
80
|
+
def __init__(self):
|
|
81
|
+
"""Initialize the factory wrapper."""
|
|
82
|
+
self._factory = OrchestratorFactory()
|
|
83
|
+
|
|
84
|
+
def create(
|
|
85
|
+
self,
|
|
86
|
+
container: DIContainer,
|
|
87
|
+
orchestrator_type: Optional[str] = None,
|
|
88
|
+
**kwargs
|
|
89
|
+
) -> BaseOrchestrator:
|
|
90
|
+
"""
|
|
91
|
+
Create an orchestrator instance.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
container: DI container
|
|
95
|
+
orchestrator_type: Type of orchestrator to create
|
|
96
|
+
**kwargs: Additional arguments for orchestrator
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
Orchestrator instance
|
|
100
|
+
"""
|
|
101
|
+
config = container.resolve(Config)
|
|
102
|
+
|
|
103
|
+
# Get orchestrator type from config if not provided
|
|
104
|
+
if orchestrator_type is None:
|
|
105
|
+
orchestrator_type = config.get('orchestrator.type', 'subprocess')
|
|
106
|
+
|
|
107
|
+
# Get orchestrator config
|
|
108
|
+
orch_config = config.get(f'orchestrator.{orchestrator_type}', {})
|
|
109
|
+
|
|
110
|
+
# Merge with provided kwargs
|
|
111
|
+
orch_config.update(kwargs)
|
|
112
|
+
|
|
113
|
+
# Inject services into orchestrator config
|
|
114
|
+
if 'hook_manager' not in orch_config and container.is_registered(JSONRPCHookManager):
|
|
115
|
+
orch_config['hook_manager'] = container.resolve_optional(JSONRPCHookManager)
|
|
116
|
+
|
|
117
|
+
# Create orchestrator
|
|
118
|
+
orchestrator = self._factory.create_orchestrator(
|
|
119
|
+
orchestrator_type,
|
|
120
|
+
**orch_config
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
logger.info(f"Created {orchestrator_type} orchestrator")
|
|
124
|
+
return orchestrator
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class AgentServiceFactory(ServiceFactory):
|
|
128
|
+
"""Factory for creating agent-related services."""
|
|
129
|
+
|
|
130
|
+
def create(
|
|
131
|
+
self,
|
|
132
|
+
container: DIContainer,
|
|
133
|
+
framework_dir: Optional[Path] = None,
|
|
134
|
+
project_dir: Optional[Path] = None
|
|
135
|
+
) -> AgentDeploymentService:
|
|
136
|
+
"""
|
|
137
|
+
Create an agent deployment service.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
container: DI container
|
|
141
|
+
framework_dir: Framework directory path
|
|
142
|
+
project_dir: Project directory path
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Agent deployment service instance
|
|
146
|
+
"""
|
|
147
|
+
config = container.resolve(Config)
|
|
148
|
+
|
|
149
|
+
# Get directories from config if not provided
|
|
150
|
+
if framework_dir is None:
|
|
151
|
+
framework_dir = Path(config.get('framework.dir', 'framework'))
|
|
152
|
+
|
|
153
|
+
if project_dir is None:
|
|
154
|
+
project_dir = Path(config.get('project.dir', '.'))
|
|
155
|
+
|
|
156
|
+
# Create service with dependencies
|
|
157
|
+
service = AgentDeploymentService()
|
|
158
|
+
|
|
159
|
+
# Inject any required dependencies
|
|
160
|
+
if hasattr(service, 'set_directories'):
|
|
161
|
+
service.set_directories(framework_dir, project_dir)
|
|
162
|
+
|
|
163
|
+
logger.info("Created agent deployment service")
|
|
164
|
+
return service
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class SessionManagerFactory(ServiceFactory):
|
|
168
|
+
"""Factory for creating session managers."""
|
|
169
|
+
|
|
170
|
+
def create(
|
|
171
|
+
self,
|
|
172
|
+
container: DIContainer,
|
|
173
|
+
session_type: str = "standard",
|
|
174
|
+
**kwargs
|
|
175
|
+
) -> Any:
|
|
176
|
+
"""
|
|
177
|
+
Create a session manager instance.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
container: DI container
|
|
181
|
+
session_type: Type of session manager
|
|
182
|
+
**kwargs: Additional configuration
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
Session manager instance
|
|
186
|
+
"""
|
|
187
|
+
config = container.resolve(Config)
|
|
188
|
+
|
|
189
|
+
if session_type == "agent":
|
|
190
|
+
from ..core.agent_session_manager import AgentSessionManager
|
|
191
|
+
|
|
192
|
+
session_dir = kwargs.get('session_dir') or Path(config.get('session.dir', '.claude-mpm/sessions'))
|
|
193
|
+
max_sessions = kwargs.get('max_sessions_per_agent', config.get('session.max_per_agent', 3))
|
|
194
|
+
|
|
195
|
+
return AgentSessionManager(
|
|
196
|
+
session_dir=session_dir,
|
|
197
|
+
max_sessions_per_agent=max_sessions
|
|
198
|
+
)
|
|
199
|
+
else:
|
|
200
|
+
from ..core.session_manager import SessionManager
|
|
201
|
+
|
|
202
|
+
session_dir = kwargs.get('session_dir') or Path(config.get('session.dir', '.claude-mpm/sessions'))
|
|
203
|
+
|
|
204
|
+
return SessionManager(session_dir=session_dir)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class ConfigurationFactory(ServiceFactory):
|
|
208
|
+
"""Factory for creating configuration instances."""
|
|
209
|
+
|
|
210
|
+
def create(
|
|
211
|
+
self,
|
|
212
|
+
container: DIContainer,
|
|
213
|
+
config_data: Optional[Dict[str, Any]] = None,
|
|
214
|
+
config_path: Optional[Path] = None,
|
|
215
|
+
env_prefix: str = "CLAUDE_MPM"
|
|
216
|
+
) -> Config:
|
|
217
|
+
"""
|
|
218
|
+
Create a configuration instance.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
container: DI container
|
|
222
|
+
config_data: Initial configuration data
|
|
223
|
+
config_path: Path to configuration file
|
|
224
|
+
env_prefix: Environment variable prefix
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Configuration instance
|
|
228
|
+
"""
|
|
229
|
+
# Load from multiple sources
|
|
230
|
+
config = Config(config_data or {}, config_path)
|
|
231
|
+
|
|
232
|
+
# Load environment variables
|
|
233
|
+
import os
|
|
234
|
+
for key, value in os.environ.items():
|
|
235
|
+
if key.startswith(env_prefix):
|
|
236
|
+
# Convert CLAUDE_MPM_FOO_BAR to foo.bar
|
|
237
|
+
config_key = key[len(env_prefix)+1:].lower().replace('_', '.')
|
|
238
|
+
config.set(config_key, value)
|
|
239
|
+
|
|
240
|
+
logger.info("Created configuration instance")
|
|
241
|
+
return config
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
# Factory registry for easy access
|
|
245
|
+
class FactoryRegistry:
|
|
246
|
+
"""Registry for all service factories."""
|
|
247
|
+
|
|
248
|
+
def __init__(self):
|
|
249
|
+
"""Initialize factory registry."""
|
|
250
|
+
self._factories: Dict[str, ServiceFactory] = {
|
|
251
|
+
'hook_manager': HookManagerFactory(),
|
|
252
|
+
'orchestrator': OrchestratorFactoryWrapper(),
|
|
253
|
+
'agent_service': AgentServiceFactory(),
|
|
254
|
+
'session_manager': SessionManagerFactory(),
|
|
255
|
+
'configuration': ConfigurationFactory(),
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
def get_factory(self, name: str) -> ServiceFactory:
|
|
259
|
+
"""Get a factory by name."""
|
|
260
|
+
if name not in self._factories:
|
|
261
|
+
raise KeyError(f"Factory '{name}' not registered")
|
|
262
|
+
return self._factories[name]
|
|
263
|
+
|
|
264
|
+
def register_factory(self, name: str, factory: ServiceFactory) -> None:
|
|
265
|
+
"""Register a new factory."""
|
|
266
|
+
self._factories[name] = factory
|
|
267
|
+
logger.debug(f"Registered factory: {name}")
|
|
268
|
+
|
|
269
|
+
def create(self, factory_name: str, container: DIContainer, **kwargs) -> Any:
|
|
270
|
+
"""Create a service using a named factory."""
|
|
271
|
+
factory = self.get_factory(factory_name)
|
|
272
|
+
return factory.create(container, **kwargs)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
# Global factory registry
|
|
276
|
+
_factory_registry = FactoryRegistry()
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def get_factory_registry() -> FactoryRegistry:
|
|
280
|
+
"""Get the global factory registry."""
|
|
281
|
+
return _factory_registry
|