claude-mpm 3.1.2__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 +17 -0
 - claude_mpm/agents/INSTRUCTIONS.md +17 -2
 - claude_mpm/cli/__init__.py +23 -14
 - claude_mpm/cli/commands/agents.py +18 -7
 - claude_mpm/cli/commands/info.py +10 -5
 - claude_mpm/cli/commands/run.py +22 -7
 - claude_mpm/cli/commands/tickets.py +17 -10
 - claude_mpm/cli/commands/ui.py +37 -15
 - claude_mpm/cli/utils.py +28 -9
 - 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 +91 -9
 - 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 +25 -76
 - claude_mpm/services/agent_modification_tracker.py +17 -98
 - claude_mpm/services/agent_persistence_service.py +13 -33
 - claude_mpm/services/agent_registry.py +43 -82
 - 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.2.dist-info → claude_mpm-3.1.3.dist-info}/METADATA +9 -1
 - {claude_mpm-3.1.2.dist-info → claude_mpm-3.1.3.dist-info}/RECORD +37 -31
 - claude_mpm/utils/import_migration_example.py +0 -80
 - {claude_mpm-3.1.2.dist-info → claude_mpm-3.1.3.dist-info}/WHEEL +0 -0
 - {claude_mpm-3.1.2.dist-info → claude_mpm-3.1.3.dist-info}/entry_points.txt +0 -0
 - {claude_mpm-3.1.2.dist-info → claude_mpm-3.1.3.dist-info}/licenses/LICENSE +0 -0
 - {claude_mpm-3.1.2.dist-info → claude_mpm-3.1.3.dist-info}/top_level.txt +0 -0
 
| 
         @@ -13,42 +13,22 @@ DESIGN DECISION: Creating a minimal stub because: 
     | 
|
| 
       13 
13 
     | 
    
         
             
            - Allows for future extension if needed
         
     | 
| 
       14 
