claude-mpm 3.1.1__py3-none-any.whl → 3.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/__main__.py +27 -3
 - claude_mpm/agents/INSTRUCTIONS.md +17 -2
 - claude_mpm/agents/templates/test-integration-agent.md +34 -0
 - claude_mpm/cli/README.md +109 -0
 - claude_mpm/cli/__init__.py +172 -0
 - claude_mpm/cli/commands/__init__.py +20 -0
 - claude_mpm/cli/commands/agents.py +202 -0
 - claude_mpm/cli/commands/info.py +94 -0
 - claude_mpm/cli/commands/run.py +95 -0
 - claude_mpm/cli/commands/tickets.py +70 -0
 - claude_mpm/cli/commands/ui.py +79 -0
 - claude_mpm/cli/parser.py +337 -0
 - claude_mpm/cli/utils.py +190 -0
 - claude_mpm/cli_enhancements.py +19 -0
 - claude_mpm/core/agent_registry.py +4 -4
 - claude_mpm/core/factories.py +1 -1
 - claude_mpm/core/service_registry.py +1 -1
 - claude_mpm/core/simple_runner.py +17 -27
 - claude_mpm/hooks/claude_hooks/hook_handler.py +53 -4
 - claude_mpm/models/__init__.py +106 -0
 - claude_mpm/models/agent_definition.py +196 -0
 - claude_mpm/models/common.py +41 -0
 - claude_mpm/models/lifecycle.py +97 -0
 - claude_mpm/models/modification.py +126 -0
 - claude_mpm/models/persistence.py +57 -0
 - claude_mpm/models/registry.py +91 -0
 - claude_mpm/security/__init__.py +8 -0
 - claude_mpm/security/bash_validator.py +393 -0
 - claude_mpm/services/agent_lifecycle_manager.py +206 -94
 - claude_mpm/services/agent_modification_tracker.py +27 -100
 - claude_mpm/services/agent_persistence_service.py +74 -0
 - claude_mpm/services/agent_registry.py +43 -82
 - claude_mpm/services/agent_versioning.py +37 -0
 - claude_mpm/services/{ticketing_service_original.py → legacy_ticketing_service.py} +16 -9
 - claude_mpm/services/ticket_manager.py +5 -4
 - claude_mpm/services/{ticket_manager_di.py → ticket_manager_dependency_injection.py} +39 -12
 - claude_mpm/services/version_control/semantic_versioning.py +10 -9
 - claude_mpm/utils/path_operations.py +20 -0
 - {claude_mpm-3.1.1.dist-info → claude_mpm-3.1.3.dist-info}/METADATA +9 -1
 - {claude_mpm-3.1.1.dist-info → claude_mpm-3.1.3.dist-info}/RECORD +45 -25
 - claude_mpm/cli_main.py +0 -13
 - claude_mpm/utils/import_migration_example.py +0 -80
 - /claude_mpm/{cli.py → cli_old.py} +0 -0
 - {claude_mpm-3.1.1.dist-info → claude_mpm-3.1.3.dist-info}/WHEEL +0 -0
 - {claude_mpm-3.1.1.dist-info → claude_mpm-3.1.3.dist-info}/entry_points.txt +0 -0
 - {claude_mpm-3.1.1.dist-info → claude_mpm-3.1.3.dist-info}/licenses/LICENSE +0 -0
 - {claude_mpm-3.1.1.dist-info → claude_mpm-3.1.3.dist-info}/top_level.txt +0 -0
 
    
        claude_mpm/cli/utils.py
    ADDED
    
    | 
         @@ -0,0 +1,190 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            """
         
     | 
| 
      
 2 
     | 
    
         
            +
            Utility functions for the CLI.
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            WHY: This module contains shared utility functions used across different CLI commands.
         
     | 
| 
      
 5 
     | 
    
         
            +
            Centralizing these functions reduces code duplication and provides a single place
         
     | 
| 
      
 6 
     | 
    
         
            +
            for common CLI operations.
         
     | 
| 
      
 7 
     | 
    
         
            +
            """
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            import sys
         
     | 
| 
      
 10 
     | 
    
         
            +
            from pathlib import Path
         
     | 
| 
      
 11 
     | 
    
         
            +
            from typing import Optional
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            from claude_mpm.utils.imports import safe_import
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            # Import logger using safe_import pattern
         
     | 
| 
      
 16 
     | 
    
         
            +
            get_logger = safe_import('claude_mpm.core.logger', None, ['get_logger'])
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            def get_user_input(input_arg: Optional[str], logger) -> str:
         
     | 
| 
      
 20 
     | 
    
         
            +
                """
         
     | 
| 
      
 21 
     | 
    
         
            +
                Get user input based on command line arguments.
         
     | 
| 
      
 22 
     | 
    
         
            +
                
         
     | 
| 
      
 23 
     | 
    
         
            +
                WHY: This function handles the three ways users can provide input:
         
     | 
| 
      
 24 
     | 
    
         
            +
                1. Direct text via -i/--input
         
     | 
| 
      
 25 
     | 
    
         
            +
                2. File path via -i/--input
         
     | 
| 
      
 26 
     | 
    
         
            +
                3. stdin (for piping)
         
     | 
| 
      
 27 
     | 
    
         
            +
                
         
     | 
| 
      
 28 
     | 
    
         
            +
                DESIGN DECISION: We check if the input is a file path first, then fall back
         
     | 
| 
      
 29 
     | 
    
         
            +
                to treating it as direct text. This allows maximum flexibility.
         
     | 
| 
      
 30 
     | 
    
         
            +
                
         
     | 
| 
      
 31 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 32 
     | 
    
         
            +
                    input_arg: The value of the -i/--input argument
         
     | 
| 
      
 33 
     | 
    
         
            +
                    logger: Logger instance for output
         
     | 
| 
      
 34 
     | 
    
         
            +
                    
         
     | 
| 
      
 35 
     | 
    
         
            +
                Returns:
         
     | 
| 
      
 36 
     | 
    
         
            +
                    The user input as a string
         
     | 
| 
      
 37 
     | 
    
         
            +
                """
         
     | 
