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
 
| 
         @@ -0,0 +1,94 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            """
         
     | 
| 
      
 2 
     | 
    
         
            +
            Info command implementation for claude-mpm.
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            WHY: This module provides system information and configuration details to help
         
     | 
| 
      
 5 
     | 
    
         
            +
            users understand their claude-mpm setup and troubleshoot issues.
         
     | 
| 
      
 6 
     | 
    
         
            +
            """
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            import shutil
         
     | 
| 
      
 9 
     | 
    
         
            +
            from pathlib import Path
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            from claude_mpm.utils.imports import safe_import
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            # Import logger using safe_import pattern
         
     | 
| 
      
 14 
     | 
    
         
            +
            get_logger = safe_import('claude_mpm.core.logger', None, ['get_logger'])
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            def show_info(args):
         
     | 
| 
      
 18 
     | 
    
         
            +
                """
         
     | 
| 
      
 19 
     | 
    
         
            +
                Show framework and configuration information.
         
     | 
| 
      
 20 
     | 
    
         
            +
                
         
     | 
| 
      
 21 
     | 
    
         
            +
                WHY: Users need to verify their installation, check dependencies, and understand
         
     | 
| 
      
 22 
     | 
    
         
            +
                what agents are available. This command provides a comprehensive overview of
         
     | 
| 
      
 23 
     | 
    
         
            +
                the claude-mpm environment.
         
     | 
| 
      
 24 
     | 
    
         
            +
                
         
     | 
| 
      
 25 
     | 
    
         
            +
                DESIGN DECISION: We check for all major components and dependencies, showing
         
     | 
| 
      
 26 
     | 
    
         
            +
                both what's working (✓) and what's missing (✗) to help with troubleshooting.
         
     | 
| 
      
 27 
     | 
    
         
            +
                
         
     | 
| 
      
 28 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 29 
     | 
    
         
            +
                    args: Parsed command line arguments
         
     | 
| 
      
 30 
     | 
    
         
            +
                """
         
     | 
| 
      
 31 
     | 
    
         
            +
                # Import FrameworkLoader using safe_import pattern
         
     | 
| 
      
 32 
     | 
    
         
            +
                FrameworkLoader = safe_import(
         
     | 
| 
      
 33 
     | 
    
         
            +
                    'claude_mpm.core.framework_loader',
         
     | 
| 
      
 34 
     | 
    
         
            +
                    None,
         
     | 
| 
      
 35 
     | 
    
         
            +
                    ['FrameworkLoader']
         
     | 
| 
      
 36 
     | 
    
         
            +
                )
         
     | 
| 
      
 37 
     | 
    
         
            +
                
         
     | 
| 
      
 38 
     | 
    
         
            +
                print("Claude MPM - Multi-Agent Project Manager")
         
     | 
| 
      
 39 
     | 
    
         
            +
                print("=" * 50)
         
     | 
| 
      
 40 
     | 
    
         
            +
                
         
     | 
| 
      
 41 
     | 
    
         
            +
                # Framework info
         
     | 
| 
      
 42 
     | 
    
         
            +
                loader = FrameworkLoader(args.framework_path)
         
     | 
| 
      
 43 
     | 
    
         
            +
                if loader.framework_content["loaded"]:
         
     | 
| 
      
 44 
     | 
    
         
            +
                    print(f"Framework: claude-multiagent-pm")
         
     | 
| 
      
 45 
     | 
    
         
            +
                    print(f"Version: {loader.framework_content['version']}")
         
     | 
| 
      
 46 
     | 
    
         
            +
                    print(f"Path: {loader.framework_path}")
         
     | 
| 
      
 47 
     | 
    
         
            +
                    print(f"Agents: {', '.join(loader.get_agent_list())}")
         
     | 
| 
      
 48 
     | 
    
         
            +
                else:
         
     | 
| 
      
 49 
     | 
    
         
            +
                    print("Framework: Not found (using minimal instructions)")
         
     | 
| 
      
 50 
     | 
    
         
            +
                
         
     | 
| 
      
 51 
     | 
    
         
            +
                print()
         
     | 
| 
      
 52 
     | 
    
         
            +
                
         
     | 
| 
      
 53 
     | 
    
         
            +
                # Configuration
         
     | 
| 
      
 54 
     | 
    
         
            +
                print("Configuration:")
         
     | 
| 
      
 55 
     | 
    
         
            +
                print(f"  Log directory: {args.log_dir or '~/.claude-mpm/logs'}")
         
     | 
| 
      
 56 
     | 
    
         
            +
                print(f"  Debug mode: {args.debug}")
         
     | 
| 
      
 57 
     | 
    
         
            +
                
         
     | 
| 
      
 58 
     | 
    
         
            +
                # Show agent hierarchy
         
     | 
| 
      
 59 
     | 
    
         
            +
                if loader.agent_registry:
         
     | 
| 
      
 60 
     | 
    
         
            +
                    hierarchy = loader.agent_registry.get_agent_hierarchy()
         
     | 
| 
      
 61 
     | 
    
         
            +
                    print("\nAgent Hierarchy:")
         
     | 
| 
      
 62 
     | 
    
         
            +
                    print(f"  Project agents: {len(hierarchy['project'])}")
         
     | 
| 
      
 63 
     | 
    
         
            +
                    print(f"  User agents: {len(hierarchy['user'])}")
         
     | 
| 
      
 64 
     | 
    
         
            +
                    print(f"  System agents: {len(hierarchy['system'])}")
         
     | 
| 
      
 65 
     | 
    
         
            +
                    
         
     | 
| 
      
 66 
     | 
    
         
            +
                    # Show core agents
         
     | 
| 
      
 67 
     | 
    
         
            +
                    core_agents = loader.agent_registry.get_core_agents()
         
     | 
| 
      
 68 
     | 
    
         
            +
                    print(f"\nCore Agents: {', '.join(core_agents)}")
         
     | 
| 
      
 69 
     | 
    
         
            +
                
         
     | 
| 
      
 70 
     | 
    
         
            +
                # Check dependencies
         
     | 
| 
      
 71 
     | 
    
         
            +
                print("\nDependencies:")
         
     | 
| 
      
 72 
     | 
    
         
            +
                
         
     | 
| 
      
 73 
     | 
    
         
            +
                # Check Claude CLI
         
     | 
| 
      
 74 
     | 
    
         
            +
                claude_path = shutil.which("claude")
         
     | 
| 
      
 75 
     | 
    
         
            +
                if claude_path:
         
     | 
| 
      
 76 
     | 
    
         
            +
                    print(f"  ✓ Claude CLI: {claude_path}")
         
     | 
| 
      
 77 
     | 
    
         
            +
                else:
         
     | 
| 
      
 78 
     | 
    
         
            +
                    print("  ✗ Claude CLI: Not found in PATH")
         
     | 
| 
      
 79 
     | 
    
         
            +
                
         
     | 
| 
      
 80 
     | 
    
         
            +
                # Check ai-trackdown-pytools
         
     | 
| 
      
 81 
     | 
    
         
            +
                try:
         
     | 
| 
      
 82 
     | 
    
         
            +
                    import ai_trackdown_pytools
         
     | 
| 
      
 83 
     | 
    
         
            +
                    print("  ✓ ai-trackdown-pytools: Installed")
         
     | 
| 
      
 84 
     | 
    
         
            +
                except ImportError:
         
     | 
| 
      
 85 
     | 
    
         
            +
                    print("  ✗ ai-trackdown-pytools: Not installed")
         
     | 
| 
      
 86 
     | 
    
         
            +
                
         
     | 
