claude-mpm 3.5.4__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/.claude-mpm/logs/hooks_20250728.log +10 -0
- 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 -102
- claude_mpm/agents/agent-template.yaml +83 -0
- 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/README.md +108 -0
- claude_mpm/cli/__init__.py +5 -1
- claude_mpm/cli/commands/__init__.py +5 -1
- claude_mpm/cli/commands/agents.py +233 -6
- claude_mpm/cli/commands/aggregate.py +462 -0
- claude_mpm/cli/commands/config.py +277 -0
- claude_mpm/cli/commands/run.py +228 -47
- claude_mpm/cli/parser.py +176 -1
- claude_mpm/cli/utils.py +9 -1
- claude_mpm/cli_module/refactoring_guide.md +253 -0
- claude_mpm/config/async_logging_config.yaml +145 -0
- claude_mpm/constants.py +19 -0
- claude_mpm/core/.claude-mpm/logs/hooks_20250730.log +34 -0
- claude_mpm/core/claude_runner.py +413 -76
- claude_mpm/core/config.py +161 -4
- claude_mpm/core/config_paths.py +0 -1
- claude_mpm/core/factories.py +9 -3
- claude_mpm/core/framework_loader.py +81 -0
- claude_mpm/dashboard/.claude-mpm/memories/README.md +36 -0
- claude_mpm/dashboard/README.md +121 -0
- claude_mpm/dashboard/static/js/dashboard.js.backup +1973 -0
- claude_mpm/dashboard/templates/.claude-mpm/memories/README.md +36 -0
- claude_mpm/dashboard/templates/.claude-mpm/memories/engineer_agent.md +39 -0
- claude_mpm/dashboard/templates/.claude-mpm/memories/version_control_agent.md +38 -0
- claude_mpm/hooks/README.md +96 -0
- claude_mpm/hooks/claude_hooks/hook_handler.py +391 -9
- claude_mpm/init.py +123 -18
- claude_mpm/models/agent_session.py +511 -0
- claude_mpm/schemas/agent_schema.json +435 -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 +326 -24
- claude_mpm/services/agents/deployment/async_agent_deployment.py +461 -0
- claude_mpm/services/agents/management/agent_management_service.py +2 -1
- claude_mpm/services/event_aggregator.py +547 -0
- claude_mpm/services/framework_claude_md_generator/README.md +92 -0
- claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +3 -3
- claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +2 -2
- claude_mpm/services/version_control/VERSION +1 -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.4.dist-info → claude_mpm-3.6.0.dist-info}/METADATA +87 -1
- {claude_mpm-3.5.4.dist-info → claude_mpm-3.6.0.dist-info}/RECORD +67 -37
- claude_mpm/agents/templates/pm.json +0 -122
- {claude_mpm-3.5.4.dist-info → claude_mpm-3.6.0.dist-info}/WHEEL +0 -0
- {claude_mpm-3.5.4.dist-info → claude_mpm-3.6.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-3.5.4.dist-info → claude_mpm-3.6.0.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-3.5.4.dist-info → claude_mpm-3.6.0.dist-info}/top_level.txt +0 -0
    
        claude_mpm/core/config.py
    CHANGED
    
    | @@ -7,8 +7,10 @@ and default values with proper validation and type conversion. | |
| 7 7 |  | 
| 8 8 | 
             
            import os
         | 
| 9 9 | 
             
            from pathlib import Path
         | 
| 10 | 
            -
            from typing import Any, Dict, Optional, Union
         | 
| 10 | 
            +
            from typing import Any, Dict, Optional, Union, List, Tuple
         | 
| 11 11 | 
             
            import logging
         | 
| 12 | 
            +
            import yaml
         | 
| 13 | 
            +
            import json
         | 
| 12 14 |  | 
| 13 15 | 
             
            from ..utils.config_manager import ConfigurationManager
         | 
| 14 16 | 
             
            from .config_paths import ConfigPaths
         | 
| @@ -49,14 +51,25 @@ class Config: | |
| 49 51 | 
             
                    if config:
         | 
| 50 52 | 
             
                        self._config.update(config)
         | 
| 51 53 |  | 
| 54 | 
            +
                    # Track where configuration was loaded from
         | 
| 55 | 
            +
                    self._loaded_from = None
         | 
| 56 | 
            +
                    
         | 
| 52 57 | 
             
                    # Load from file if provided
         | 
| 53 58 | 
             
                    if config_file:
         | 
| 54 59 | 
             
                        self.load_file(config_file)
         | 
| 60 | 
            +
                        self._loaded_from = str(config_file)
         | 
| 55 61 | 
             
                    else:
         | 
| 56 62 | 
             
                        # Try to load from standard location: .claude-mpm/configuration.yaml
         | 
