claude-mpm 4.1.2__py3-none-any.whl → 4.1.3__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/templates/engineer.json +33 -11
- claude_mpm/cli/commands/agents.py +556 -1009
- claude_mpm/cli/commands/memory.py +248 -927
- claude_mpm/cli/commands/run.py +139 -484
- claude_mpm/cli/startup_logging.py +76 -0
- claude_mpm/core/agent_registry.py +6 -10
- claude_mpm/core/framework_loader.py +114 -595
- claude_mpm/core/logging_config.py +2 -4
- claude_mpm/hooks/claude_hooks/event_handlers.py +7 -117
- claude_mpm/hooks/claude_hooks/hook_handler.py +91 -755
- claude_mpm/hooks/claude_hooks/hook_handler_original.py +1040 -0
- claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +347 -0
- claude_mpm/hooks/claude_hooks/services/__init__.py +13 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +190 -0
- claude_mpm/hooks/claude_hooks/services/duplicate_detector.py +106 -0
- claude_mpm/hooks/claude_hooks/services/state_manager.py +282 -0
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +374 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +42 -454
- claude_mpm/services/agents/deployment/base_agent_locator.py +132 -0
- claude_mpm/services/agents/deployment/deployment_results_manager.py +185 -0
- claude_mpm/services/agents/deployment/single_agent_deployer.py +315 -0
- claude_mpm/services/agents/memory/agent_memory_manager.py +42 -508
- claude_mpm/services/agents/memory/memory_categorization_service.py +165 -0
- claude_mpm/services/agents/memory/memory_file_service.py +103 -0
- claude_mpm/services/agents/memory/memory_format_service.py +201 -0
- claude_mpm/services/agents/memory/memory_limits_service.py +99 -0
- claude_mpm/services/agents/registry/__init__.py +1 -1
- claude_mpm/services/cli/__init__.py +18 -0
- claude_mpm/services/cli/agent_cleanup_service.py +407 -0
- claude_mpm/services/cli/agent_dependency_service.py +395 -0
- claude_mpm/services/cli/agent_listing_service.py +463 -0
- claude_mpm/services/cli/agent_output_formatter.py +605 -0
- claude_mpm/services/cli/agent_validation_service.py +589 -0
- claude_mpm/services/cli/dashboard_launcher.py +424 -0
- claude_mpm/services/cli/memory_crud_service.py +617 -0
- claude_mpm/services/cli/memory_output_formatter.py +604 -0
- claude_mpm/services/cli/session_manager.py +513 -0
- claude_mpm/services/cli/socketio_manager.py +498 -0
- claude_mpm/services/cli/startup_checker.py +370 -0
- claude_mpm/services/core/cache_manager.py +311 -0
- claude_mpm/services/core/memory_manager.py +637 -0
- claude_mpm/services/core/path_resolver.py +498 -0
- claude_mpm/services/core/service_container.py +520 -0
- claude_mpm/services/core/service_interfaces.py +436 -0
- claude_mpm/services/diagnostics/checks/agent_check.py +65 -19
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.3.dist-info}/METADATA +1 -1
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.3.dist-info}/RECORD +52 -22
- claude_mpm/cli/commands/run_config_checker.py +0 -159
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.3.dist-info}/WHEEL +0 -0
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.3.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.3.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Consolidated Core Service Interfaces
|
|
3
|
+
====================================
|
|
4
|
+
|
|
5
|
+
WHY: This module consolidates all core service interfaces from across the codebase
|
|
6
|
+
to provide a single source of truth for service contracts. This enables better
|
|
7
|
+
dependency injection and service management.
|
|
8
|
+
|
|
9
|
+
DESIGN DECISION: Rather than having interfaces scattered across multiple files,
|
|
10
|
+
we consolidate them here while maintaining backward compatibility through imports
|
|
11
|
+
in the original locations.
|
|
12
|
+
|
|
13
|
+
INCLUDES:
|
|
14
|
+
- ICacheManager (from cache_manager.py)
|
|
15
|
+
- IPathResolver (new)
|
|
16
|
+
- IMemoryManager (new)
|
|
17
|
+
- IFrameworkLoader (new)
|
|
18
|
+
- Other core service interfaces needed for DI
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from abc import ABC, abstractmethod
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Any, Dict, List, Optional, Set, Tuple
|
|
24
|
+
|
|
25
|
+
# Import CLI service interfaces
|
|
26
|
+
|
|
27
|
+
# Re-export infrastructure interfaces for convenience
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# Cache Manager Interface (moved from cache_manager.py)
|
|
31
|
+
class ICacheManager(ABC):
|
|
32
|
+
"""Interface for framework-specific cache management service."""
|
|
33
|
+
|
|
34
|
+
@abstractmethod
|
|
35
|
+
def get_capabilities(self) -> Optional[str]:
|
|
36
|
+
"""Get cached agent capabilities."""
|
|
37
|
+
|
|
38
|
+
@abstractmethod
|
|
39
|
+
def set_capabilities(self, value: str) -> None:
|
|
40
|
+
"""Set agent capabilities cache."""
|
|
41
|
+
|
|
42
|
+
@abstractmethod
|
|
43
|
+
def get_deployed_agents(self) -> Optional[Set[str]]:
|
|
44
|
+
"""Get cached deployed agents set."""
|
|
45
|
+
|
|
46
|
+
@abstractmethod
|
|
47
|
+
def set_deployed_agents(self, agents: Set[str]) -> None:
|
|
48
|
+
"""Set deployed agents cache."""
|
|
49
|
+
|
|
50
|
+
@abstractmethod
|
|
51
|
+
def get_agent_metadata(
|
|
52
|
+
self, agent_file: str
|
|
53
|
+
) -> Optional[Tuple[Optional[Dict[str, Any]], float]]:
|
|
54
|
+
"""Get cached agent metadata for a specific file."""
|
|
55
|
+
|
|
56
|
+
@abstractmethod
|
|
57
|
+
def set_agent_metadata(
|
|
58
|
+
self, agent_file: str, metadata: Optional[Dict[str, Any]], mtime: float
|
|
59
|
+
) -> None:
|
|
60
|
+
"""Set agent metadata cache for a specific file."""
|
|
61
|
+
|
|
62
|
+
@abstractmethod
|
|
63
|
+
def get_memories(self) -> Optional[Dict[str, Any]]:
|
|
64
|
+
"""Get cached memories."""
|
|
65
|
+
|
|
66
|
+
@abstractmethod
|
|
67
|
+
def set_memories(self, memories: Dict[str, Any]) -> None:
|
|
68
|
+
"""Set memories cache."""
|
|
69
|
+
|
|
70
|
+
@abstractmethod
|
|
71
|
+
def clear_all(self) -> None:
|
|
72
|
+
"""Clear all caches."""
|
|
73
|
+
|
|
74
|
+
@abstractmethod
|
|
75
|
+
def clear_agent_caches(self) -> None:
|
|
76
|
+
"""Clear agent-related caches only."""
|
|
77
|
+
|
|
78
|
+
@abstractmethod
|
|
79
|
+
def clear_memory_caches(self) -> None:
|
|
80
|
+
"""Clear memory-related caches only."""
|
|
81
|
+
|
|
82
|
+
@abstractmethod
|
|
83
|
+
def is_cache_valid(self, cache_time: float, ttl: float) -> bool:
|
|
84
|
+
"""Check if a cache entry is still valid based on its timestamp and TTL."""
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# Path Resolution Interface
|
|
88
|
+
class IPathResolver(ABC):
|
|
89
|
+
"""Interface for path resolution and validation service."""
|
|
90
|
+
|
|
91
|
+
@abstractmethod
|
|
92
|
+
def resolve_path(self, path: str, base_dir: Optional[Path] = None) -> Path:
|
|
93
|
+
"""
|
|
94
|
+
Resolve a path relative to a base directory.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
path: The path to resolve (can be relative or absolute)
|
|
98
|
+
base_dir: Base directory for relative paths (defaults to cwd)
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
The resolved absolute path
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
@abstractmethod
|
|
105
|
+
def validate_path(self, path: Path, must_exist: bool = False) -> bool:
|
|
106
|
+
"""
|
|
107
|
+
Validate a path for security and existence.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
path: The path to validate
|
|
111
|
+
must_exist: Whether the path must exist
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
True if path is valid, False otherwise
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
@abstractmethod
|
|
118
|
+
def ensure_directory(self, path: Path) -> Path:
|
|
119
|
+
"""
|
|
120
|
+
Ensure a directory exists, creating it if necessary.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
path: The directory path
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
The directory path
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
@abstractmethod
|
|
130
|
+
def find_project_root(self, start_path: Optional[Path] = None) -> Optional[Path]:
|
|
131
|
+
"""
|
|
132
|
+
Find the project root directory.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
start_path: Starting path for search (defaults to cwd)
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Project root path or None if not found
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# Memory Management Interface
|
|
143
|
+
class IMemoryManager(ABC):
|
|
144
|
+
"""Interface for agent memory management service."""
|
|
145
|
+
|
|
146
|
+
@abstractmethod
|
|
147
|
+
def load_memories(self, agent_name: Optional[str] = None) -> Dict[str, Any]:
|
|
148
|
+
"""
|
|
149
|
+
Load memories for an agent or all agents.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
agent_name: Specific agent name or None for all
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
Dictionary of memories
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
@abstractmethod
|
|
159
|
+
def save_memory(
|
|
160
|
+
self, key: str, value: Any, agent_name: Optional[str] = None
|
|
161
|
+
) -> None:
|
|
162
|
+
"""
|
|
163
|
+
Save a memory entry.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
key: Memory key
|
|
167
|
+
value: Memory value
|
|
168
|
+
agent_name: Agent name or None for global
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
@abstractmethod
|
|
172
|
+
def search_memories(
|
|
173
|
+
self, query: str, agent_name: Optional[str] = None
|
|
174
|
+
) -> List[Dict[str, Any]]:
|
|
175
|
+
"""
|
|
176
|
+
Search memories by query.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
query: Search query
|
|
180
|
+
agent_name: Specific agent or None for all
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
List of matching memory entries
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
@abstractmethod
|
|
187
|
+
def clear_memories(self, agent_name: Optional[str] = None) -> None:
|
|
188
|
+
"""
|
|
189
|
+
Clear memories for an agent or all agents.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
agent_name: Specific agent or None for all
|
|
193
|
+
"""
|
|
194
|
+
|
|
195
|
+
@abstractmethod
|
|
196
|
+
def get_memory_stats(self) -> Dict[str, Any]:
|
|
197
|
+
"""
|
|
198
|
+
Get memory system statistics.
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
Dictionary with memory statistics
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
# Framework Loader Interface
|
|
206
|
+
class IFrameworkLoader(ABC):
|
|
207
|
+
"""Interface for framework loading and instruction management."""
|
|
208
|
+
|
|
209
|
+
@abstractmethod
|
|
210
|
+
def load_instructions(self) -> str:
|
|
211
|
+
"""
|
|
212
|
+
Load and format framework instructions.
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
Formatted instructions for injection
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
@abstractmethod
|
|
219
|
+
def get_agent_capabilities(self) -> str:
|
|
220
|
+
"""
|
|
221
|
+
Get formatted agent capabilities.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
Agent capabilities text
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
@abstractmethod
|
|
228
|
+
def get_deployed_agents(self) -> Set[str]:
|
|
229
|
+
"""
|
|
230
|
+
Get set of deployed agent names.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
Set of agent names
|
|
234
|
+
"""
|
|
235
|
+
|
|
236
|
+
@abstractmethod
|
|
237
|
+
def reload(self) -> None:
|
|
238
|
+
"""Reload framework instructions and clear caches."""
|
|
239
|
+
|
|
240
|
+
@abstractmethod
|
|
241
|
+
def get_version(self) -> Optional[str]:
|
|
242
|
+
"""
|
|
243
|
+
Get framework version.
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
Version string or None
|
|
247
|
+
"""
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
# File System Service Interface
|
|
251
|
+
class IFileSystemService(ABC):
|
|
252
|
+
"""Interface for file system operations."""
|
|
253
|
+
|
|
254
|
+
@abstractmethod
|
|
255
|
+
def read_file(self, path: Path, encoding: str = "utf-8") -> str:
|
|
256
|
+
"""
|
|
257
|
+
Read file contents.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
path: File path
|
|
261
|
+
encoding: File encoding
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
File contents
|
|
265
|
+
"""
|
|
266
|
+
|
|
267
|
+
@abstractmethod
|
|
268
|
+
def write_file(self, path: Path, content: str, encoding: str = "utf-8") -> None:
|
|
269
|
+
"""
|
|
270
|
+
Write content to file.
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
path: File path
|
|
274
|
+
content: Content to write
|
|
275
|
+
encoding: File encoding
|
|
276
|
+
"""
|
|
277
|
+
|
|
278
|
+
@abstractmethod
|
|
279
|
+
def copy_file(self, source: Path, destination: Path) -> None:
|
|
280
|
+
"""
|
|
281
|
+
Copy file from source to destination.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
source: Source file path
|
|
285
|
+
destination: Destination file path
|
|
286
|
+
"""
|
|
287
|
+
|
|
288
|
+
@abstractmethod
|
|
289
|
+
def delete_file(self, path: Path) -> bool:
|
|
290
|
+
"""
|
|
291
|
+
Delete a file.
|
|
292
|
+
|
|
293
|
+
Args:
|
|
294
|
+
path: File path
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
True if deleted, False if not found
|
|
298
|
+
"""
|
|
299
|
+
|
|
300
|
+
@abstractmethod
|
|
301
|
+
def list_directory(self, path: Path, pattern: Optional[str] = None) -> List[Path]:
|
|
302
|
+
"""
|
|
303
|
+
List directory contents.
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
path: Directory path
|
|
307
|
+
pattern: Optional glob pattern
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
List of paths
|
|
311
|
+
"""
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
# Environment Service Interface
|
|
315
|
+
class IEnvironmentService(ABC):
|
|
316
|
+
"""Interface for environment and configuration management."""
|
|
317
|
+
|
|
318
|
+
@abstractmethod
|
|
319
|
+
def get_env(self, key: str, default: Optional[str] = None) -> Optional[str]:
|
|
320
|
+
"""
|
|
321
|
+
Get environment variable.
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
key: Environment variable key
|
|
325
|
+
default: Default value if not found
|
|
326
|
+
|
|
327
|
+
Returns:
|
|
328
|
+
Environment value or default
|
|
329
|
+
"""
|
|
330
|
+
|
|
331
|
+
@abstractmethod
|
|
332
|
+
def set_env(self, key: str, value: str) -> None:
|
|
333
|
+
"""
|
|
334
|
+
Set environment variable.
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
key: Environment variable key
|
|
338
|
+
value: Value to set
|
|
339
|
+
"""
|
|
340
|
+
|
|
341
|
+
@abstractmethod
|
|
342
|
+
def get_config_dir(self) -> Path:
|
|
343
|
+
"""
|
|
344
|
+
Get configuration directory.
|
|
345
|
+
|
|
346
|
+
Returns:
|
|
347
|
+
Configuration directory path
|
|
348
|
+
"""
|
|
349
|
+
|
|
350
|
+
@abstractmethod
|
|
351
|
+
def get_data_dir(self) -> Path:
|
|
352
|
+
"""
|
|
353
|
+
Get data directory.
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
Data directory path
|
|
357
|
+
"""
|
|
358
|
+
|
|
359
|
+
@abstractmethod
|
|
360
|
+
def get_cache_dir(self) -> Path:
|
|
361
|
+
"""
|
|
362
|
+
Get cache directory.
|
|
363
|
+
|
|
364
|
+
Returns:
|
|
365
|
+
Cache directory path
|
|
366
|
+
"""
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
# Process Management Interface
|
|
370
|
+
class IProcessManager(ABC):
|
|
371
|
+
"""Interface for process and subprocess management."""
|
|
372
|
+
|
|
373
|
+
@abstractmethod
|
|
374
|
+
def run_command(
|
|
375
|
+
self,
|
|
376
|
+
command: List[str],
|
|
377
|
+
cwd: Optional[Path] = None,
|
|
378
|
+
env: Optional[Dict[str, str]] = None,
|
|
379
|
+
timeout: Optional[float] = None,
|
|
380
|
+
) -> Tuple[int, str, str]:
|
|
381
|
+
"""
|
|
382
|
+
Run a command and return result.
|
|
383
|
+
|
|
384
|
+
Args:
|
|
385
|
+
command: Command and arguments
|
|
386
|
+
cwd: Working directory
|
|
387
|
+
env: Environment variables
|
|
388
|
+
timeout: Command timeout in seconds
|
|
389
|
+
|
|
390
|
+
Returns:
|
|
391
|
+
Tuple of (return_code, stdout, stderr)
|
|
392
|
+
"""
|
|
393
|
+
|
|
394
|
+
@abstractmethod
|
|
395
|
+
def start_process(
|
|
396
|
+
self,
|
|
397
|
+
command: List[str],
|
|
398
|
+
cwd: Optional[Path] = None,
|
|
399
|
+
env: Optional[Dict[str, str]] = None,
|
|
400
|
+
) -> int:
|
|
401
|
+
"""
|
|
402
|
+
Start a background process.
|
|
403
|
+
|
|
404
|
+
Args:
|
|
405
|
+
command: Command and arguments
|
|
406
|
+
cwd: Working directory
|
|
407
|
+
env: Environment variables
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
Process ID
|
|
411
|
+
"""
|
|
412
|
+
|
|
413
|
+
@abstractmethod
|
|
414
|
+
def stop_process(self, pid: int, timeout: float = 5.0) -> bool:
|
|
415
|
+
"""
|
|
416
|
+
Stop a process.
|
|
417
|
+
|
|
418
|
+
Args:
|
|
419
|
+
pid: Process ID
|
|
420
|
+
timeout: Timeout for graceful shutdown
|
|
421
|
+
|
|
422
|
+
Returns:
|
|
423
|
+
True if stopped successfully
|
|
424
|
+
"""
|
|
425
|
+
|
|
426
|
+
@abstractmethod
|
|
427
|
+
def is_process_running(self, pid: int) -> bool:
|
|
428
|
+
"""
|
|
429
|
+
Check if process is running.
|
|
430
|
+
|
|
431
|
+
Args:
|
|
432
|
+
pid: Process ID
|
|
433
|
+
|
|
434
|
+
Returns:
|
|
435
|
+
True if process is running
|
|
436
|
+
"""
|
|
@@ -110,15 +110,31 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
110
110
|
)
|
|
111
111
|
|
|
112
112
|
def _check_deployed_agents(self) -> DiagnosticResult:
|
|
113
|
-
"""Check deployed agents in user
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
"""Check deployed agents in both project and user directories."""
|
|
114
|
+
import os
|
|
115
|
+
|
|
116
|
+
# Check project-level agents first (preferred in development)
|
|
117
|
+
project_agents_dir = Path(os.getcwd()) / ".claude" / "agents"
|
|
118
|
+
user_agents_dir = Path.home() / ".claude" / "agents"
|
|
119
|
+
|
|
120
|
+
# Determine which directory to check
|
|
121
|
+
if project_agents_dir.exists():
|
|
122
|
+
agents_dir = project_agents_dir
|
|
123
|
+
location = "project"
|
|
124
|
+
elif user_agents_dir.exists():
|
|
125
|
+
agents_dir = user_agents_dir
|
|
126
|
+
location = "user"
|
|
127
|
+
else:
|
|
128
|
+
# Neither exists, default to user directory for error message
|
|
117
129
|
return DiagnosticResult(
|
|
118
130
|
category="Deployed Agents",
|
|
119
131
|
status=DiagnosticStatus.ERROR,
|
|
120
|
-
message="
|
|
121
|
-
details={
|
|
132
|
+
message="No agents directory found (checked project and user)",
|
|
133
|
+
details={
|
|
134
|
+
"project_path": str(project_agents_dir),
|
|
135
|
+
"user_path": str(user_agents_dir),
|
|
136
|
+
"count": 0,
|
|
137
|
+
},
|
|
122
138
|
fix_command="claude-mpm agents deploy",
|
|
123
139
|
fix_description="Create agents directory and deploy agents",
|
|
124
140
|
)
|
|
@@ -130,8 +146,8 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
130
146
|
return DiagnosticResult(
|
|
131
147
|
category="Deployed Agents",
|
|
132
148
|
status=DiagnosticStatus.ERROR,
|
|
133
|
-
message="No agents deployed",
|
|
134
|
-
details={"path": str(agents_dir), "count": 0},
|
|
149
|
+
message=f"No agents deployed in {location} directory",
|
|
150
|
+
details={"path": str(agents_dir), "location": location, "count": 0},
|
|
135
151
|
fix_command="claude-mpm agents deploy",
|
|
136
152
|
fix_description="Deploy available agents",
|
|
137
153
|
)
|
|
@@ -145,9 +161,10 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
145
161
|
return DiagnosticResult(
|
|
146
162
|
category="Deployed Agents",
|
|
147
163
|
status=DiagnosticStatus.WARNING,
|
|
148
|
-
message=f"Missing core agents: {', '.join(missing_core)}",
|
|
164
|
+
message=f"Missing core agents in {location}: {', '.join(missing_core)}",
|
|
149
165
|
details={
|
|
150
166
|
"path": str(agents_dir),
|
|
167
|
+
"location": location,
|
|
151
168
|
"count": len(agent_files),
|
|
152
169
|
"deployed": deployed_names,
|
|
153
170
|
"missing_core": missing_core,
|
|
@@ -159,9 +176,10 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
159
176
|
return DiagnosticResult(
|
|
160
177
|
category="Deployed Agents",
|
|
161
178
|
status=DiagnosticStatus.OK,
|
|
162
|
-
message=f"{len(agent_files)} agents deployed",
|
|
179
|
+
message=f"{len(agent_files)} agents deployed ({location} level)",
|
|
163
180
|
details={
|
|
164
181
|
"path": str(agents_dir),
|
|
182
|
+
"location": location,
|
|
165
183
|
"count": len(agent_files),
|
|
166
184
|
"deployed": deployed_names,
|
|
167
185
|
},
|
|
@@ -170,14 +188,23 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
170
188
|
def _check_agent_versions(self) -> DiagnosticResult:
|
|
171
189
|
"""Check if deployed agents are up-to-date."""
|
|
172
190
|
try:
|
|
191
|
+
import os
|
|
192
|
+
|
|
173
193
|
from ....services.agents.deployment.agent_version_manager import (
|
|
174
194
|
AgentVersionManager,
|
|
175
195
|
)
|
|
176
196
|
|
|
177
197
|
version_manager = AgentVersionManager()
|
|
178
|
-
agents_dir = Path.home() / ".claude" / "agents"
|
|
179
198
|
|
|
180
|
-
|
|
199
|
+
# Check both project and user directories
|
|
200
|
+
project_agents_dir = Path(os.getcwd()) / ".claude" / "agents"
|
|
201
|
+
user_agents_dir = Path.home() / ".claude" / "agents"
|
|
202
|
+
|
|
203
|
+
if project_agents_dir.exists():
|
|
204
|
+
agents_dir = project_agents_dir
|
|
205
|
+
elif user_agents_dir.exists():
|
|
206
|
+
agents_dir = user_agents_dir
|
|
207
|
+
else:
|
|
181
208
|
return DiagnosticResult(
|
|
182
209
|
category="Agent Versions",
|
|
183
210
|
status=DiagnosticStatus.SKIPPED,
|
|
@@ -232,12 +259,21 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
232
259
|
def _validate_agents(self) -> DiagnosticResult:
|
|
233
260
|
"""Validate agent configurations."""
|
|
234
261
|
try:
|
|
262
|
+
import os
|
|
263
|
+
|
|
235
264
|
from ....services.agents.deployment.agent_validator import AgentValidator
|
|
236
265
|
|
|
237
266
|
AgentValidator()
|
|
238
|
-
agents_dir = Path.home() / ".claude" / "agents"
|
|
239
267
|
|
|
240
|
-
|
|
268
|
+
# Check both project and user directories
|
|
269
|
+
project_agents_dir = Path(os.getcwd()) / ".claude" / "agents"
|
|
270
|
+
user_agents_dir = Path.home() / ".claude" / "agents"
|
|
271
|
+
|
|
272
|
+
if project_agents_dir.exists():
|
|
273
|
+
agents_dir = project_agents_dir
|
|
274
|
+
elif user_agents_dir.exists():
|
|
275
|
+
agents_dir = user_agents_dir
|
|
276
|
+
else:
|
|
241
277
|
return DiagnosticResult(
|
|
242
278
|
category="Agent Validation",
|
|
243
279
|
status=DiagnosticStatus.SKIPPED,
|
|
@@ -290,11 +326,23 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
290
326
|
|
|
291
327
|
def _check_common_issues(self) -> DiagnosticResult:
|
|
292
328
|
"""Check for common agent-related issues."""
|
|
329
|
+
import os
|
|
330
|
+
|
|
293
331
|
issues = []
|
|
294
332
|
|
|
333
|
+
# Check both project and user directories
|
|
334
|
+
project_agents_dir = Path(os.getcwd()) / ".claude" / "agents"
|
|
335
|
+
user_agents_dir = Path.home() / ".claude" / "agents"
|
|
336
|
+
|
|
337
|
+
if project_agents_dir.exists():
|
|
338
|
+
agents_dir = project_agents_dir
|
|
339
|
+
elif user_agents_dir.exists():
|
|
340
|
+
agents_dir = user_agents_dir
|
|
341
|
+
else:
|
|
342
|
+
agents_dir = None
|
|
343
|
+
|
|
295
344
|
# Check for duplicate agents
|
|
296
|
-
agents_dir
|
|
297
|
-
if agents_dir.exists():
|
|
345
|
+
if agents_dir and agents_dir.exists():
|
|
298
346
|
agent_names = {}
|
|
299
347
|
for agent_file in agents_dir.glob("*.md"):
|
|
300
348
|
name = agent_file.stem.lower()
|
|
@@ -304,9 +352,7 @@ class AgentCheck(BaseDiagnosticCheck):
|
|
|
304
352
|
agent_names[name] = agent_file
|
|
305
353
|
|
|
306
354
|
# Check permissions
|
|
307
|
-
if agents_dir.exists():
|
|
308
|
-
import os
|
|
309
|
-
|
|
355
|
+
if agents_dir and agents_dir.exists():
|
|
310
356
|
if not os.access(agents_dir, os.R_OK):
|
|
311
357
|
issues.append("Agents directory not readable")
|
|
312
358
|
if not os.access(agents_dir, os.W_OK):
|