| 
      
 87 
     | 
    
         
            +
                # Check Claude Code hooks
         
     | 
| 
      
 88 
     | 
    
         
            +
                claude_settings = Path.home() / ".claude" / "settings.json"
         
     | 
| 
      
 89 
     | 
    
         
            +
                if claude_settings.exists():
         
     | 
| 
      
 90 
     | 
    
         
            +
                    print("  ✓ Claude Code Hooks: Installed")
         
     | 
| 
      
 91 
     | 
    
         
            +
                    print("     Use /mpm commands in Claude Code")
         
     | 
| 
      
 92 
     | 
    
         
            +
                else:
         
     | 
| 
      
 93 
     | 
    
         
            +
                    print("  ✗ Claude Code Hooks: Not installed")
         
     | 
| 
      
 94 
     | 
    
         
            +
                    print("     Run: python scripts/install_hooks.py")
         
     | 
| 
         @@ -0,0 +1,95 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            """
         
     | 
| 
      
 2 
     | 
    
         
            +
            Run command implementation for claude-mpm.
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            WHY: This module handles the main 'run' command which starts Claude sessions.
         
     | 
| 
      
 5 
     | 
    
         
            +
            It's the most commonly used command and handles both interactive and non-interactive modes.
         
     | 
| 
      
 6 
     | 
    
         
            +
            """
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            import subprocess
         
     | 
| 
      
 9 
     | 
    
         
            +
            import sys
         
     | 
| 
      
 10 
     | 
    
         
            +
            from pathlib import Path
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            from claude_mpm.utils.imports import safe_import
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            # Import with safe_import pattern for better error handling
         
     | 
| 
      
 15 
     | 
    
         
            +
            get_logger = safe_import('claude_mpm.core.logger', None, ['get_logger'])
         
     | 
| 
      
 16 
     | 
    
         
            +
            LogLevel = safe_import('claude_mpm.constants', None, ['LogLevel'])
         
     | 
| 
      
 17 
     | 
    
         
            +
            get_user_input, list_agent_versions_at_startup = safe_import(
         
     | 
| 
      
 18 
     | 
    
         
            +
                'claude_mpm.cli.utils', 
         
     | 
| 
      
 19 
     | 
    
         
            +
                None, 
         
     | 
| 
      
 20 
     | 
    
         
            +
                ['get_user_input', 'list_agent_versions_at_startup']
         
     | 
| 
      
 21 
     | 
    
         
            +
            )
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            def run_session(args):
         
     | 
| 
      
 25 
     | 
    
         
            +
                """
         
     | 
| 
      
 26 
     | 
    
         
            +
                Run a simplified Claude session.
         
     | 
| 
      
 27 
     | 
    
         
            +
                
         
     | 
| 
      
 28 
     | 
    
         
            +
                WHY: This is the primary command that users interact with. It sets up the
         
     | 
| 
      
 29 
     | 
    
         
            +
                environment, optionally deploys agents, and launches Claude with the MPM framework.
         
     | 
| 
      
 30 
     | 
    
         
            +
                
         
     | 
| 
      
 31 
     | 
    
         
            +
                DESIGN DECISION: We use SimpleClaudeRunner to handle the complexity of
         
     | 
| 
      
 32 
     | 
    
         
            +
                subprocess management and hook integration, keeping this function focused
         
     | 
| 
      
 33 
     | 
    
         
            +
                on high-level orchestration.
         
     | 
| 
      
 34 
     | 
    
         
            +
                
         
     | 
| 
      
 35 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 36 
     | 
    
         
            +
                    args: Parsed command line arguments
         
     | 
| 
      
 37 
     | 
    
         
            +
                """
         
     | 
| 
      
 38 
     | 
    
         
            +
                import os
         
     | 
| 
      
 39 
     | 
    
         
            +
                logger = get_logger("cli")
         
     | 
| 
      
 40 
     | 
    
         
            +
                if args.logging != LogLevel.OFF.value:
         
     | 
| 
      
 41 
     | 
    
         
            +
                    logger.info("Starting Claude MPM session")
         
     | 
| 
      
 42 
     | 
    
         
            +
                    # Log working directory context
         
     | 
| 
      
 43 
     | 
    
         
            +
                    logger.info(f"Working directory: {os.getcwd()}")
         
     | 
| 
      
 44 
     | 
    
         
            +
                    if args.debug:
         
     | 
| 
      
 45 
     | 
    
         
            +
                        logger.debug(f"User PWD (from env): {os.environ.get('CLAUDE_MPM_USER_PWD', 'Not set')}")
         
     | 
| 
      
 46 
     | 
    
         
            +
                        logger.debug(f"Framework path: {os.environ.get('CLAUDE_MPM_FRAMEWORK_PATH', 'Not set')}")
         
     | 
| 
      
 47 
     | 
    
         
            +
                
         
     | 
| 
      
 48 
     | 
    
         
            +
                # Import SimpleClaudeRunner using safe_import pattern
         
     | 
| 
      
 49 
     | 
    
         
            +
                SimpleClaudeRunner, create_simple_context = safe_import(
         
     | 
| 
      
 50 
     | 
    
         
            +
                    'claude_mpm.core.simple_runner',
         
     | 
| 
      
 51 
     | 
    
         
            +
                    None,
         
     | 
| 
      
 52 
     | 
    
         
            +
                    ['SimpleClaudeRunner', 'create_simple_context']
         
     | 
| 
      
 53 
     | 
    
         
            +
                )
         
     | 
| 
      
 54 
     | 
    
         
            +
                
         
     | 
| 
      
 55 
     | 
    
         
            +
                # Skip native agents if disabled
         
     | 
| 
      
 56 
     | 
    
         
            +
                if getattr(args, 'no_native_agents', False):
         
     | 
| 
      
 57 
     | 
    
         
            +
                    print("Native agents disabled")
         
     | 
| 
      
 58 
     | 
    
         
            +
                else:
         
     | 
| 
      
 59 
     | 
    
         
            +
                    # List deployed agent versions at startup
         
     | 
| 
      
 60 
     | 
    
         
            +
                    list_agent_versions_at_startup()
         
     | 
| 
      
 61 
     | 
    
         
            +
                
         
     | 
| 
      
 62 
     | 
    
         
            +
                # Create simple runner
         
     | 
| 
      
 63 
     | 
    
         
            +
                enable_tickets = not args.no_tickets
         
     | 
| 
      
 64 
     | 
    
         
            +
                claude_args = getattr(args, 'claude_args', []) or []
         
     | 
| 
      
 65 
     | 
    
         
            +
                runner = SimpleClaudeRunner(
         
     | 
| 
      
 66 
     | 
    
         
            +
                    enable_tickets=enable_tickets,
         
     | 
| 
      
 67 
     | 
    
         
            +
                    log_level=args.logging,
         
     | 
| 
      
 68 
     | 
    
         
            +
                    claude_args=claude_args
         
     | 
| 
      
 69 
     | 
    
         
            +
                )
         
     | 
| 
      
 70 
     | 
    
         
            +
                
         
     | 
| 
      
 71 
     | 
    
         
            +
                # Create basic context
         
     | 
| 
      
 72 
     | 
    
         
            +
                context = create_simple_context()
         
     | 
| 
      
 73 
     | 
    
         
            +
                
         
     | 
| 
      
 74 
     | 
    
         
            +
                # Run session based on mode
         
     | 
| 
      
 75 
     | 
    
         
            +
                if args.non_interactive or args.input:
         
     | 