| 57 63 | 
             
                        default_config = Path.cwd() / ".claude-mpm" / "configuration.yaml"
         | 
| 58 64 | 
             
                        if default_config.exists():
         | 
| 59 65 | 
             
                            self.load_file(default_config)
         | 
| 66 | 
            +
                            self._loaded_from = str(default_config)
         | 
| 67 | 
            +
                        else:
         | 
| 68 | 
            +
                            # Also try .yml extension
         | 
| 69 | 
            +
                            alt_config = Path.cwd() / ".claude-mpm" / "configuration.yml"
         | 
| 70 | 
            +
                            if alt_config.exists():
         | 
| 71 | 
            +
                                self.load_file(alt_config)
         | 
| 72 | 
            +
                                self._loaded_from = str(alt_config)
         | 
| 60 73 |  | 
| 61 74 | 
             
                    # Load from environment variables (new and legacy prefixes)
         | 
| 62 75 | 
             
                    self._load_env_vars()
         | 
| @@ -66,21 +79,64 @@ class Config: | |
| 66 79 | 
             
                    self._apply_defaults()
         | 
| 67 80 |  | 
| 68 81 | 
             
                def load_file(self, file_path: Union[str, Path]) -> None:
         | 
| 69 | 
            -
                    """Load configuration from file. | 
| 82 | 
            +
                    """Load configuration from file with enhanced error handling.
         | 
| 83 | 
            +
                    
         | 
| 84 | 
            +
                    WHY: Configuration loading failures can cause silent issues. We need
         | 
| 85 | 
            +
                    to provide clear, actionable error messages to help users fix problems.
         | 
| 86 | 
            +
                    """
         | 
| 70 87 | 
             
                    file_path = Path(file_path)
         | 
| 71 88 |  | 
| 72 89 | 
             
                    if not file_path.exists():
         | 
| 73 90 | 
             
                        logger.warning(f"Configuration file not found: {file_path}")
         | 
| 91 | 
            +
                        logger.info(f"TIP: Create a configuration file with: mkdir -p {file_path.parent} && touch {file_path}")
         | 
| 74 92 | 
             
                        return
         | 
| 75 93 |  | 
| 76 94 | 
             
                    try:
         | 
| 95 | 
            +
                        # Check if file is readable
         | 
| 96 | 
            +
                        if not os.access(file_path, os.R_OK):
         | 
| 97 | 
            +
                            logger.error(f"Configuration file is not readable: {file_path}")
         | 
| 98 | 
            +
                            logger.info(f"TIP: Fix permissions with: chmod 644 {file_path}")
         | 
| 99 | 
            +
                            return
         | 
| 100 | 
            +
                            
         | 
| 101 | 
            +
                        # Check file size (warn if too large)
         | 
| 102 | 
            +
                        file_size = file_path.stat().st_size
         | 
| 103 | 
            +
                        if file_size > 1024 * 1024:  # 1MB
         | 
| 104 | 
            +
                            logger.warning(f"Configuration file is large ({file_size} bytes): {file_path}")
         | 
| 105 | 
            +
                            
         | 
| 106 | 
            +
                        # Try to load the configuration
         | 
| 77 107 | 
             
                        file_config = self._config_mgr.load_auto(file_path)
         | 
| 78 108 | 
             
                        if file_config:
         | 
| 79 109 | 
             
                            self._config = self._config_mgr.merge_configs(self._config, file_config)
         | 
| 80 | 
            -
                            logger.info(f" | 
| 81 | 
            -
             | 
| 110 | 
            +
                            logger.info(f"✓ Successfully loaded configuration from {file_path}")
         | 
| 111 | 
            +
                            
         | 
| 112 | 
            +
                            # Log important configuration values for debugging
         | 
| 113 | 
            +
                            if logger.isEnabledFor(logging.DEBUG):
         | 
| 114 | 
            +
                                response_logging = file_config.get('response_logging', {})
         | 
| 115 | 
            +
                                if response_logging:
         | 
| 116 | 
            +
                                    logger.debug(f"Response logging enabled: {response_logging.get('enabled', False)}")
         | 
| 117 | 
            +
                                    logger.debug(f"Response logging format: {response_logging.get('format', 'json')}")
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                    except yaml.YAMLError as e:
         | 
| 120 | 
            +
                        logger.error(f"YAML syntax error in {file_path}: {e}")
         | 
| 121 | 
            +
                        if hasattr(e, 'problem_mark'):
         | 
| 122 | 
            +
                            mark = e.problem_mark
         | 
| 123 | 
            +
                            logger.error(f"Error at line {mark.line + 1}, column {mark.column + 1}")
         | 
| 124 | 
            +
                        logger.info("TIP: Validate your YAML at https://www.yamllint.com/ or run: python scripts/validate_configuration.py")
         | 
| 125 | 
            +
                        logger.info("TIP: Common issue - YAML requires spaces, not tabs. Fix with: sed -i '' 's/\t/    /g' " + str(file_path))
         | 
| 126 | 
            +
                        # Store error for later retrieval
         | 
| 127 | 
            +
                        self._config['_load_error'] = str(e)
         | 
| 128 | 
            +
                        
         | 
| 129 | 
            +
                    except json.JSONDecodeError as e:
         | 
| 130 | 
            +
                        logger.error(f"JSON syntax error in {file_path}: {e}")
         | 
| 131 | 
            +
                        logger.error(f"Error at line {e.lineno}, column {e.colno}")
         | 
| 132 | 
            +
                        logger.info("TIP: Validate your JSON at https://jsonlint.com/")
         | 
| 133 | 
            +
                        self._config['_load_error'] = str(e)
         | 
| 134 | 
            +
                        
         | 
| 82 135 | 
             
                    except Exception as e:
         | 
| 83 136 | 
             
                        logger.error(f"Failed to load configuration from {file_path}: {e}")
         | 
| 137 | 
            +
                        logger.info("TIP: Check file permissions and format (YAML/JSON)")
         | 
| 138 | 
            +
                        logger.info("TIP: Run validation with: python scripts/validate_configuration.py")
         | 
| 139 | 
            +
                        self._config['_load_error'] = str(e)
         | 
| 84 140 |  | 
| 85 141 | 
             
                def _load_env_vars(self) -> None:
         | 
| 86 142 | 
             
                    """Load configuration from environment variables."""
         | 
| @@ -323,6 +379,12 @@ class Config: | |
| 323 379 | 
             
                                    "emergency_stop": True
         | 
| 324 380 | 
             
                                }
         | 
| 325 381 | 
             
                            }
         | 
| 382 | 
            +
                        },
         | 
| 383 | 
            +
                        # Agent deployment configuration
         | 
| 384 | 
            +
                        "agent_deployment": {
         | 
| 385 | 
            +
                            "excluded_agents": [],              # List of agent IDs to exclude from deployment
         | 
| 386 | 
            +
                            "exclude_dependencies": False,      # Whether to exclude agent dependencies too
         | 
| 387 | 
            +
                            "case_sensitive": False            # Whether agent name matching is case-sensitive
         | 
| 326 388 | 
             
                        }
         | 
| 327 389 | 
             
                    }
         | 
| 328 390 |  | 
| @@ -515,6 +577,101 @@ class Config: | |
| 515 577 |  | 
| 516 578 | 
             
                    return base_config
         | 
| 517 579 |  | 
| 580 | 
            +
                def validate_configuration(self) -> Tuple[bool, List[str], List[str]]:
         | 
| 581 | 
            +
                    """Validate the loaded configuration programmatically.
         | 
