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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/INSTRUCTIONS.md +182 -299
- claude_mpm/agents/agent_loader.py +283 -57
- claude_mpm/agents/agent_loader_integration.py +6 -9
- claude_mpm/agents/base_agent.json +2 -1
- claude_mpm/agents/base_agent_loader.py +1 -1
- claude_mpm/cli/__init__.py +5 -7
- claude_mpm/cli/commands/__init__.py +0 -2
- claude_mpm/cli/commands/agents.py +1 -1
- claude_mpm/cli/commands/memory.py +1 -1
- claude_mpm/cli/commands/run.py +12 -0
- claude_mpm/cli/parser.py +0 -13
- claude_mpm/cli/utils.py +1 -1
- claude_mpm/config/__init__.py +44 -2
- claude_mpm/config/agent_config.py +348 -0
- claude_mpm/config/paths.py +322 -0
- claude_mpm/constants.py +0 -1
- claude_mpm/core/__init__.py +2 -5
- claude_mpm/core/agent_registry.py +63 -17
- claude_mpm/core/claude_runner.py +354 -43
- claude_mpm/core/config.py +7 -1
- claude_mpm/core/config_aliases.py +4 -3
- claude_mpm/core/config_paths.py +151 -0
- claude_mpm/core/factories.py +4 -50
- claude_mpm/core/logger.py +11 -13
- claude_mpm/core/service_registry.py +2 -2
- claude_mpm/dashboard/static/js/components/agent-inference.js +101 -25
- claude_mpm/dashboard/static/js/components/event-processor.js +3 -2
- claude_mpm/hooks/claude_hooks/hook_handler.py +343 -83
- claude_mpm/hooks/memory_integration_hook.py +1 -1
- claude_mpm/init.py +37 -6
- claude_mpm/scripts/socketio_daemon.py +6 -2
- claude_mpm/services/__init__.py +71 -3
- claude_mpm/services/agents/__init__.py +85 -0
- claude_mpm/services/agents/deployment/__init__.py +21 -0
- claude_mpm/services/{agent_deployment.py → agents/deployment/agent_deployment.py} +192 -41
- claude_mpm/services/{agent_lifecycle_manager.py → agents/deployment/agent_lifecycle_manager.py} +11 -10
- claude_mpm/services/agents/loading/__init__.py +11 -0
- claude_mpm/services/{agent_profile_loader.py → agents/loading/agent_profile_loader.py} +9 -8
- claude_mpm/services/{base_agent_manager.py → agents/loading/base_agent_manager.py} +2 -2
- claude_mpm/services/{framework_agent_loader.py → agents/loading/framework_agent_loader.py} +116 -40
- claude_mpm/services/agents/management/__init__.py +9 -0
- claude_mpm/services/{agent_management_service.py → agents/management/agent_management_service.py} +6 -5
- claude_mpm/services/agents/memory/__init__.py +21 -0
- claude_mpm/services/{agent_memory_manager.py → agents/memory/agent_memory_manager.py} +3 -3
- claude_mpm/services/agents/registry/__init__.py +29 -0
- claude_mpm/services/{agent_registry.py → agents/registry/agent_registry.py} +101 -16
- claude_mpm/services/{deployed_agent_discovery.py → agents/registry/deployed_agent_discovery.py} +12 -2
- claude_mpm/services/{agent_modification_tracker.py → agents/registry/modification_tracker.py} +6 -5
- claude_mpm/services/async_session_logger.py +584 -0
- claude_mpm/services/claude_session_logger.py +299 -0
- claude_mpm/services/framework_claude_md_generator/content_assembler.py +2 -2
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +17 -17
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +3 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +1 -1
- claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +1 -1
- claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +19 -24
- claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +1 -1
- claude_mpm/services/framework_claude_md_generator.py +4 -2
- claude_mpm/services/memory/__init__.py +17 -0
- claude_mpm/services/{memory_builder.py → memory/builder.py} +3 -3
- claude_mpm/services/memory/cache/__init__.py +14 -0
- claude_mpm/services/{shared_prompt_cache.py → memory/cache/shared_prompt_cache.py} +1 -1
- claude_mpm/services/memory/cache/simple_cache.py +317 -0
- claude_mpm/services/{memory_optimizer.py → memory/optimizer.py} +1 -1
- claude_mpm/services/{memory_router.py → memory/router.py} +1 -1
- claude_mpm/services/optimized_hook_service.py +542 -0
- claude_mpm/services/project_registry.py +14 -8
- claude_mpm/services/response_tracker.py +237 -0
- claude_mpm/services/ticketing_service_original.py +4 -2
- claude_mpm/services/version_control/branch_strategy.py +3 -1
- claude_mpm/utils/paths.py +12 -10
- claude_mpm/utils/session_logging.py +114 -0
- claude_mpm/validation/agent_validator.py +2 -1
- {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/METADATA +26 -20
- {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/RECORD +83 -106
- claude_mpm/cli/commands/ui.py +0 -57
- claude_mpm/core/simple_runner.py +0 -1046
- claude_mpm/hooks/builtin/__init__.py +0 -1
- claude_mpm/hooks/builtin/logging_hook_example.py +0 -165
- claude_mpm/hooks/builtin/memory_hooks_example.py +0 -67
- claude_mpm/hooks/builtin/mpm_command_hook.py +0 -125
- claude_mpm/hooks/builtin/post_delegation_hook_example.py +0 -124
- claude_mpm/hooks/builtin/pre_delegation_hook_example.py +0 -125
- claude_mpm/hooks/builtin/submit_hook_example.py +0 -100
- claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +0 -237
- claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +0 -240
- claude_mpm/hooks/builtin/workflow_start_hook.py +0 -181
- claude_mpm/orchestration/__init__.py +0 -6
- claude_mpm/orchestration/archive/direct_orchestrator.py +0 -195
- claude_mpm/orchestration/archive/factory.py +0 -215
- claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +0 -188
- claude_mpm/orchestration/archive/hook_integration_example.py +0 -178
- claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +0 -826
- claude_mpm/orchestration/archive/orchestrator.py +0 -501
- claude_mpm/orchestration/archive/pexpect_orchestrator.py +0 -252
- claude_mpm/orchestration/archive/pty_orchestrator.py +0 -270
- claude_mpm/orchestration/archive/simple_orchestrator.py +0 -82
- claude_mpm/orchestration/archive/subprocess_orchestrator.py +0 -801
- claude_mpm/orchestration/archive/system_prompt_orchestrator.py +0 -278
- claude_mpm/orchestration/archive/wrapper_orchestrator.py +0 -187
- claude_mpm/schemas/workflow_validator.py +0 -411
- claude_mpm/services/parent_directory_manager/__init__.py +0 -577
- claude_mpm/services/parent_directory_manager/backup_manager.py +0 -258
- claude_mpm/services/parent_directory_manager/config_manager.py +0 -210
- claude_mpm/services/parent_directory_manager/deduplication_manager.py +0 -279
- claude_mpm/services/parent_directory_manager/framework_protector.py +0 -143
- claude_mpm/services/parent_directory_manager/operations.py +0 -186
- claude_mpm/services/parent_directory_manager/state_manager.py +0 -624
- claude_mpm/services/parent_directory_manager/template_deployer.py +0 -579
- claude_mpm/services/parent_directory_manager/validation_manager.py +0 -378
- claude_mpm/services/parent_directory_manager/version_control_helper.py +0 -339
- claude_mpm/services/parent_directory_manager/version_manager.py +0 -222
- claude_mpm/ui/__init__.py +0 -1
- claude_mpm/ui/rich_terminal_ui.py +0 -295
- claude_mpm/ui/terminal_ui.py +0 -328
- /claude_mpm/services/{agent_versioning.py → agents/deployment/agent_versioning.py} +0 -0
- /claude_mpm/services/{agent_capabilities_generator.py → agents/management/agent_capabilities_generator.py} +0 -0
- /claude_mpm/services/{agent_persistence_service.py → agents/memory/agent_persistence_service.py} +0 -0
- {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/WHEEL +0 -0
- {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-3.4.27.dist-info → claude_mpm-3.5.0.dist-info}/licenses/LICENSE +0 -0
- {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
|
|