| 
      
 76 
     | 
    
         
            +
                    # Non-interactive mode
         
     | 
| 
      
 77 
     | 
    
         
            +
                    user_input = get_user_input(args.input, logger)
         
     | 
| 
      
 78 
     | 
    
         
            +
                    success = runner.run_oneshot(user_input, context)
         
     | 
| 
      
 79 
     | 
    
         
            +
                    if not success:
         
     | 
| 
      
 80 
     | 
    
         
            +
                        logger.error("Session failed")
         
     | 
| 
      
 81 
     | 
    
         
            +
                else:
         
     | 
| 
      
 82 
     | 
    
         
            +
                    # Interactive mode
         
     | 
| 
      
 83 
     | 
    
         
            +
                    if getattr(args, 'intercept_commands', False):
         
     | 
| 
      
 84 
     | 
    
         
            +
                        # Use the interactive wrapper for command interception
         
     | 
| 
      
 85 
     | 
    
         
            +
                        # WHY: Command interception requires special handling of stdin/stdout
         
     | 
| 
      
 86 
     | 
    
         
            +
                        # which is better done in a separate Python script
         
     | 
| 
      
 87 
     | 
    
         
            +
                        wrapper_path = Path(__file__).parent.parent.parent.parent.parent / "scripts" / "interactive_wrapper.py"
         
     | 
| 
      
 88 
     | 
    
         
            +
                        if wrapper_path.exists():
         
     | 
| 
      
 89 
     | 
    
         
            +
                            print("Starting interactive session with command interception...")
         
     | 
| 
      
 90 
     | 
    
         
            +
                            subprocess.run([sys.executable, str(wrapper_path)])
         
     | 
| 
      
 91 
     | 
    
         
            +
                        else:
         
     | 
| 
      
 92 
     | 
    
         
            +
                            logger.warning("Interactive wrapper not found, falling back to normal mode")
         
     | 
| 
      
 93 
     | 
    
         
            +
                            runner.run_interactive(context)
         
     | 
| 
      
 94 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 95 
     | 
    
         
            +
                        runner.run_interactive(context)
         
     | 
| 
         @@ -0,0 +1,70 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            """
         
     | 
| 
      
 2 
     | 
    
         
            +
            Tickets command implementation for claude-mpm.
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            WHY: This module handles ticket listing functionality, allowing users to view
         
     | 
| 
      
 5 
     | 
    
         
            +
            recent tickets created during Claude sessions.
         
     | 
| 
      
 6 
     | 
    
         
            +
            """
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            from claude_mpm.utils.imports import safe_import
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            # Import logger using safe_import pattern
         
     | 
| 
      
 11 
     | 
    
         
            +
            get_logger = safe_import('claude_mpm.core.logger', None, ['get_logger'])
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            def list_tickets(args):
         
     | 
| 
      
 15 
     | 
    
         
            +
                """
         
     | 
| 
      
 16 
     | 
    
         
            +
                List recent tickets.
         
     | 
| 
      
 17 
     | 
    
         
            +
                
         
     | 
| 
      
 18 
     | 
    
         
            +
                WHY: Users need to review tickets created during Claude sessions. This command
         
     | 
| 
      
 19 
     | 
    
         
            +
                provides a quick way to see recent tickets with their status and metadata.
         
     | 
| 
      
 20 
     | 
    
         
            +
                
         
     | 
| 
      
 21 
     | 
    
         
            +
                DESIGN DECISION: We show tickets in a compact format with emoji status indicators
         
     | 
| 
      
 22 
     | 
    
         
            +
                for better visual scanning. The limit is configurable to allow users to see more
         
     | 
| 
      
 23 
     | 
    
         
            +
                or fewer tickets as needed.
         
     | 
| 
      
 24 
     | 
    
         
            +
                
         
     | 
| 
      
 25 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 26 
     | 
    
         
            +
                    args: Parsed command line arguments with 'limit' attribute
         
     | 
| 
      
 27 
     | 
    
         
            +
                """
         
     | 
| 
      
 28 
     | 
    
         
            +
                logger = get_logger("cli")
         
     | 
| 
      
 29 
     | 
    
         
            +
                
         
     | 
| 
      
 30 
     | 
    
         
            +
                # Import TicketManager using safe_import pattern
         
     | 
| 
      
 31 
     | 
    
         
            +
                TicketManager = safe_import(
         
     | 
| 
      
 32 
     | 
    
         
            +
                    '...services.ticket_manager',
         
     | 
| 
      
 33 
     | 
    
         
            +
                    'claude_mpm.services.ticket_manager',
         
     | 
| 
      
 34 
     | 
    
         
            +
                    ['TicketManager']
         
     | 
| 
      
 35 
     | 
    
         
            +
                )
         
     | 
| 
      
 36 
     | 
    
         
            +
                
         
     | 
| 
      
 37 
     | 
    
         
            +
                if not TicketManager:
         
     | 
| 
      
 38 
     | 
    
         
            +
                    logger.error("ai-trackdown-pytools not installed")
         
     | 
| 
      
 39 
     | 
    
         
            +
                    print("Error: ai-trackdown-pytools not installed")
         
     | 
| 
      
 40 
     | 
    
         
            +
                    print("Install with: pip install ai-trackdown-pytools")
         
     | 
| 
      
 41 
     | 
    
         
            +
                    return
         
     | 
| 
      
 42 
     | 
    
         
            +
                
         
     | 
| 
      
 43 
     | 
    
         
            +
                try:
         
     | 
| 
      
 44 
     | 
    
         
            +
                    ticket_manager = TicketManager()
         
     | 
| 
      
 45 
     | 
    
         
            +
                    tickets = ticket_manager.list_recent_tickets(limit=args.limit)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    
         
     | 
| 
      
 47 
     | 
    
         
            +
                    if not tickets:
         
     | 
| 
      
 48 
     | 
    
         
            +
                        print("No tickets found")
         
     | 
| 
      
 49 
     | 
    
         
            +
                        return
         
     | 
| 
      
 50 
     | 
    
         
            +
                    
         
     | 
| 
      
 51 
     | 
    
         
            +
                    print(f"Recent tickets (showing {len(tickets)}):")
         
     | 
| 
      
 52 
     | 
    
         
            +
                    print("-" * 80)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    
         
     | 
| 
      
 54 
     | 
    
         
            +
                    for ticket in tickets:
         
     | 
| 
      
 55 
     | 
    
         
            +
                        # Use emoji to indicate status visually
         
     | 
| 
      
 56 
     | 
    
         
            +
                        status_emoji = {
         
     | 
| 
      
 57 
     | 
    
         
            +
                            "open": "🔵",
         
     | 
| 
      
 58 
     | 
    
         
            +
                            "in_progress": "🟡",
         
     | 
| 
      
 59 
     | 
    
         
            +
                            "done": "🟢",
         
     | 
| 
      
 60 
     | 
    
         
            +
                            "closed": "⚫"
         
     | 
| 
      
 61 
     | 
    
         
            +
                        }.get(ticket['status'], "⚪")
         
     | 
| 
      
 62 
     | 
    
         
            +
                        
         
     | 
| 
      
 63 
     | 
    
         
            +
                        print(f"{status_emoji} [{ticket['id']}] {ticket['title']}")
         
     | 
| 
      
 64 
     | 
    
         
            +
                        print(f"   Priority: {ticket['priority']} | Tags: {', '.join(ticket['tags'])}")
         
     | 
| 
      
 65 
     | 
    
         
            +
                        print(f"   Created: {ticket['created_at']}")
         
     | 