| 582 | 
            +
                    
         | 
| 583 | 
            +
                    WHY: Provide a programmatic way to validate configuration that can be
         | 
| 584 | 
            +
                    used by other components to check configuration health.
         | 
| 585 | 
            +
                    
         | 
| 586 | 
            +
                    Returns:
         | 
| 587 | 
            +
                        Tuple of (is_valid, errors, warnings)
         | 
| 588 | 
            +
                    """
         | 
| 589 | 
            +
                    errors = []
         | 
| 590 | 
            +
                    warnings = []
         | 
| 591 | 
            +
                    
         | 
| 592 | 
            +
                    # Check if there was a load error
         | 
| 593 | 
            +
                    if '_load_error' in self._config:
         | 
| 594 | 
            +
                        errors.append(f"Configuration load error: {self._config['_load_error']}")
         | 
| 595 | 
            +
                        
         | 
| 596 | 
            +
                    # Validate response_logging configuration
         | 
| 597 | 
            +
                    response_logging = self.get('response_logging', {})
         | 
| 598 | 
            +
                    if response_logging:
         | 
| 599 | 
            +
                        # Check enabled field
         | 
| 600 | 
            +
                        if 'enabled' in response_logging and not isinstance(response_logging['enabled'], bool):
         | 
| 601 | 
            +
                            errors.append(
         | 
| 602 | 
            +
                                f"response_logging.enabled must be boolean, got {type(response_logging['enabled']).__name__}"
         | 
| 603 | 
            +
                            )
         | 
| 604 | 
            +
                            
         | 
| 605 | 
            +
                        # Check format field
         | 
| 606 | 
            +
                        if 'format' in response_logging:
         | 
| 607 | 
            +
                            valid_formats = ['json', 'syslog', 'journald']
         | 
| 608 | 
            +
                            if response_logging['format'] not in valid_formats:
         | 
| 609 | 
            +
                                errors.append(
         | 
| 610 | 
            +
                                    f"response_logging.format must be one of {valid_formats}, "
         | 
| 611 | 
            +
                                    f"got '{response_logging['format']}'"
         | 
| 612 | 
            +
                                )
         | 
| 613 | 
            +
                                
         | 
| 614 | 
            +
                        # Check session_directory
         | 
| 615 | 
            +
                        if 'session_directory' in response_logging:
         | 
| 616 | 
            +
                            session_dir = Path(response_logging['session_directory'])
         | 
| 617 | 
            +
                            if session_dir.is_absolute() and not session_dir.parent.exists():
         | 
| 618 | 
            +
                                warnings.append(
         | 
| 619 | 
            +
                                    f"Parent directory for session_directory does not exist: {session_dir.parent}"
         | 
| 620 | 
            +
                                )
         | 
| 621 | 
            +
                                
         | 
| 622 | 
            +
                    # Validate memory configuration
         | 
| 623 | 
            +
                    memory_config = self.get('memory', {})
         | 
| 624 | 
            +
                    if memory_config:
         | 
| 625 | 
            +
                        if 'enabled' in memory_config and not isinstance(memory_config['enabled'], bool):
         | 
| 626 | 
            +
                            errors.append(f"memory.enabled must be boolean")
         | 
| 627 | 
            +
                            
         | 
| 628 | 
            +
                        # Check limits
         | 
| 629 | 
            +
                        limits = memory_config.get('limits', {})
         | 
| 630 | 
            +
                        for field in ['default_size_kb', 'max_sections', 'max_items_per_section']:
         | 
| 631 | 
            +
                            if field in limits:
         | 
| 632 | 
            +
                                value = limits[field]
         | 
| 633 | 
            +
                                if not isinstance(value, int) or value <= 0:
         | 
| 634 | 
            +
                                    errors.append(f"memory.limits.{field} must be positive integer, got {value}")
         | 
| 635 | 
            +
                                    
         | 
| 636 | 
            +
                    # Validate health thresholds
         | 
| 637 | 
            +
                    health_thresholds = self.get('health_thresholds', {})
         | 
| 638 | 
            +
                    if health_thresholds:
         | 
| 639 | 
            +
                        cpu = health_thresholds.get('cpu_percent')
         | 
| 640 | 
            +
                        if cpu is not None and (not isinstance(cpu, (int, float)) or cpu < 0 or cpu > 100):
         | 
| 641 | 
            +
                            errors.append(f"health_thresholds.cpu_percent must be 0-100, got {cpu}")
         | 
| 642 | 
            +
                            
         | 
| 643 | 
            +
                        mem = health_thresholds.get('memory_mb')
         | 
| 644 | 
            +
                        if mem is not None and (not isinstance(mem, (int, float)) or mem <= 0):
         | 
| 645 | 
            +
                            errors.append(f"health_thresholds.memory_mb must be positive, got {mem}")
         | 
| 646 | 
            +
                            
         | 
| 647 | 
            +
                    is_valid = len(errors) == 0
         | 
| 648 | 
            +
                    return is_valid, errors, warnings
         | 
| 649 | 
            +
                    
         | 
| 650 | 
            +
                def get_configuration_status(self) -> Dict[str, Any]:
         | 
| 651 | 
            +
                    """Get detailed configuration status for debugging.
         | 