| 
      
 38 
     | 
    
         
            +
                if input_arg:
         
     | 
| 
      
 39 
     | 
    
         
            +
                    # Check if it's a file path
         
     | 
| 
      
 40 
     | 
    
         
            +
                    input_path = Path(input_arg)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    if input_path.exists():
         
     | 
| 
      
 42 
     | 
    
         
            +
                        logger.info(f"Reading input from file: {input_path}")
         
     | 
| 
      
 43 
     | 
    
         
            +
                        return input_path.read_text()
         
     | 
| 
      
 44 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 45 
     | 
    
         
            +
                        logger.info("Using command line input")
         
     | 
| 
      
 46 
     | 
    
         
            +
                        return input_arg
         
     | 
| 
      
 47 
     | 
    
         
            +
                else:
         
     | 
| 
      
 48 
     | 
    
         
            +
                    # Read from stdin
         
     | 
| 
      
 49 
     | 
    
         
            +
                    logger.info("Reading input from stdin")
         
     | 
| 
      
 50 
     | 
    
         
            +
                    return sys.stdin.read()
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
            def get_agent_versions_display() -> Optional[str]:
         
     | 
| 
      
 54 
     | 
    
         
            +
                """
         
     | 
| 
      
 55 
     | 
    
         
            +
                Get formatted agent versions display as a string.
         
     | 
| 
      
 56 
     | 
    
         
            +
                
         
     | 
| 
      
 57 
     | 
    
         
            +
                WHY: This function provides a single source of truth for agent version
         
     | 
| 
      
 58 
     | 
    
         
            +
                information that can be displayed both at startup and on-demand via the
         
     | 
| 
      
 59 
     | 
    
         
            +
                /mpm agents command. This ensures consistency in how agent versions are
         
     | 
| 
      
 60 
     | 
    
         
            +
                presented to users.
         
     | 
| 
      
 61 
     | 
    
         
            +
                
         
     | 
| 
      
 62 
     | 
    
         
            +
                Returns:
         
     | 
| 
      
 63 
     | 
    
         
            +
                    Formatted string containing agent version information, or None if failed
         
     | 
| 
      
 64 
     | 
    
         
            +
                """
         
     | 
| 
      
 65 
     | 
    
         
            +
                # Import AgentDeploymentService using safe_import pattern
         
     | 
| 
      
 66 
     | 
    
         
            +
                AgentDeploymentService = safe_import(
         
     | 
| 
      
 67 
     | 
    
         
            +
                    'claude_mpm.services.agent_deployment',
         
     | 
| 
      
 68 
     | 
    
         
            +
                    None,
         
     | 
| 
      
 69 
     | 
    
         
            +
                    ['AgentDeploymentService']
         
     | 
| 
      
 70 
     | 
    
         
            +
                )
         
     | 
| 
      
 71 
     | 
    
         
            +
                
         
     | 
| 
      
 72 
     | 
    
         
            +
                if not AgentDeploymentService:
         
     | 
| 
      
 73 
     | 
    
         
            +
                    return None
         
     | 
| 
      
 74 
     | 
    
         
            +
                    
         
     | 
| 
      
 75 
     | 
    
         
            +
                try:
         
     | 
| 
      
 76 
     | 
    
         
            +
                    deployment_service = AgentDeploymentService()
         
     | 
| 
      
 77 
     | 
    
         
            +
                    
         
     | 
| 
      
 78 
     | 
    
         
            +
                    # Get deployed agents
         
     | 
| 
      
 79 
     | 
    
         
            +
                    verification = deployment_service.verify_deployment()
         
     | 
| 
      
 80 
     | 
    
         
            +
                    if not verification.get("agents_found"):
         
     | 
| 
      
 81 
     | 
    
         
            +
                        return None
         
     | 
| 
      
 82 
     | 
    
         
            +
                        
         
     | 
| 
      
 83 
     | 
    
         
            +
                    output_lines = []
         
     | 
| 
      
 84 
     | 
    
         
            +
                    output_lines.append("\nDeployed Agent Versions:")
         
     | 
| 
      
 85 
     | 
    
         
            +
                    output_lines.append("-" * 40)
         
     | 
| 
      
 86 
     | 
    
         
            +
                    
         
     | 
| 
      
 87 
     | 
    
         
            +
                    # Sort agents by name for consistent display
         
     | 
| 
      
 88 
     | 
    
         
            +
                    agents = sorted(verification["agents_found"], key=lambda x: x.get('name', x.get('file', '')))
         
     | 
| 
      
 89 
     | 
    
         
            +
                    
         
     | 
| 
      
 90 
     | 
    
         
            +
                    for agent in agents:
         
     | 
| 
      
 91 
     | 
    
         
            +
                        name = agent.get('name', 'unknown')
         
     | 
| 
      
 92 
     | 
    
         
            +
                        version = agent.get('version', 'unknown')
         
     | 
| 
      
 93 
     | 
    
         
            +
                        # Format: name (version)
         
     | 
| 
      
 94 
     | 
    
         
            +
                        output_lines.append(f"  {name:<20} {version}")
         
     | 
| 
      
 95 
     | 
    
         
            +
                    
         
     | 
| 
      
 96 
     | 
    
         
            +
                    # Add base agent version info
         
     | 
| 
      
 97 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 98 
     | 
    
         
            +
                        import json
         
     | 
| 
      
 99 
     | 
    
         
            +
                        base_agent_path = deployment_service.base_agent_path
         
     | 
| 
      
 100 
     | 
    
         
            +
                        if base_agent_path.exists():
         
     | 
| 
      
 101 
     | 
    
         
            +
                            base_data = json.loads(base_agent_path.read_text())
         
     | 
| 
      
 102 
     | 
    
         
            +
                            # Parse version the same way as AgentDeploymentService
         
     | 
| 
      
 103 
     | 
    
         
            +
                            raw_version = base_data.get('base_version') or base_data.get('version', 0)
         
     | 
| 
      
 104 
     | 
    
         
            +
                            base_version_tuple = deployment_service._parse_version(raw_version)
         
     | 
