claude-mpm 3.5.6__py3-none-any.whl → 3.6.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/BASE_AGENT_TEMPLATE.md +96 -23
- claude_mpm/agents/BASE_PM.md +273 -0
- claude_mpm/agents/INSTRUCTIONS.md +114 -103
- claude_mpm/agents/agent_loader.py +36 -1
- claude_mpm/agents/async_agent_loader.py +421 -0
- claude_mpm/agents/templates/code_analyzer.json +81 -0
- claude_mpm/agents/templates/data_engineer.json +18 -3
- claude_mpm/agents/templates/documentation.json +18 -3
- claude_mpm/agents/templates/engineer.json +19 -4
- claude_mpm/agents/templates/ops.json +18 -3
- claude_mpm/agents/templates/qa.json +20 -4
- claude_mpm/agents/templates/research.json +20 -4
- claude_mpm/agents/templates/security.json +18 -3
- claude_mpm/agents/templates/version_control.json +16 -3
- claude_mpm/cli/__init__.py +5 -1
- claude_mpm/cli/commands/__init__.py +5 -1
- claude_mpm/cli/commands/agents.py +212 -3
- claude_mpm/cli/commands/aggregate.py +462 -0
- claude_mpm/cli/commands/config.py +277 -0
- claude_mpm/cli/commands/run.py +224 -36
- claude_mpm/cli/parser.py +176 -1
- claude_mpm/constants.py +19 -0
- claude_mpm/core/claude_runner.py +320 -44
- claude_mpm/core/config.py +161 -4
- claude_mpm/core/framework_loader.py +81 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +391 -9
- claude_mpm/init.py +40 -5
- claude_mpm/models/agent_session.py +511 -0
- claude_mpm/scripts/__init__.py +15 -0
- claude_mpm/scripts/start_activity_logging.py +86 -0
- claude_mpm/services/agents/deployment/agent_deployment.py +165 -19
- claude_mpm/services/agents/deployment/async_agent_deployment.py +461 -0
- claude_mpm/services/event_aggregator.py +547 -0
- claude_mpm/utils/agent_dependency_loader.py +655 -0
- claude_mpm/utils/console.py +11 -0
- claude_mpm/utils/dependency_cache.py +376 -0
- claude_mpm/utils/dependency_strategies.py +343 -0
- claude_mpm/utils/environment_context.py +310 -0
- {claude_mpm-3.5.6.dist-info → claude_mpm-3.6.0.dist-info}/METADATA +47 -3
- {claude_mpm-3.5.6.dist-info → claude_mpm-3.6.0.dist-info}/RECORD +45 -31
- claude_mpm/agents/templates/pm.json +0 -122
- {claude_mpm-3.5.6.dist-info → claude_mpm-3.6.0.dist-info}/WHEEL +0 -0
- {claude_mpm-3.5.6.dist-info → claude_mpm-3.6.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-3.5.6.dist-info → claude_mpm-3.6.0.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-3.5.6.dist-info → claude_mpm-3.6.0.dist-info}/top_level.txt +0 -0
| @@ -37,6 +37,7 @@ from typing import Optional, List, Dict, Any | |
| 37 37 | 
             
            from claude_mpm.core.logger import get_logger
         | 
| 38 38 | 
             
            from claude_mpm.constants import EnvironmentVars, Paths, AgentMetadata
         | 
| 39 39 | 
             
            from claude_mpm.config.paths import paths
         | 
| 40 | 
            +
            from claude_mpm.core.config import Config
         | 
| 40 41 |  | 
| 41 42 |  | 
| 42 43 | 
             
            class AgentDeploymentService:
         | 
| @@ -131,7 +132,7 @@ class AgentDeploymentService: | |
| 131 132 | 
             
                    self.logger.info(f"Templates directory: {self.templates_dir}")
         | 
| 132 133 | 
             
                    self.logger.info(f"Base agent path: {self.base_agent_path}")
         | 
| 133 134 |  | 
| 134 | 
            -
                def deploy_agents(self, target_dir: Optional[Path] = None, force_rebuild: bool = False, deployment_mode: str = "update") -> Dict[str, Any]:
         | 