14 
     | 
    
         
             
            """
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
     | 
    
         
            -
            from  
     | 
| 
       17 
     | 
    
         
            -
            from enum import Enum
         
     | 
| 
       18 
     | 
    
         
            -
            from typing import Optional, Any, Dict
         
     | 
| 
      
 16 
     | 
    
         
            +
            from typing import Optional, Any
         
     | 
| 
       19 
17 
     | 
    
         
             
            import time
         
     | 
| 
       20 
18 
     | 
    
         | 
| 
      
 19 
     | 
    
         
            +
            from claude_mpm.models.persistence import (
         
     | 
| 
      
 20 
     | 
    
         
            +
                PersistenceStrategy,
         
     | 
| 
      
 21 
     | 
    
         
            +
                PersistenceOperation,
         
     | 
| 
      
 22 
     | 
    
         
            +
                PersistenceRecord
         
     | 
| 
      
 23 
     | 
    
         
            +
            )
         
     | 
| 
       21 
24 
     | 
    
         | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
                 
     | 
| 
       25 
     | 
    
         
            -
                 
     | 
| 
       26 
     | 
    
         
            -
                 
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
            class PersistenceOperation(Enum):
         
     | 
| 
       30 
     | 
    
         
            -
                """Persistence operation types."""
         
     | 
| 
       31 
     | 
    
         
            -
                CREATE = "create"
         
     | 
| 
       32 
     | 
    
         
            -
                UPDATE = "update"
         
     | 
| 
       33 
     | 
    
         
            -
                DELETE = "delete"
         
     | 
| 
       34 
     | 
    
         
            -
                BACKUP = "backup"
         
     | 
| 
       35 
     | 
    
         
            -
                RESTORE = "restore"
         
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
            @dataclass
         
     | 
| 
       39 
     | 
    
         
            -
            class PersistenceRecord:
         
     | 
| 
       40 
     | 
    
         
            -
                """Record of a persistence operation."""
         
     | 
| 
       41 
     | 
    
         
            -
                operation_id: str
         
     | 
| 
       42 
     | 
    
         
            -
                operation_type: PersistenceOperation
         
     | 
| 
       43 
     | 
    
         
            -
                agent_name: str
         
     | 
| 
       44 
     | 
    
         
            -
                source_tier: Any
         
     | 
| 
       45 
     | 
    
         
            -
                target_tier: Optional[Any] = None
         
     | 
| 
       46 
     | 
    
         
            -
                strategy: Optional[PersistenceStrategy] = None
         
     | 
| 
       47 
     | 
    
         
            -
                success: bool = True
         
     | 
| 
       48 
     | 
    
         
            -
                timestamp: float = 0.0
         
     | 
| 
       49 
     | 
    
         
            -
                file_path: Optional[str] = None
         
     | 
| 
       50 
     | 
    
         
            -
                error_message: Optional[str] = None
         
     | 
| 
       51 
     | 
    
         
            -
                metadata: Dict[str, Any] = None
         
     | 
| 
      
 25 
     | 
    
         
            +
            # Backward compatibility exports
         
     | 
| 
      
 26 
     | 
    
         
            +
            __all__ = [
         
     | 
| 
      
 27 
     | 
    
         
            +
                'PersistenceStrategy',
         
     | 
| 
      
 28 
     | 
    
         
            +
                'PersistenceOperation',
         
     | 
| 
      
 29 
     | 
    
         
            +
                'PersistenceRecord',
         
     | 
| 
      
 30 
     | 
    
         
            +
                'AgentPersistenceService'
         
     | 
| 
      
 31 
     | 
    
         
            +
            ]
         
     | 
| 
       52 
32 
     | 
    
         | 
| 
       53 
33 
     | 
    
         | 
| 
       54 
34 
     | 
    
         
             
            class AgentPersistenceService:
         
     | 
| 
         @@ -25,36 +25,35 @@ import hashlib 
     | 
|
| 
       25 
25 
     | 
    
         
             
            import logging
         
     | 
| 
       26 
26 
     | 
    
         
             
            from pathlib import Path
         
     | 
| 
       27 
27 
     | 
    
         
             
            from typing import Dict, List, Optional, Set, Tuple, Any, Union
         
     | 
| 
       28 
     | 
    
         
            -
            from dataclasses import  
     | 
| 
      
 28 
     | 
    
         
            +
            from dataclasses import asdict
         
     | 
| 
       29 
29 
     | 
    
         
             
            from datetime import datetime
         
     | 
| 
       30 
30 
     | 
    
         
             
            from enum import Enum
         
     | 
| 
       31 
31 
     | 
    
         | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
                'engineer', 'architect', 'qa', 'security', 'documentation',
         
     | 
| 
       41 
     | 
    
         
            -
                'ops', 'data', 'research', 'version_control'
         
     | 
| 
       42 
     | 
    
         
            -
            }
         
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
            SPECIALIZED_AGENT_TYPES = {
         
     | 
| 
       45 
     | 
    
         
            -
                'pm_orchestrator', 'frontend', 'backend', 'devops', 'ml',
         
     | 
| 
       46 
     | 
    
         
            -
                'database', 'api', 'mobile', 'cloud', 'testing'
         
     | 
| 
       47 
     | 
    
         
            -
            }
         
     | 
| 
      
 32 
     | 
    
         
            +
            from claude_mpm.models.registry import AgentTier, AgentRegistryMetadata
         
     | 
| 
      
 33 
     | 
    
         
            +
            from claude_mpm.models.common import (
         
     | 
| 
      
 34 
     | 
    
         
            +
                CORE_AGENT_TYPES,
         
     | 
| 
      
 35 
     | 
    
         
            +
                SPECIALIZED_AGENT_TYPES,
         
     | 
| 
      
 36 
     | 
    
         
            +
                ALL_AGENT_TYPES,
         
     | 
| 
      
 37 
     | 
    
         
            +
                AGENT_FILE_EXTENSIONS,
         
     | 
| 
      
 38 
     | 
    
         
            +
                AGENT_IGNORE_PATTERNS
         
     | 
| 
      
 39 
     | 
    
         
            +
            )
         
     | 
| 
       48 
40 
     | 
    
         | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
      
 41 
     | 
    
         
            +
            logger = logging.getLogger(__name__)
         
     | 
| 
       50 
42 
     | 
    
         | 
| 
      
 43 
     | 
    
         
            +
            # Backward compatibility exports
         
     | 
| 
      
 44 
     | 
    
         
            +
            # Export AgentRegistryMetadata as AgentMetadata for existing code
         
     | 
| 
      
 45 
     | 
    
         
            +
            AgentMetadata = AgentRegistryMetadata
         
     | 
| 
       51 
46 
     | 
    
         | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
                 
     | 
| 
       54 
     | 
    
         
            -
                 
     | 
| 
       55 
     | 
    
         
            -
                 
     | 
| 
      
 47 
     | 
    
         
            +
            __all__ = [
         
     | 
| 
      
 48 
     | 
    
         
            +
                'AgentTier',
         
     | 
| 
      
 49 
     | 
    
         
            +
                'AgentType',
         
     | 
| 
      
 50 
     | 
    
         
            +
                'AgentMetadata',  # Alias for AgentRegistryMetadata
         
     | 
| 
      
 51 
     | 
    
         
            +
                'AgentRegistryMetadata',
         
     | 
| 
      
 52 
     | 
    
         
            +
                'AgentRegistry'
         
     | 
| 
      
 53 
     | 
    
         
            +
            ]
         
     | 
| 
       56 
54 
     | 
    
         | 
| 
       57 
55 
     | 
    
         | 
| 
      
 56 
     | 
    
         
            +
            # Keep local AgentType enum for internal classification logic
         
     | 
| 
       58 
57 
     | 
    
         
             
            class AgentType(Enum):
         
     | 
| 
       59 
58 
     | 
    
         
             
                """Agent classification types."""
         
     | 
| 
       60 
59 
     | 
    
         
             
                CORE = "core"
         
     | 
| 
         @@ -63,44 +62,6 @@ class AgentType(Enum): 
     | 
|
| 
       63 
62 
     | 
    
         
             
                UNKNOWN = "unknown"
         
     | 
| 
       64 
63 
     | 
    
         | 
| 
       65 
64 
     | 
    
         | 
| 
       66 
     | 
    
         
            -
            # ============================================================================
         
     | 
| 
       67 
     | 
    
         
            -
            # Data Models
         
     | 
| 
       68 
     | 
    
         
            -
            # ============================================================================
         
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
            @dataclass
         
     | 
| 
       71 
     | 
    
         
            -
            class AgentMetadata:
         
     | 
| 
       72 
     | 
    
         
            -
                """Complete metadata for discovered agent."""
         
     | 
| 
       73 
     | 
    
         
            -
                name: str
         
     | 
| 
       74 
     | 
    
         
            -
                path: str
         
     | 
| 
       75 
     | 
    
         
            -
                tier: AgentTier
         
     | 
| 
       76 
     | 
    
         
            -
                agent_type: AgentType
         
     | 
| 
       77 
     | 
    
         
            -
                description: str = ""
         
     | 
| 
       78 
     | 
    
         
            -
                version: str = "0.0.0"
         
     | 
| 
       79 
     | 
    
         
            -
                dependencies: List[str] = field(default_factory=list)
         
     | 
| 
       80 
     | 
    
         
            -
                capabilities: List[str] = field(default_factory=list)
         
     | 
| 
       81 
     | 
    
         
            -
                created_at: float = field(default_factory=time.time)
         
     | 
| 
       82 
     | 
    
         
            -
                last_modified: float = field(default_factory=time.time)
         
     | 
| 
       83 
     | 
    
         
            -
                file_size: int = 0
         
     | 
| 
       84 
     | 
    
         
            -
                checksum: str = ""
         
     | 
| 
       85 
     | 
    
         
            -
                is_valid: bool = True
         
     | 
| 
       86 
     | 
    
         
            -
                validation_errors: List[str] = field(default_factory=list)
         
     | 
| 
       87 
     | 
    
         
            -
                metadata: Dict[str, Any] = field(default_factory=dict)
         
     | 
| 
       88 
     | 
    
         
            -
                
         
     | 
| 
       89 
     | 
    
         
            -
                def to_dict(self) -> Dict[str, Any]:
         
     | 
| 
       90 
     | 
    
         
            -
                    """Convert to dictionary for serialization."""
         
     | 
| 
       91 
     | 
    
         
            -
                    data = asdict(self)
         
     | 
| 
       92 
     | 
    
         
            -
                    data['tier'] = self.tier.value
         
     | 
| 
       93 
     | 
    
         
            -
                    data['agent_type'] = self.agent_type.value
         
     | 
| 
       94 
     | 
    
         
            -
                    return data
         
     | 
| 
       95 
     | 
    
         
            -
                
         
     | 
| 
       96 
     | 
    
         
            -
                @classmethod
         
     | 
| 
       97 
     | 
    
         
            -
                def from_dict(cls, data: Dict[str, Any]) -> 'AgentMetadata':
         
     | 
| 
       98 
     | 
    
         
            -
                    """Create from dictionary."""
         
     | 
| 
       99 
     | 
    
         
            -
                    data['tier'] = AgentTier(data['tier'])
         
     | 
| 
       100 
     | 
    
         
            -
                    data['agent_type'] = AgentType(data['agent_type'])
         
     | 
| 
       101 
     | 
    
         
            -
                    return cls(**data)
         
     | 
| 
       102 
     | 
    
         
            -
             
     | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
       104 
65 
     | 
    
         
             
            # ============================================================================
         
     | 
| 
       105 
66 
     | 
    
         
             
            # Main Registry Class
         
     | 
| 
       106 
67 
     | 
    
         
             
            # ============================================================================
         
     | 
| 
         @@ -119,7 +80,7 @@ class AgentRegistry: 
     | 
|
| 
       119 
80 
     | 
    
         
             
                    self.model_selector = model_selector
         
     | 
| 
       120 
81 
     | 
    
         | 
| 
       121 
82 
     | 
    
         
             
                    # Registry storage
         
     | 
| 
       122 
     | 
    
         
            -
                    self.registry: Dict[str,  
     | 
| 
      
 83 
     | 
    
         
            +
                    self.registry: Dict[str, AgentRegistryMetadata] = {}
         
     | 
| 
       123 
84 
     | 
    
         
             
                    self.discovery_paths: List[Path] = []
         
     | 
| 
       124 
85 
     | 
    
         | 
| 
       125 
86 
     | 
    
         
             
                    # Cache configuration
         
     | 
| 
         @@ -128,8 +89,8 @@ class AgentRegistry: 
     | 
|
| 
       128 
89 
     | 
    
         
             
                    self.cache_prefix = "agent_registry"
         
     | 
| 
       129 
90 
     | 
    
         | 
| 
       130 
91 
     | 
    
         
             
                    # Discovery configuration
         
     | 
| 
       131 
     | 
    
         
            -
                    self.file_extensions =  
     | 
| 
       132 
     | 
    
         
            -
                    self.ignore_patterns =  
     | 
| 
      
 92 
     | 
    
         
            +
                    self.file_extensions = AGENT_FILE_EXTENSIONS
         
     | 
| 
      
 93 
     | 
    
         
            +
                    self.ignore_patterns = AGENT_IGNORE_PATTERNS
         
     | 
| 
       133 
94 
     | 
    
         | 
| 
       134 
95 
     | 
    
         
             
                    # Statistics
         
     | 
| 
       135 
96 
     | 
    
         
             
                    self.discovery_stats = {
         
     | 
| 
         @@ -169,7 +130,7 @@ class AgentRegistry: 
     | 
|
| 
       169 
130 
     | 
    
         
             
                # Discovery Methods
         
     | 
| 
       170 
131 
     | 
    
         
             
                # ========================================================================
         
     | 
| 
       171 
132 
     | 
    
         | 
| 
       172 
     | 
    
         
            -
                def discover_agents(self, force_refresh: bool = False) -> Dict[str,  
     | 
| 
      
 133 
     | 
    
         
            +
                def discover_agents(self, force_refresh: bool = False) -> Dict[str, AgentRegistryMetadata]:
         
     | 
| 
       173 
134 
     | 
    
         
             
                    """
         
     | 