| 
      
 105 
     | 
    
         
            +
                            base_version_str = deployment_service._format_version_display(base_version_tuple)
         
     | 
| 
      
 106 
     | 
    
         
            +
                            output_lines.append(f"\n  Base Agent Version:  {base_version_str}")
         
     | 
| 
      
 107 
     | 
    
         
            +
                    except:
         
     | 
| 
      
 108 
     | 
    
         
            +
                        pass
         
     | 
| 
      
 109 
     | 
    
         
            +
                    
         
     | 
| 
      
 110 
     | 
    
         
            +
                    # Check for agents needing migration
         
     | 
| 
      
 111 
     | 
    
         
            +
                    if verification.get("agents_needing_migration"):
         
     | 
| 
      
 112 
     | 
    
         
            +
                        output_lines.append(f"\n  ⚠️  {len(verification['agents_needing_migration'])} agent(s) need migration to semantic versioning")
         
     | 
| 
      
 113 
     | 
    
         
            +
                        output_lines.append(f"     Run 'claude-mpm agents deploy' to update")
         
     | 
| 
      
 114 
     | 
    
         
            +
                        
         
     | 
| 
      
 115 
     | 
    
         
            +
                    output_lines.append("-" * 40)
         
     | 
| 
      
 116 
     | 
    
         
            +
                    return "\n".join(output_lines)
         
     | 
| 
      
 117 
     | 
    
         
            +
                except Exception as e:
         
     | 
| 
      
 118 
     | 
    
         
            +
                    # Log error but don't fail
         
     | 
| 
      
 119 
     | 
    
         
            +
                    logger = get_logger("cli")
         
     | 
| 
      
 120 
     | 
    
         
            +
                    logger.debug(f"Failed to get agent versions: {e}")
         
     | 
| 
      
 121 
     | 
    
         
            +
                    return None
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
            def list_agent_versions_at_startup() -> None:
         
     | 
| 
      
 125 
     | 
    
         
            +
                """
         
     | 
| 
      
 126 
     | 
    
         
            +
                List deployed agent versions at startup.
         
     | 
| 
      
 127 
     | 
    
         
            +
                
         
     | 
| 
      
 128 
     | 
    
         
            +
                WHY: Users want to see what agents are available when they start a session.
         
     | 
| 
      
 129 
     | 
    
         
            +
                This provides immediate feedback about the deployed agent environment.
         
     | 
| 
      
 130 
     | 
    
         
            +
                """
         
     | 
| 
      
 131 
     | 
    
         
            +
                agent_versions = get_agent_versions_display()
         
     | 
| 
      
 132 
     | 
    
         
            +
                if agent_versions:
         
     | 
| 
      
 133 
     | 
    
         
            +
                    print(agent_versions)
         
     | 
| 
      
 134 
     | 
    
         
            +
                    print()  # Extra newline after the display
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
            def setup_logging(args) -> object:
         
     | 
| 
      
 138 
     | 
    
         
            +
                """
         
     | 
| 
      
 139 
     | 
    
         
            +
                Set up logging based on parsed arguments.
         
     | 
| 
      
 140 
     | 
    
         
            +
                
         
     | 
| 
      
 141 
     | 
    
         
            +
                WHY: This centralizes logging setup logic, handling the deprecated --debug flag
         
     | 
| 
      
 142 
     | 
    
         
            +
                and the new --logging argument consistently across all commands.
         
     | 
| 
      
 143 
     | 
    
         
            +
                
         
     | 
| 
      
 144 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 145 
     | 
    
         
            +
                    args: Parsed command line arguments
         
     | 
| 
      
 146 
     | 
    
         
            +
                    
         
     | 
| 
      
 147 
     | 
    
         
            +
                Returns:
         
     | 
| 
      
 148 
     | 
    
         
            +
                    Logger instance
         
     | 
| 
      
 149 
     | 
    
         
            +
                """
         
     | 
| 
      
 150 
     | 
    
         
            +
                from ..core.logger import setup_logging as core_setup_logging, get_logger
         
     | 
| 
      
 151 
     | 
    
         
            +
                from ..constants import LogLevel
         
     | 
| 
      
 152 
     | 
    
         
            +
                
         
     | 
| 
      
 153 
     | 
    
         
            +
                # Handle deprecated --debug flag
         
     | 
| 
      
 154 
     | 
    
         
            +
                if hasattr(args, 'debug') and args.debug and args.logging == LogLevel.INFO.value:
         
     | 
| 
      
 155 
     | 
    
         
            +
                    args.logging = LogLevel.DEBUG.value
         
     | 
| 
      
 156 
     | 
    
         
            +
                
         
     | 
| 
      
 157 
     | 
    
         
            +
                # Only setup logging if not OFF
         
     | 
| 
      
 158 
     | 
    
         
            +
                if args.logging != LogLevel.OFF.value:
         
     | 
| 
      
 159 
     | 
    
         
            +
                    logger = core_setup_logging(level=args.logging, log_dir=args.log_dir)
         
     | 
| 
      
 160 
     | 
    
         
            +
                else:
         
     | 
| 
      
 161 
     | 
    
         
            +
                    # Minimal logger for CLI feedback
         
     | 
| 
      
 162 
     | 
    
         
            +
                    import logging
         
     | 
| 
      
 163 
     | 
    
         
            +
                    logger = logging.getLogger("cli")
         
     | 
| 
      
 164 
     | 
    
         
            +
                    logger.setLevel(logging.WARNING)
         
     | 
| 
      
 165 
     | 
    
         
            +
                
         
     | 
| 
      
 166 
     | 
    
         
            +
                return logger
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
            def ensure_directories() -> None:
         
     | 
| 
      
 170 
     | 
    
         
            +
                """
         
     | 
| 
      
 171 
     | 
    
         
            +
                Ensure required directories exist on first run.
         
     | 
| 
      
 172 
     | 
    
         
            +
                
         
     | 
| 
      
 173 
     | 
    
         
            +
                WHY: Claude-mpm needs certain directories to function properly. Rather than
         
     | 
| 
      
 174 
     | 
    
         
            +
                failing when they don't exist, we create them automatically for a better
         
     | 
| 
      
 175 
     | 
    
         
            +
                user experience.
         
     | 
| 
      
 176 
     | 
    
         
            +
                """
         
     | 