| 135 | 
            +
                def deploy_agents(self, target_dir: Optional[Path] = None, force_rebuild: bool = False, deployment_mode: str = "update", config: Optional[Config] = None, use_async: bool = True) -> Dict[str, Any]:
         | 
| 135 136 | 
             
                    """
         | 
| 136 137 | 
             
                    Build and deploy agents by combining base_agent.md with templates.
         | 
| 137 138 | 
             
                    Also deploys system instructions for PM framework.
         | 
| @@ -140,6 +141,12 @@ class AgentDeploymentService: | |
| 140 141 | 
             
                    - "update": Normal update mode - skip agents with matching versions (default)
         | 
| 141 142 | 
             
                    - "project": Project deployment mode - always deploy all agents regardless of version
         | 
| 142 143 |  | 
| 144 | 
            +
                    CONFIGURATION:
         | 
| 145 | 
            +
                    The config parameter or default configuration is used to determine:
         | 
| 146 | 
            +
                    - Which agents to exclude from deployment
         | 
| 147 | 
            +
                    - Case sensitivity for agent name matching
         | 
| 148 | 
            +
                    - Whether to exclude agent dependencies
         | 
| 149 | 
            +
                    
         | 
| 143 150 | 
             
                    METRICS COLLECTED:
         | 
| 144 151 | 
             
                    - Deployment start/end timestamps
         | 
| 145 152 | 
             
                    - Individual agent deployment durations
         | 
| @@ -181,6 +188,8 @@ class AgentDeploymentService: | |
| 181 188 | 
             
                        target_dir: Target directory for agents (default: .claude/agents/)
         | 
| 182 189 | 
             
                        force_rebuild: Force rebuild even if agents exist (useful for troubleshooting)
         | 
| 183 190 | 
             
                        deployment_mode: "update" for version-aware updates, "project" for always deploy
         | 
| 191 | 
            +
                        config: Optional configuration object (loads default if not provided)
         | 
| 192 | 
            +
                        use_async: Use async operations for 50-70% faster deployment (default: True)
         | 
| 184 193 |  | 
| 185 194 | 
             
                    Returns:
         | 
| 186 195 | 
             
                        Dictionary with deployment results:
         | 
| @@ -196,6 +205,64 @@ class AgentDeploymentService: | |
| 196 205 | 
             
                    # METRICS: Record deployment start time for performance tracking
         | 
| 197 206 | 
             
                    deployment_start_time = time.time()
         | 
| 198 207 |  | 
| 208 | 
            +
                    # Try async deployment for better performance if requested
         | 
| 209 | 
            +
                    if use_async:
         | 
| 210 | 
            +
                        try:
         | 
| 211 | 
            +
                            from .async_agent_deployment import deploy_agents_async_wrapper
         | 
| 212 | 
            +
                            self.logger.info("Using async deployment for improved performance")
         | 
| 213 | 
            +
                            
         | 
| 214 | 
            +
                            # Run async deployment
         | 
| 215 | 
            +
                            results = deploy_agents_async_wrapper(
         | 
| 216 | 
            +
                                templates_dir=self.templates_dir,
         | 
| 217 | 
            +
                                base_agent_path=self.base_agent_path,
         | 
| 218 | 
            +
                                working_directory=self.working_directory,
         | 
| 219 | 
            +
                                target_dir=target_dir,
         | 
| 220 | 
            +
                                force_rebuild=force_rebuild,
         | 
| 221 | 
            +
                                config=config
         | 
| 222 | 
            +
                            )
         | 
| 223 | 
            +
                            
         | 
| 224 | 
            +
                            # Add metrics about async vs sync
         | 
| 225 | 
            +
                            if 'metrics' in results:
         | 
| 226 | 
            +
                                results['metrics']['deployment_method'] = 'async'
         | 
| 227 | 
            +
                                duration_ms = results['metrics'].get('duration_ms', 0)
         | 
| 228 | 
            +
                                self.logger.info(f"Async deployment completed in {duration_ms:.1f}ms")
         | 
| 229 | 
            +
                                
         | 
| 230 | 
            +
                                # Update internal metrics
         | 
| 231 | 
            +
                                self._deployment_metrics['total_deployments'] += 1
         | 
| 232 | 
            +
                                if not results.get('errors'):
         | 
| 233 | 
            +
                                    self._deployment_metrics['successful_deployments'] += 1
         | 
| 234 | 
            +
                                else:
         | 
| 235 | 
            +
                                    self._deployment_metrics['failed_deployments'] += 1
         | 
| 236 | 
            +
                            
         | 
| 237 | 
            +
                            return results
         | 
| 238 | 
            +
                            
         | 
| 239 | 
            +
                        except ImportError:
         | 
| 240 | 
            +
                            self.logger.warning("Async deployment not available, falling back to sync")
         | 
| 241 | 
            +
                        except Exception as e:
         | 
| 242 | 
            +
                            self.logger.warning(f"Async deployment failed, falling back to sync: {e}")
         | 
| 243 | 
            +
                    
         | 
| 244 | 
            +
                    # Continue with synchronous deployment
         | 
| 245 | 
            +
                    self.logger.info("Using synchronous deployment")
         | 
| 246 | 
            +
                    
         | 
| 247 | 
            +
                    # Load configuration if not provided
         | 
| 248 | 
            +
                    if config is None:
         | 
| 249 | 
            +
                        config = Config()
         | 
| 250 | 
            +
                    
         | 
| 251 | 
            +
                    # Get agent exclusion configuration
         | 
| 252 | 
            +
                    excluded_agents = config.get('agent_deployment.excluded_agents', [])
         | 
| 253 | 
            +
                    case_sensitive = config.get('agent_deployment.case_sensitive', False)
         | 
| 254 | 
            +
                    exclude_dependencies = config.get('agent_deployment.exclude_dependencies', False)
         | 
| 255 | 
            +
                    
         | 
| 256 | 
            +
                    # Normalize excluded agents list for comparison
         | 
| 257 | 
            +
                    if not case_sensitive:
         | 
| 258 | 
            +
                        excluded_agents = [agent.lower() for agent in excluded_agents]
         | 
| 259 | 
            +
                    
         | 
| 260 | 
            +
                    # Log exclusion configuration if agents are being excluded
         | 
| 261 | 
            +
                    if excluded_agents:
         | 
| 262 | 
            +
                        self.logger.info(f"Excluding agents from deployment: {excluded_agents}")
         | 
| 263 | 
            +
                        self.logger.debug(f"Case sensitive matching: {case_sensitive}")
         | 
| 264 | 
            +
                        self.logger.debug(f"Exclude dependencies: {exclude_dependencies}")
         | 
| 265 | 
            +
                    
         | 
| 199 266 | 
             
                    if not target_dir:
         | 
| 200 267 | 
             
                        # Default to working directory's .claude/agents directory (not home)
         | 
| 201 268 | 
             
                        # This ensures we deploy to the user's project directory, not the framework directory
         | 
| @@ -294,17 +361,54 @@ class AgentDeploymentService: | |
| 294 361 |  | 
| 295 362 | 
             
                        # Get all template files
         | 
| 296 363 | 
             
                        template_files = list(self.templates_dir.glob("*.json"))
         | 
| 297 | 
            -
                         | 
| 298 | 
            -
                        #  | 
| 299 | 
            -
                         | 
| 300 | 
            -
                         | 
| 301 | 
            -
             | 
| 302 | 
            -
             | 
| 303 | 
            -
             | 
| 304 | 
            -
             | 
| 305 | 
            -
             | 
| 364 | 
            +
                        
         | 
| 365 | 
            +
                        # Build the combined exclusion set
         | 
| 366 | 
            +
                        # Start with hardcoded exclusions (these are ALWAYS excluded)
         | 
| 367 | 
            +
                        hardcoded_exclusions = {"__init__", "MEMORIES", "TODOWRITE", "INSTRUCTIONS", "README", "pm", "PM", "project_manager"}
         | 
| 368 | 
            +
                        
         | 
| 369 | 
            +
                        # Add user-configured exclusions
         | 
| 370 | 
            +
                        user_exclusions = set()
         | 
| 371 | 
            +
                        for agent in excluded_agents:
         | 
| 372 | 
            +
                            if case_sensitive:
         | 
| 373 | 
            +
                                user_exclusions.add(agent)
         | 
| 374 | 
            +
                            else:
         | 
| 375 | 
            +
                                # For case-insensitive, we'll check during filtering
         | 
| 376 | 
            +
                                user_exclusions.add(agent.lower())
         | 
| 377 | 
            +
                        
         | 
| 378 | 
            +
                        # Filter out excluded agents
         | 
| 379 | 
            +
                        filtered_files = []
         | 
| 380 | 
            +
                        excluded_count = 0
         | 
| 381 | 
            +
                        for f in template_files:
         | 
| 382 | 
            +
                            agent_name = f.stem
         | 
| 383 | 
            +
                            
         | 
| 384 | 
            +
                            # Check hardcoded exclusions (always case-sensitive)
         | 
| 385 | 
            +
                            if agent_name in hardcoded_exclusions:
         | 
| 386 | 
            +
                                self.logger.debug(f"Excluding {agent_name}: hardcoded system exclusion")
         | 
| 387 | 
            +
                                excluded_count += 1
         | 
| 388 | 
            +
                                continue
         | 
| 389 | 
            +
                            
         | 
| 390 | 
            +
                            # Check file patterns
         | 
| 391 | 
            +
                            if agent_name.startswith(".") or agent_name.endswith(".backup"):
         | 
| 392 | 
            +
                                self.logger.debug(f"Excluding {agent_name}: file pattern exclusion")
         | 
| 393 | 
            +
                                excluded_count += 1
         | 
| 394 | 
            +
                                continue
         | 
| 395 | 
            +
                            
         | 
| 396 | 
            +
                            # Check user-configured exclusions
         | 
| 397 | 
            +
                            compare_name = agent_name.lower() if not case_sensitive else agent_name
         | 
| 398 | 
            +
                            if compare_name in user_exclusions:
         | 
| 399 | 
            +
                                self.logger.info(f"Excluding {agent_name}: user-configured exclusion")
         | 
| 400 | 
            +
                                excluded_count += 1
         | 
| 401 | 
            +
                                continue
         | 
| 402 | 
            +
                            
         | 
| 403 | 
            +
                            # Agent is not excluded, add to filtered list
         | 
| 404 | 
            +
                            filtered_files.append(f)
         | 
| 405 | 
            +
                        
         | 
| 406 | 
            +
                        template_files = filtered_files
         | 
| 306 407 | 
             
                        results["total"] = len(template_files)
         | 
| 307 408 |  | 
| 409 | 
            +
                        if excluded_count > 0:
         | 
| 410 | 
            +
                            self.logger.info(f"Excluded {excluded_count} agents from deployment")
         | 
| 411 | 
            +
                        
         | 
| 308 412 | 
             
                        for template_file in template_files:
         | 
| 309 413 | 
             
                            try:
         | 
| 310 414 | 
             
                                # METRICS: Track individual agent deployment time
         | 
| @@ -983,6 +1087,17 @@ temperature: {temperature}""" | |
| 983 1087 |  | 
| 984 1088 | 
             
                    # List deployed agents
         | 