| 
      
 66 
     | 
    
         
            +
                        print()
         
     | 
| 
      
 67 
     | 
    
         
            +
                        
         
     | 
| 
      
 68 
     | 
    
         
            +
                except Exception as e:
         
     | 
| 
      
 69 
     | 
    
         
            +
                    logger.error(f"Error listing tickets: {e}")
         
     | 
| 
      
 70 
     | 
    
         
            +
                    print(f"Error: {e}")
         
     | 
| 
         @@ -0,0 +1,79 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            """
         
     | 
| 
      
 2 
     | 
    
         
            +
            UI command implementation for claude-mpm.
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            WHY: This module provides terminal UI functionality for users who prefer a
         
     | 
| 
      
 5 
     | 
    
         
            +
            visual interface over command-line interaction.
         
     | 
| 
      
 6 
     | 
    
         
            +
            """
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            from claude_mpm.utils.imports import safe_import
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            # Import logger using safe_import pattern
         
     | 
| 
      
 11 
     | 
    
         
            +
            get_logger = safe_import('claude_mpm.core.logger', None, ['get_logger'])
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            def run_terminal_ui(args):
         
     | 
| 
      
 15 
     | 
    
         
            +
                """
         
     | 
| 
      
 16 
     | 
    
         
            +
                Run the terminal UI.
         
     | 
| 
      
 17 
     | 
    
         
            +
                
         
     | 
| 
      
 18 
     | 
    
         
            +
                WHY: Some users prefer a visual interface with multiple panes showing different
         
     | 
| 
      
 19 
     | 
    
         
            +
                aspects of the system. This command launches either a rich terminal UI or a
         
     | 
| 
      
 20 
     | 
    
         
            +
                basic curses UI depending on availability and user preference.
         
     | 
| 
      
 21 
     | 
    
         
            +
                
         
     | 
| 
      
 22 
     | 
    
         
            +
                DESIGN DECISION: We try the rich UI first as it provides a better experience,
         
     | 
| 
      
 23 
     | 
    
         
            +
                but fall back to curses if rich is not available. This ensures the UI works
         
     | 
| 
      
 24 
     | 
    
         
            +
                on all systems.
         
     | 
| 
      
 25 
     | 
    
         
            +
                
         
     | 
| 
      
 26 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 27 
     | 
    
         
            +
                    args: Parsed command line arguments with optional 'mode' attribute
         
     | 
| 
      
 28 
     | 
    
         
            +
                """
         
     | 
| 
      
 29 
     | 
    
         
            +
                logger = get_logger("cli")
         
     | 
| 
      
 30 
     | 
    
         
            +
                
         
     | 
| 
      
 31 
     | 
    
         
            +
                ui_mode = getattr(args, 'mode', 'terminal')
         
     | 
| 
      
 32 
     | 
    
         
            +
                
         
     | 
| 
      
 33 
     | 
    
         
            +
                try:
         
     | 
| 
      
 34 
     | 
    
         
            +
                    if ui_mode == 'terminal':
         
     | 
| 
      
 35 
     | 
    
         
            +
                        # Try rich UI first using safe_import
         
     | 
| 
      
 36 
     | 
    
         
            +
                        run_rich_ui = safe_import(
         
     | 
| 
      
 37 
     | 
    
         
            +
                            '...ui.rich_terminal_ui',
         
     | 
| 
      
 38 
     | 
    
         
            +
                            'claude_mpm.ui.rich_terminal_ui',
         
     | 
| 
      
 39 
     | 
    
         
            +
                            ['main']
         
     | 
| 
      
 40 
     | 
    
         
            +
                        )
         
     | 
| 
      
 41 
     | 
    
         
            +
                        
         
     | 
| 
      
 42 
     | 
    
         
            +
                        if run_rich_ui:
         
     | 
| 
      
 43 
     | 
    
         
            +
                            logger.info("Starting rich terminal UI...")
         
     | 
| 
      
 44 
     | 
    
         
            +
                            run_rich_ui()
         
     | 
| 
      
 45 
     | 
    
         
            +
                        else:
         
     | 
| 
      
 46 
     | 
    
         
            +
                            # Fallback to curses UI
         
     | 
| 
      
 47 
     | 
    
         
            +
                            logger.info("Rich not available, falling back to curses UI...")
         
     | 
| 
      
 48 
     | 
    
         
            +
                            TerminalUI = safe_import(
         
     | 
| 
      
 49 
     | 
    
         
            +
                                '...ui.terminal_ui',
         
     | 
| 
      
 50 
     | 
    
         
            +
                                'claude_mpm.ui.terminal_ui',
         
     | 
| 
      
 51 
     | 
    
         
            +
                                ['TerminalUI']
         
     | 
| 
      
 52 
     | 
    
         
            +
                            )
         
     | 
| 
      
 53 
     | 
    
         
            +
                            if TerminalUI:
         
     | 
| 
      
 54 
     | 
    
         
            +
                                ui = TerminalUI()
         
     | 
| 
      
 55 
     | 
    
         
            +
                                ui.run()
         
     | 
| 
      
 56 
     | 
    
         
            +
                            else:
         
     | 
| 
      
 57 
     | 
    
         
            +
                                logger.error("UI module not found")
         
     | 
| 
      
 58 
     | 
    
         
            +
                                print("Error: Terminal UI requires 'curses' (built-in) or 'rich' (pip install rich)")
         
     | 
| 
      
 59 
     | 
    
         
            +
                                return 1
         
     | 
| 
      
 60 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 61 
     | 
    
         
            +
                        # Use curses UI explicitly
         
     | 
| 
      
 62 
     | 
    
         
            +
                        TerminalUI = safe_import(
         
     | 
| 
      
 63 
     | 
    
         
            +
                            '...ui.terminal_ui',
         
     | 
| 
      
 64 
     | 
    
         
            +
                            'claude_mpm.ui.terminal_ui',
         
     | 
| 
      
 65 
     | 
    
         
            +
                            ['TerminalUI']
         
     | 
| 
      
 66 
     | 
    
         
            +
                        )
         
     | 
| 
      
 67 
     | 
    
         
            +
                        if TerminalUI:
         
     | 
| 
      
 68 
     | 
    
         
            +
                            ui = TerminalUI()
         
     | 
| 
      
 69 
     | 
    
         
            +
                            ui.run()
         
     | 
| 
      
 70 
     | 
    
         
            +
                        else:
         
     | 
| 
      
 71 
     | 
    
         
            +
                            logger.error("UI module not found")
         
     | 
| 
      
 72 
     | 
    
         
            +
                            print("Error: Terminal UI not available")
         
     | 
| 
      
 73 
     | 
    
         
            +
                            return 1
         
     | 
| 
      
 74 
     | 
    
         
            +
                except Exception as e:
         
     | 
| 
      
 75 
     | 
    
         
            +
                    logger.error(f"Error running terminal UI: {e}")
         
     | 
| 
      
 76 
     | 
    
         
            +
                    print(f"Error: {e}")
         
     | 
| 
      
 77 
     | 
    
         
            +
                    return 1
         
     | 
| 
      
 78 
     | 
    
         
            +
                
         
     | 