| 
      
 177 
     | 
    
         
            +
                # Import ensure_directories using safe_import pattern
         
     | 
| 
      
 178 
     | 
    
         
            +
                init_ensure_directories = safe_import(
         
     | 
| 
      
 179 
     | 
    
         
            +
                    'claude_mpm.init',
         
     | 
| 
      
 180 
     | 
    
         
            +
                    None,
         
     | 
| 
      
 181 
     | 
    
         
            +
                    ['ensure_directories']
         
     | 
| 
      
 182 
     | 
    
         
            +
                )
         
     | 
| 
      
 183 
     | 
    
         
            +
                
         
     | 
| 
      
 184 
     | 
    
         
            +
                if init_ensure_directories:
         
     | 
| 
      
 185 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 186 
     | 
    
         
            +
                        init_ensure_directories()
         
     | 
| 
      
 187 
     | 
    
         
            +
                    except Exception:
         
     | 
| 
      
 188 
     | 
    
         
            +
                        # Continue even if initialization fails
         
     | 
| 
      
 189 
     | 
    
         
            +
                        # The individual commands will handle missing directories as needed
         
     | 
| 
      
 190 
     | 
    
         
            +
                        pass
         
     | 
    
        claude_mpm/cli_enhancements.py
    CHANGED
    
    | 
         @@ -1,6 +1,25 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            """
         
     | 
| 
       2 
2 
     | 
    
         
             
            Enhanced CLI operations for claude-mpm.
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
      
 4 
     | 
    
         
            +
            WHY THIS FILE EXISTS:
         
     | 
| 
      
 5 
     | 
    
         
            +
            This module provides an alternative CLI implementation with enhanced error handling
         
     | 
| 
      
 6 
     | 
    
         
            +
            and validation features. It was created to explore advanced CLI patterns including:
         
     | 
| 
      
 7 
     | 
    
         
            +
            - Comprehensive prerequisite validation
         
     | 
| 
      
 8 
     | 
    
         
            +
            - User-friendly error messages with suggestions
         
     | 
| 
      
 9 
     | 
    
         
            +
            - Dry-run mode for testing
         
     | 
| 
      
 10 
     | 
    
         
            +
            - Profile validation and generation
         
     | 
| 
      
 11 
     | 
    
         
            +
            - Rich terminal output with status indicators
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            CURRENT STATUS: This is an experimental/alternative CLI implementation that uses
         
     | 
| 
      
 14 
     | 
    
         
            +
            Click instead of argparse. It's kept separate from the main CLI to:
         
     | 
| 
      
 15 
     | 
    
         
            +
            1. Preserve the existing CLI behavior
         
     | 
| 
      
 16 
     | 
    
         
            +
            2. Allow testing of new features without breaking the main interface
         
     | 
| 
      
 17 
     | 
    
         
            +
            3. Provide a reference implementation for future CLI enhancements
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            NOTE: This CLI is not currently used in production. The main CLI is in cli/__init__.py.
         
     | 
| 
      
 20 
     | 
    
         
            +
            To use this enhanced CLI, you would need to create a separate entry point or
         
     | 
| 
      
 21 
     | 
    
         
            +
            integrate selected features into the main CLI.
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
       4 
23 
     | 
    
         
             
            Implements error handling and user guidance patterns from awesome-claude-code.
         
     | 
| 
       5 
24 
     | 
    
         
             
            """
         
     | 
| 
       6 
25 
     | 
    
         | 
| 
         @@ -20,10 +20,10 @@ import importlib.util 
     | 
|
| 
       20 
20 
     | 
    
         
             
            from datetime import datetime
         
     | 
| 
       21 
21 
     | 
    
         
             
            from dataclasses import dataclass
         
     | 
| 
       22 
22 
     | 
    
         | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
      
 23 
     | 
    
         
            +
            from claude_mpm.utils.imports import safe_import
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            # Import logger using safe_import pattern
         
     | 
| 
      
 26 
     | 
    
         
            +
            get_logger = safe_import('claude_mpm.core.logger', None, ['get_logger'])
         
     | 
| 
       27 
27 
     | 
    
         | 
| 
       28 
28 
     | 
    
         | 
| 
       29 
29 
     | 
    
         
             
            @dataclass
         
     | 
    
        claude_mpm/core/factories.py
    CHANGED
    
    | 
         @@ -12,7 +12,7 @@ from typing import Any, Dict, Optional, Type, TypeVar 
     | 
|
| 
       12 
12 
     | 
    
         
             
            from .container import DIContainer
         
     | 
| 
       13 
13 
     | 
    
         
             
            from .logger import get_logger
         
     | 
| 
       14 
14 
     | 
    
         
             
            from .config import Config
         
     | 
| 
       15 
     | 
    
         
            -
            from  
     | 
| 
      
 15 
     | 
    
         
            +
            from claude_mpm.services.agent_deployment import AgentDeploymentService
         
     | 
| 
       16 
16 
     | 
    
         
             
            from ..orchestration.factory import OrchestratorFactory
         
     | 
| 
       17 
17 
     | 
    
         
             
            from ..orchestration.base import BaseOrchestrator
         
     | 
| 
       18 
18 
     | 
    
         | 
| 
         @@ -38,7 +38,7 @@ class ServiceRegistry: 
     | 
|
| 
       38 
38 
     | 
    
         
             
                    """Register all core framework services."""
         
     | 
| 
       39 
39 
     | 
    
         
             
                    from ..services.shared_prompt_cache import SharedPromptCache
         
     | 
| 
       40 
40 
     | 
    
         
             
                    from ..services.ticket_manager import TicketManager
         
     | 
| 
       41 
     | 
    
         
            -
                    from  
     | 
| 
      
 41 
     | 
    
         
            +
                    from claude_mpm.services.agent_deployment import AgentDeploymentService
         
     | 