| 652 | 
            +
                    
         | 
| 653 | 
            +
                    WHY: Provide a comprehensive view of configuration state for
         | 
| 654 | 
            +
                    troubleshooting and health checks.
         | 
| 655 | 
            +
                    
         | 
| 656 | 
            +
                    Returns:
         | 
| 657 | 
            +
                        Dictionary with configuration status information
         | 
| 658 | 
            +
                    """
         | 
| 659 | 
            +
                    is_valid, errors, warnings = self.validate_configuration()
         | 
| 660 | 
            +
                    
         | 
| 661 | 
            +
                    status = {
         | 
| 662 | 
            +
                        'valid': is_valid,
         | 
| 663 | 
            +
                        'errors': errors,
         | 
| 664 | 
            +
                        'warnings': warnings,
         | 
| 665 | 
            +
                        'loaded_from': getattr(self, '_loaded_from', 'defaults'),
         | 
| 666 | 
            +
                        'key_count': len(self._config),
         | 
| 667 | 
            +
                        'has_response_logging': 'response_logging' in self._config,
         | 
| 668 | 
            +
                        'has_memory_config': 'memory' in self._config,
         | 
| 669 | 
            +
                        'response_logging_enabled': self.get('response_logging.enabled', False),
         | 
| 670 | 
            +
                        'memory_enabled': self.get('memory.enabled', False)
         | 
| 671 | 
            +
                    }
         | 
| 672 | 
            +
                    
         | 
| 673 | 
            +
                    return status
         | 
| 674 | 
            +
             | 
| 518 675 | 
             
                def __repr__(self) -> str:
         | 
| 519 676 | 
             
                    """String representation of configuration."""
         | 
| 520 677 | 
             
                    return f"<Config({len(self._config)} keys)>"
         | 
    
        claude_mpm/core/config_paths.py
    CHANGED
    
    
    
        claude_mpm/core/factories.py
    CHANGED
    
    | @@ -57,14 +57,20 @@ class AgentServiceFactory(ServiceFactory): | |
| 57 57 | 
             
                    config = container.resolve(Config)
         | 
| 58 58 |  | 
| 59 59 | 
             
                    # Get directories from config if not provided
         | 
| 60 | 
            +
                    import os
         | 
| 61 | 
            +
                    
         | 
| 60 62 | 
             
                    if framework_dir is None:
         | 
| 61 63 | 
             
                        framework_dir = Path(config.get('framework.dir', 'framework'))
         | 
| 62 64 |  | 
| 63 65 | 
             
                    if project_dir is None:
         | 
| 64 | 
            -
                         | 
| 66 | 
            +
                        # Check for user working directory from environment
         | 
| 67 | 
            +
                        if 'CLAUDE_MPM_USER_PWD' in os.environ:
         | 
| 68 | 
            +
                            project_dir = Path(os.environ['CLAUDE_MPM_USER_PWD'])
         | 
| 69 | 
            +
                        else:
         | 
| 70 | 
            +
                            project_dir = Path(config.get('project.dir', '.'))
         | 
| 65 71 |  | 
| 66 | 
            -
                    # Create service with  | 
| 67 | 
            -
                    service = AgentDeploymentService()
         | 
| 72 | 
            +
                    # Create service with proper working directory
         | 
| 73 | 
            +
                    service = AgentDeploymentService(working_directory=project_dir)
         | 
| 68 74 |  | 
| 69 75 | 
             
                    # Inject any required dependencies
         | 
| 70 76 | 
             
                    if hasattr(service, 'set_directories'):
         | 
| @@ -274,6 +274,13 @@ class FrameworkLoader: | |
| 274 274 | 
             
                            if self.framework_last_modified:
         | 
| 275 275 | 
             
                                content["instructions_last_modified"] = self.framework_last_modified
         | 
| 276 276 |  | 
| 277 | 
            +
                    # Load BASE_PM.md for core framework requirements
         | 
| 278 | 
            +
                    base_pm_path = self.framework_path / "src" / "claude_mpm" / "agents" / "BASE_PM.md"
         | 
| 279 | 
            +
                    if base_pm_path.exists():
         | 
| 280 | 
            +
                        base_pm_content = self._try_load_file(base_pm_path, "BASE_PM framework requirements")
         | 
| 281 | 
            +
                        if base_pm_content:
         | 
| 282 | 
            +
                            content["base_pm_instructions"] = base_pm_content
         | 
| 283 | 
            +
                    
         | 
| 277 284 | 
             
                    # Discover agent directories
         | 
| 278 285 | 
             
                    agents_dir, templates_dir, main_dir = self._discover_framework_paths()
         | 
| 279 286 |  | 
| @@ -308,6 +315,17 @@ class FrameworkLoader: | |
| 308 315 | 
             
                        if self.framework_content["working_claude_md"]:
         | 
| 309 316 | 
             
                            instructions += f"\n\n## Working Directory Instructions\n{self.framework_content['working_claude_md']}\n"
         | 
| 310 317 |  | 
| 318 | 
            +
                        # Add dynamic agent capabilities section
         | 
| 319 | 
            +
                        instructions += self._generate_agent_capabilities_section()
         | 
| 320 | 
            +
                        
         | 
| 321 | 
            +
                        # Add current date for temporal awareness
         | 
| 322 | 
            +
                        instructions += f"\n\n## Temporal Context\n**Today's Date**: {datetime.now().strftime('%Y-%m-%d')}\n"
         | 
| 323 | 
            +
                        instructions += "Apply date awareness to all time-sensitive tasks and decisions.\n"
         | 
| 324 | 
            +
                        
         | 
| 325 | 
            +
                        # Add BASE_PM.md framework requirements AFTER INSTRUCTIONS.md
         | 
| 326 | 
            +
                        if self.framework_content.get("base_pm_instructions"):
         | 
| 327 | 
            +
                            instructions += f"\n\n{self.framework_content['base_pm_instructions']}\n"
         | 
| 328 | 
            +
                        
         | 
| 311 329 | 
             
                        return instructions
         | 
| 312 330 |  | 
| 313 331 | 
             
                    # Otherwise fall back to generating framework
         | 
| @@ -413,6 +431,69 @@ Extract tickets from these patterns: | |
| 413 431 |  | 
| 414 432 | 
             
                    return instructions
         | 
| 415 433 |  | 
| 434 | 
            +
                def _generate_agent_capabilities_section(self) -> str:
         | 
| 435 | 
            +
                    """Generate dynamic agent capabilities section from deployed agents."""
         | 
| 436 | 
            +
                    try:
         | 
| 437 | 
            +
                        # Try to get agents from agent_loader
         | 
| 438 | 
            +
                        from claude_mpm.agents.agent_loader import list_available_agents
         | 
| 439 | 
            +
                        agents = list_available_agents()
         | 
| 440 | 
            +
                        
         | 
| 441 | 
            +
                        if not agents:
         | 
| 442 | 
            +
                            return ""
         | 
| 443 | 
            +
                        
         | 
| 444 | 
            +
                        # Build capabilities section
         | 
| 445 | 
            +
                        section = "\n\n## Available Agent Capabilities\n\n"
         | 
| 446 | 
            +
                        section += "You have the following specialized agents available for delegation:\n\n"
         | 
| 447 | 
            +
                        
         | 
| 448 | 
            +
                        # Group agents by category
         | 
| 449 | 
            +
                        categories = {}
         | 
| 450 | 
            +
                        for agent_id, info in agents.items():
         | 
| 451 | 
            +
                            category = info.get('category', 'general')
         | 
| 452 | 
            +
                            if category not in categories:
         | 
| 453 | 
            +
                                categories[category] = []
         | 
| 454 | 
            +
                            categories[category].append((agent_id, info))
         | 
| 455 | 
            +
                        
         | 
| 456 | 
            +
                        # List agents by category
         | 
| 457 | 
            +
                        for category in sorted(categories.keys()):
         | 
| 458 | 
            +
                            section += f"\n### {category.title()} Agents\n"
         | 
| 459 | 
            +
                            for agent_id, info in sorted(categories[category]):
         | 
| 460 | 
            +
                                name = info.get('name', agent_id)
         | 
| 461 | 
            +
                                desc = info.get('description', 'Specialized agent')
         | 
| 462 | 
            +
                                tools = info.get('tools', [])
         | 
| 463 | 
            +
                                section += f"- **{name}** (`{agent_id}`): {desc}\n"
         | 
| 464 | 
            +
                                if tools:
         | 
| 465 | 
            +
                                    section += f"  - Tools: {', '.join(tools[:5])}"
         | 
| 466 | 
            +
                                    if len(tools) > 5:
         | 
| 467 | 
            +
                                        section += f" (+{len(tools)-5} more)"
         | 
| 468 | 
            +
                                    section += "\n"
         | 
| 469 | 
            +
                        
         | 
| 470 | 
            +
                        # Add summary
         | 
| 471 | 
            +
                        section += f"\n**Total Available Agents**: {len(agents)}\n"
         | 
| 472 | 
            +
                        section += "Use the agent ID in parentheses when delegating tasks via the Task tool.\n"
         | 
| 473 | 
            +
                        
         | 
| 474 | 
            +
                        return section
         | 
| 475 | 
            +
                        
         | 
| 476 | 
            +
                    except Exception as e:
         | 
| 477 | 
            +
                        self.logger.warning(f"Could not generate dynamic agent capabilities: {e}")
         | 
| 478 | 
            +
                        # Return static fallback
         | 
| 479 | 
            +
                        return """
         | 