| 
      
 79 
     | 
    
         
            +
                return 0
         
     | 
    
        claude_mpm/cli/parser.py
    ADDED
    
    | 
         @@ -0,0 +1,337 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            """
         
     | 
| 
      
 2 
     | 
    
         
            +
            Argument parser for claude-mpm CLI.
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            WHY: This module centralizes all argument parsing logic to avoid duplication and provide
         
     | 
| 
      
 5 
     | 
    
         
            +
            a single source of truth for CLI arguments. It uses inheritance to share common arguments
         
     | 
| 
      
 6 
     | 
    
         
            +
            across commands while keeping command-specific args organized.
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            DESIGN DECISION: We use a base parser factory pattern to create parsers with common
         
     | 
| 
      
 9 
     | 
    
         
            +
            arguments, then extend them for specific commands. This reduces duplication while
         
     | 
| 
      
 10 
     | 
    
         
            +
            maintaining flexibility.
         
     | 
| 
      
 11 
     | 
    
         
            +
            """
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            import argparse
         
     | 
| 
      
 14 
     | 
    
         
            +
            from pathlib import Path
         
     | 
| 
      
 15 
     | 
    
         
            +
            from typing import Optional, List
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            from ..constants import CLICommands, CLIPrefix, AgentCommands, LogLevel
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            def add_common_arguments(parser: argparse.ArgumentParser, version: str = None) -> None:
         
     | 
| 
      
 21 
     | 
    
         
            +
                """
         
     | 
| 
      
 22 
     | 
    
         
            +
                Add common arguments that apply to all commands.
         
     | 
| 
      
 23 
     | 
    
         
            +
                
         
     | 
| 
      
 24 
     | 
    
         
            +
                WHY: These arguments are needed across multiple commands, so we centralize them
         
     | 
| 
      
 25 
     | 
    
         
            +
                to ensure consistency and avoid duplication.
         
     | 
| 
      
 26 
     | 
    
         
            +
                
         
     | 
| 
      
 27 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 28 
     | 
    
         
            +
                    parser: The argument parser to add arguments to
         
     | 
| 
      
 29 
     | 
    
         
            +
                    version: Version string to display (only needed for main parser)
         
     | 
| 
      
 30 
     | 
    
         
            +
                """
         
     | 
| 
      
 31 
     | 
    
         
            +
                # Version - only add to main parser, not subparsers
         
     | 
| 
      
 32 
     | 
    
         
            +
                if version is not None:
         
     | 
| 
      
 33 
     | 
    
         
            +
                    parser.add_argument(
         
     | 
| 
      
 34 
     | 
    
         
            +
                        "--version",
         
     | 
| 
      
 35 
     | 
    
         
            +
                        action="version",
         
     | 
| 
      
 36 
     | 
    
         
            +
                        version=f"%(prog)s {version}"
         
     | 
| 
      
 37 
     | 
    
         
            +
                    )
         
     | 
| 
      
 38 
     | 
    
         
            +
                
         
     | 
| 
      
 39 
     | 
    
         
            +
                # Logging arguments
         
     | 
| 
      
 40 
     | 
    
         
            +
                logging_group = parser.add_argument_group('logging options')
         
     | 
| 
      
 41 
     | 
    
         
            +
                logging_group.add_argument(
         
     | 
| 
      
 42 
     | 
    
         
            +
                    "-d", "--debug",
         
     | 
| 
      
 43 
     | 
    
         
            +
                    action="store_true",
         
     | 
| 
      
 44 
     | 
    
         
            +
                    help="Enable debug logging (deprecated, use --logging DEBUG)"
         
     | 
| 
      
 45 
     | 
    
         
            +
                )
         
     | 
| 
      
 46 
     | 
    
         
            +
                logging_group.add_argument(
         
     | 
| 
      
 47 
     | 
    
         
            +
                    "--logging",
         
     | 
| 
      
 48 
     | 
    
         
            +
                    choices=[level.value for level in LogLevel],
         
     | 
| 
      
 49 
     | 
    
         
            +
                    default=LogLevel.INFO.value,
         
     | 
| 
      
 50 
     | 
    
         
            +
                    help="Logging level (default: INFO)"
         
     | 
| 
      
 51 
     | 
    
         
            +
                )
         
     | 
| 
      
 52 
     | 
    
         
            +
                logging_group.add_argument(
         
     | 
| 
      
 53 
     | 
    
         
            +
                    "--log-dir",
         
     | 
| 
      
 54 
     | 
    
         
            +
                    type=Path,
         
     | 
| 
      
 55 
     | 
    
         
            +
                    help="Custom log directory (default: ~/.claude-mpm/logs)"
         
     | 
| 
      
 56 
     | 
    
         
            +
                )
         
     | 
| 
      
 57 
     | 
    
         
            +
                
         
     | 
| 
      
 58 
     | 
    
         
            +
                # Framework configuration
         
     | 
| 
      
 59 
     | 
    
         
            +
                framework_group = parser.add_argument_group('framework options')
         
     | 
| 
      
 60 
     | 
    
         
            +
                framework_group.add_argument(
         
     | 
| 
      
 61 
     | 
    
         
            +
                    "--framework-path",
         
     | 
| 
      
 62 
     | 
    
         
            +
                    type=Path,
         
     | 
| 
      
 63 
     | 
    
         
            +
                    help="Path to claude-mpm framework"
         
     | 
| 
      
 64 
     | 
    
         
            +
                )
         
     | 
| 
      
 65 
     | 
    
         
            +
                framework_group.add_argument(
         
     | 
| 
      
 66 
     | 
    
         
            +
                    "--agents-dir",
         
     | 
| 
      
 67 
     | 
    
         
            +
                    type=Path,
         
     | 
| 
      
 68 
     | 
    
         
            +
                    help="Custom agents directory to use"
         
     | 
| 
      
 69 
     | 
    
         
            +
                )
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
            def add_run_arguments(parser: argparse.ArgumentParser) -> None:
         
     | 
| 
      
 73 
     | 
    
         
            +
                """
         
     | 
| 
      
 74 
     | 
    
         
            +
                Add arguments specific to the run command.
         
     | 
| 
      
 75 
     | 
    
         
            +
                
         
     | 
| 
      
 76 
     | 
    
         
            +
                WHY: The run command has specific arguments for controlling how Claude sessions
         
     | 
| 
      
 77 
     | 
    
         
            +
                are executed, including hook management, ticket creation, and interaction modes.
         
     | 
| 
      
 78 
     | 
    
         
            +
                
         
     | 
| 
      
 79 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 80 
     | 
    
         
            +
                    parser: The argument parser to add arguments to
         
     | 
| 
      
 81 
     | 
    
         
            +
                """
         
     | 
| 
      
 82 
     | 
    
         
            +
                run_group = parser.add_argument_group('run options')
         
     | 
| 
      
 83 
     | 
    
         
            +
                
         
     | 
| 
      
 84 
     | 
    
         
            +
                run_group.add_argument(
         
     | 
| 
      
 85 
     | 
    
         
            +
                    "--no-hooks",
         
     | 
| 
      
 86 
     | 
    
         
            +
                    action="store_true",
         
     | 
| 
      
 87 
     | 
    
         
            +
                    help="Disable hook service (runs without hooks)"
         
     | 
| 
      
 88 
     | 
    
         
            +
                )
         
     | 
| 
      
 89 
     | 
    
         
            +
                run_group.add_argument(
         
     | 
| 
      
 90 
     | 
    
         
            +
                    "--no-tickets",
         
     | 
| 
      
 91 
     | 
    
         
            +
                    action="store_true",
         
     | 
| 
      
 92 
     | 
    
         
            +
                    help="Disable automatic ticket creation"
         
     | 
| 
      
 93 
     | 
    
         
            +
                )
         
     | 
