claude-mpm 3.7.8__py3-none-any.whl → 3.9.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.
Files changed (100) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_PM.md +0 -106
  3. claude_mpm/agents/INSTRUCTIONS.md +0 -96
  4. claude_mpm/agents/MEMORY.md +94 -0
  5. claude_mpm/agents/WORKFLOW.md +86 -0
  6. claude_mpm/agents/templates/code_analyzer.json +2 -2
  7. claude_mpm/agents/templates/data_engineer.json +1 -1
  8. claude_mpm/agents/templates/documentation.json +1 -1
  9. claude_mpm/agents/templates/engineer.json +1 -1
  10. claude_mpm/agents/templates/ops.json +1 -1
  11. claude_mpm/agents/templates/qa.json +1 -1
  12. claude_mpm/agents/templates/research.json +1 -1
  13. claude_mpm/agents/templates/security.json +1 -1
  14. claude_mpm/agents/templates/ticketing.json +3 -8
  15. claude_mpm/agents/templates/version_control.json +1 -1
  16. claude_mpm/agents/templates/web_qa.json +2 -2
  17. claude_mpm/agents/templates/web_ui.json +2 -2
  18. claude_mpm/cli/__init__.py +2 -2
  19. claude_mpm/cli/commands/__init__.py +2 -1
  20. claude_mpm/cli/commands/agents.py +8 -3
  21. claude_mpm/cli/commands/tickets.py +596 -19
  22. claude_mpm/cli/parser.py +217 -5
  23. claude_mpm/config/__init__.py +30 -39
  24. claude_mpm/config/socketio_config.py +8 -5
  25. claude_mpm/constants.py +13 -0
  26. claude_mpm/core/__init__.py +8 -18
  27. claude_mpm/core/cache.py +596 -0
  28. claude_mpm/core/claude_runner.py +166 -622
  29. claude_mpm/core/config.py +7 -3
  30. claude_mpm/core/constants.py +339 -0
  31. claude_mpm/core/container.py +548 -38
  32. claude_mpm/core/exceptions.py +392 -0
  33. claude_mpm/core/framework_loader.py +249 -93
  34. claude_mpm/core/interactive_session.py +479 -0
  35. claude_mpm/core/interfaces.py +424 -0
  36. claude_mpm/core/lazy.py +467 -0
  37. claude_mpm/core/logging_config.py +444 -0
  38. claude_mpm/core/oneshot_session.py +465 -0
  39. claude_mpm/core/optimized_agent_loader.py +485 -0
  40. claude_mpm/core/optimized_startup.py +490 -0
  41. claude_mpm/core/service_registry.py +52 -26
  42. claude_mpm/core/socketio_pool.py +162 -5
  43. claude_mpm/core/types.py +292 -0
  44. claude_mpm/core/typing_utils.py +477 -0
  45. claude_mpm/hooks/claude_hooks/hook_handler.py +213 -99
  46. claude_mpm/init.py +2 -1
  47. claude_mpm/services/__init__.py +78 -14
  48. claude_mpm/services/agent/__init__.py +24 -0
  49. claude_mpm/services/agent/deployment.py +2548 -0
  50. claude_mpm/services/agent/management.py +598 -0
  51. claude_mpm/services/agent/registry.py +813 -0
  52. claude_mpm/services/agents/deployment/agent_deployment.py +728 -308
  53. claude_mpm/services/agents/memory/agent_memory_manager.py +160 -4
  54. claude_mpm/services/async_session_logger.py +8 -3
  55. claude_mpm/services/communication/__init__.py +21 -0
  56. claude_mpm/services/communication/socketio.py +1933 -0
  57. claude_mpm/services/communication/websocket.py +479 -0
  58. claude_mpm/services/core/__init__.py +123 -0
  59. claude_mpm/services/core/base.py +247 -0
  60. claude_mpm/services/core/interfaces.py +951 -0
  61. claude_mpm/services/framework_claude_md_generator/__init__.py +10 -3
  62. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +14 -11
  63. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +23 -23
  64. claude_mpm/services/framework_claude_md_generator.py +3 -2
  65. claude_mpm/services/health_monitor.py +4 -3
  66. claude_mpm/services/hook_service.py +64 -4
  67. claude_mpm/services/infrastructure/__init__.py +21 -0
  68. claude_mpm/services/infrastructure/logging.py +202 -0
  69. claude_mpm/services/infrastructure/monitoring.py +893 -0
  70. claude_mpm/services/memory/indexed_memory.py +648 -0
  71. claude_mpm/services/project/__init__.py +21 -0
  72. claude_mpm/services/project/analyzer.py +864 -0
  73. claude_mpm/services/project/registry.py +608 -0
  74. claude_mpm/services/project_analyzer.py +95 -2
  75. claude_mpm/services/recovery_manager.py +15 -9
  76. claude_mpm/services/response_tracker.py +3 -5
  77. claude_mpm/services/socketio/__init__.py +25 -0
  78. claude_mpm/services/socketio/handlers/__init__.py +25 -0
  79. claude_mpm/services/socketio/handlers/base.py +121 -0
  80. claude_mpm/services/socketio/handlers/connection.py +198 -0
  81. claude_mpm/services/socketio/handlers/file.py +213 -0
  82. claude_mpm/services/socketio/handlers/git.py +723 -0
  83. claude_mpm/services/socketio/handlers/memory.py +27 -0
  84. claude_mpm/services/socketio/handlers/project.py +25 -0
  85. claude_mpm/services/socketio/handlers/registry.py +145 -0
  86. claude_mpm/services/socketio_client_manager.py +12 -7
  87. claude_mpm/services/socketio_server.py +156 -30
  88. claude_mpm/services/ticket_manager.py +172 -9
  89. claude_mpm/services/ticket_manager_di.py +1 -1
  90. claude_mpm/services/version_control/semantic_versioning.py +80 -7
  91. claude_mpm/services/version_control/version_parser.py +528 -0
  92. claude_mpm/utils/error_handler.py +1 -1
  93. claude_mpm/validation/agent_validator.py +27 -14
  94. claude_mpm/validation/frontmatter_validator.py +231 -0
  95. {claude_mpm-3.7.8.dist-info → claude_mpm-3.9.0.dist-info}/METADATA +38 -128
  96. {claude_mpm-3.7.8.dist-info → claude_mpm-3.9.0.dist-info}/RECORD +100 -59
  97. {claude_mpm-3.7.8.dist-info → claude_mpm-3.9.0.dist-info}/WHEEL +0 -0
  98. {claude_mpm-3.7.8.dist-info → claude_mpm-3.9.0.dist-info}/entry_points.txt +0 -0
  99. {claude_mpm-3.7.8.dist-info → claude_mpm-3.9.0.dist-info}/licenses/LICENSE +0 -0
  100. {claude_mpm-3.7.8.dist-info → claude_mpm-3.9.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,467 @@
1
+ #!/usr/bin/env python3
2
+ """Lazy loading service infrastructure for deferred instantiation.
3
+
4
+ This module provides a lazy loading mechanism for services that are expensive
5
+ to initialize but may not be used in every execution path.
6
+
7
+ WHY lazy loading:
8
+ - Reduces startup time from 3-5 seconds to <2 seconds
9
+ - Defers heavy initialization until services are actually needed
10
+ - Maintains backward compatibility with eager instantiation
11
+ - Provides transparent access to underlying services
12
+ """
13
+
14
+ import asyncio
15
+ import functools
16
+ import threading
17
+ import time
18
+ from typing import Any, Callable, Dict, Optional, Type, TypeVar, Generic
19
+ from datetime import datetime
20
+ from dataclasses import dataclass, field
21
+
22
+ from ..core.logger import get_logger
23
+
24
+ T = TypeVar('T')
25
+
26
+
27
+ @dataclass
28
+ class LazyMetrics:
29
+ """Metrics for lazy loading performance."""
30
+ created_at: datetime = field(default_factory=datetime.now)
31
+ first_access: Optional[datetime] = None
32
+ initialization_time: float = 0.0
33
+ access_count: int = 0
34
+ initialization_error: Optional[Exception] = None
35
+ is_initialized: bool = False
36
+
37
+
38
+ class LazyService(Generic[T]):
39
+ """Lazy loading wrapper for expensive services.
40
+
41
+ WHY this design:
42
+ - Services are only initialized when first accessed
43
+ - Thread-safe initialization ensures single instance
44
+ - Transparent proxy pattern preserves service interface
45
+ - Metrics tracking for performance monitoring
46
+
47
+ Example:
48
+ # Wrap expensive service
49
+ lazy_analyzer = LazyService(
50
+ service_class=ProjectAnalyzer,
51
+ init_args=(),
52
+ init_kwargs={'config': config}
53
+ )
54
+
55
+ # Service is not initialized yet
56
+ assert not lazy_analyzer.is_initialized
57
+
58
+ # First access triggers initialization
59
+ result = lazy_analyzer.analyze() # Initializes here
60
+
61
+ # Subsequent accesses use cached instance
62
+ result2 = lazy_analyzer.analyze() # No initialization
63
+ """
64
+
65
+ def __init__(
66
+ self,
67
+ service_class: Type[T],
68
+ init_args: tuple = (),
69
+ init_kwargs: dict = None,
70
+ name: Optional[str] = None,
71
+ eager: bool = False
72
+ ):
73
+ """Initialize lazy service wrapper.
74
+
75
+ Args:
76
+ service_class: The class to lazily instantiate
77
+ init_args: Positional arguments for initialization
78
+ init_kwargs: Keyword arguments for initialization
79
+ name: Optional name for logging
80
+ eager: If True, initialize immediately (for testing)
81
+ """
82
+ self._service_class = service_class
83
+ self._init_args = init_args
84
+ self._init_kwargs = init_kwargs or {}
85
+ self._name = name or service_class.__name__
86
+ self._eager = eager
87
+
88
+ self._instance: Optional[T] = None
89
+ self._lock = threading.RLock()
90
+ self._metrics = LazyMetrics()
91
+ self._logger = get_logger(f"lazy.{self._name}")
92
+
93
+ # Initialize immediately if eager mode
94
+ if eager:
95
+ self._ensure_initialized()
96
+
97
+ @property
98
+ def is_initialized(self) -> bool:
99
+ """Check if service has been initialized."""
100
+ return self._instance is not None
101
+
102
+ @property
103
+ def metrics(self) -> LazyMetrics:
104
+ """Get lazy loading metrics."""
105
+ return self._metrics
106
+
107
+ def _ensure_initialized(self) -> T:
108
+ """Ensure service is initialized, creating if necessary.
109
+
110
+ Thread-safe initialization with metrics tracking.
111
+ """
112
+ if self._instance is not None:
113
+ return self._instance
114
+
115
+ with self._lock:
116
+ # Double-check pattern for thread safety
117
+ if self._instance is not None:
118
+ return self._instance
119
+
120
+ # Track initialization
121
+ start_time = time.time()
122
+ if self._metrics.first_access is None:
123
+ self._metrics.first_access = datetime.now()
124
+
125
+ try:
126
+ self._logger.debug(f"Initializing lazy service: {self._name}")
127
+
128
+ # Create the actual service instance
129
+ self._instance = self._service_class(
130
+ *self._init_args,
131
+ **self._init_kwargs
132
+ )
133
+
134
+ # Update metrics
135
+ self._metrics.initialization_time = time.time() - start_time
136
+ self._metrics.is_initialized = True
137
+
138
+ self._logger.info(
139
+ f"Lazy service {self._name} initialized in "
140
+ f"{self._metrics.initialization_time:.2f}s"
141
+ )
142
+
143
+ return self._instance
144
+
145
+ except Exception as e:
146
+ self._metrics.initialization_error = e
147
+ self._logger.error(f"Failed to initialize {self._name}: {e}")
148
+ raise
149
+
150
+ def __getattr__(self, name: str) -> Any:
151
+ """Proxy attribute access to the underlying service.
152
+
153
+ This is called when an attribute is not found on LazyService itself.
154
+ It ensures initialization and forwards the request.
155
+ """
156
+ # Avoid recursion for internal attributes
157
+ if name.startswith('_'):
158
+ raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
159
+
160
+ # Initialize if needed
161
+ instance = self._ensure_initialized()
162
+ self._metrics.access_count += 1
163
+
164
+ # Forward attribute access
165
+ return getattr(instance, name)
166
+
167
+ def __call__(self, *args, **kwargs) -> Any:
168
+ """Make LazyService callable if underlying service is."""
169
+ instance = self._ensure_initialized()
170
+ self._metrics.access_count += 1
171
+ return instance(*args, **kwargs)
172
+
173
+ def __repr__(self) -> str:
174
+ """String representation of lazy service."""
175
+ status = "initialized" if self.is_initialized else "pending"
176
+ return f"<LazyService({self._name}) [{status}]>"
177
+
178
+
179
+ class LazyServiceRegistry:
180
+ """Registry for managing lazy services.
181
+
182
+ WHY registry pattern:
183
+ - Central management of all lazy services
184
+ - Bulk initialization for testing
185
+ - Performance metrics aggregation
186
+ - Service dependency resolution
187
+ """
188
+
189
+ def __init__(self):
190
+ self._services: Dict[str, LazyService] = {}
191
+ self._logger = get_logger("lazy_registry")
192
+ self._lock = threading.Lock()
193
+
194
+ def register(
195
+ self,
196
+ name: str,
197
+ service_class: Type,
198
+ init_args: tuple = (),
199
+ init_kwargs: dict = None,
200
+ eager: bool = False
201
+ ) -> LazyService:
202
+ """Register a lazy service.
203
+
204
+ Args:
205
+ name: Unique name for the service
206
+ service_class: The service class to wrap
207
+ init_args: Initialization arguments
208
+ init_kwargs: Initialization keyword arguments
209
+ eager: Whether to initialize immediately
210
+
211
+ Returns:
212
+ The registered LazyService instance
213
+ """
214
+ with self._lock:
215
+ if name in self._services:
216
+ self._logger.warning(f"Overwriting existing service: {name}")
217
+
218
+ service = LazyService(
219
+ service_class=service_class,
220
+ init_args=init_args,
221
+ init_kwargs=init_kwargs,
222
+ name=name,
223
+ eager=eager
224
+ )
225
+
226
+ self._services[name] = service
227
+ self._logger.debug(f"Registered lazy service: {name}")
228
+ return service
229
+
230
+ def get(self, name: str) -> Optional[LazyService]:
231
+ """Get a registered service by name."""
232
+ return self._services.get(name)
233
+
234
+ def initialize_all(self) -> Dict[str, float]:
235
+ """Initialize all registered services.
236
+
237
+ Useful for testing or preloading.
238
+
239
+ Returns:
240
+ Dictionary mapping service names to initialization times
241
+ """
242
+ init_times = {}
243
+
244
+ for name, service in self._services.items():
245
+ if not service.is_initialized:
246
+ try:
247
+ service._ensure_initialized()
248
+ init_times[name] = service.metrics.initialization_time
249
+ except Exception as e:
250
+ self._logger.error(f"Failed to initialize {name}: {e}")
251
+ init_times[name] = -1.0
252
+
253
+ return init_times
254
+
255
+ def get_metrics(self) -> Dict[str, Dict[str, Any]]:
256
+ """Get metrics for all registered services."""
257
+ metrics = {}
258
+
259
+ for name, service in self._services.items():
260
+ m = service.metrics
261
+ metrics[name] = {
262
+ "initialized": service.is_initialized,
263
+ "first_access": m.first_access.isoformat() if m.first_access else None,
264
+ "initialization_time": m.initialization_time,
265
+ "access_count": m.access_count,
266
+ "has_error": m.initialization_error is not None
267
+ }
268
+
269
+ return metrics
270
+
271
+ def reset(self):
272
+ """Clear all registered services."""
273
+ with self._lock:
274
+ self._services.clear()
275
+ self._logger.debug("Registry reset")
276
+
277
+
278
+ # Global registry instance
279
+ _registry = LazyServiceRegistry()
280
+
281
+
282
+ def lazy_load(
283
+ service_class: Type,
284
+ name: Optional[str] = None,
285
+ init_args: tuple = (),
286
+ init_kwargs: dict = None
287
+ ) -> LazyService:
288
+ """Convenience function to create and register a lazy service.
289
+
290
+ Args:
291
+ service_class: The service class to wrap
292
+ name: Optional name (defaults to class name)
293
+ init_args: Initialization arguments
294
+ init_kwargs: Initialization keyword arguments
295
+
296
+ Returns:
297
+ Registered LazyService instance
298
+
299
+ Example:
300
+ # Create lazy service
301
+ analyzer = lazy_load(
302
+ ProjectAnalyzer,
303
+ init_kwargs={'config': config}
304
+ )
305
+
306
+ # Use like normal service
307
+ result = analyzer.analyze_project()
308
+ """
309
+ if name is None:
310
+ name = service_class.__name__
311
+
312
+ return _registry.register(
313
+ name=name,
314
+ service_class=service_class,
315
+ init_args=init_args,
316
+ init_kwargs=init_kwargs
317
+ )
318
+
319
+
320
+ def get_lazy_service(name: str) -> Optional[LazyService]:
321
+ """Get a registered lazy service by name."""
322
+ return _registry.get(name)
323
+
324
+
325
+ def get_lazy_metrics() -> Dict[str, Dict[str, Any]]:
326
+ """Get metrics for all lazy services."""
327
+ return _registry.get_metrics()
328
+
329
+
330
+ def initialize_all_services() -> Dict[str, float]:
331
+ """Initialize all registered lazy services."""
332
+ return _registry.initialize_all()
333
+
334
+
335
+ class lazy_property:
336
+ """Decorator for lazy property initialization.
337
+
338
+ WHY decorator pattern:
339
+ - Pythonic approach for lazy attributes
340
+ - Caches expensive computations
341
+ - Thread-safe initialization
342
+ - Transparent to callers
343
+
344
+ Example:
345
+ class MyService:
346
+ @lazy_property
347
+ def expensive_data(self):
348
+ # This only runs once
349
+ return self._compute_expensive_data()
350
+ """
351
+
352
+ def __init__(self, func: Callable) -> None:
353
+ self.func = func
354
+ self.lock = threading.RLock()
355
+ functools.update_wrapper(self, func)
356
+
357
+ def __get__(self, obj: Any, objtype: Type = None) -> Any:
358
+ if obj is None:
359
+ return self
360
+
361
+ # Check if already cached
362
+ val = obj.__dict__.get(self.func.__name__)
363
+ if val is not None:
364
+ return val
365
+
366
+ # Thread-safe initialization
367
+ with self.lock:
368
+ # Double-check pattern
369
+ val = obj.__dict__.get(self.func.__name__)
370
+ if val is not None:
371
+ return val
372
+
373
+ # Compute and cache
374
+ val = self.func(obj)
375
+ obj.__dict__[self.func.__name__] = val
376
+ return val
377
+
378
+
379
+ class AsyncLazyService(Generic[T]):
380
+ """Async version of LazyService for async services.
381
+
382
+ Supports services that require async initialization.
383
+ """
384
+
385
+ def __init__(
386
+ self,
387
+ service_class: Type[T],
388
+ init_args: tuple = (),
389
+ init_kwargs: dict = None,
390
+ name: Optional[str] = None
391
+ ):
392
+ self._service_class = service_class
393
+ self._init_args = init_args
394
+ self._init_kwargs = init_kwargs or {}
395
+ self._name = name or service_class.__name__
396
+
397
+ self._instance: Optional[T] = None
398
+ self._lock = asyncio.Lock()
399
+ self._metrics = LazyMetrics()
400
+ self._logger = get_logger(f"async_lazy.{self._name}")
401
+
402
+ @property
403
+ def is_initialized(self) -> bool:
404
+ """Check if service has been initialized."""
405
+ return self._instance is not None
406
+
407
+ async def _ensure_initialized(self) -> T:
408
+ """Ensure service is initialized asynchronously."""
409
+ if self._instance is not None:
410
+ return self._instance
411
+
412
+ async with self._lock:
413
+ if self._instance is not None:
414
+ return self._instance
415
+
416
+ start_time = time.time()
417
+ if self._metrics.first_access is None:
418
+ self._metrics.first_access = datetime.now()
419
+
420
+ try:
421
+ self._logger.debug(f"Async initializing: {self._name}")
422
+
423
+ # Handle both async and sync initialization
424
+ if asyncio.iscoroutinefunction(self._service_class):
425
+ self._instance = await self._service_class(
426
+ *self._init_args,
427
+ **self._init_kwargs
428
+ )
429
+ else:
430
+ # Run sync initialization in executor
431
+ loop = asyncio.get_event_loop()
432
+ self._instance = await loop.run_in_executor(
433
+ None,
434
+ self._service_class,
435
+ *self._init_args,
436
+ **self._init_kwargs
437
+ )
438
+
439
+ self._metrics.initialization_time = time.time() - start_time
440
+ self._metrics.is_initialized = True
441
+
442
+ self._logger.info(
443
+ f"Async service {self._name} initialized in "
444
+ f"{self._metrics.initialization_time:.2f}s"
445
+ )
446
+
447
+ return self._instance
448
+
449
+ except Exception as e:
450
+ self._metrics.initialization_error = e
451
+ self._logger.error(f"Failed to initialize {self._name}: {e}")
452
+ raise
453
+
454
+ def __getattr__(self, name: str) -> Any:
455
+ """Proxy attribute access to the underlying service."""
456
+ if name.startswith('_'):
457
+ raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
458
+
459
+ async def async_wrapper(*args, **kwargs):
460
+ instance = await self._ensure_initialized()
461
+ self._metrics.access_count += 1
462
+ attr = getattr(instance, name)
463
+ if asyncio.iscoroutinefunction(attr):
464
+ return await attr(*args, **kwargs)
465
+ return attr(*args, **kwargs)
466
+
467
+ return async_wrapper