| 985 1089 | 
             
                    agent_files = list(agents_dir.glob("*.md"))
         | 
| 1090 | 
            +
                    
         | 
| 1091 | 
            +
                    # Get exclusion configuration for logging purposes
         | 
| 1092 | 
            +
                    try:
         | 
| 1093 | 
            +
                        from claude_mpm.core.config import Config
         | 
| 1094 | 
            +
                        config = Config()
         | 
| 1095 | 
            +
                        excluded_agents = config.get('agent_deployment.excluded_agents', [])
         | 
| 1096 | 
            +
                        if excluded_agents:
         | 
| 1097 | 
            +
                            self.logger.debug(f"Note: The following agents are configured for exclusion: {excluded_agents}")
         | 
| 1098 | 
            +
                    except Exception:
         | 
| 1099 | 
            +
                        pass  # Ignore config loading errors in verification
         | 
| 1100 | 
            +
                    
         | 
| 986 1101 | 
             
                    for agent_file in agent_files:
         | 
| 987 1102 | 
             
                        try:
         | 
| 988 1103 | 
             
                            # Read first few lines to get agent name from YAML
         | 
| @@ -1123,15 +1238,46 @@ temperature: {temperature}""" | |
| 1123 1238 | 
             
                        return agents
         | 
| 1124 1239 |  | 
| 1125 1240 | 
             
                    template_files = sorted(self.templates_dir.glob("*.json"))
         | 
| 1126 | 
            -
                     | 
| 1127 | 
            -
                    #  | 
| 1128 | 
            -
                     | 
| 1129 | 
            -
             | 
| 1130 | 
            -
                         | 
| 1131 | 
            -
                         | 
| 1132 | 
            -
                         | 
| 1133 | 
            -
                         | 
| 1134 | 
            -
             | 
| 1241 | 
            +
                    
         | 
| 1242 | 
            +
                    # Load configuration for exclusions
         | 
| 1243 | 
            +
                    try:
         | 
| 1244 | 
            +
                        from claude_mpm.core.config import Config
         | 
| 1245 | 
            +
                        config = Config()
         | 
| 1246 | 
            +
                        excluded_agents = config.get('agent_deployment.excluded_agents', [])
         | 
| 1247 | 
            +
                        case_sensitive = config.get('agent_deployment.case_sensitive', False)
         | 
| 1248 | 
            +
                        
         | 
| 1249 | 
            +
                        # Normalize excluded agents for comparison
         | 
| 1250 | 
            +
                        if not case_sensitive:
         | 
| 1251 | 
            +
                            excluded_agents = [agent.lower() for agent in excluded_agents]
         | 
| 1252 | 
            +
                    except Exception:
         | 
| 1253 | 
            +
                        # If config loading fails, use empty exclusion list
         | 
| 1254 | 
            +
                        excluded_agents = []
         | 
| 1255 | 
            +
                        case_sensitive = False
         | 
| 1256 | 
            +
                    
         | 
| 1257 | 
            +
                    # Build combined exclusion set
         | 
| 1258 | 
            +
                    hardcoded_exclusions = {"__init__", "MEMORIES", "TODOWRITE", "INSTRUCTIONS", "README", "pm", "PM", "project_manager"}
         | 
| 1259 | 
            +
                    
         | 
| 1260 | 
            +
                    # Filter out excluded agents
         | 
| 1261 | 
            +
                    filtered_files = []
         | 
| 1262 | 
            +
                    for f in template_files:
         | 
| 1263 | 
            +
                        agent_name = f.stem
         | 
| 1264 | 
            +
                        
         | 
| 1265 | 
            +
                        # Check hardcoded exclusions
         | 
| 1266 | 
            +
                        if agent_name in hardcoded_exclusions:
         | 
| 1267 | 
            +
                            continue
         | 
| 1268 | 
            +
                        
         | 
| 1269 | 
            +
                        # Check file patterns
         | 
| 1270 | 
            +
                        if agent_name.startswith(".") or agent_name.endswith(".backup"):
         | 
| 1271 | 
            +
                            continue
         | 
| 1272 | 
            +
                        
         | 
| 1273 | 
            +
                        # Check user-configured exclusions
         | 
| 1274 | 
            +
                        compare_name = agent_name.lower() if not case_sensitive else agent_name
         | 
| 1275 | 
            +
                        if compare_name in excluded_agents:
         | 
| 1276 | 
            +
                            continue
         | 
| 1277 | 
            +
                        
         | 
| 1278 | 
            +
                        filtered_files.append(f)
         | 
| 1279 | 
            +
                    
         | 
| 1280 | 
            +
                    template_files = filtered_files
         | 
| 1135 1281 |  | 
| 1136 1282 | 
             
                    for template_file in template_files:
         | 
| 1137 1283 | 
             
                        try:
         |