| 
      
 94 
     | 
    
         
            +
                run_group.add_argument(
         
     | 
| 
      
 95 
     | 
    
         
            +
                    "--intercept-commands",
         
     | 
| 
      
 96 
     | 
    
         
            +
                    action="store_true",
         
     | 
| 
      
 97 
     | 
    
         
            +
                    help="Enable command interception in interactive mode (intercepts /mpm: commands)"
         
     | 
| 
      
 98 
     | 
    
         
            +
                )
         
     | 
| 
      
 99 
     | 
    
         
            +
                run_group.add_argument(
         
     | 
| 
      
 100 
     | 
    
         
            +
                    "--no-native-agents",
         
     | 
| 
      
 101 
     | 
    
         
            +
                    action="store_true",
         
     | 
| 
      
 102 
     | 
    
         
            +
                    help="Disable deployment of Claude Code native agents"
         
     | 
| 
      
 103 
     | 
    
         
            +
                )
         
     | 
| 
      
 104 
     | 
    
         
            +
                
         
     | 
| 
      
 105 
     | 
    
         
            +
                # Input/output options
         
     | 
| 
      
 106 
     | 
    
         
            +
                io_group = parser.add_argument_group('input/output options')
         
     | 
| 
      
 107 
     | 
    
         
            +
                io_group.add_argument(
         
     | 
| 
      
 108 
     | 
    
         
            +
                    "-i", "--input",
         
     | 
| 
      
 109 
     | 
    
         
            +
                    type=str,
         
     | 
| 
      
 110 
     | 
    
         
            +
                    help="Input text or file path (for non-interactive mode)"
         
     | 
| 
      
 111 
     | 
    
         
            +
                )
         
     | 
| 
      
 112 
     | 
    
         
            +
                io_group.add_argument(
         
     | 
| 
      
 113 
     | 
    
         
            +
                    "--non-interactive",
         
     | 
| 
      
 114 
     | 
    
         
            +
                    action="store_true",
         
     | 
| 
      
 115 
     | 
    
         
            +
                    help="Run in non-interactive mode (read from stdin or --input)"
         
     | 
| 
      
 116 
     | 
    
         
            +
                )
         
     | 
| 
      
 117 
     | 
    
         
            +
                
         
     | 
| 
      
 118 
     | 
    
         
            +
                # Claude CLI arguments
         
     | 
| 
      
 119 
     | 
    
         
            +
                parser.add_argument(
         
     | 
| 
      
 120 
     | 
    
         
            +
                    "claude_args",
         
     | 
| 
      
 121 
     | 
    
         
            +
                    nargs=argparse.REMAINDER,
         
     | 
| 
      
 122 
     | 
    
         
            +
                    help="Additional arguments to pass to Claude CLI (use -- before Claude args)"
         
     | 
| 
      
 123 
     | 
    
         
            +
                )
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
            def create_parser(prog_name: str = "claude-mpm", version: str = "0.0.0") -> argparse.ArgumentParser:
         
     | 
| 
      
 127 
     | 
    
         
            +
                """
         
     | 
| 
      
 128 
     | 
    
         
            +
                Create the main argument parser with all subcommands.
         
     | 
| 
      
 129 
     | 
    
         
            +
                
         
     | 
| 
      
 130 
     | 
    
         
            +
                WHY: This factory function creates a complete parser with all commands and their
         
     | 
| 
      
 131 
     | 
    
         
            +
                arguments. It's the single entry point for creating the CLI parser, ensuring
         
     | 
| 
      
 132 
     | 
    
         
            +
                consistency across the application.
         
     | 
| 
      
 133 
     | 
    
         
            +
                
         
     | 
| 
      
 134 
     | 
    
         
            +
                DESIGN DECISION: We use subparsers for commands to provide a clean, git-like
         
     | 
| 
      
 135 
     | 
    
         
            +
                interface while maintaining backward compatibility with the original CLI.
         
     | 
| 
      
 136 
     | 
    
         
            +
                
         
     | 
| 
      
 137 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 138 
     | 
    
         
            +
                    prog_name: The program name to use
         
     | 
| 
      
 139 
     | 
    
         
            +
                    version: The version string to display
         
     | 
| 
      
 140 
     | 
    
         
            +
                    
         
     | 
| 
      
 141 
     | 
    
         
            +
                Returns:
         
     | 
| 
      
 142 
     | 
    
         
            +
                    Configured ArgumentParser instance
         
     | 
| 
      
 143 
     | 
    
         
            +
                """
         
     | 
| 
      
 144 
     | 
    
         
            +
                # Main parser
         
     | 
| 
      
 145 
     | 
    
         
            +
                parser = argparse.ArgumentParser(
         
     | 
| 
      
 146 
     | 
    
         
            +
                    prog=prog_name,
         
     | 
| 
      
 147 
     | 
    
         
            +
                    description=f"Claude Multi-Agent Project Manager v{version} - Orchestrate Claude with agent delegation and ticket tracking",
         
     | 
| 
      
 148 
     | 
    
         
            +
                    epilog="By default, runs an orchestrated Claude session. Use 'claude-mpm' for interactive mode or 'claude-mpm -i \"prompt\"' for non-interactive mode.\n\nTo pass arguments to Claude CLI, use -- separator: claude-mpm run -- --model sonnet --temperature 0.1",
         
     | 
| 
      
 149 
     | 
    
         
            +
                    formatter_class=argparse.RawDescriptionHelpFormatter
         
     | 
| 
      
 150 
     | 
    
         
            +
                )
         
     | 
| 
      
 151 
     | 
    
         
            +
                
         
     | 
| 
      
 152 
     | 
    
         
            +
                # Add common arguments to main parser with version
         
     | 
| 
      
 153 
     | 
    
         
            +
                add_common_arguments(parser, version=version)
         
     | 
| 
      
 154 
     | 
    
         
            +
                
         
     | 
| 
      
 155 
     | 
    
         
            +
                # Add run-specific arguments at top level for default behavior
         
     | 
| 
      
 156 
     | 
    
         
            +
                # WHY: This maintains backward compatibility - users can run `claude-mpm -i "prompt"`
         
     | 
| 
      
 157 
     | 
    
         
            +
                # without specifying the 'run' command
         
     | 
| 
      
 158 
     | 
    
         
            +
                # NOTE: We don't add claude_args here because REMAINDER interferes with subcommands
         
     | 
| 
      
 159 
     | 
    
         
            +
                run_group = parser.add_argument_group('run options (when no command specified)')
         
     | 
| 
      
 160 
     | 
    
         
            +
                
         
     | 
| 
      
 161 
     | 
    
         
            +
                run_group.add_argument(
         
     | 
| 
      
 162 
     | 
    
         
            +
                    "--no-hooks",
         
     | 
| 
      
 163 
     | 
    
         
            +
                    action="store_true",
         
     | 
| 
      
 164 
     | 
    
         
            +
                    help="Disable hook service (runs without hooks)"
         
     | 
| 
      
 165 
     | 
    
         
            +
                )
         
     | 
| 
      
 166 
     | 
    
         
            +
                run_group.add_argument(
         
     | 
| 
      
 167 
     | 
    
         
            +
                    "--no-tickets",
         
     | 
| 
      
 168 
     | 
    
         
            +
                    action="store_true",
         
     | 
| 
      
 169 
     | 
    
         
            +
                    help="Disable automatic ticket creation"
         
     | 
| 
      
 170 
     | 
    
         
            +
                )
         
     | 
| 
      
 171 
     | 
    
         
            +
                run_group.add_argument(
         
     | 
| 
      
 172 
     | 
    
         
            +
                    "--intercept-commands",
         
     | 
| 
      
 173 
     | 
    
         
            +
                    action="store_true",
         
     | 
| 
      
 174 
     | 
    
         
            +
                    help="Enable command interception in interactive mode (intercepts /mpm: commands)"
         
     | 
| 
      
 175 
     | 
    
         
            +
                )
         
     | 