| 480 | 
            +
             | 
| 481 | 
            +
            ## Available Agent Capabilities
         | 
| 482 | 
            +
             | 
| 483 | 
            +
            You have the following specialized agents available for delegation:
         | 
| 484 | 
            +
             | 
| 485 | 
            +
            - **Engineer Agent**: Code implementation and development
         | 
| 486 | 
            +
            - **Research Agent**: Investigation and analysis
         | 
| 487 | 
            +
            - **QA Agent**: Testing and quality assurance
         | 
| 488 | 
            +
            - **Documentation Agent**: Documentation creation and maintenance
         | 
| 489 | 
            +
            - **Security Agent**: Security analysis and protection
         | 
| 490 | 
            +
            - **Data Engineer Agent**: Data management and pipelines
         | 
| 491 | 
            +
            - **Ops Agent**: Deployment and operations
         | 
| 492 | 
            +
            - **Version Control Agent**: Git operations and version management
         | 
| 493 | 
            +
             | 
| 494 | 
            +
            Use these agents to delegate specialized work via the Task tool.
         | 
| 495 | 
            +
            """
         | 
| 496 | 
            +
                
         | 
| 416 497 | 
             
                def _format_minimal_framework(self) -> str:
         | 
| 417 498 | 
             
                    """Format minimal framework instructions when full framework not available."""
         | 
| 418 499 | 
             
                    return """
         | 