| 
       174 
135 
     | 
    
         
             
                    Discover all available agents across configured paths.
         
     | 
| 
       175 
136 
     | 
    
         | 
| 
         @@ -269,7 +230,7 @@ class AgentRegistry: 
     | 
|
| 
       269 
230 
     | 
    
         | 
| 
       270 
231 
     | 
    
         
             
                    return name
         
     | 
| 
       271 
232 
     | 
    
         | 
| 
       272 
     | 
    
         
            -
                def _create_agent_metadata(self, file_path: Path, agent_name: str, tier: AgentTier) ->  
     | 
| 
      
 233 
     | 
    
         
            +
                def _create_agent_metadata(self, file_path: Path, agent_name: str, tier: AgentTier) -> AgentRegistryMetadata:
         
     | 
| 
       273 
234 
     | 
    
         
             
                    """Create agent metadata from file."""
         
     | 
| 
       274 
235 
     | 
    
         
             
                    # Get file stats
         
     | 
| 
       275 
236 
     | 
    
         
             
                    stat = file_path.stat()
         
     | 
| 
         @@ -324,11 +285,11 @@ class AgentRegistry: 
     | 
|
| 
       324 
285 
     | 
    
         
             
                    except Exception as e:
         
     | 
| 
       325 
286 
     | 
    
         
             
                        logger.warning(f"Failed to parse {file_path}: {e}")
         
     | 