| 
      
 176 
     | 
    
         
            +
                run_group.add_argument(
         
     | 
| 
      
 177 
     | 
    
         
            +
                    "--no-native-agents",
         
     | 
| 
      
 178 
     | 
    
         
            +
                    action="store_true",
         
     | 
| 
      
 179 
     | 
    
         
            +
                    help="Disable deployment of Claude Code native agents"
         
     | 
| 
      
 180 
     | 
    
         
            +
                )
         
     | 
| 
      
 181 
     | 
    
         
            +
                
         
     | 
| 
      
 182 
     | 
    
         
            +
                # Input/output options
         
     | 
| 
      
 183 
     | 
    
         
            +
                io_group = parser.add_argument_group('input/output options (when no command specified)')
         
     | 
| 
      
 184 
     | 
    
         
            +
                io_group.add_argument(
         
     | 
| 
      
 185 
     | 
    
         
            +
                    "-i", "--input",
         
     | 
| 
      
 186 
     | 
    
         
            +
                    type=str,
         
     | 
| 
      
 187 
     | 
    
         
            +
                    help="Input text or file path (for non-interactive mode)"
         
     | 
| 
      
 188 
     | 
    
         
            +
                )
         
     | 
| 
      
 189 
     | 
    
         
            +
                io_group.add_argument(
         
     | 
| 
      
 190 
     | 
    
         
            +
                    "--non-interactive",
         
     | 
| 
      
 191 
     | 
    
         
            +
                    action="store_true",
         
     | 
| 
      
 192 
     | 
    
         
            +
                    help="Run in non-interactive mode (read from stdin or --input)"
         
     | 
| 
      
 193 
     | 
    
         
            +
                )
         
     | 
| 
      
 194 
     | 
    
         
            +
                
         
     | 
| 
      
 195 
     | 
    
         
            +
                # Create subparsers for commands
         
     | 
| 
      
 196 
     | 
    
         
            +
                subparsers = parser.add_subparsers(
         
     | 
| 
      
 197 
     | 
    
         
            +
                    dest="command",
         
     | 
| 
      
 198 
     | 
    
         
            +
                    help="Available commands",
         
     | 
| 
      
 199 
     | 
    
         
            +
                    metavar="COMMAND"
         
     | 
| 
      
 200 
     | 
    
         
            +
                )
         
     | 
| 
      
 201 
     | 
    
         
            +
                
         
     | 
| 
      
 202 
     | 
    
         
            +
                # Run command (explicit)
         
     | 
| 
      
 203 
     | 
    
         
            +
                run_parser = subparsers.add_parser(
         
     | 
| 
      
 204 
     | 
    
         
            +
                    CLICommands.RUN.value,
         
     | 
| 
      
 205 
     | 
    
         
            +
                    help="Run orchestrated Claude session (default)",
         
     | 
| 
      
 206 
     | 
    
         
            +
                    formatter_class=argparse.RawDescriptionHelpFormatter
         
     | 
| 
      
 207 
     | 
    
         
            +
                )
         
     | 
| 
      
 208 
     | 
    
         
            +
                add_common_arguments(run_parser)
         
     | 
| 
      
 209 
     | 
    
         
            +
                add_run_arguments(run_parser)
         
     | 
| 
      
 210 
     | 
    
         
            +
                
         
     | 
| 
      
 211 
     | 
    
         
            +
                # Tickets command
         
     | 
| 
      
 212 
     | 
    
         
            +
                tickets_parser = subparsers.add_parser(
         
     | 
| 
      
 213 
     | 
    
         
            +
                    CLICommands.TICKETS.value,
         
     | 
| 
      
 214 
     | 
    
         
            +
                    help="List recent tickets"
         
     | 
| 
      
 215 
     | 
    
         
            +
                )
         
     | 
| 
      
 216 
     | 
    
         
            +
                add_common_arguments(tickets_parser)
         
     | 
| 
      
 217 
     | 
    
         
            +
                tickets_parser.add_argument(
         
     | 
| 
      
 218 
     | 
    
         
            +
                    "-n", "--limit",
         
     | 
| 
      
 219 
     | 
    
         
            +
                    type=int,
         
     | 
| 
      
 220 
     | 
    
         
            +
                    default=10,
         
     | 
| 
      
 221 
     | 
    
         
            +
                    help="Number of tickets to show"
         
     | 
| 
      
 222 
     | 
    
         
            +
                )
         
     | 
| 
      
 223 
     | 
    
         
            +
                
         
     | 
| 
      
 224 
     | 
    
         
            +
                # Info command
         
     | 
| 
      
 225 
     | 
    
         
            +
                info_parser = subparsers.add_parser(
         
     | 
| 
      
 226 
     | 
    
         
            +
                    CLICommands.INFO.value,
         
     | 
| 
      
 227 
     | 
    
         
            +
                    help="Show framework and configuration info"
         
     | 
| 
      
 228 
     | 
    
         
            +
                )
         
     | 
| 
      
 229 
     | 
    
         
            +
                add_common_arguments(info_parser)
         
     | 
| 
      
 230 
     | 
    
         
            +
                
         
     | 
| 
      
 231 
     | 
    
         
            +
                # UI command
         
     | 
| 
      
 232 
     | 
    
         
            +
                ui_parser = subparsers.add_parser(
         
     | 
| 
      
 233 
     | 
    
         
            +
                    CLICommands.UI.value,
         
     | 
| 
      
 234 
     | 
    
         
            +
                    help="Launch terminal UI with multiple panes"
         
     | 
| 
      
 235 
     | 
    
         
            +
                )
         
     | 
| 
      
 236 
     | 
    
         
            +
                add_common_arguments(ui_parser)
         
     | 
| 
      
 237 
     | 
    
         
            +
                ui_parser.add_argument(
         
     | 
| 
      
 238 
     | 
    
         
            +
                    "--mode",
         
     | 
| 
      
 239 
     | 
    
         
            +
                    choices=["terminal", "curses"],
         
     | 
| 
      
 240 
     | 
    
         
            +
                    default="terminal",
         
     | 
| 
      
 241 
     | 
    
         
            +
                    help="UI mode to launch (default: terminal)"
         
     | 
| 
      
 242 
     | 
    
         
            +
                )
         
     | 
| 
      
 243 
     | 
    
         
            +
                
         
     | 
| 
      
 244 
     | 
    
         
            +
                # Agents command with subcommands
         
     | 
| 
      
 245 
     | 
    
         
            +
                agents_parser = subparsers.add_parser(
         
     | 
| 
      
 246 
     | 
    
         
            +
                    CLICommands.AGENTS.value,
         
     | 
| 
      
 247 
     | 
    
         
            +
                    help="Manage Claude Code native agents"
         
     | 
| 
      
 248 
     | 
    
         
            +
                )
         
     | 
| 
      
 249 
     | 
    
         
            +
                add_common_arguments(agents_parser)
         
     | 
| 
      
 250 
     | 
    
         
            +
                
         
     | 
| 
      
 251 
     | 
    
         
            +
                agents_subparsers = agents_parser.add_subparsers(
         
     | 
| 
      
 252 
     | 
    
         
            +
                    dest="agents_command",
         
     | 
| 
      
 253 
     | 
    
         
            +
                    help="Agent commands",
         
     | 
| 
      
 254 
     | 
    
         
            +
                    metavar="SUBCOMMAND"
         
     | 
| 
      
 255 
     | 
    
         
            +
                )
         
     | 