| @@ -0,0 +1,36 @@ | |
| 1 | 
            +
            # Agent Memory System
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            ## Purpose
         | 
| 4 | 
            +
            Each agent maintains project-specific knowledge in these files. Agents read their memory file before tasks and update it when they learn something new.
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            ## Manual Editing
         | 
| 7 | 
            +
            Feel free to edit these files to:
         | 
| 8 | 
            +
            - Add project-specific guidelines
         | 
| 9 | 
            +
            - Remove outdated information  
         | 
| 10 | 
            +
            - Reorganize for better clarity
         | 
| 11 | 
            +
            - Add domain-specific knowledge
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            ## Memory Limits
         | 
| 14 | 
            +
            - Max file size: 8KB (~2000 tokens)
         | 
| 15 | 
            +
            - Max sections: 10
         | 
| 16 | 
            +
            - Max items per section: 15
         | 
| 17 | 
            +
            - Files auto-truncate when limits exceeded
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            ## File Format
         | 
| 20 | 
            +
            Standard markdown with structured sections. Agents expect:
         | 
| 21 | 
            +
            - Project Architecture
         | 
| 22 | 
            +
            - Implementation Guidelines
         | 
| 23 | 
            +
            - Common Mistakes to Avoid
         | 
| 24 | 
            +
            - Current Technical Context
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            ## How It Works
         | 