| 
       326 
287 
     | 
    
         | 
| 
       327 
     | 
    
         
            -
                    return  
     | 
| 
      
 288 
     | 
    
         
            +
                    return AgentRegistryMetadata(
         
     | 
| 
       328 
289 
     | 
    
         
             
                        name=agent_name,
         
     | 
| 
       329 
290 
     | 
    
         
             
                        path=str(file_path),
         
     | 
| 
       330 
291 
     | 
    
         
             
                        tier=tier,
         
     | 
| 
       331 
     | 
    
         
            -
                        agent_type=agent_type,
         
     | 
| 
      
 292 
     | 
    
         
            +
                        agent_type=agent_type.value,  # Convert enum to string
         
     | 
| 
       332 
293 
     | 
    
         
             
                        description=description,
         
     | 
| 
       333 
294 
     | 
    
         
             
                        version=version,
         
     | 
| 
       334 
295 
     | 
    
         
             
                        capabilities=capabilities,
         
     | 
| 
         @@ -379,7 +340,7 @@ class AgentRegistry: 
     | 
|
| 
       379 
340 
     | 
    
         
             
                def _apply_tier_precedence(self) -> None:
         
     | 
| 
       380 
341 
     | 
    
         
             
                    """Apply tier precedence rules to discovered agents."""
         
     | 
| 
       381 
342 
     | 
    
         
             
                    # Group agents by name
         
     | 
| 
       382 
     | 
    
         
            -
                    agents_by_name: Dict[str, List[ 
     | 
| 
      
 343 
     | 
    
         
            +
                    agents_by_name: Dict[str, List[AgentRegistryMetadata]] = {}
         
     | 
| 
       383 
344 
     | 
    
         | 
| 
       384 
345 
     | 
    
         
             
                    for agent in self.registry.values():
         
     | 
| 
       385 
346 
     | 
    
         
             
                        if agent.name not in agents_by_name:
         
     | 
| 
         @@ -403,7 +364,7 @@ class AgentRegistry: 
     | 
|
| 
       403 
364 
     | 
    
         
             
                # Validation Methods
         
     | 
| 
       404 
365 
     | 
    
         
             
                # ========================================================================
         
     | 
| 
       405 
366 
     | 
    
         | 
| 
       406 
     | 
    
         
            -
                def _validate_agent(self, metadata:  
     | 
| 
      
 367 
     | 
    
         
            +
                def _validate_agent(self, metadata: AgentRegistryMetadata) -> bool:
         
     | 
| 
       407 
368 
     | 
    
         
             
                    """Validate agent metadata and file."""
         
     | 
| 
       408 
369 
     | 
    
         
             
                    errors = []
         
     | 
| 
       409 
370 
     | 
    
         | 
| 
         @@ -437,7 +398,7 @@ class AgentRegistry: 
     | 
|
| 
       437 
398 
     | 
    
         
             
                # Cache Methods
         
     | 
| 
       438 
399 
     | 
    
         
             
                # ========================================================================
         
     | 
| 
       439 
400 
     | 
    
         | 
| 
       440 
     | 
    
         
            -
                def _get_cached_registry(self) -> Optional[Dict[str,  
     | 
| 
      
 401 
     | 
    
         
            +
                def _get_cached_registry(self) -> Optional[Dict[str, AgentRegistryMetadata]]:
         
     | 
| 
       441 
402 
     | 
    
         
             
                    """Get registry from cache if available."""
         
     | 
| 
       442 
403 
     | 
    
         
             
                    if not self.cache_service:
         
     | 
| 
       443 
404 
     | 
    
         
             
                        return None
         
     | 
| 
         @@ -450,7 +411,7 @@ class AgentRegistry: 
     | 
|
| 
       450 
411 
     | 
    
         
             
                            # Deserialize metadata
         
     | 
| 
       451 
412 
     | 
    
         
             
                            registry = {}
         
     | 
| 
       452 
413 
     | 
    
         
             
                            for name, data in cached_data.items():
         
     | 
| 
       453 
     | 
    
         
            -
                                registry[name] =  
     | 
| 
      
 414 
     | 
    
         
            +
                                registry[name] = AgentRegistryMetadata.from_dict(data)
         
     | 
| 
       454 
415 
     | 
    
         
             
                            return registry
         
     | 
| 
       455 
416 
     | 
    
         | 
| 
       456 
417 
     | 
    
         
             
                    except Exception as e:
         
     | 
| 
         @@ -491,7 +452,7 @@ class AgentRegistry: 
     | 
|
| 
       491 
452 
     | 
    
         
             
                # Query Methods
         
     | 
| 
       492 
453 
     | 
    
         
             
                # ========================================================================
         
     | 
| 
       493 
454 
     | 
    
         | 
| 
       494 
     | 
    
         
            -
                def get_agent(self, name: str) -> Optional[ 
     | 
| 
      
 455 
     | 
    
         
            +
                def get_agent(self, name: str) -> Optional[AgentRegistryMetadata]:
         
     | 
| 
       495 
456 
     | 
    
         
             
                    """Get metadata for a specific agent."""
         
     | 
| 
       496 
457 
     | 
    
         
             
                    # Ensure registry is populated
         
     | 
| 
       497 
458 
     | 
    
         
             
                    if not self.registry:
         
     | 
| 
         @@ -500,7 +461,7 @@ class AgentRegistry: 
     | 
|
| 
       500 
461 
     | 
    
         
             
                    return self.registry.get(name)
         
     | 
| 
       501 
462 
     | 
    
         | 
| 
       502 
463 
     | 
    
         
             
                def list_agents(self, tier: Optional[AgentTier] = None, 
         
     | 
| 
       503 
     | 
    
         
            -
                               agent_type: Optional[ 
     | 
| 
      
 464 
     | 
    
         
            +
                               agent_type: Optional[str] = None) -> List[AgentRegistryMetadata]:
         
     | 
| 
       504 
465 
     | 
    
         
             
                    """List agents with optional filtering."""
         
     | 
| 
       505 
466 
     | 
    
         
             
                    # Ensure registry is populated
         
     | 
| 
       506 
467 
     | 
    
         
             
                    if not self.registry:
         
     | 
| 
         @@ -524,19 +485,19 @@ class AgentRegistry: 
     | 
|
| 
       524 
485 
     | 
    
         | 
| 
       525 
486 
     | 
    
         
             
                    return sorted(self.registry.keys())
         
     | 
| 
       526 
487 
     | 
    
         | 
| 
       527 
     | 
    
         
            -
                def get_core_agents(self) -> List[ 
     | 
| 
      
 488 
     | 
    
         
            +
                def get_core_agents(self) -> List[AgentRegistryMetadata]:
         
     | 
| 
       528 
489 
     | 
    
         
             
                    """Get all core framework agents."""
         
     | 
| 
       529 
     | 
    
         
            -
                    return self.list_agents(agent_type=AgentType.CORE)
         
     | 
| 
      
 490 
     | 
    
         
            +
                    return self.list_agents(agent_type=AgentType.CORE.value)
         
     | 
| 
       530 
491 
     | 
    
         | 
| 
       531 
     | 
    
         
            -
                def get_specialized_agents(self) -> List[ 
     | 
| 
      
 492 
     | 
    
         
            +
                def get_specialized_agents(self) -> List[AgentRegistryMetadata]:
         
     | 
| 
       532 
493 
     | 
    
         
             
                    """Get all specialized agents."""
         
     | 
| 
       533 
     | 
    
         
            -
                    return self.list_agents(agent_type=AgentType.SPECIALIZED)
         
     | 
| 
      
 494 
     | 
    
         
            +
                    return self.list_agents(agent_type=AgentType.SPECIALIZED.value)
         
     | 
| 
       534 
495 
     | 
    
         | 
| 
       535 
     | 
    
         
            -
                def get_custom_agents(self) -> List[ 
     | 
| 
      
 496 
     | 
    
         
            +
                def get_custom_agents(self) -> List[AgentRegistryMetadata]:
         
     | 
| 
       536 
497 
     | 
    
         
             
                    """Get all custom user-defined agents."""
         
     | 
| 
       537 
     | 
    
         
            -
                    return self.list_agents(agent_type=AgentType.CUSTOM)
         
     | 
| 
      
 498 
     | 
    
         
            +
                    return self.list_agents(agent_type=AgentType.CUSTOM.value)
         
     | 
| 
       538 
499 
     | 
    
         | 
| 
       539 
     | 
    
         
            -
                def search_agents(self, query: str) -> List[ 
     | 
| 
      
 500 
     | 
    
         
            +
                def search_agents(self, query: str) -> List[AgentRegistryMetadata]:
         
     | 
| 
       540 
501 
     | 
    
         
             
                    """Search agents by name or description."""
         
     | 
| 
       541 
502 
     | 
    
         
             
                    if not self.registry:
         
     | 
| 
       542 
503 
     | 
    
         
             
                        self.discover_agents()
         
     | 
| 
         @@ -579,7 +540,7 @@ class AgentRegistry: 
     | 
|
| 
       579 
540 
     | 
    
         | 
| 
       580 
541 
     | 
    
         
             
                    # Count by type
         
     | 
| 
       581 
542 
     | 
    
         
             
                    for agent in self.registry.values():
         
     | 
| 
       582 
     | 
    
         
            -
                        agent_type = agent.agent_type 
     | 
| 
      
 543 
     | 
    
         
            +
                        agent_type = agent.agent_type  # Already a string
         
     | 
| 
       583 
544 
     | 
    
         
             
                        stats['agents_by_type'][agent_type] = stats['agents_by_type'].get(agent_type, 0) + 1
         
     | 
| 
       584 
545 
     | 
    
         | 
| 
       585 
546 
     | 
    
         
             
                    # Validation stats
         
     | 
| 
         @@ -667,7 +628,7 @@ class AgentRegistry: 
     | 
|
| 
       667 
628 
     | 
    
         | 
| 
       668 
629 
     | 
    
         
             
                    # Import agents
         
     | 
| 
       669 
630 
     | 
    
         
             
                    for name, agent_data in data.get('agents', {}).items():
         
     | 
| 
       670 
     | 
    
         
            -
                        self.registry[name] =  
     | 
| 
      
 631 
     | 
    
         
            +
                        self.registry[name] = AgentRegistryMetadata.from_dict(agent_data)
         
     | 
| 
       671 
632 
     | 
    
         | 
| 
       672 
633 
     | 
    
         
             
                    # Cache imported registry
         
     | 
| 
       673 
634 
     | 
    
         
             
                    if self.cache_enabled:
         
     | 
| 
         @@ -1,10 +1,13 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            #!/usr/bin/env python3
         
     | 
| 
       2 
2 
     | 
    
         
             
            """
         
     | 
| 
       3 
     | 
    
         
            -
            Ticketing Service
         
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
      
 3 
     | 
    
         
            +
            Legacy Ticketing Service (DEPRECATED)
         
     | 
| 
      
 4 
     | 
    
         
            +
            =====================================
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
      
 6 
     | 
    
         
            +
            DEPRECATED: This is the original ticketing service implementation.
         
     | 
| 
      
 7 
     | 
    
         
            +
            For new development, use TicketManager or TicketManagerDependencyInjection instead.
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            This service is kept for backward compatibility and reference purposes.
         
     | 
| 
      
 10 
     | 
    
         
            +
            It wraps ai-trackdown-pytools for simplified ticket management.
         
     | 
| 
       8 
11 
     | 
    
         | 
| 
       9 
12 
     | 
    
         
             
            Key Features:
         
     | 
| 
       10 
13 
     | 
    
         
             
            - Singleton pattern for consistent ticket management
         
     | 
| 
         @@ -44,15 +47,19 @@ from datetime import datetime 
     | 
|
| 
       44 
47 
     | 
    
         
             
            from pathlib import Path
         
     | 
| 
       45 
48 
     | 
    
         
             
            from typing import Any, Dict, List, Optional, Union
         
     | 
| 
       46 
49 
     | 
    
         | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
      
 50 
     | 
    
         
            +
            from claude_mpm.utils.imports import safe_import
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
            # Import ai-trackdown-pytools using safe_import pattern
         
     | 
| 
      
 53 
     | 
    
         
            +
            trackdown_imports = safe_import('ai_trackdown_pytools', None, ['Task', 'Project'])
         
     | 
| 
      
 54 
     | 
    
         
            +
            TaskManager = safe_import('ai_trackdown_pytools.core.task', None, ['TaskManager'])
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
            if trackdown_imports and TaskManager:
         
     | 
| 
      
 57 
     | 
    
         
            +
                Task, Project = trackdown_imports
         
     | 
| 
       51 
58 
     | 
    
         
             
                AI_TRACKDOWN_AVAILABLE = True
         
     | 
| 
       52 
59 
     | 
    
         
             
                # Map to expected names
         
     | 
| 
       53 
60 
     | 
    
         
             
                Ticket = Task
         
     | 
| 
       54 
61 
     | 
    
         
             
                TicketManager = TaskManager
         
     | 
| 
       55 
     | 
    
         
            -
             
     | 
| 
      
 62 
     | 
    
         
            +
            else:
         
     | 
| 
       56 
63 
     | 
    
         
             
                AI_TRACKDOWN_AVAILABLE = False
         
     | 
| 
       57 
64 
     | 
    
         
             
                # Define fallback classes for type hints
         
     | 
| 
       58 
65 
     | 
    
         
             
                class TicketStatus:
         
     | 
| 
         @@ -4,10 +4,11 @@ from pathlib import Path 
     | 
|
| 
       4 
4 
     | 
    
         
             
            from typing import Optional, Dict, Any, List
         
     | 
| 
       5 
5 
     | 
    
         
             
            from datetime import datetime
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
      
 7 
     | 
    
         
            +
            from claude_mpm.utils.imports import safe_import
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            # Import logger using safe_import pattern
         
     | 
| 
      
 10 
     | 
    
         
            +
            # Note: safe_import cannot handle relative imports as primary, use absolute import first
         
     | 
| 
      
 11 
     | 
    
         
            +
            get_logger = safe_import('claude_mpm.core.logger', None, ['get_logger'])
         
     | 
| 
       11 
12 
     | 
    
         | 
| 
       12 
13 
     | 
    
         | 
| 
       13 
14 
     | 
    
         
             
            class TicketManager:
         
     | 
| 
         @@ -1,16 +1,39 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            """
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
      
 2 
     | 
    
         
            +
            Ticket Manager with Dependency Injection
         
     | 
| 
      
 3 
     | 
    
         
            +
            ========================================
         
     | 
| 
       3 
4 
     | 
    
         | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
      
 5 
     | 
    
         
            +
            Modern implementation of the ticket manager service with full dependency injection support.
         
     | 
| 
      
 6 
     | 
    
         
            +
            This is the recommended implementation for new development requiring ticket management.
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            Key Features:
         
     | 
| 
      
 9 
     | 
    
         
            +
            - Constructor-based dependency injection
         
     | 
| 
      
 10 
     | 
    
         
            +
            - Interface-based design for testability
         
     | 
| 
      
 11 
     | 
    
         
            +
            - Easy mocking for unit tests
         
     | 
| 
      
 12 
     | 
    
         
            +
            - Follows SOLID principles
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            Usage:
         
     | 
| 
      
 15 
     | 
    
         
            +
                # With dependency injection container
         
     | 
| 
      
 16 
     | 
    
         
            +
                ticket_manager = container.get(TicketManagerDependencyInjection)
         
     | 
| 
      
 17 
     | 
    
         
            +
                
         
     | 
| 
      
 18 
     | 
    
         
            +
                # Manual instantiation with dependencies
         
     | 
| 
      
 19 
     | 
    
         
            +
                ticket_manager = TicketManagerDependencyInjection(
         
     | 
| 
      
 20 
     | 
    
         
            +
                    config=config_service,
         
     | 
| 
      
 21 
     | 
    
         
            +
                    task_adapter=mock_adapter  # For testing
         
     | 
| 
      
 22 
     | 
    
         
            +
                )
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            This implementation supersedes the legacy_ticketing_service.py.
         
     | 
| 
       5 
25 
     | 
    
         
             
            """
         
     | 
| 
       6 
26 
     | 
    
         | 
| 
       7 
27 
     | 
    
         
             
            from pathlib import Path
         
     | 
| 
       8 
28 
     | 
    
         
             
            from typing import Optional, Dict, Any, List
         
     | 
| 
       9 
29 
     | 
    
         
             
            from datetime import datetime
         
     | 
| 
       10 
30 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
            from  
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
      
 31 
     | 
    
         
            +
            from claude_mpm.utils.imports import safe_import
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            # Import dependencies using safe_import pattern
         
     | 
| 
      
 34 
     | 
    
         
            +
            InjectableService = safe_import('claude_mpm.core.injectable_service', None, ['InjectableService'])
         
     | 
| 
      
 35 
     | 
    
         
            +
            Config = safe_import('claude_mpm.core.config', None, ['Config'])
         
     | 
| 
      
 36 
     | 
    
         
            +
            get_logger = safe_import('claude_mpm.core.logger', None, ['get_logger'])
         
     | 
| 
       14 
37 
     | 
    
         | 
| 
       15 
38 
     | 
    
         | 
| 
       16 
39 
     | 
    
         
             
            class ITaskManagerAdapter:
         
     | 
| 
         @@ -40,10 +63,17 @@ class AITrackdownAdapter(ITaskManagerAdapter): 
     | 
|
| 
       40 
63 
     | 
    
         | 
| 
       41 
64 
     | 
    
         
             
                def _init_task_manager(self):
         
     | 
| 
       42 
65 
     | 
    
         
             
                    """Initialize ai-trackdown-pytools TaskManager."""
         
     | 
| 
       43 
     | 
    
         
            -
                     
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
      
 66 
     | 
    
         
            +
                    # Import using safe_import pattern
         
     | 
| 
      
 67 
     | 
    
         
            +
                    TaskManager = safe_import('ai_trackdown_pytools.core.task', None, ['TaskManager'])
         
     | 
| 
      
 68 
     | 
    
         
            +
                    trackdown_imports = safe_import('ai_trackdown_pytools', None, ['Config', 'Project'])
         
     | 
| 
      
 69 
     | 
    
         
            +
                    
         
     | 
| 
      
 70 
     | 
    
         
            +
                    if not TaskManager or not trackdown_imports:
         
     | 
| 
      
 71 
     | 
    
         
            +
                        self.logger.error("ai-trackdown-pytools not installed")
         
     | 
| 
      
 72 
     | 
    
         
            +
                        return None
         
     | 
| 
       46 
73 
     | 
    
         | 
| 
      
 74 
     | 
    
         
            +
                    TrackdownConfig, Project = trackdown_imports
         
     | 
| 
      
 75 
     | 
    
         
            +
                    
         
     | 
| 
      
 76 
     | 
    
         
            +
                    try:
         
     | 
| 
       47 
77 
     | 
    
         
             
                        # Ensure tickets directory exists
         
     | 
| 
       48 
78 
     | 
    
         
             
                        tickets_dir = self.project_path / "tickets"
         
     | 
| 
       49 
79 
     | 
    
         
             
                        if not tickets_dir.exists():
         
     | 
| 
         @@ -67,9 +97,6 @@ class AITrackdownAdapter(ITaskManagerAdapter): 
     | 
|
| 
       67 
97 
     | 
    
         
             
                        # Initialize task manager directly
         
     | 
| 
       68 
98 
     | 
    
         
             
                        return TaskManager(self.project_path)
         
     | 
| 
       69 
99 
     | 
    
         | 
| 
       70 
     | 
    
         
            -
                    except ImportError:
         
     | 
| 
       71 
     | 
    
         
            -
                        self.logger.error("ai-trackdown-pytools not installed")
         
     | 
| 
       72 
     | 
    
         
            -
                        return None
         
     | 
| 
       73 
100 
     | 
    
         
             
                    except Exception as e:
         
     | 
| 
       74 
101 
     | 
    
         
             
                        self.logger.error(f"Failed to initialize TaskManager: {e}")
         
     | 
| 
       75 
102 
     | 
    
         
             
                        return None
         
     | 
| 
         @@ -93,7 +120,7 @@ class AITrackdownAdapter(ITaskManagerAdapter): 
     | 
|
| 
       93 
120 
     | 
    
         
             
                    return self._task_manager.load_task(task_id)
         
     | 
| 
       94 
121 
     | 
    
         | 
| 
       95 
122 
     | 
    
         | 
| 
       96 
     | 
    
         
            -
            class  
     | 
| 
      
 123 
     | 
    
         
            +
            class TicketManagerDependencyInjection(InjectableService):
         
     | 
| 
       97 
124 
     | 
    
         
             
                """
         
     | 
| 
       98 
125 
     | 
    
         
             
                Enhanced Ticket Manager with Dependency Injection.
         
     | 
| 
       99 
126 
     | 
    
         | 
| 
         @@ -46,7 +46,10 @@ from dataclasses import dataclass, field 
     | 
|
| 
       46 
46 
     | 
    
         
             
            from enum import Enum
         
     | 
| 
       47 
47 
     | 
    
         
             
            import logging
         
     | 
| 
       48 
48 
     | 
    
         | 
| 
       49 
     | 
    
         
            -
            from  
     | 
| 
      
 49 
     | 
    
         
            +
            from claude_mpm.utils.imports import safe_import
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
            # Import dependencies using safe_import pattern
         
     | 
| 
      
 52 
     | 
    
         
            +
            ConfigurationManager = safe_import('claude_mpm.utils.config_manager', None, ['ConfigurationManager'])
         
     | 
| 
       50 
53 
     | 
    
         | 
| 
       51 
54 
     | 
    
         | 
| 
       52 
55 
     | 
    
         
             
            class VersionBumpType(Enum):
         
     | 
| 
         @@ -367,14 +370,12 @@ class SemanticVersionManager: 
     | 
|
| 
       367 
370 
     | 
    
         | 
| 
       368 
371 
     | 
    
         
             
                def _parse_pyproject_toml_version(self, file_path: Path) -> Optional[str]:
         
     | 
| 
       369 
372 
     | 
    
         
             
                    """Parse version from pyproject.toml."""
         
     | 
| 
       370 
     | 
    
         
            -
                     
     | 
| 
       371 
     | 
    
         
            -
             
     | 
| 
       372 
     | 
    
         
            -
                     
     | 
| 
       373 
     | 
    
         
            -
             
     | 
| 
       374 
     | 
    
         
            -
             
     | 
| 
       375 
     | 
    
         
            -
                         
     | 
| 
       376 
     | 
    
         
            -
                            # Fallback to simple regex parsing
         
     | 
| 
       377 
     | 
    
         
            -
                            return self._parse_toml_version_regex(file_path)
         
     | 
| 
      
 373 
     | 
    
         
            +
                    # Try to import tomllib (Python 3.11+) or tomli as fallback
         
     | 
| 
      
 374 
     | 
    
         
            +
                    tomllib = safe_import('tomllib', 'tomli')
         
     | 
| 
      
 375 
     | 
    
         
            +
                    
         
     | 
| 
      
 376 
     | 
    
         
            +
                    if not tomllib:
         
     | 
| 
      
 377 
     | 
    
         
            +
                        # Fallback to simple regex parsing
         
     | 
| 
      
 378 
     | 
    
         
            +
                        return self._parse_toml_version_regex(file_path)
         
     | 
| 
       378 
379 
     | 
    
         | 
| 
       379 
380 
     | 
    
         
             
                    try:
         
     | 
| 
       380 
381 
     | 
    
         
             
                        with open(file_path, "rb") as f:
         
     | 
| 
         @@ -2,6 +2,26 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            This module provides a centralized PathOperations class for common path validation
         
     | 
| 
       4 
4 
     | 
    
         
             
            and file operations, reducing code duplication across the codebase.
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            ARCHITECTURAL DECISION: Separation of Concerns
         
     | 
| 
      
 7 
     | 
    
         
            +
            ---------------------------------------------
         
     | 
| 
      
 8 
     | 
    
         
            +
            This module (path_operations.py) and paths.py serve distinct purposes and should
         
     | 
| 
      
 9 
     | 
    
         
            +
            remain separate:
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            1. path_operations.py (this file):
         
     | 
| 
      
 12 
     | 
    
         
            +
               - Provides safe file I/O operations (read, write, copy, delete)
         
     | 
| 
      
 13 
     | 
    
         
            +
               - Handles file validation and permissions
         
     | 
| 
      
 14 
     | 
    
         
            +
               - Implements error handling and recovery mechanisms
         
     | 
| 
      
 15 
     | 
    
         
            +
               - Focus: File system operations with safety guarantees
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            2. paths.py:
         
     | 
| 
      
 18 
     | 
    
         
            +
               - Provides path discovery and resolution logic
         
     | 
| 
      
 19 
     | 
    
         
            +
               - Handles framework/project directory structure navigation
         
     | 
| 
      
 20 
     | 
    
         
            +
               - Implements caching for performance optimization
         
     | 
| 
      
 21 
     | 
    
         
            +
               - Focus: Path resolution and directory structure
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            This separation follows the Single Responsibility Principle and makes the codebase
         
     | 
| 
      
 24 
     | 
    
         
            +
            more maintainable by keeping distinct concerns in separate modules.
         
     | 
| 
       5 
25 
     | 
    
         
             
            """
         
     | 
| 
       6 
26 
     | 
    
         | 
| 
       7 
27 
     | 
    
         
             
            import os
         
     | 
| 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            Metadata-Version: 2.4
         
     | 
| 
       2 
2 
     | 
    
         
             
            Name: claude-mpm
         
     | 
| 
       3 
     | 
    
         
            -
            Version: 3.1. 
     | 
| 
      
 3 
     | 
    
         
            +
            Version: 3.1.3
         
     | 
| 
       4 
4 
     | 
    
         
             
            Summary: Claude Multi-agent Project Manager - Clean orchestration with ticket management
         
     | 
| 
       5 
5 
     | 
    
         
             
            Home-page: https://github.com/bobmatnyc/claude-mpm
         
     | 
| 
       6 
6 
     | 
    
         
             
            Author: Claude MPM Team
         
     | 
| 
         @@ -191,6 +191,14 @@ Claude MPM provides a modular framework for extending Claude's capabilities: 
     | 
|
| 
       191 
191 
     | 
    
         | 
| 
       192 
192 
     | 
    
         
             
            ## Key Features
         
     | 
| 
       193 
193 
     | 
    
         | 
| 
      
 194 
     | 
    
         
            +
            ### Security Features
         
     | 
| 
      
 195 
     | 
    
         
            +
            - **File Access Restrictions**: Agents are restricted to their working directory
         
     | 
| 
      
 196 
     | 
    
         
            +
            - **Bash Command Validation**: Comprehensive validation prevents writes outside project directory
         
     | 
| 
      
 197 
     | 
    
         
            +
            - **Path Traversal Protection**: Blocks attempts to escape the working directory
         
     | 
| 
      
 198 
     | 
    
         
            +
            - **Dangerous Command Blocking**: Prevents execution of sudo, rm -rf, and other risky commands
         
     | 
| 
      
 199 
     | 
    
         
            +
            - **Security Audit Logging**: All file operations are logged for security monitoring
         
     | 
| 
      
 200 
     | 
    
         
            +
            - See [Security Documentation](docs/SECURITY.md) for details
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
       194 
202 
     | 
    
         
             
            ### Agent System
         
     | 
| 
       195 
203 
     | 
    
         
             
            - Specialized agents for different domains (Research, Engineer, etc.)
         
     | 
| 
       196 
204 
     | 
    
         
             
            - Dynamic agent discovery and registration
         
     |