| 
      
 256 
     | 
    
         
            +
                
         
     | 
| 
      
 257 
     | 
    
         
            +
                # List agents
         
     | 
| 
      
 258 
     | 
    
         
            +
                list_agents_parser = agents_subparsers.add_parser(
         
     | 
| 
      
 259 
     | 
    
         
            +
                    AgentCommands.LIST.value,
         
     | 
| 
      
 260 
     | 
    
         
            +
                    help="List available agents"
         
     | 
| 
      
 261 
     | 
    
         
            +
                )
         
     | 
| 
      
 262 
     | 
    
         
            +
                list_agents_parser.add_argument(
         
     | 
| 
      
 263 
     | 
    
         
            +
                    "--system",
         
     | 
| 
      
 264 
     | 
    
         
            +
                    action="store_true",
         
     | 
| 
      
 265 
     | 
    
         
            +
                    help="List system agents"
         
     | 
| 
      
 266 
     | 
    
         
            +
                )
         
     | 
| 
      
 267 
     | 
    
         
            +
                list_agents_parser.add_argument(
         
     | 
| 
      
 268 
     | 
    
         
            +
                    "--deployed",
         
     | 
| 
      
 269 
     | 
    
         
            +
                    action="store_true", 
         
     | 
| 
      
 270 
     | 
    
         
            +
                    help="List deployed agents"
         
     | 
| 
      
 271 
     | 
    
         
            +
                )
         
     | 
| 
      
 272 
     | 
    
         
            +
                
         
     | 
| 
      
 273 
     | 
    
         
            +
                # Deploy agents
         
     | 
| 
      
 274 
     | 
    
         
            +
                deploy_agents_parser = agents_subparsers.add_parser(
         
     | 
| 
      
 275 
     | 
    
         
            +
                    AgentCommands.DEPLOY.value,
         
     | 
| 
      
 276 
     | 
    
         
            +
                    help="Deploy system agents"
         
     | 
| 
      
 277 
     | 
    
         
            +
                )
         
     | 
| 
      
 278 
     | 
    
         
            +
                deploy_agents_parser.add_argument(
         
     | 
| 
      
 279 
     | 
    
         
            +
                    "--target",
         
     | 
| 
      
 280 
     | 
    
         
            +
                    type=Path,
         
     | 
| 
      
 281 
     | 
    
         
            +
                    help="Target directory (default: .claude/agents/)"
         
     | 
| 
      
 282 
     | 
    
         
            +
                )
         
     | 
| 
      
 283 
     | 
    
         
            +
                
         
     | 
| 
      
 284 
     | 
    
         
            +
                # Force deploy agents
         
     | 
| 
      
 285 
     | 
    
         
            +
                force_deploy_parser = agents_subparsers.add_parser(
         
     | 
| 
      
 286 
     | 
    
         
            +
                    AgentCommands.FORCE_DEPLOY.value,
         
     | 
| 
      
 287 
     | 
    
         
            +
                    help="Force deploy all system agents"
         
     | 
| 
      
 288 
     | 
    
         
            +
                )
         
     | 
| 
      
 289 
     | 
    
         
            +
                force_deploy_parser.add_argument(
         
     | 
| 
      
 290 
     | 
    
         
            +
                    "--target",
         
     | 
| 
      
 291 
     | 
    
         
            +
                    type=Path,
         
     | 
| 
      
 292 
     | 
    
         
            +
                    help="Target directory (default: .claude/agents/)"
         
     | 
| 
      
 293 
     | 
    
         
            +
                )
         
     | 
| 
      
 294 
     | 
    
         
            +
                
         
     | 
| 
      
 295 
     | 
    
         
            +
                # Clean agents
         
     | 
| 
      
 296 
     | 
    
         
            +
                clean_agents_parser = agents_subparsers.add_parser(
         
     | 
| 
      
 297 
     | 
    
         
            +
                    AgentCommands.CLEAN.value,
         
     | 
| 
      
 298 
     | 
    
         
            +
                    help="Remove deployed system agents"
         
     | 
| 
      
 299 
     | 
    
         
            +
                )
         
     | 
| 
      
 300 
     | 
    
         
            +
                clean_agents_parser.add_argument(
         
     | 
| 
      
 301 
     | 
    
         
            +
                    "--target",
         
     | 
| 
      
 302 
     | 
    
         
            +
                    type=Path,
         
     | 
| 
      
 303 
     | 
    
         
            +
                    help="Target directory (default: .claude/)"
         
     | 
| 
      
 304 
     | 
    
         
            +
                )
         
     | 
| 
      
 305 
     | 
    
         
            +
                
         
     | 
| 
      
 306 
     | 
    
         
            +
                return parser
         
     | 
| 
      
 307 
     | 
    
         
            +
             
     | 
| 
      
 308 
     | 
    
         
            +
             
     | 
| 
      
 309 
     | 
    
         
            +
            def preprocess_args(argv: Optional[List[str]] = None) -> List[str]:
         
     | 
| 
      
 310 
     | 
    
         
            +
                """
         
     | 
| 
      
 311 
     | 
    
         
            +
                Preprocess arguments to handle --mpm: prefix commands.
         
     | 
| 
      
 312 
     | 
    
         
            +
                
         
     | 
| 
      
 313 
     | 
    
         
            +
                WHY: We support both --mpm:command and regular command syntax for flexibility
         
     | 
| 
      
 314 
     | 
    
         
            +
                and backward compatibility. This function normalizes the input.
         
     | 
| 
      
 315 
     | 
    
         
            +
                
         
     | 
| 
      
 316 
     | 
    
         
            +
                Args:
         
     | 
| 
      
 317 
     | 
    
         
            +
                    argv: List of command line arguments, or None to use sys.argv[1:]
         
     | 
| 
      
 318 
     | 
    
         
            +
                    
         
     | 
| 
      
 319 
     | 
    
         
            +
                Returns:
         
     | 
| 
      
 320 
     | 
    
         
            +
                    Processed list of arguments with prefixes removed
         
     | 
| 
      
 321 
     | 
    
         
            +
                """
         
     | 
| 
      
 322 
     | 
    
         
            +
                import sys
         
     | 
| 
      
 323 
     | 
    
         
            +
                
         
     | 
| 
      
 324 
     | 
    
         
            +
                if argv is None:
         
     | 
| 
      
 325 
     | 
    
         
            +
                    argv = sys.argv[1:]
         
     | 
| 
      
 326 
     | 
    
         
            +
                
         
     | 
| 
      
 327 
     | 
    
         
            +
                # Convert --mpm:command to command for argparse compatibility
         
     | 
| 
      
 328 
     | 
    
         
            +
                processed_args = []
         
     | 
| 
      
 329 
     | 
    
         
            +
                for arg in argv:
         
     | 
| 
      
 330 
     | 
    
         
            +
                    if arg.startswith(CLIPrefix.MPM.value):
         
     | 
| 
      
 331 
     | 
    
         
            +
                        # Extract command after prefix
         
     | 
| 
      
 332 
     | 
    
         
            +
                        command = arg[len(CLIPrefix.MPM.value):]
         
     | 
| 
      
 333 
     | 
    
         
            +
                        processed_args.append(command)
         
     | 
| 
      
 334 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 335 
     | 
    
         
            +
                        processed_args.append(arg)
         
     | 
| 
      
 336 
     | 
    
         
            +
                
         
     | 
| 
      
 337 
     | 
    
         
            +
                return processed_args
         
     |