| 27 | 
            +
            1. Agents read their memory file before starting tasks
         | 
| 28 | 
            +
            2. Agents add learnings during or after task completion
         | 
| 29 | 
            +
            3. Files automatically enforce size limits
         | 
| 30 | 
            +
            4. Developers can manually edit for accuracy
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            ## Memory File Lifecycle
         | 
| 33 | 
            +
            - Created automatically when agent first runs
         | 
| 34 | 
            +
            - Updated through hook system after delegations
         | 
| 35 | 
            +
            - Manually editable by developers
         | 
| 36 | 
            +
            - Version controlled with project
         | 
| @@ -0,0 +1,121 @@ | |
| 1 | 
            +
            # Claude MPM Web Dashboard
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            This directory contains the modular web dashboard for Claude MPM monitoring and management.
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            ## Structure
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            ```
         | 
| 8 | 
            +
            src/claude_mpm/dashboard/
         | 
| 9 | 
            +
            ├── static/
         | 
| 10 | 
            +
            │   ├── css/
         | 
| 11 | 
            +
            │   │   └── dashboard.css          # Main stylesheet
         | 
| 12 | 
            +
            │   └── js/
         | 
| 13 | 
            +
            │       ├── socket-client.js       # Socket.IO connection management
         | 
| 14 | 
            +
            │       ├── dashboard.js          # Main dashboard application
         | 
| 15 | 
            +
            │       └── components/
         | 
| 16 | 
            +
            │           ├── event-viewer.js    # Event display and filtering
         | 
| 17 | 
            +
            │           ├── module-viewer.js   # Event detail viewer
         | 
| 18 | 
            +
            │           └── session-manager.js # Session management
         | 
| 19 | 
            +
            ├── templates/
         | 
| 20 | 
            +
            │   └── index.html               # Main dashboard HTML
         | 
| 21 | 
            +
            ├── index.html                   # Root index with redirect
         | 
| 22 | 
            +
            ├── test_dashboard.html          # Test version for verification
         | 
| 23 | 
            +
            └── README.md                    # This file
         | 
| 24 | 
            +
            ```
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            ## Components
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            ### SocketClient (`socket-client.js`)
         | 
| 29 | 
            +
            - Manages WebSocket connections to the Claude MPM server
         | 
| 30 | 
            +
            - Handles event reception and processing
         | 
| 31 | 
            +
            - Provides callbacks for connection state changes
         | 
| 32 | 
            +
            - Maintains event history and session tracking
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            ### EventViewer (`components/event-viewer.js`)
         | 
| 35 | 
            +
            - Displays events in a filterable list
         | 
| 36 | 
            +
            - Supports search, type filtering, and session filtering
         | 
| 37 | 
            +
            - Handles event selection and keyboard navigation
         | 
| 38 | 
            +
            - Updates metrics display (total events, events per minute, etc.)
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            ### ModuleViewer (`components/module-viewer.js`)
         | 
| 41 | 
            +
            - Shows detailed information about selected events
         | 
| 42 | 
            +
            - Provides structured views for different event types
         | 
| 43 | 
            +
            - Displays raw JSON data for debugging
         | 
