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,407 @@
1
+ """
2
+ Agent Cleanup Service
3
+ =====================
4
+
5
+ WHY: This service manages agent cleanup operations including removing deployed agents
6
+ and cleaning up orphaned agents. It provides a clean interface for the CLI to perform
7
+ cleanup operations with dry-run support and progress reporting.
8
+
9
+ DESIGN DECISIONS:
10
+ - Separates cleanup logic from CLI command implementation
11
+ - Provides dry-run mode for safe preview of changes
12
+ - Identifies orphaned agents (deployed without templates)
13
+ - Validates cleanup operations before execution
14
+ - Generates detailed cleanup reports for user feedback
15
+ - Maintains backward compatibility with existing deployment service
16
+ """
17
+
18
+ from abc import ABC, abstractmethod
19
+ from pathlib import Path
20
+ from typing import Any, Dict, List, Optional
21
+
22
+ from ...core.logger import get_logger
23
+
24
+
25
+ class IAgentCleanupService(ABC):
26
+ """Interface for agent cleanup operations."""
27
+
28
+ @abstractmethod
29
+ def clean_deployed_agents(
30
+ self, agents_dir: Optional[Path] = None
31
+ ) -> Dict[str, Any]:
32
+ """Clean up deployed agents while preserving user-created agents."""
33
+
34
+ @abstractmethod
35
+ def clean_orphaned_agents(
36
+ self, agents_dir: Optional[Path] = None, dry_run: bool = True
37
+ ) -> Dict[str, Any]:
38
+ """Clean up orphaned agents that don't have templates."""
39
+
40
+ @abstractmethod
41
+ def get_orphaned_agents(
42
+ self, agents_dir: Optional[Path] = None
43
+ ) -> List[Dict[str, Any]]:
44
+ """Find orphaned agents (deployed without templates)."""
45
+
46
+ @abstractmethod
47
+ def perform_cleanup(
48
+ self,
49
+ agents_dir: Optional[Path] = None,
50
+ cleanup_type: str = "all",
51
+ dry_run: bool = False,
52
+ ) -> Dict[str, Any]:
53
+ """Execute cleanup with specified options."""
54
+
55
+ @abstractmethod
56
+ def validate_cleanup(self, agents_dir: Optional[Path] = None) -> Dict[str, Any]:
57
+ """Verify cleanup operation safety before execution."""
58
+
59
+
60
+ class AgentCleanupService(IAgentCleanupService):
61
+ """Service for managing agent cleanup operations with robust error handling."""
62
+
63
+ def __init__(self, deployment_service=None):
64
+ """
65
+ Initialize the cleanup service.
66
+
67
+ Args:
68
+ deployment_service: Optional deployment service for cleanup operations
69
+ """
70
+ self.logger = get_logger(__name__)
71
+ self._deployment_service = deployment_service
72
+ self._multi_source_service = None
73
+
74
+ def _get_deployment_service(self):
75
+ """Get or create deployment service instance."""
76
+ if self._deployment_service is None:
77
+ from ...services.agents.deployment.agent_deployment import (
78
+ AgentDeploymentService,
79
+ )
80
+
81
+ self._deployment_service = AgentDeploymentService()
82
+ return self._deployment_service
83
+
84
+ def _get_multi_source_service(self):
85
+ """Get or create multi-source deployment service instance."""
86
+ if self._multi_source_service is None:
87
+ from ...services.agents.deployment.multi_source_deployment_service import (
88
+ MultiSourceAgentDeploymentService,
89
+ )
90
+
91
+ self._multi_source_service = MultiSourceAgentDeploymentService()
92
+ return self._multi_source_service
93
+
94
+ def _determine_agents_dir(self, agents_dir: Optional[Path] = None) -> Path:
95
+ """
96
+ Determine the agents directory to use.
97
+
98
+ Args:
99
+ agents_dir: Optional explicit agents directory
100
+
101
+ Returns:
102
+ Path to agents directory
103
+ """
104
+ if agents_dir:
105
+ return agents_dir
106
+
107
+ # Check for project-level .claude/agents first
108
+ project_agents_dir = Path.cwd() / ".claude" / "agents"
109
+ if project_agents_dir.exists():
110
+ return project_agents_dir
111
+
112
+ # Fall back to user home directory
113
+ return Path.home() / ".claude" / "agents"
114
+
115
+ def clean_deployed_agents(
116
+ self, agents_dir: Optional[Path] = None
117
+ ) -> Dict[str, Any]:
118
+ """
119
+ Clean up deployed agents while preserving user-created agents.
120
+
121
+ This removes system-deployed agents (authored by claude-mpm) while
122
+ preserving any user-created agents in the directory.
123
+
124
+ Args:
125
+ agents_dir: Optional agents directory to clean
126
+
127
+ Returns:
128
+ Dictionary containing cleanup results
129
+ """
130
+ try:
131
+ deployment_service = self._get_deployment_service()
132
+
133
+ # Use deployment service's clean_deployment method
134
+ if hasattr(deployment_service, "clean_deployment"):
135
+ result = deployment_service.clean_deployment()
136
+
137
+ # Ensure consistent result format
138
+ if not isinstance(result, dict):
139
+ result = {"success": bool(result)}
140
+
141
+ # Add cleaned_count for backward compatibility
142
+ if "cleaned_count" not in result:
143
+ removed_count = len(result.get("removed", []))
144
+ result["cleaned_count"] = removed_count
145
+
146
+ return result
147
+ # Fallback if method doesn't exist
148
+ return {
149
+ "success": False,
150
+ "error": "Deployment service does not support cleanup",
151
+ "cleaned_count": 0,
152
+ }
153
+
154
+ except Exception as e:
155
+ self.logger.error(f"Error cleaning deployed agents: {e}", exc_info=True)
156
+ return {"success": False, "error": str(e), "cleaned_count": 0}
157
+
158
+ def clean_orphaned_agents(
159
+ self, agents_dir: Optional[Path] = None, dry_run: bool = True
160
+ ) -> Dict[str, Any]:
161
+ """
162
+ Clean up orphaned agents that don't have templates.
163
+
164
+ Orphaned agents are deployed agents that no longer have corresponding
165
+ templates in the codebase. This method identifies and optionally removes them.
166
+
167
+ Args:
168
+ agents_dir: Optional agents directory to clean
169
+ dry_run: If True, only report what would be removed
170
+
171
+ Returns:
172
+ Dictionary containing cleanup results
173
+ """
174
+ try:
175
+ agents_dir = self._determine_agents_dir(agents_dir)
176
+
177
+ if not agents_dir.exists():
178
+ return {
179
+ "success": True,
180
+ "message": f"Agents directory not found: {agents_dir}",
181
+ "orphaned": [],
182
+ "removed": [],
183
+ "errors": [],
184
+ }
185
+
186
+ multi_source_service = self._get_multi_source_service()
187
+
188
+ # Perform cleanup using multi-source service
189
+ results = multi_source_service.cleanup_orphaned_agents(
190
+ agents_dir, dry_run=dry_run
191
+ )
192
+
193
+ # Add success flag for consistent interface
194
+ if "success" not in results:
195
+ results["success"] = not bool(results.get("errors"))
196
+
197
+ return results
198
+
199
+ except Exception as e:
200
+ self.logger.error(f"Error cleaning orphaned agents: {e}", exc_info=True)
201
+ return {
202
+ "success": False,
203
+ "error": str(e),
204
+ "orphaned": [],
205
+ "removed": [],
206
+ "errors": [str(e)],
207
+ }
208
+
209
+ def get_orphaned_agents(
210
+ self, agents_dir: Optional[Path] = None
211
+ ) -> List[Dict[str, Any]]:
212
+ """
213
+ Find orphaned agents (deployed without templates).
214
+
215
+ This method identifies deployed agents that no longer have corresponding
216
+ templates in the codebase without removing them.
217
+
218
+ Args:
219
+ agents_dir: Optional agents directory to check
220
+
221
+ Returns:
222
+ List of orphaned agent information dictionaries
223
+ """
224
+ try:
225
+ agents_dir = self._determine_agents_dir(agents_dir)
226
+
227
+ if not agents_dir.exists():
228
+ return []
229
+
230
+ multi_source_service = self._get_multi_source_service()
231
+
232
+ # Discover all available agents from all sources
233
+ all_agents = multi_source_service.discover_agents_from_all_sources()
234
+
235
+ # Detect orphaned agents
236
+ orphaned = multi_source_service.detect_orphaned_agents(
237
+ agents_dir, all_agents
238
+ )
239
+
240
+ return orphaned
241
+
242
+ except Exception as e:
243
+ self.logger.error(f"Error finding orphaned agents: {e}", exc_info=True)
244
+ return []
245
+
246
+ def perform_cleanup(
247
+ self,
248
+ agents_dir: Optional[Path] = None,
249
+ cleanup_type: str = "all",
250
+ dry_run: bool = False,
251
+ ) -> Dict[str, Any]:
252
+ """
253
+ Execute cleanup with specified options.
254
+
255
+ This method provides a unified interface for different cleanup operations.
256
+
257
+ Args:
258
+ agents_dir: Optional agents directory to clean
259
+ cleanup_type: Type of cleanup ("all", "deployed", "orphaned")
260
+ dry_run: If True, only preview changes without executing
261
+
262
+ Returns:
263
+ Dictionary containing cleanup results
264
+ """
265
+ try:
266
+ results = {
267
+ "success": True,
268
+ "cleanup_type": cleanup_type,
269
+ "dry_run": dry_run,
270
+ "operations": [],
271
+ }
272
+
273
+ if cleanup_type in ["all", "deployed"]:
274
+ # Clean deployed agents
275
+ deployed_result = self.clean_deployed_agents(agents_dir)
276
+ results["operations"].append(
277
+ {"type": "deployed", "result": deployed_result}
278
+ )
279
+ if not deployed_result.get("success", False):
280
+ results["success"] = False
281
+
282
+ if cleanup_type in ["all", "orphaned"]:
283
+ # Clean orphaned agents
284
+ orphaned_result = self.clean_orphaned_agents(
285
+ agents_dir, dry_run=dry_run
286
+ )
287
+ results["operations"].append(
288
+ {"type": "orphaned", "result": orphaned_result}
289
+ )
290
+ if not orphaned_result.get("success", False):
291
+ results["success"] = False
292
+
293
+ # Calculate totals
294
+ total_cleaned = 0
295
+ total_errors = 0
296
+ for op in results["operations"]:
297
+ result = op["result"]
298
+ if op["type"] == "deployed":
299
+ total_cleaned += result.get("cleaned_count", 0)
300
+ elif op["type"] == "orphaned":
301
+ if dry_run:
302
+ total_cleaned += len(result.get("orphaned", []))
303
+ else:
304
+ total_cleaned += len(result.get("removed", []))
305
+ total_errors += len(result.get("errors", []))
306
+
307
+ results["total_cleaned"] = total_cleaned
308
+ results["total_errors"] = total_errors
309
+
310
+ return results
311
+
312
+ except Exception as e:
313
+ self.logger.error(f"Error performing cleanup: {e}", exc_info=True)
314
+ return {
315
+ "success": False,
316
+ "error": str(e),
317
+ "cleanup_type": cleanup_type,
318
+ "dry_run": dry_run,
319
+ "operations": [],
320
+ }
321
+
322
+ def validate_cleanup(self, agents_dir: Optional[Path] = None) -> Dict[str, Any]:
323
+ """
324
+ Verify cleanup operation safety before execution.
325
+
326
+ This method checks for potential issues that might occur during cleanup
327
+ and provides warnings about what will be removed.
328
+
329
+ Args:
330
+ agents_dir: Optional agents directory to validate
331
+
332
+ Returns:
333
+ Dictionary containing validation results
334
+ """
335
+ try:
336
+ agents_dir = self._determine_agents_dir(agents_dir)
337
+
338
+ validation = {
339
+ "success": True,
340
+ "agents_dir": str(agents_dir),
341
+ "exists": agents_dir.exists(),
342
+ "warnings": [],
343
+ "info": [],
344
+ }
345
+
346
+ if not agents_dir.exists():
347
+ validation["info"].append(
348
+ f"Agents directory does not exist: {agents_dir}"
349
+ )
350
+ return validation
351
+
352
+ # Count deployed agents
353
+ deployed_count = 0
354
+ user_created_count = 0
355
+
356
+ for agent_file in agents_dir.glob("*.md"):
357
+ try:
358
+ content = agent_file.read_text()
359
+ if "author: claude-mpm" in content.lower():
360
+ deployed_count += 1
361
+ else:
362
+ user_created_count += 1
363
+ except Exception as e:
364
+ validation["warnings"].append(
365
+ f"Could not read {agent_file.name}: {e}"
366
+ )
367
+
368
+ validation["deployed_count"] = deployed_count
369
+ validation["user_created_count"] = user_created_count
370
+
371
+ # Check for orphaned agents
372
+ orphaned = self.get_orphaned_agents(agents_dir)
373
+ validation["orphaned_count"] = len(orphaned)
374
+
375
+ if orphaned:
376
+ validation["orphaned_agents"] = [
377
+ {"name": agent["name"], "version": agent.get("version", "unknown")}
378
+ for agent in orphaned
379
+ ]
380
+
381
+ # Add informational messages
382
+ if deployed_count > 0:
383
+ validation["info"].append(
384
+ f"Found {deployed_count} system-deployed agent(s) that can be cleaned"
385
+ )
386
+
387
+ if user_created_count > 0:
388
+ validation["info"].append(
389
+ f"Found {user_created_count} user-created agent(s) that will be preserved"
390
+ )
391
+
392
+ if orphaned:
393
+ validation["warnings"].append(
394
+ f"Found {len(orphaned)} orphaned agent(s) without templates"
395
+ )
396
+
397
+ return validation
398
+
399
+ except Exception as e:
400
+ self.logger.error(f"Error validating cleanup: {e}", exc_info=True)
401
+ return {
402
+ "success": False,
403
+ "error": str(e),
404
+ "agents_dir": str(agents_dir) if agents_dir else "unknown",
405
+ "warnings": [str(e)],
406
+ "info": [],
407
+ }