claude-mpm 4.1.2__py3-none-any.whl → 4.1.4__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 (87) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +16 -19
  3. claude_mpm/agents/MEMORY.md +21 -49
  4. claude_mpm/agents/templates/OPTIMIZATION_REPORT.md +156 -0
  5. claude_mpm/agents/templates/api_qa.json +36 -116
  6. claude_mpm/agents/templates/backup/data_engineer_agent_20250726_234551.json +42 -9
  7. claude_mpm/agents/templates/backup/documentation_agent_20250726_234551.json +29 -6
  8. claude_mpm/agents/templates/backup/engineer_agent_20250726_234551.json +34 -6
  9. claude_mpm/agents/templates/backup/ops_agent_20250726_234551.json +41 -9
  10. claude_mpm/agents/templates/backup/qa_agent_20250726_234551.json +30 -8
  11. claude_mpm/agents/templates/backup/research_agent_2025011_234551.json +2 -2
  12. claude_mpm/agents/templates/backup/research_agent_20250726_234551.json +29 -6
  13. claude_mpm/agents/templates/backup/research_memory_efficient.json +2 -2
  14. claude_mpm/agents/templates/backup/security_agent_20250726_234551.json +41 -9
  15. claude_mpm/agents/templates/backup/version_control_agent_20250726_234551.json +23 -7
  16. claude_mpm/agents/templates/code_analyzer.json +18 -36
  17. claude_mpm/agents/templates/data_engineer.json +43 -14
  18. claude_mpm/agents/templates/documentation.json +55 -74
  19. claude_mpm/agents/templates/engineer.json +57 -40
  20. claude_mpm/agents/templates/imagemagick.json +7 -2
  21. claude_mpm/agents/templates/memory_manager.json +1 -1
  22. claude_mpm/agents/templates/ops.json +36 -4
  23. claude_mpm/agents/templates/project_organizer.json +23 -71
  24. claude_mpm/agents/templates/qa.json +34 -2
  25. claude_mpm/agents/templates/refactoring_engineer.json +9 -5
  26. claude_mpm/agents/templates/research.json +36 -4
  27. claude_mpm/agents/templates/security.json +29 -2
  28. claude_mpm/agents/templates/ticketing.json +3 -3
  29. claude_mpm/agents/templates/vercel_ops_agent.json +2 -2
  30. claude_mpm/agents/templates/version_control.json +28 -2
  31. claude_mpm/agents/templates/web_qa.json +38 -151
  32. claude_mpm/agents/templates/web_ui.json +2 -2
  33. claude_mpm/cli/commands/agent_manager.py +221 -1
  34. claude_mpm/cli/commands/agents.py +556 -1009
  35. claude_mpm/cli/commands/memory.py +248 -927
  36. claude_mpm/cli/commands/run.py +139 -484
  37. claude_mpm/cli/parsers/agent_manager_parser.py +34 -0
  38. claude_mpm/cli/startup_logging.py +76 -0
  39. claude_mpm/core/agent_registry.py +6 -10
  40. claude_mpm/core/framework_loader.py +205 -595
  41. claude_mpm/core/log_manager.py +49 -1
  42. claude_mpm/core/logging_config.py +2 -4
  43. claude_mpm/hooks/claude_hooks/event_handlers.py +7 -117
  44. claude_mpm/hooks/claude_hooks/hook_handler.py +91 -755
  45. claude_mpm/hooks/claude_hooks/hook_handler_original.py +1040 -0
  46. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +347 -0
  47. claude_mpm/hooks/claude_hooks/services/__init__.py +13 -0
  48. claude_mpm/hooks/claude_hooks/services/connection_manager.py +190 -0
  49. claude_mpm/hooks/claude_hooks/services/duplicate_detector.py +106 -0
  50. claude_mpm/hooks/claude_hooks/services/state_manager.py +282 -0
  51. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +374 -0
  52. claude_mpm/services/agents/deployment/agent_deployment.py +42 -454
  53. claude_mpm/services/agents/deployment/base_agent_locator.py +132 -0
  54. claude_mpm/services/agents/deployment/deployment_results_manager.py +185 -0
  55. claude_mpm/services/agents/deployment/single_agent_deployer.py +315 -0
  56. claude_mpm/services/agents/memory/agent_memory_manager.py +42 -508
  57. claude_mpm/services/agents/memory/memory_categorization_service.py +165 -0
  58. claude_mpm/services/agents/memory/memory_file_service.py +103 -0
  59. claude_mpm/services/agents/memory/memory_format_service.py +201 -0
  60. claude_mpm/services/agents/memory/memory_limits_service.py +99 -0
  61. claude_mpm/services/agents/registry/__init__.py +1 -1
  62. claude_mpm/services/cli/__init__.py +18 -0
  63. claude_mpm/services/cli/agent_cleanup_service.py +407 -0
  64. claude_mpm/services/cli/agent_dependency_service.py +395 -0
  65. claude_mpm/services/cli/agent_listing_service.py +463 -0
  66. claude_mpm/services/cli/agent_output_formatter.py +605 -0
  67. claude_mpm/services/cli/agent_validation_service.py +589 -0
  68. claude_mpm/services/cli/dashboard_launcher.py +424 -0
  69. claude_mpm/services/cli/memory_crud_service.py +617 -0
  70. claude_mpm/services/cli/memory_output_formatter.py +604 -0
  71. claude_mpm/services/cli/session_manager.py +513 -0
  72. claude_mpm/services/cli/socketio_manager.py +498 -0
  73. claude_mpm/services/cli/startup_checker.py +370 -0
  74. claude_mpm/services/core/cache_manager.py +311 -0
  75. claude_mpm/services/core/memory_manager.py +637 -0
  76. claude_mpm/services/core/path_resolver.py +498 -0
  77. claude_mpm/services/core/service_container.py +520 -0
  78. claude_mpm/services/core/service_interfaces.py +436 -0
  79. claude_mpm/services/diagnostics/checks/agent_check.py +65 -19
  80. claude_mpm/services/memory/router.py +116 -10
  81. {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.4.dist-info}/METADATA +1 -1
  82. {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.4.dist-info}/RECORD +86 -55
  83. claude_mpm/cli/commands/run_config_checker.py +0 -159
  84. {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.4.dist-info}/WHEEL +0 -0
  85. {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.4.dist-info}/entry_points.txt +0 -0
  86. {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.4.dist-info}/licenses/LICENSE +0 -0
  87. {claude_mpm-4.1.2.dist-info → claude_mpm-4.1.4.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 directory."""
114
- agents_dir = Path.home() / ".claude" / "agents"
115
-
116
- if not agents_dir.exists():
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="Agents directory does not exist",
121
- details={"path": str(agents_dir), "count": 0},
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
- if not agents_dir.exists():
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
- if not agents_dir.exists():
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 = Path.home() / ".claude" / "agents"
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):