| 44 | 
            +
            - Organizes events by class/category
         | 
| 45 | 
            +
             | 
| 46 | 
            +
            ### SessionManager (`components/session-manager.js`)
         | 
| 47 | 
            +
            - Manages session selection and filtering
         | 
| 48 | 
            +
            - Updates session dropdown with available sessions
         | 
| 49 | 
            +
            - Tracks current active session
         | 
| 50 | 
            +
            - Updates footer information
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            ### Dashboard (`dashboard.js`)
         | 
| 53 | 
            +
            - Main application coordinator
         | 
| 54 | 
            +
            - Handles tab switching between Events, Agents, Tools, and Files
         | 
| 55 | 
            +
            - Manages component interactions
         | 
| 56 | 
            +
            - Provides global functions for backward compatibility
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            ## Features
         | 
| 59 | 
            +
             | 
| 60 | 
            +
            ### File-Centric Files Tab
         | 
| 61 | 
            +
            The Files tab now shows a file-centric view where:
         | 
| 62 | 
            +
            - Each file appears only once in the list
         | 
| 63 | 
            +
            - Files show read/write icons based on operations performed
         | 
| 64 | 
            +
            - Most recently accessed files appear at the bottom
         | 
| 65 | 
            +
            - Clicking a file shows all operations performed on it with timestamps and agent information
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            ### Tab Organization
         | 
| 68 | 
            +
            - **Events**: All events with filtering and search
         | 
| 69 | 
            +
            - **Agents**: Agent-specific events and operations
         | 
| 70 | 
            +
            - **Tools**: Tool usage and parameters
         | 
| 71 | 
            +
            - **Files**: File operations organized by file path
         | 
| 72 | 
            +
             | 
| 73 | 
            +
            ### Enhanced Event Details
         | 
| 74 | 
            +
            - Structured views for different event types
         | 
| 75 | 
            +
            - Todo checklists with status indicators
         | 
| 76 | 
            +
            - Agent and tool information
         | 
| 77 | 
            +
            - Session tracking and filtering
         | 
| 78 | 
            +
             | 
| 79 | 
            +
            ## Usage
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            ### Development
         | 
| 82 | 
            +
            For development and testing, use `test_dashboard.html` which includes module verification.
         | 
| 83 | 
            +
             | 
| 84 | 
            +
            ### Production
         | 
| 85 | 
            +
            Use `templates/dashboard.html` as the main template, ensuring all static files are served correctly.
         | 
| 86 | 
            +
             | 
| 87 | 
            +
            ### Integration
         | 
| 88 | 
            +
            The dashboard can be integrated into a Flask/FastAPI application by serving the static files and using the template.
         | 
| 89 | 
            +
             | 
| 90 | 
            +
            ## Migration from Original
         | 
| 91 | 
            +
             | 
| 92 | 
            +
            The original monolithic `claude_mpm_socketio_dashboard.html` has been replaced with a modular structure:
         | 
| 93 | 
            +
             | 
| 94 | 
            +
            1. **CSS**: Extracted to `static/css/dashboard.css`
         | 
| 95 | 
            +
            2. **JavaScript**: Split into logical modules in `static/js/`
         | 
| 96 | 
            +
            3. **HTML**: Clean template in `templates/dashboard.html`
         | 
| 97 | 
            +
             | 
| 98 | 
            +
            All original functionality has been preserved while improving:
         | 
| 99 | 
            +
            - Code organization and maintainability
         | 
| 100 | 
            +
            - Module reusability
         | 
| 101 | 
            +
            - Easier debugging and development
         | 
| 102 | 
            +
            - Better separation of concerns
         | 
| 103 | 
            +
             | 
| 104 | 
            +
            ## Backward Compatibility
         | 
| 105 | 
            +
             | 
| 106 | 
            +
            Global functions are maintained for compatibility:
         | 
| 107 | 
            +
            - `connectSocket()`
         | 
| 108 | 
            +
            - `disconnectSocket()`
         | 
| 109 | 
            +
            - `clearEvents()`
         | 
| 110 | 
            +
            - `exportEvents()`
         | 
| 111 | 
            +
            - `clearSelection()`
         | 
| 112 | 
            +
            - `switchTab(tabName)`
         | 
| 113 | 
            +
             | 
| 114 | 
            +
            ## Browser Support
         | 
| 115 | 
            +
             | 
| 116 | 
            +
            Requires modern browsers with support for:
         | 
| 117 | 
            +
            - ES6 Classes
         | 
| 118 | 
            +
            - Fetch API
         | 
| 119 | 
            +
            - Custom Events
         | 
| 120 | 
            +
            - CSS Grid/Flexbox
         | 
| 121 | 
            +
            - Socket.IO 4.x
         |