| 
       42 
42 
     | 
    
         
             
                    from .session_manager import SessionManager
         
     | 
| 
       43 
43 
     | 
    
         
             
                    from .agent_session_manager import AgentSessionManager
         
     | 
| 
       44 
44 
     | 
    
         
             
                    from .config import Config
         
     | 
    
        claude_mpm/core/simple_runner.py
    CHANGED
    
    | 
         @@ -195,16 +195,15 @@ class SimpleClaudeRunner: 
     | 
|
| 
       195 
195 
     | 
    
         
             
                            clean_env.pop(var, None)
         
     | 
| 
       196 
196 
     | 
    
         | 
| 
       197 
197 
     | 
    
         
             
                        # Set the correct working directory for Claude Code
         
     | 
| 
       198 
     | 
    
         
            -
                        #  
     | 
| 
      
 198 
     | 
    
         
            +
                        # WHY: We're already in the user's directory thanks to __main__.py restoration
         
     | 
| 
      
 199 
     | 
    
         
            +
                        # We just need to ensure CLAUDE_WORKSPACE is set correctly
         
     | 
| 
      
 200 
     | 
    
         
            +
                        current_dir = os.getcwd()
         
     | 
| 
      
 201 
     | 
    
         
            +
                        clean_env['CLAUDE_WORKSPACE'] = current_dir
         
     | 
| 
      
 202 
     | 
    
         
            +
                        self.logger.info(f"Working directory: {current_dir}")
         
     | 
| 
      
 203 
     | 
    
         
            +
                        
         
     | 
| 
      
 204 
     | 
    
         
            +
                        # Log directory context for debugging
         
     | 
| 
       199 
205 
     | 
    
         
             
                        if 'CLAUDE_MPM_USER_PWD' in clean_env:
         
     | 
| 
       200 
     | 
    
         
            -
                             
     | 
| 
       201 
     | 
    
         
            -
                            clean_env['CLAUDE_WORKSPACE'] = user_pwd
         
     | 
| 
       202 
     | 
    
         
            -
                            # Also change to that directory before launching Claude
         
     | 
| 
       203 
     | 
    
         
            -
                            try:
         
     | 
| 
       204 
     | 
    
         
            -
                                os.chdir(user_pwd)
         
     | 
| 
       205 
     | 
    
         
            -
                                self.logger.info(f"Changed working directory to: {user_pwd}")
         
     | 
| 
       206 
     | 
    
         
            -
                            except Exception as e:
         
     | 
| 
       207 
     | 
    
         
            -
                                self.logger.warning(f"Could not change to user directory {user_pwd}: {e}")
         
     | 
| 
      
 206 
     | 
    
         
            +
                            self.logger.debug(f"User PWD from env: {clean_env['CLAUDE_MPM_USER_PWD']}")
         
     | 
| 
       208 
207 
     | 
    
         | 
| 
       209 
208 
     | 
    
         
             
                        print("Launching Claude...")
         
     | 
| 
       210 
209 
     | 
    
         | 
| 
         @@ -313,19 +312,15 @@ class SimpleClaudeRunner: 
     | 
|
| 
       313 
312 
     | 
    
         
             
                        env = os.environ.copy()
         
     | 
| 
       314 
313 
     | 
    
         | 
| 
       315 
314 
     | 
    
         
             
                        # Set the correct working directory for Claude Code
         
     | 
| 
      
 315 
     | 
    
         
            +
                        # WHY: We're already in the user's directory thanks to __main__.py restoration
         
     | 
| 
      
 316 
     | 
    
         
            +
                        # We just need to ensure CLAUDE_WORKSPACE is set correctly
         
     | 
| 
      
 317 
     | 
    
         
            +
                        current_dir = os.getcwd()
         
     | 
| 
      
 318 
     | 
    
         
            +
                        env['CLAUDE_WORKSPACE'] = current_dir
         
     | 
| 
      
 319 
     | 
    
         
            +
                        self.logger.info(f"Working directory for subprocess: {current_dir}")
         
     | 
| 
      
 320 
     | 
    
         
            +
                        
         
     | 
| 
      
 321 
     | 
    
         
            +
                        # Log directory context for debugging
         
     | 
| 
       316 
322 
     | 
    
         
             
                        if 'CLAUDE_MPM_USER_PWD' in env:
         
     | 
| 
       317 
     | 
    
         
            -
                             
     | 
| 
       318 
     | 
    
         
            -
                            env['CLAUDE_WORKSPACE'] = user_pwd
         
     | 
| 
       319 
     | 
    
         
            -
                            # Change to that directory before running Claude
         
     | 
| 
       320 
     | 
    
         
            -
                            try:
         
     | 
| 
       321 
     | 
    
         
            -
                                original_cwd = os.getcwd()
         
     | 
| 
       322 
     | 
    
         
            -
                                os.chdir(user_pwd)
         
     | 
| 
       323 
     | 
    
         
            -
                                self.logger.info(f"Changed working directory to: {user_pwd}")
         
     | 
| 
       324 
     | 
    
         
            -
                            except Exception as e:
         
     | 
| 
       325 
     | 
    
         
            -
                                self.logger.warning(f"Could not change to user directory {user_pwd}: {e}")
         
     | 
| 
       326 
     | 
    
         
            -
                                original_cwd = None
         
     | 
| 
       327 
     | 
    
         
            -
                        else:
         
     | 
| 
       328 
     | 
    
         
            -
                            original_cwd = None
         
     | 
| 
      
 323 
     | 
    
         
            +
                            self.logger.debug(f"User PWD from env: {env['CLAUDE_MPM_USER_PWD']}")
         
     | 
| 
       329 
324 
     | 
    
         | 
| 
       330 
325 
     | 
    
         
             
                        # Run Claude
         
     | 
| 
       331 
326 
     | 
    
         
             
                        if self.project_logger:
         
     | 
| 
         @@ -337,12 +332,7 @@ class SimpleClaudeRunner: 
     | 
|
| 
       337 
332 
     | 
    
         | 
| 
       338 
