claude-mpm 3.4.27__py3-none-any.whl → 3.5.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 (123) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/INSTRUCTIONS.md +182 -299
  3. claude_mpm/agents/agent_loader.py +283 -57
  4. claude_mpm/agents/agent_loader_integration.py +6 -9
  5. claude_mpm/agents/base_agent.json +2 -1
  6. claude_mpm/agents/base_agent_loader.py +1 -1
  7. claude_mpm/cli/__init__.py +5 -7
  8. claude_mpm/cli/commands/__init__.py +0 -2
  9. claude_mpm/cli/commands/agents.py +1 -1
  10. claude_mpm/cli/commands/memory.py +1 -1
  11. claude_mpm/cli/commands/run.py +12 -0
  12. claude_mpm/cli/parser.py +0 -13
  13. claude_mpm/cli/utils.py +1 -1
  14. claude_mpm/config/__init__.py +44 -2
  15. claude_mpm/config/agent_config.py +348 -0
  16. claude_mpm/config/paths.py +322 -0
  17. claude_mpm/constants.py +0 -1
  18. claude_mpm/core/__init__.py +2 -5
  19. claude_mpm/core/agent_registry.py +63 -17
  20. claude_mpm/core/claude_runner.py +354 -43
  21. claude_mpm/core/config.py +7 -1
  22. claude_mpm/core/config_aliases.py +4 -3
  23. claude_mpm/core/config_paths.py +151 -0
  24. claude_mpm/core/factories.py +4 -50
  25. claude_mpm/core/logger.py +11 -13
  26. claude_mpm/core/service_registry.py +2 -2
  27. claude_mpm/dashboard/static/js/components/agent-inference.js +101 -25
  28. claude_mpm/dashboard/static/js/components/event-processor.js +3 -2
  29. claude_mpm/hooks/claude_hooks/hook_handler.py +343 -83
  30. claude_mpm/hooks/memory_integration_hook.py +1 -1
  31. claude_mpm/init.py +37 -6
  32. claude_mpm/scripts/socketio_daemon.py +6 -2
  33. claude_mpm/services/__init__.py +71 -3
  34. claude_mpm/services/agents/__init__.py +85 -0
  35. claude_mpm/services/agents/deployment/__init__.py +21 -0
  36. claude_mpm/services/{agent_deployment.py → agents/deployment/agent_deployment.py} +192 -41
  37. claude_mpm/services/{agent_lifecycle_manager.py → agents/deployment/agent_lifecycle_manager.py} +11 -10
  38. claude_mpm/services/agents/loading/__init__.py +11 -0
  39. claude_mpm/services/{agent_profile_loader.py → agents/loading/agent_profile_loader.py} +9 -8
  40. claude_mpm/services/{base_agent_manager.py → agents/loading/base_agent_manager.py} +2 -2
  41. claude_mpm/services/{framework_agent_loader.py → agents/loading/framework_agent_loader.py} +116 -40
  42. claude_mpm/services/agents/management/__init__.py +9 -0
  43. claude_mpm/services/{agent_management_service.py → agents/management/agent_management_service.py} +6 -5
  44. claude_mpm/services/agents/memory/__init__.py +21 -0
  45. claude_mpm/services/{agent_memory_manager.py → agents/memory/agent_memory_manager.py} +3 -3
  46. claude_mpm/services/agents/registry/__init__.py +29 -0
  47. claude_mpm/services/{agent_registry.py → agents/registry/agent_registry.py} +101 -16
  48. claude_mpm/services/{deployed_agent_discovery.py → agents/registry/deployed_agent_discovery.py} +12 -2
  49. claude_mpm/services/{agent_modification_tracker.py → agents/registry/modification_tracker.py} +6 -5
  50. claude_mpm/services/async_session_logger.py +584 -0
  51. claude_mpm/services/claude_session_logger.py +299 -0
  52. claude_mpm/services/framework_claude_md_generator/content_assembler.py +2 -2
  53. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +17 -17
  54. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +3 -3
  55. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +1 -1
  56. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +1 -1
  57. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +19 -24
  58. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +1 -1
  59. claude_mpm/services/framework_claude_md_generator.py +4 -2
  60. claude_mpm/services/memory/__init__.py +17 -0
  61. claude_mpm/services/{memory_builder.py → memory/builder.py} +3 -3
  62. claude_mpm/services/memory/cache/__init__.py +14 -0
  63. claude_mpm/services/{shared_prompt_cache.py → memory/cache/shared_prompt_cache.py} +1 -1
  64. claude_mpm/services/memory/cache/simple_cache.py +317 -0
  65. claude_mpm/services/{memory_optimizer.py → memory/optimizer.py} +1 -1
  66. claude_mpm/services/{memory_router.py → memory/router.py} +1 -1
  67. claude_mpm/services/optimized_hook_service.py +542 -0
  68. claude_mpm/services/project_registry.py +14 -8
  69. claude_mpm/services/response_tracker.py +237 -0
  70. claude_mpm/services/ticketing_service_original.py +4 -2
  71. claude_mpm/services/version_control/branch_strategy.py +3 -1
  72. claude_mpm/utils/paths.py +12 -10
  73. claude_mpm/utils/session_logging.py +114 -0
  74. claude_mpm/validation/agent_validator.py +2 -1
  75. {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/METADATA +26 -20
  76. {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/RECORD +83 -106
  77. claude_mpm/cli/commands/ui.py +0 -57
  78. claude_mpm/core/simple_runner.py +0 -1046
  79. claude_mpm/hooks/builtin/__init__.py +0 -1
  80. claude_mpm/hooks/builtin/logging_hook_example.py +0 -165
  81. claude_mpm/hooks/builtin/memory_hooks_example.py +0 -67
  82. claude_mpm/hooks/builtin/mpm_command_hook.py +0 -125
  83. claude_mpm/hooks/builtin/post_delegation_hook_example.py +0 -124
  84. claude_mpm/hooks/builtin/pre_delegation_hook_example.py +0 -125
  85. claude_mpm/hooks/builtin/submit_hook_example.py +0 -100
  86. claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +0 -237
  87. claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +0 -240
  88. claude_mpm/hooks/builtin/workflow_start_hook.py +0 -181
  89. claude_mpm/orchestration/__init__.py +0 -6
  90. claude_mpm/orchestration/archive/direct_orchestrator.py +0 -195
  91. claude_mpm/orchestration/archive/factory.py +0 -215
  92. claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +0 -188
  93. claude_mpm/orchestration/archive/hook_integration_example.py +0 -178
  94. claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +0 -826
  95. claude_mpm/orchestration/archive/orchestrator.py +0 -501
  96. claude_mpm/orchestration/archive/pexpect_orchestrator.py +0 -252
  97. claude_mpm/orchestration/archive/pty_orchestrator.py +0 -270
  98. claude_mpm/orchestration/archive/simple_orchestrator.py +0 -82
  99. claude_mpm/orchestration/archive/subprocess_orchestrator.py +0 -801
  100. claude_mpm/orchestration/archive/system_prompt_orchestrator.py +0 -278
  101. claude_mpm/orchestration/archive/wrapper_orchestrator.py +0 -187
  102. claude_mpm/schemas/workflow_validator.py +0 -411
  103. claude_mpm/services/parent_directory_manager/__init__.py +0 -577
  104. claude_mpm/services/parent_directory_manager/backup_manager.py +0 -258
  105. claude_mpm/services/parent_directory_manager/config_manager.py +0 -210
  106. claude_mpm/services/parent_directory_manager/deduplication_manager.py +0 -279
  107. claude_mpm/services/parent_directory_manager/framework_protector.py +0 -143
  108. claude_mpm/services/parent_directory_manager/operations.py +0 -186
  109. claude_mpm/services/parent_directory_manager/state_manager.py +0 -624
  110. claude_mpm/services/parent_directory_manager/template_deployer.py +0 -579
  111. claude_mpm/services/parent_directory_manager/validation_manager.py +0 -378
  112. claude_mpm/services/parent_directory_manager/version_control_helper.py +0 -339
  113. claude_mpm/services/parent_directory_manager/version_manager.py +0 -222
  114. claude_mpm/ui/__init__.py +0 -1
  115. claude_mpm/ui/rich_terminal_ui.py +0 -295
  116. claude_mpm/ui/terminal_ui.py +0 -328
  117. /claude_mpm/services/{agent_versioning.py → agents/deployment/agent_versioning.py} +0 -0
  118. /claude_mpm/services/{agent_capabilities_generator.py → agents/management/agent_capabilities_generator.py} +0 -0
  119. /claude_mpm/services/{agent_persistence_service.py → agents/memory/agent_persistence_service.py} +0 -0
  120. {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/WHEEL +0 -0
  121. {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/entry_points.txt +0 -0
  122. {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/licenses/LICENSE +0 -0
  123. {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,317 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Simple In-Memory Cache Service Implementation
4
+ ============================================
5
+
6
+ Provides a thread-safe in-memory cache implementation with TTL support,
7
+ file modification tracking, and cache invalidation capabilities.
8
+
9
+ Features:
10
+ - TTL-based expiration
11
+ - File modification time tracking for automatic invalidation
12
+ - Pattern-based cache invalidation
13
+ - Thread-safe operations
14
+ - Performance metrics tracking
15
+ """
16
+
17
+ import time
18
+ import threading
19
+ import fnmatch
20
+ import os
21
+ from pathlib import Path
22
+ from typing import Any, Dict, Optional, Set, List
23
+ from dataclasses import dataclass, field
24
+ import logging
25
+
26
+ from claude_mpm.core.interfaces import ICacheService
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+
31
+ @dataclass
32
+ class CacheEntry:
33
+ """Represents a single cache entry with metadata."""
34
+ key: str
35
+ value: Any
36
+ created_at: float
37
+ ttl: Optional[float] = None
38
+ expires_at: Optional[float] = None
39
+ access_count: int = 0
40
+ last_accessed: float = field(default_factory=time.time)
41
+ tracked_files: Set[Path] = field(default_factory=set)
42
+ file_mtimes: Dict[str, float] = field(default_factory=dict)
43
+
44
+ def is_expired(self) -> bool:
45
+ """Check if the cache entry has expired."""
46
+ if self.expires_at is None:
47
+ return False
48
+ return time.time() > self.expires_at
49
+
50
+ def is_stale(self) -> bool:
51
+ """Check if tracked files have been modified."""
52
+ for file_path in self.tracked_files:
53
+ if file_path.exists():
54
+ current_mtime = file_path.stat().st_mtime
55
+ stored_mtime = self.file_mtimes.get(str(file_path), 0)
56
+ if current_mtime > stored_mtime:
57
+ return True
58
+ else:
59
+ # File was deleted, cache is stale
60
+ return True
61
+ return False
62
+
63
+
64
+ class SimpleCacheService(ICacheService):
65
+ """
66
+ Thread-safe in-memory cache service with TTL and file tracking support.
67
+
68
+ This implementation provides:
69
+ - Automatic expiration based on TTL
70
+ - File modification tracking for cache invalidation
71
+ - Pattern-based invalidation
72
+ - Thread safety with read-write locks
73
+ - Performance metrics
74
+ """
75
+
76
+ def __init__(self, default_ttl: int = 3600, max_size: int = 1000):
77
+ """
78
+ Initialize the cache service.
79
+
80
+ Args:
81
+ default_ttl: Default time-to-live in seconds (default: 1 hour)
82
+ max_size: Maximum number of cache entries (default: 1000)
83
+ """
84
+ self.cache: Dict[str, CacheEntry] = {}
85
+ self.default_ttl = default_ttl
86
+ self.max_size = max_size
87
+ self.lock = threading.RLock()
88
+
89
+ # Performance metrics
90
+ self.metrics = {
91
+ 'hits': 0,
92
+ 'misses': 0,
93
+ 'sets': 0,
94
+ 'deletes': 0,
95
+ 'invalidations': 0,
96
+ 'evictions': 0,
97
+ 'stale_hits': 0
98
+ }
99
+
100
+ # Start background cleanup thread
101
+ self.cleanup_interval = 60 # seconds
102
+ self.cleanup_thread = threading.Thread(target=self._cleanup_loop, daemon=True)
103
+ self.cleanup_thread.start()
104
+
105
+ logger.info(f"SimpleCacheService initialized with TTL={default_ttl}s, max_size={max_size}")
106
+
107
+ def get(self, key: str) -> Any:
108
+ """
109
+ Get value from cache.
110
+
111
+ Args:
112
+ key: Cache key
113
+
114
+ Returns:
115
+ Cached value or None if not found/expired/stale
116
+ """
117
+ with self.lock:
118
+ entry = self.cache.get(key)
119
+
120
+ if entry is None:
121
+ self.metrics['misses'] += 1
122
+ return None
123
+
124
+ # Check expiration
125
+ if entry.is_expired():
126
+ del self.cache[key]
127
+ self.metrics['misses'] += 1
128
+ logger.debug(f"Cache miss (expired): {key}")
129
+ return None
130
+
131
+ # Check if files have been modified
132
+ if entry.is_stale():
133
+ del self.cache[key]
134
+ self.metrics['stale_hits'] += 1
135
+ self.metrics['misses'] += 1
136
+ logger.debug(f"Cache miss (stale): {key}")
137
+ return None
138
+
139
+ # Update access metadata
140
+ entry.access_count += 1
141
+ entry.last_accessed = time.time()
142
+
143
+ self.metrics['hits'] += 1
144
+ return entry.value
145
+
146
+ def set(self, key: str, value: Any, ttl: Optional[int] = None,
147
+ tracked_files: Optional[List[Path]] = None) -> None:
148
+ """
149
+ Set value in cache with optional TTL and file tracking.
150
+
151
+ Args:
152
+ key: Cache key
153
+ value: Value to cache
154
+ ttl: Time-to-live in seconds (uses default if None)
155
+ tracked_files: List of files to track for modification
156
+ """
157
+ with self.lock:
158
+ # Evict entries if at max size
159
+ if len(self.cache) >= self.max_size and key not in self.cache:
160
+ self._evict_lru()
161
+
162
+ ttl = ttl if ttl is not None else self.default_ttl
163
+ expires_at = time.time() + ttl if ttl > 0 else None
164
+
165
+ # Track file modification times
166
+ file_mtimes = {}
167
+ tracked_file_set = set()
168
+
169
+ if tracked_files:
170
+ for file_path in tracked_files:
171
+ if isinstance(file_path, str):
172
+ file_path = Path(file_path)
173
+ if file_path.exists():
174
+ tracked_file_set.add(file_path)
175
+ file_mtimes[str(file_path)] = file_path.stat().st_mtime
176
+
177
+ entry = CacheEntry(
178
+ key=key,
179
+ value=value,
180
+ created_at=time.time(),
181
+ ttl=ttl,
182
+ expires_at=expires_at,
183
+ tracked_files=tracked_file_set,
184
+ file_mtimes=file_mtimes
185
+ )
186
+
187
+ self.cache[key] = entry
188
+ self.metrics['sets'] += 1
189
+
190
+ logger.debug(f"Cache set: {key} (TTL={ttl}s, tracking {len(tracked_file_set)} files)")
191
+
192
+ def delete(self, key: str) -> bool:
193
+ """
194
+ Delete key from cache.
195
+
196
+ Args:
197
+ key: Cache key
198
+
199
+ Returns:
200
+ True if key was deleted, False if not found
201
+ """
202
+ with self.lock:
203
+ if key in self.cache:
204
+ del self.cache[key]
205
+ self.metrics['deletes'] += 1
206
+ logger.debug(f"Cache delete: {key}")
207
+ return True
208
+ return False
209
+
210
+ def invalidate(self, pattern: str) -> int:
211
+ """
212
+ Invalidate keys matching pattern.
213
+
214
+ Args:
215
+ pattern: Unix-style pattern (e.g., "agent_*")
216
+
217
+ Returns:
218
+ Number of keys invalidated
219
+ """
220
+ with self.lock:
221
+ keys_to_delete = []
222
+
223
+ for key in self.cache.keys():
224
+ if fnmatch.fnmatch(key, pattern):
225
+ keys_to_delete.append(key)
226
+
227
+ for key in keys_to_delete:
228
+ del self.cache[key]
229
+
230
+ count = len(keys_to_delete)
231
+ if count > 0:
232
+ self.metrics['invalidations'] += count
233
+ logger.info(f"Invalidated {count} cache entries matching pattern: {pattern}")
234
+
235
+ return count
236
+
237
+ def clear(self) -> None:
238
+ """Clear all cache entries."""
239
+ with self.lock:
240
+ count = len(self.cache)
241
+ self.cache.clear()
242
+ self.metrics['invalidations'] += count
243
+ logger.info(f"Cleared {count} cache entries")
244
+
245
+ def get_cache_metrics(self) -> Dict[str, Any]:
246
+ """
247
+ Get cache performance metrics.
248
+
249
+ Returns:
250
+ Dictionary containing cache metrics
251
+ """
252
+ with self.lock:
253
+ total_requests = self.metrics['hits'] + self.metrics['misses']
254
+ hit_rate = (self.metrics['hits'] / total_requests * 100) if total_requests > 0 else 0
255
+
256
+ return {
257
+ 'size': len(self.cache),
258
+ 'max_size': self.max_size,
259
+ 'hits': self.metrics['hits'],
260
+ 'misses': self.metrics['misses'],
261
+ 'hit_rate': f"{hit_rate:.2f}%",
262
+ 'sets': self.metrics['sets'],
263
+ 'deletes': self.metrics['deletes'],
264
+ 'invalidations': self.metrics['invalidations'],
265
+ 'evictions': self.metrics['evictions'],
266
+ 'stale_hits': self.metrics['stale_hits'],
267
+ 'total_requests': total_requests
268
+ }
269
+
270
+ def track_file(self, key: str, file_path: Path) -> None:
271
+ """
272
+ Add a file to track for an existing cache entry.
273
+
274
+ Args:
275
+ key: Cache key
276
+ file_path: File path to track
277
+ """
278
+ with self.lock:
279
+ entry = self.cache.get(key)
280
+ if entry and file_path.exists():
281
+ entry.tracked_files.add(file_path)
282
+ entry.file_mtimes[str(file_path)] = file_path.stat().st_mtime
283
+ logger.debug(f"Added file tracking for {key}: {file_path}")
284
+
285
+ def _evict_lru(self) -> None:
286
+ """Evict least recently used entry."""
287
+ if not self.cache:
288
+ return
289
+
290
+ # Find LRU entry
291
+ lru_key = min(self.cache.keys(),
292
+ key=lambda k: self.cache[k].last_accessed)
293
+
294
+ del self.cache[lru_key]
295
+ self.metrics['evictions'] += 1
296
+ logger.debug(f"Evicted LRU entry: {lru_key}")
297
+
298
+ def _cleanup_loop(self) -> None:
299
+ """Background thread to clean up expired entries."""
300
+ while True:
301
+ time.sleep(self.cleanup_interval)
302
+ self._cleanup_expired()
303
+
304
+ def _cleanup_expired(self) -> None:
305
+ """Remove expired and stale entries."""
306
+ with self.lock:
307
+ keys_to_delete = []
308
+
309
+ for key, entry in self.cache.items():
310
+ if entry.is_expired() or entry.is_stale():
311
+ keys_to_delete.append(key)
312
+
313
+ for key in keys_to_delete:
314
+ del self.cache[key]
315
+
316
+ if keys_to_delete:
317
+ logger.debug(f"Cleaned up {len(keys_to_delete)} expired/stale entries")
@@ -29,7 +29,7 @@ from typing import Dict, List, Optional, Any, Set, Tuple
29
29
  from datetime import datetime
30
30
  from difflib import SequenceMatcher
31
31
 
32
- from claude_mpm.core import LoggerMixin
32
+ from claude_mpm.core.mixins import LoggerMixin
33
33
  from claude_mpm.core.config import Config
34
34
  from claude_mpm.utils.paths import PathResolver
35
35
 
@@ -24,7 +24,7 @@ import re
24
24
  from typing import Dict, List, Optional, Any, Tuple
25
25
  from datetime import datetime
26
26
 
27
- from claude_mpm.core import LoggerMixin
27
+ from claude_mpm.core.mixins import LoggerMixin
28
28
  from claude_mpm.core.config import Config
29
29
 
30
30