333 
     | 
    
         
             
                        result = subprocess.run(cmd, capture_output=True, text=True, env=env)
         
     | 
| 
       339 
334 
     | 
    
         | 
| 
       340 
     | 
    
         
            -
                        #  
     | 
| 
       341 
     | 
    
         
            -
                        if original_cwd:
         
     | 
| 
       342 
     | 
    
         
            -
                            try:
         
     | 
| 
       343 
     | 
    
         
            -
                                os.chdir(original_cwd)
         
     | 
| 
       344 
     | 
    
         
            -
                            except Exception:
         
     | 
| 
       345 
     | 
    
         
            -
                                pass
         
     | 
| 
      
 335 
     | 
    
         
            +
                        # No need to restore directory since we didn't change it
         
     | 
| 
       346 
336 
     | 
    
         
             
                        execution_time = time.time() - start_time
         
     | 
| 
       347 
337 
     | 
    
         | 
| 
       348 
338 
     | 
    
         
             
                        if result.returncode == 0:
         
     | 
| 
         @@ -44,6 +44,7 @@ project_root = Path(__file__).parent.parent.parent.parent 
     | 
|
| 
       44 
44 
     | 
    
         
             
            sys.path.insert(0, str(project_root / 'src'))
         
     | 
| 
       45 
45 
     | 
    
         | 
| 
       46 
46 
     | 
    
         
             
            from claude_mpm.core.logger import get_logger, setup_logging, LogLevel
         
     | 
| 
      
 47 
     | 
    
         
            +
            from claude_mpm.security import BashSecurityValidator
         
     | 
| 
       47 
48 
     | 
    
         | 
| 
       48 
49 
     | 
    
         
             
            # Don't initialize global logging here - we'll do it per-project
         
     | 
| 
       49 
50 
     | 
    
         
             
            logger = None
         
     | 
| 
         @@ -329,6 +330,7 @@ class ClaudeHookHandler: 
     | 
|
| 
       329 
330 
     | 
    
         
             
                    - Checking for path traversal attempts
         
     | 
| 
       330 
331 
     | 
    
         
             
                    - Ensuring file operations stay within the working directory
         
     | 
| 
       331 
332 
     | 
    
         
             
                    - Blocking dangerous operations
         
     | 
| 
      
 333 
     | 
    
         
            +
                    - Validating bash commands for security violations
         
     | 
| 
       332 
334 
     | 
    
         | 
| 
       333 
335 
     | 
    
         
             
                    Security Design:
         
     | 
| 
       334 
336 
     | 
    
         
             
                    - Fail-secure: Block suspicious operations
         
     | 
| 
         @@ -342,6 +344,29 @@ class ClaudeHookHandler: 
     | 
|
| 
       342 
344 
     | 
    
         
             
                    tool_name = self.event.get('tool_name', '')
         
     | 
| 
       343 
345 
     | 
    
         
             
                    tool_input = self.event.get('tool_input', {})
         
     | 
| 
       344 
346 
     | 
    
         | 
| 
      
 347 
     | 
    
         
            +
                    # Get the working directory from the event
         
     | 
| 
      
 348 
     | 
    
         
            +
                    working_dir = Path(self.event.get('cwd', os.getcwd())).resolve()
         
     | 
| 
      
 349 
     | 
    
         
            +
                    
         
     | 
| 
      
 350 
     | 
    
         
            +
                    # Special handling for Bash tool - validate commands for security
         
     | 
| 
      
 351 
     | 
    
         
            +
                    if tool_name == 'Bash':
         
     | 
| 
      
 352 
     | 
    
         
            +
                        command = tool_input.get('command', '')
         
     | 
| 
      
 353 
     | 
    
         
            +
                        if command:
         
     | 
| 
      
 354 
     | 
    
         
            +
                            # Create bash validator for this working directory
         
     | 
| 
      
 355 
     | 
    
         
            +
                            bash_validator = BashSecurityValidator(working_dir)
         
     | 
| 
      
 356 
     | 
    
         
            +
                            is_valid, error_msg = bash_validator.validate_command(command)
         
     | 
| 
      
 357 
     | 
    
         
            +
                            
         
     | 
| 
      
 358 
     | 
    
         
            +
                            if not is_valid:
         
     | 
| 
      
 359 
     | 
    
         
            +
                                if logger:
         
     | 
| 
      
 360 
     | 
    
         
            +
                                    logger.warning(f"Security: Blocked Bash command: {command[:100]}...")
         
     | 
| 
      
 361 
     | 
    
         
            +
                                
         
     | 
| 
      
 362 
     | 
    
         
            +
                                response = {
         
     | 
| 
      
 363 
     | 
    
         
            +
                                    "action": "block",
         
     | 
| 
      
 364 
     | 
    
         
            +
                                    "error": error_msg
         
     | 
| 
      
 365 
     | 
    
         
            +
                                }
         
     | 
| 
      
 366 
     | 
    
         
            +
                                print(json.dumps(response))
         
     | 
| 
      
 367 
     | 
    
         
            +
                                sys.exit(0)
         
     | 
| 
      
 368 
     | 
    
         
            +
                                return
         
     | 
| 
      
 369 
     | 
    
         
            +
                    
         
     | 
| 
       345 
370 
     | 
    
         
             
                    # List of tools that perform write operations
         
     | 
| 
       346 
371 
     | 
    
         
             
                    # These tools need special security checks to prevent
         
     | 
| 
       347 
372 
     | 
    
         
             
                    # writing outside the project directory
         
     | 
| 
         @@ -349,9 +374,6 @@ class ClaudeHookHandler: 
     | 
|
| 
       349 
374 
     | 
    
         | 
| 
       350 
375 
     | 
    
         
             
                    # Check if this is a write operation
         
     | 
| 
       351 
376 
     | 
    
         
             
                    if tool_name in write_tools:
         
     | 
| 
       352 
     | 
    
         
            -
                        # Get the working directory from the event
         
     | 
| 
       353 
     | 
    
         
            -
                        working_dir = Path(self.event.get('cwd', os.getcwd())).resolve()
         
     | 
| 
       354 
     | 
    
         
            -
                        
         
     | 
| 
       355 
377 
     | 
    
         
             
                        # Extract file path based on tool type
         
     | 
| 
       356 
378 
     | 
    
         
             
                        file_path = None
         
     | 
| 
       357 
379 
     | 
    
         
             
                        if tool_name in ['Write', 'Edit', 'NotebookEdit']:
         
     | 
| 
         @@ -414,7 +436,34 @@ class ClaudeHookHandler: 
     | 
|
| 
       414 
436 
     | 
    
         
             
                                sys.exit(0)
         
     | 
| 
       415 
437 
     | 
    
         
             
                                return
         
     | 
| 
       416 
438 
     | 
    
         | 
| 
       417 
     | 
    
         
            -
                    #  
     | 
| 
      
 439 
     | 
    
         
            +
                    # Additional security checks for other tools that might access files
         
     | 
| 
      
 440 
     | 
    
         
            +
                    read_tools = ['Read', 'LS', 'Glob', 'Grep', 'NotebookRead']
         
     | 
| 
      
 441 
     | 
    
         
            +
                    
         
     | 
| 
      
 442 
     | 
    
         
            +
                    if tool_name in read_tools:
         
     | 
| 
      
 443 
     | 
    
         
            +
                        # For read operations, we allow them but log for auditing
         
     | 
| 
      
 444 
     | 
    
         
            +
                        file_path = None
         
     | 
| 
      
 445 
     | 
    
         
            +
                        if tool_name == 'Read':
         
     | 
| 
      
 446 
     | 
    
         
            +
                            file_path = tool_input.get('file_path')
         
     | 
| 
      
 447 
     | 
    
         
            +
                        elif tool_name == 'NotebookRead':
         
     | 
| 
      
 448 
     | 
    
         
            +
                            file_path = tool_input.get('notebook_path')
         
     | 
| 
      
 449 
     | 
    
         
            +
                        elif tool_name == 'LS':
         
     | 
| 
      
 450 
     | 
    
         
            +
                            file_path = tool_input.get('path')
         
     | 
| 
      
 451 
     | 
    
         
            +
                        elif tool_name in ['Glob', 'Grep']:
         
     | 
| 
      
 452 
     | 
    
         
            +
                            file_path = tool_input.get('path', '.')
         
     | 
| 
      
 453 
     | 
    
         
            +
                        
         
     | 
| 
      
 454 
     | 
    
         
            +
                        if file_path and logger:
         
     | 
| 
      
 455 
     | 
    
         
            +
                            # Log read operations for security auditing
         
     | 
| 
      
 456 
     | 
    
         
            +
                            logger.info(f"Security audit: {tool_name} accessing path: {file_path}")
         
     | 
| 
      
 457 
     | 
    
         
            +
                    
         
     | 
| 
      
 458 
     | 
    
         
            +
                    # Check for other potentially dangerous tools
         
     | 
| 
      
 459 
     | 
    
         
            +
                    if tool_name in ['WebFetch', 'WebSearch']:
         
     | 
| 
      
 460 
     | 
    
         
            +
                        # Log web access for security auditing
         
     | 
| 
      
 461 
     | 
    
         
            +
                        if logger:
         
     | 
| 
      
 462 
     | 
    
         
            +
                            url = tool_input.get('url', '') if tool_name == 'WebFetch' else 'N/A'
         
     | 
| 
      
 463 
     | 
    
         
            +
                            query = tool_input.get('query', '') if tool_name == 'WebSearch' else 'N/A'
         
     | 
| 
      
 464 
     | 
    
         
            +
                            logger.info(f"Security audit: {tool_name} - URL: {url}, Query: {query}")
         
     | 
| 
      
 465 
     | 
    
         
            +
                    
         
     | 
| 
      
 466 
     | 
    
         
            +
                    # For all other operations, continue normally
         
     | 
| 
       418 
467 
     | 
    
         
             
                    return self._continue()
         
     | 
| 
       419 
468 
     | 
    
         | 
| 
       420 
469 
     | 
    
         
             
                def _handle_post_tool_use(self):
         
     | 
| 
         @@ -0,0 +1,106 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            """
         
     | 
| 
      
 2 
     | 
    
         
            +
            Claude MPM Models Package
         
     | 
| 
      
 3 
     | 
    
         
            +
            =========================
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            Centralized data models for the Claude MPM system.
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            WHY: This package centralizes all data models to:
         
     | 
| 
      
 8 
     | 
    
         
            +
            - Prevent circular imports
         
     | 
| 
      
 9 
     | 
    
         
            +
            - Reduce code duplication
         
     | 
| 
      
 10 
     | 
    
         
            +
            - Ensure consistent data structures
         
     | 
| 
      
 11 
     | 
    
         
            +
            - Make models easily discoverable
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            DESIGN DECISION: Models are organized by domain:
         
     | 
| 
      
 14 
     | 
    
         
            +
            - agent_definition: Core agent behavior models
         
     | 
| 
      
 15 
     | 
    
         
            +
            - lifecycle: Agent lifecycle management models
         
     | 
| 
      
 16 
     | 
    
         
            +
            - modification: Change tracking models
         
     | 
| 
      
 17 
     | 
    
         
            +
            - persistence: Storage operation models
         
     | 
| 
      
 18 
     | 
    
         
            +
            - registry: Discovery and management models
         
     | 
| 
      
 19 
     | 
    
         
            +
            - common: Shared constants and enums
         
     | 
| 
      
 20 
     | 
    
         
            +
            """
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            # Agent definition models
         
     | 
| 
      
 23 
     | 
    
         
            +
            from .agent_definition import (
         
     | 
| 
      
 24 
     | 
    
         
            +
                AgentType,
         
     | 
| 
      
 25 
     | 
    
         
            +
                AgentSection,
         
     | 
| 
      
 26 
     | 
    
         
            +
                AgentPermissions,
         
     | 
| 
      
 27 
     | 
    
         
            +
                AgentWorkflow,
         
     | 
| 
      
 28 
     | 
    
         
            +
                AgentMetadata,
         
     | 
| 
      
 29 
     | 
    
         
            +
                AgentDefinition
         
     | 
| 
      
 30 
     | 
    
         
            +
            )
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            # Lifecycle models
         
     | 
| 
      
 33 
     | 
    
         
            +
            from .lifecycle import (
         
     | 
| 
      
 34 
     | 
    
         
            +
                LifecycleOperation,
         
     | 
| 
      
 35 
     | 
    
         
            +
                LifecycleState,
         
     | 
| 
      
 36 
     | 
    
         
            +
                AgentLifecycleRecord,
         
     | 
| 
      
 37 
     | 
    
         
            +
                LifecycleOperationResult
         
     | 
| 
      
 38 
     | 
    
         
            +
            )
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            # Modification models
         
     | 
| 
      
 41 
     | 
    
         
            +
            from .modification import (
         
     | 
| 
      
 42 
     | 
    
         
            +
                ModificationType,
         
     | 
| 
      
 43 
     | 
    
         
            +
                ModificationTier,
         
     | 
| 
      
 44 
     | 
    
         
            +
                AgentModification,
         
     | 
| 
      
 45 
     | 
    
         
            +
                ModificationHistory
         
     | 
| 
      
 46 
     | 
    
         
            +
            )
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
            # Persistence models
         
     | 
| 
      
 49 
     | 
    
         
            +
            from .persistence import (
         
     | 
| 
      
 50 
     | 
    
         
            +
                PersistenceStrategy,
         
     | 
| 
      
 51 
     | 
    
         
            +
                PersistenceOperation,
         
     | 
| 
      
 52 
     | 
    
         
            +
                PersistenceRecord
         
     | 
| 
      
 53 
     | 
    
         
            +
            )
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
            # Registry models
         
     | 
| 
      
 56 
     | 
    
         
            +
            from .registry import (
         
     | 
| 
      
 57 
     | 
    
         
            +
                AgentTier,
         
     | 
| 
      
 58 
     | 
    
         
            +
                AgentRegistryMetadata
         
     | 
| 
      
 59 
     | 
    
         
            +
            )
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
            # Common constants
         
     | 
| 
      
 62 
     | 
    
         
            +
            from .common import (
         
     | 
| 
      
 63 
     | 
    
         
            +
                AGENT_FILE_EXTENSIONS,
         
     | 
| 
      
 64 
     | 
    
         
            +
                AGENT_IGNORE_PATTERNS,
         
     | 
| 
      
 65 
     | 
    
         
            +
                CORE_AGENT_TYPES,
         
     | 
| 
      
 66 
     | 
    
         
            +
                SPECIALIZED_AGENT_TYPES,
         
     | 
| 
      
 67 
     | 
    
         
            +
                ALL_AGENT_TYPES
         
     | 
| 
      
 68 
     | 
    
         
            +
            )
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
            __all__ = [
         
     | 
| 
      
 71 
     | 
    
         
            +
                # Agent definition
         
     | 
| 
      
 72 
     | 
    
         
            +
                'AgentType',
         
     | 
| 
      
 73 
     | 
    
         
            +
                'AgentSection',
         
     | 
| 
      
 74 
     | 
    
         
            +
                'AgentPermissions',
         
     | 
| 
      
 75 
     | 
    
         
            +
                'AgentWorkflow',
         
     | 
| 
      
 76 
     | 
    
         
            +
                'AgentMetadata',
         
     | 
| 
      
 77 
     | 
    
         
            +
                'AgentDefinition',
         
     | 
| 
      
 78 
     | 
    
         
            +
                
         
     | 
| 
      
 79 
     | 
    
         
            +
                # Lifecycle
         
     | 
| 
      
 80 
     | 
    
         
            +
                'LifecycleOperation',
         
     | 
| 
      
 81 
     | 
    
         
            +
                'LifecycleState',
         
     | 
| 
      
 82 
     | 
    
         
            +
                'AgentLifecycleRecord',
         
     | 
| 
      
 83 
     | 
    
         
            +
                'LifecycleOperationResult',
         
     | 
| 
      
 84 
     | 
    
         
            +
                
         
     | 
| 
      
 85 
     | 
    
         
            +
                # Modification
         
     | 
| 
      
 86 
     | 
    
         
            +
                'ModificationType',
         
     | 
| 
      
 87 
     | 
    
         
            +
                'ModificationTier',
         
     | 
| 
      
 88 
     | 
    
         
            +
                'AgentModification',
         
     | 
| 
      
 89 
     | 
    
         
            +
                'ModificationHistory',
         
     | 
| 
      
 90 
     | 
    
         
            +
                
         
     | 
| 
      
 91 
     | 
    
         
            +
                # Persistence
         
     | 
| 
      
 92 
     | 
    
         
            +
                'PersistenceStrategy',
         
     | 
| 
      
 93 
     | 
    
         
            +
                'PersistenceOperation',
         
     | 
| 
      
 94 
     | 
    
         
            +
                'PersistenceRecord',
         
     | 
| 
      
 95 
     | 
    
         
            +
                
         
     | 
| 
      
 96 
     | 
    
         
            +
                # Registry
         
     | 
| 
      
 97 
     | 
    
         
            +
                'AgentTier',
         
     | 
| 
      
 98 
     | 
    
         
            +
                'AgentRegistryMetadata',
         
     | 
| 
      
 99 
     | 
    
         
            +
                
         
     | 
| 
      
 100 
     | 
    
         
            +
                # Common
         
     | 
| 
      
 101 
     | 
    
         
            +
                'AGENT_FILE_EXTENSIONS',
         
     | 
| 
      
 102 
     | 
    
         
            +
                'AGENT_IGNORE_PATTERNS',
         
     | 
| 
      
 103 
     | 
    
         
            +
                'CORE_AGENT_TYPES',
         
     | 
| 
      
 104 
     | 
    
         
            +
                'SPECIALIZED_AGENT_TYPES',
         
     | 
| 
      
 105 
     | 
    
         
            +
                'ALL_AGENT_TYPES'
         
     | 
| 
      
 106 
     | 
    
         
            +
            ]
         
     |