claude-mpm 0.3.0__py3-none-any.whl → 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/_version.py +3 -2
 - claude_mpm/agents/INSTRUCTIONS.md +23 -0
 - claude_mpm/agents/__init__.py +2 -2
 - claude_mpm/agents/agent-template.yaml +83 -0
 - claude_mpm/agents/agent_loader.py +66 -90
 - claude_mpm/agents/base_agent_loader.py +10 -15
 - claude_mpm/cli.py +41 -47
 - claude_mpm/cli_enhancements.py +297 -0
 - claude_mpm/core/agent_name_normalizer.py +49 -0
 - claude_mpm/core/factories.py +1 -46
 - claude_mpm/core/service_registry.py +0 -8
 - claude_mpm/core/simple_runner.py +50 -0
 - claude_mpm/generators/__init__.py +5 -0
 - claude_mpm/generators/agent_profile_generator.py +137 -0
 - claude_mpm/hooks/README.md +75 -221
 - claude_mpm/hooks/builtin/mpm_command_hook.py +125 -0
 - claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +8 -7
 - claude_mpm/hooks/claude_hooks/__init__.py +5 -0
 - claude_mpm/hooks/claude_hooks/hook_handler.py +399 -0
 - claude_mpm/hooks/claude_hooks/hook_wrapper.sh +47 -0
 - claude_mpm/hooks/validation_hooks.py +181 -0
 - claude_mpm/services/agent_management_service.py +4 -4
 - claude_mpm/services/agent_profile_loader.py +1 -1
 - claude_mpm/services/agent_registry.py +0 -1
 - claude_mpm/services/base_agent_manager.py +3 -3
 - claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +57 -31
 - claude_mpm/utils/error_handler.py +247 -0
 - claude_mpm/validation/__init__.py +5 -0
 - claude_mpm/validation/agent_validator.py +175 -0
 - {claude_mpm-0.3.0.dist-info → claude_mpm-1.1.0.dist-info}/METADATA +44 -7
 - {claude_mpm-0.3.0.dist-info → claude_mpm-1.1.0.dist-info}/RECORD +34 -30
 - claude_mpm/config/hook_config.py +0 -42
 - claude_mpm/hooks/hook_client.py +0 -264
 - claude_mpm/hooks/hook_runner.py +0 -370
 - claude_mpm/hooks/json_rpc_executor.py +0 -259
 - claude_mpm/hooks/json_rpc_hook_client.py +0 -319
 - claude_mpm/services/hook_service.py +0 -388
 - claude_mpm/services/hook_service_manager.py +0 -223
 - claude_mpm/services/json_rpc_hook_manager.py +0 -92
 - {claude_mpm-0.3.0.dist-info → claude_mpm-1.1.0.dist-info}/WHEEL +0 -0
 - {claude_mpm-0.3.0.dist-info → claude_mpm-1.1.0.dist-info}/entry_points.txt +0 -0
 - {claude_mpm-0.3.0.dist-info → claude_mpm-1.1.0.dist-info}/top_level.txt +0 -0
 
    
        claude_mpm/_version.py
    CHANGED
    
    | 
         @@ -6,10 +6,10 @@ try: 
     | 
|
| 
       6 
6 
     | 
    
         
             
                try:
         
     | 
| 
       7 
7 
     | 
    
         
             
                    __version__ = version("claude-mpm")
         
     | 
| 
       8 
8 
     | 
    
         
             
                except PackageNotFoundError:
         
     | 
| 
       9 
     | 
    
         
            -
                    __version__ = "1. 
     | 
| 
      
 9 
     | 
    
         
            +
                    __version__ = "1.1.0"
         
     | 
| 
       10 
10 
     | 
    
         
             
            except ImportError:
         
     | 
| 
       11 
11 
     | 
    
         
             
                # Fallback for older Python versions
         
     | 
| 
       12 
     | 
    
         
            -
                __version__ = "1. 
     | 
| 
      
 12 
     | 
    
         
            +
                __version__ = "1.1.0"
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
       14 
14 
     | 
    
         
             
            # This file may be overwritten by setuptools-scm during build
         
     | 
| 
       15 
15 
     | 
    
         
             
            # The try/except ensures we always have a version available
         
     | 
| 
         @@ -25,6 +25,7 @@ def get_version_tuple(): 
     | 
|
| 
       25 
25 
     | 
    
         
             
            __version_info__ = get_version_tuple()
         
     | 
| 
       26 
26 
     | 
    
         | 
| 
       27 
27 
     | 
    
         
             
            # Version history
         
     | 
| 
      
 28 
     | 
    
         
            +
            # 1.1.0 - BREAKING: Removed JSON-RPC hooks, enhanced Claude Code hooks with project-specific logging
         
     | 
| 
       28 
29 
     | 
    
         
             
            # 1.0.0 - BREAKING: Architecture simplification, TodoWrite hooks, enhanced CLI, terminal UI
         
     | 
| 
       29 
30 
     | 
    
         
             
            # 0.5.0 - Comprehensive deployment support for PyPI, npm, and local installation
         
     | 
| 
       30 
31 
     | 
    
         
             
            # 0.3.0 - Added hook service architecture for context filtering and ticket automation
         
     | 
| 
         @@ -156,6 +156,29 @@ Context: 
     | 
|
| 
       156 
156 
     | 
    
         
             
            4. **Workload Balancing**: Distribute tasks across agents when possible
         
     | 
| 
       157 
157 
     | 
    
         
             
            5. **Specialization Depth**: Prefer specialist over generalist for complex tasks
         
     | 
| 
       158 
158 
     | 
    
         | 
| 
      
 159 
     | 
    
         
            +
            ### Task Tool Agent Name Format
         
     | 
| 
      
 160 
     | 
    
         
            +
            The Task tool accepts agent names in **both** formats for flexibility:
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
            **TodoWrite Format (Capitalized)** → **Task Tool Format (lowercase-hyphenated)**
         
     | 
| 
      
 163 
     | 
    
         
            +
            - `"Research"` → `"research"`
         
     | 
| 
      
 164 
     | 
    
         
            +
            - `"Engineer"` → `"engineer"`
         
     | 
| 
      
 165 
     | 
    
         
            +
            - `"QA"` → `"qa"`
         
     | 
| 
      
 166 
     | 
    
         
            +
            - `"Documentation"` → `"documentation"`
         
     | 
| 
      
 167 
     | 
    
         
            +
            - `"Security"` → `"security"`
         
     | 
| 
      
 168 
     | 
    
         
            +
            - `"Ops"` → `"ops"`
         
     | 
| 
      
 169 
     | 
    
         
            +
            - `"Version Control"` → `"version-control"`
         
     | 
| 
      
 170 
     | 
    
         
            +
            - `"Data Engineer"` → `"data-engineer"`
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 172 
     | 
    
         
            +
            **Both formats are valid** in the Task tool:
         
     | 
| 
      
 173 
     | 
    
         
            +
            - ✅ `Task(description="Analyze patterns", subagent_type="Research")` 
         
     | 
| 
      
 174 
     | 
    
         
            +
            - ✅ `Task(description="Analyze patterns", subagent_type="research")`
         
     | 
| 
      
 175 
     | 
    
         
            +
            - ✅ `Task(description="Update docs", subagent_type="Documentation")`
         
     | 
| 
      
 176 
     | 
    
         
            +
            - ✅ `Task(description="Update docs", subagent_type="documentation")`
         
     | 
| 
      
 177 
     | 
    
         
            +
            - ✅ `Task(description="Git operations", subagent_type="Version Control")`
         
     | 
| 
      
 178 
     | 
    
         
            +
            - ✅ `Task(description="Git operations", subagent_type="version-control")`
         
     | 
| 
      
 179 
     | 
    
         
            +
             
     | 
| 
      
 180 
     | 
    
         
            +
            **Agent Name Normalization**: When you receive a Task tool call with capitalized format (matching TodoWrite prefixes), automatically normalize it to the lowercase-hyphenated format internally for delegation.
         
     | 
| 
      
 181 
     | 
    
         
            +
             
     | 
| 
       159 
182 
     | 
    
         
             
            ## Advanced Verification & Error Handling
         
     | 
| 
       160 
183 
     | 
    
         | 
| 
       161 
184 
     | 
    
         
             
            ### Output Quality Gates
         
     | 
    
        claude_mpm/agents/__init__.py
    CHANGED
    
    | 
         @@ -4,8 +4,8 @@ Claude PM Framework Agents Package 
     | 
|
| 
       4 
4 
     | 
    
         
             
            System-level agent implementations with Task Tool integration.
         
     | 
| 
       5 
5 
     | 
    
         
             
            These agents provide specialized prompts and capabilities for PM orchestration.
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
     | 
    
         
            -
            Uses unified agent loader to load prompts from  
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
      
 7 
     | 
    
         
            +
            Uses unified agent loader to load prompts from JSON templates in agents/templates/
         
     | 
| 
      
 8 
     | 
    
         
            +
            for better structure and maintainability.
         
     | 
| 
       9 
9 
     | 
    
         
             
            """
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
            # Import from unified agent loader
         
     | 
| 
         @@ -0,0 +1,83 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Agent Profile Template Configuration
         
     | 
| 
      
 2 
     | 
    
         
            +
            # Inspired by awesome-claude-code's template system
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            profile:
         
     | 
| 
      
 5 
     | 
    
         
            +
              name: "{{AGENT_NAME}}"
         
     | 
| 
      
 6 
     | 
    
         
            +
              version: "{{VERSION}}"
         
     | 
| 
      
 7 
     | 
    
         
            +
              description: "{{DESCRIPTION}}"
         
     | 
| 
      
 8 
     | 
    
         
            +
              author: "{{AUTHOR}}"
         
     | 
| 
      
 9 
     | 
    
         
            +
              created: "{{CREATED_DATE}}"
         
     | 
| 
      
 10 
     | 
    
         
            +
              
         
     | 
| 
      
 11 
     | 
    
         
            +
              # Categories for organizing agents
         
     | 
| 
      
 12 
     | 
    
         
            +
              categories:
         
     | 
| 
      
 13 
     | 
    
         
            +
                - id: analysis
         
     | 
| 
      
 14 
     | 
    
         
            +
                  title: "Code Analysis Agents"
         
     | 
| 
      
 15 
     | 
    
         
            +
                  description: |
         
     | 
| 
      
 16 
     | 
    
         
            +
                    > Agents specialized in analyzing codebases, identifying patterns, and providing insights
         
     | 
| 
      
 17 
     | 
    
         
            +
                  icon: "🔍"
         
     | 
| 
      
 18 
     | 
    
         
            +
                
         
     | 
| 
      
 19 
     | 
    
         
            +
                - id: implementation
         
     | 
| 
      
 20 
     | 
    
         
            +
                  title: "Implementation Agents"
         
     | 
| 
      
 21 
     | 
    
         
            +
                  description: |
         
     | 
| 
      
 22 
     | 
    
         
            +
                    > Agents focused on writing and modifying code
         
     | 
| 
      
 23 
     | 
    
         
            +
                  icon: "🛠️"
         
     | 
| 
      
 24 
     | 
    
         
            +
                
         
     | 
| 
      
 25 
     | 
    
         
            +
                - id: testing
         
     | 
| 
      
 26 
     | 
    
         
            +
                  title: "Testing & QA Agents"
         
     | 
| 
      
 27 
     | 
    
         
            +
                  description: |
         
     | 
| 
      
 28 
     | 
    
         
            +
                    > Agents dedicated to testing, quality assurance, and validation
         
     | 
| 
      
 29 
     | 
    
         
            +
                  icon: "🧪"
         
     | 
| 
      
 30 
     | 
    
         
            +
                
         
     | 
| 
      
 31 
     | 
    
         
            +
                - id: security
         
     | 
| 
      
 32 
     | 
    
         
            +
                  title: "Security Agents"
         
     | 
| 
      
 33 
     | 
    
         
            +
                  description: |
         
     | 
| 
      
 34 
     | 
    
         
            +
                    > Agents specialized in security analysis and vulnerability detection
         
     | 
| 
      
 35 
     | 
    
         
            +
                  icon: "🔒"
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            agents:
         
     | 
| 
      
 38 
     | 
    
         
            +
              - name: "{{AGENT_ID}}"
         
     | 
| 
      
 39 
     | 
    
         
            +
                category: "{{CATEGORY}}"
         
     | 
| 
      
 40 
     | 
    
         
            +
                role: "{{ROLE}}"
         
     | 
| 
      
 41 
     | 
    
         
            +
                description: "{{AGENT_DESCRIPTION}}"
         
     | 
| 
      
 42 
     | 
    
         
            +
                
         
     | 
| 
      
 43 
     | 
    
         
            +
                capabilities:
         
     | 
| 
      
 44 
     | 
    
         
            +
                  - "{{CAPABILITY_1}}"
         
     | 
| 
      
 45 
     | 
    
         
            +
                  - "{{CAPABILITY_2}}"
         
     | 
| 
      
 46 
     | 
    
         
            +
                  - "{{CAPABILITY_3}}"
         
     | 
| 
      
 47 
     | 
    
         
            +
                
         
     | 
| 
      
 48 
     | 
    
         
            +
                constraints:
         
     | 
| 
      
 49 
     | 
    
         
            +
                  - "{{CONSTRAINT_1}}"
         
     | 
| 
      
 50 
     | 
    
         
            +
                  - "{{CONSTRAINT_2}}"
         
     | 
| 
      
 51 
     | 
    
         
            +
                
         
     | 
| 
      
 52 
     | 
    
         
            +
                tools:
         
     | 
| 
      
 53 
     | 
    
         
            +
                  - name: "{{TOOL_NAME}}"
         
     | 
| 
      
 54 
     | 
    
         
            +
                    description: "{{TOOL_DESCRIPTION}}"
         
     | 
| 
      
 55 
     | 
    
         
            +
                    required: true
         
     | 
| 
      
 56 
     | 
    
         
            +
                
         
     | 
| 
      
 57 
     | 
    
         
            +
                prompt_template: |
         
     | 
| 
      
 58 
     | 
    
         
            +
                  You are a {{ROLE}} agent specializing in {{SPECIALIZATION}}.
         
     | 
| 
      
 59 
     | 
    
         
            +
                  
         
     | 
| 
      
 60 
     | 
    
         
            +
                  ## Your Capabilities:
         
     | 
| 
      
 61 
     | 
    
         
            +
                  {{CAPABILITIES_LIST}}
         
     | 
| 
      
 62 
     | 
    
         
            +
                  
         
     | 
| 
      
 63 
     | 
    
         
            +
                  ## Your Constraints:
         
     | 
| 
      
 64 
     | 
    
         
            +
                  {{CONSTRAINTS_LIST}}
         
     | 
| 
      
 65 
     | 
    
         
            +
                  
         
     | 
| 
      
 66 
     | 
    
         
            +
                  ## Context:
         
     | 
| 
      
 67 
     | 
    
         
            +
                  {context}
         
     | 
| 
      
 68 
     | 
    
         
            +
                  
         
     | 
| 
      
 69 
     | 
    
         
            +
                  ## Task:
         
     | 
| 
      
 70 
     | 
    
         
            +
                  {task}
         
     | 
| 
      
 71 
     | 
    
         
            +
                  
         
     | 
| 
      
 72 
     | 
    
         
            +
                  ## Additional Instructions:
         
     | 
| 
      
 73 
     | 
    
         
            +
                  {{ADDITIONAL_INSTRUCTIONS}}
         
     | 
| 
      
 74 
     | 
    
         
            +
                
         
     | 
| 
      
 75 
     | 
    
         
            +
                examples:
         
     | 
| 
      
 76 
     | 
    
         
            +
                  - scenario: "{{EXAMPLE_SCENARIO}}"
         
     | 
| 
      
 77 
     | 
    
         
            +
                    input: "{{EXAMPLE_INPUT}}"
         
     | 
| 
      
 78 
     | 
    
         
            +
                    expected_output: "{{EXAMPLE_OUTPUT}}"
         
     | 
| 
      
 79 
     | 
    
         
            +
                
         
     | 
| 
      
 80 
     | 
    
         
            +
                best_practices:
         
     | 
| 
      
 81 
     | 
    
         
            +
                  - "{{BEST_PRACTICE_1}}"
         
     | 
| 
      
 82 
     | 
    
         
            +
                  - "{{BEST_PRACTICE_2}}"
         
     | 
| 
      
 83 
     | 
    
         
            +
                  - "{{BEST_PRACTICE_3}}"
         
     | 
| 
         @@ -3,11 +3,11 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            Unified Agent Loader System
         
     | 
| 
       4 
4 
     | 
    
         
             
            ==========================
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
     | 
    
         
            -
            Provides unified loading of agent prompts from  
     | 
| 
      
 6 
     | 
    
         
            +
            Provides unified loading of agent prompts from JSON template files.
         
     | 
| 
       7 
7 
     | 
    
         
             
            Integrates with SharedPromptCache for performance optimization.
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
            Key Features:
         
     | 
| 
       10 
     | 
    
         
            -
            - Loads agent prompts from  
     | 
| 
      
 10 
     | 
    
         
            +
            - Loads agent prompts from src/claude_mpm/agents/templates/*.json files
         
     | 
| 
       11 
11 
     | 
    
         
             
            - Handles base_agent.md prepending
         
     | 
| 
       12 
12 
     | 
    
         
             
            - Provides backward-compatible get_*_agent_prompt() functions
         
     | 
| 
       13 
13 
     | 
    
         
             
            - Uses SharedPromptCache for performance
         
     | 
| 
         @@ -20,7 +20,7 @@ For advanced agent management features (CRUD, versioning, section updates), use: 
     | 
|
| 
       20 
20 
     | 
    
         
             
            Usage:
         
     | 
| 
       21 
21 
     | 
    
         
             
                from claude_pm.agents.agent_loader import get_documentation_agent_prompt
         
     | 
| 
       22 
22 
     | 
    
         | 
| 
       23 
     | 
    
         
            -
                # Get agent prompt from  
     | 
| 
      
 23 
     | 
    
         
            +
                # Get agent prompt from JSON template
         
     | 
| 
       24 
24 
     | 
    
         
             
                prompt = get_documentation_agent_prompt()
         
     | 
| 
       25 
25 
     | 
    
         
             
            """
         
     | 
| 
       26 
26 
     | 
    
         | 
| 
         @@ -49,64 +49,29 @@ class ModelType: 
     | 
|
| 
       49 
49 
     | 
    
         
             
            logger = logging.getLogger(__name__)
         
     | 
| 
       50 
50 
     | 
    
         | 
| 
       51 
51 
     | 
    
         | 
| 
       52 
     | 
    
         
            -
            def  
     | 
| 
       53 
     | 
    
         
            -
                """Get the  
     | 
| 
       54 
     | 
    
         
            -
                #  
     | 
| 
       55 
     | 
    
         
            -
                 
     | 
| 
       56 
     | 
    
         
            -
                    framework_root = PathResolver.get_framework_root()
         
     | 
| 
       57 
     | 
    
         
            -
                    
         
     | 
| 
       58 
     | 
    
         
            -
                    # Check if we're running from a wheel installation
         
     | 
| 
       59 
     | 
    
         
            -
                    try:
         
     | 
| 
       60 
     | 
    
         
            -
                        import claude_pm
         
     | 
| 
       61 
     | 
    
         
            -
                        package_path = Path(claude_pm.__file__).parent
         
     | 
| 
       62 
     | 
    
         
            -
                        path_str = str(package_path.resolve())
         
     | 
| 
       63 
     | 
    
         
            -
                        if 'site-packages' in path_str or 'dist-packages' in path_str:
         
     | 
| 
       64 
     | 
    
         
            -
                            # For wheel installations, check data directory
         
     | 
| 
       65 
     | 
    
         
            -
                            data_agent_roles = package_path / "data" / "framework" / "agent-roles"
         
     | 
| 
       66 
     | 
    
         
            -
                            if data_agent_roles.exists():
         
     | 
| 
       67 
     | 
    
         
            -
                                logger.debug(f"Using wheel installation agent-roles: {data_agent_roles}")
         
     | 
| 
       68 
     | 
    
         
            -
                                return data_agent_roles
         
     | 
| 
       69 
     | 
    
         
            -
                    except Exception:
         
     | 
| 
       70 
     | 
    
         
            -
                        pass
         
     | 
| 
       71 
     | 
    
         
            -
                    
         
     | 
| 
       72 
     | 
    
         
            -
                    # Check framework structure
         
     | 
| 
       73 
     | 
    
         
            -
                    agent_roles_dir = framework_root / "framework" / "agent-roles"
         
     | 
| 
       74 
     | 
    
         
            -
                    if agent_roles_dir.exists():
         
     | 
| 
       75 
     | 
    
         
            -
                        logger.debug(f"Using framework agent-roles: {agent_roles_dir}")
         
     | 
| 
       76 
     | 
    
         
            -
                        return agent_roles_dir
         
     | 
| 
       77 
     | 
    
         
            -
                    
         
     | 
| 
       78 
     | 
    
         
            -
                    # Try agents directory as fallback
         
     | 
| 
       79 
     | 
    
         
            -
                    agents_dir = PathResolver.get_agents_dir()
         
     | 
| 
       80 
     | 
    
         
            -
                    logger.debug(f"Using agents directory: {agents_dir}")
         
     | 
| 
       81 
     | 
    
         
            -
                    return agents_dir
         
     | 
| 
       82 
     | 
    
         
            -
                    
         
     | 
| 
       83 
     | 
    
         
            -
                except FileNotFoundError as e:
         
     | 
| 
       84 
     | 
    
         
            -
                    # Ultimate fallback
         
     | 
| 
       85 
     | 
    
         
            -
                    logger.warning(f"PathResolver could not find framework root: {e}")
         
     | 
| 
       86 
     | 
    
         
            -
                    fallback = Path(__file__).parent.parent.parent / "framework" / "agent-roles"
         
     | 
| 
       87 
     | 
    
         
            -
                    logger.warning(f"Using fallback agent-roles path: {fallback}")
         
     | 
| 
       88 
     | 
    
         
            -
                    return fallback
         
     | 
| 
      
 52 
     | 
    
         
            +
            def _get_agent_templates_dir() -> Path:
         
     | 
| 
      
 53 
     | 
    
         
            +
                """Get the agent templates directory."""
         
     | 
| 
      
 54 
     | 
    
         
            +
                # Agent templates are now in the agents/templates directory
         
     | 
| 
      
 55 
     | 
    
         
            +
                return Path(__file__).parent / "templates"
         
     | 
| 
       89 
56 
     | 
    
         | 
| 
       90 
57 
     | 
    
         | 
| 
       91 
     | 
    
         
            -
            #  
     | 
| 
       92 
     | 
    
         
            -
             
     | 
| 
      
 58 
     | 
    
         
            +
            # Agent templates directory
         
     | 
| 
      
 59 
     | 
    
         
            +
            AGENT_TEMPLATES_DIR = _get_agent_templates_dir()
         
     | 
| 
       93 
60 
     | 
    
         | 
| 
       94 
61 
     | 
    
         
             
            # Cache prefix for agent prompts
         
     | 
| 
       95 
62 
     | 
    
         
             
            AGENT_CACHE_PREFIX = "agent_prompt:"
         
     | 
| 
       96 
63 
     | 
    
         | 
| 
       97 
     | 
    
         
            -
            # Agent name mappings (agent name ->  
     | 
| 
      
 64 
     | 
    
         
            +
            # Agent name mappings (agent name -> JSON file name)
         
     | 
| 
       98 
65 
     | 
    
         
             
            AGENT_MAPPINGS = {
         
     | 
| 
       99 
     | 
    
         
            -
                "documentation": " 
     | 
| 
       100 
     | 
    
         
            -
                "version_control": " 
     | 
| 
       101 
     | 
    
         
            -
                "qa": " 
     | 
| 
       102 
     | 
    
         
            -
                "research": " 
     | 
| 
       103 
     | 
    
         
            -
                "ops": " 
     | 
| 
       104 
     | 
    
         
            -
                "security": " 
     | 
| 
       105 
     | 
    
         
            -
                "engineer": " 
     | 
| 
       106 
     | 
    
         
            -
                "data_engineer": " 
     | 
| 
       107 
     | 
    
         
            -
                 
     | 
| 
       108 
     | 
    
         
            -
                "orchestrator": "pm-orchestrator-agent.md",
         
     | 
| 
       109 
     | 
    
         
            -
                "pm_orchestrator": "pm-orchestrator-agent.md"
         
     | 
| 
      
 66 
     | 
    
         
            +
                "documentation": "documentation_agent.json",
         
     | 
| 
      
 67 
     | 
    
         
            +
                "version_control": "version_control_agent.json",
         
     | 
| 
      
 68 
     | 
    
         
            +
                "qa": "qa_agent.json",
         
     | 
| 
      
 69 
     | 
    
         
            +
                "research": "research_agent.json",
         
     | 
| 
      
 70 
     | 
    
         
            +
                "ops": "ops_agent.json",
         
     | 
| 
      
 71 
     | 
    
         
            +
                "security": "security_agent.json",
         
     | 
| 
      
 72 
     | 
    
         
            +
                "engineer": "engineer_agent.json",
         
     | 
| 
      
 73 
     | 
    
         
            +
                "data_engineer": "data_engineer_agent.json"
         
     | 
| 
      
 74 
     | 
    
         
            +
                # Note: pm, orchestrator, and pm_orchestrator agents are handled separately
         
     | 
| 
       110 
75 
     | 
    
         
             
            }
         
     | 
| 
       111 
76 
     | 
    
         | 
| 
       112 
77 
     | 
    
         
             
            # Model configuration thresholds
         
     | 
| 
         @@ -142,19 +107,19 @@ MODEL_NAME_MAPPINGS = { 
     | 
|
| 
       142 
107 
     | 
    
         | 
| 
       143 
108 
     | 
    
         
             
            def load_agent_prompt_from_md(agent_name: str, force_reload: bool = False) -> Optional[str]:
         
     | 
| 
       144 
109 
     | 
    
         
             
                """
         
     | 
| 
       145 
     | 
    
         
            -
                Load agent prompt from  
     | 
| 
      
 110 
     | 
    
         
            +
                Load agent prompt from JSON template file.
         
     | 
| 
       146 
111 
     | 
    
         | 
| 
       147 
112 
     | 
    
         
             
                Args:
         
     | 
| 
       148 
113 
     | 
    
         
             
                    agent_name: Agent name (e.g., 'documentation', 'ticketing')
         
     | 
| 
       149 
114 
     | 
    
         
             
                    force_reload: Force reload from file, bypassing cache
         
     | 
| 
       150 
115 
     | 
    
         | 
| 
       151 
116 
     | 
    
         
             
                Returns:
         
     | 
| 
       152 
     | 
    
         
            -
                    str: Agent prompt content from  
     | 
| 
      
 117 
     | 
    
         
            +
                    str: Agent prompt content from JSON template, or None if not found
         
     | 
| 
       153 
118 
     | 
    
         
             
                """
         
     | 
| 
       154 
119 
     | 
    
         
             
                try:
         
     | 
| 
       155 
120 
     | 
    
         
             
                    # Get cache instance
         
     | 
| 
       156 
121 
     | 
    
         
             
                    cache = SharedPromptCache.get_instance()
         
     | 
| 
       157 
     | 
    
         
            -
                    cache_key = f"{AGENT_CACHE_PREFIX}{agent_name}: 
     | 
| 
      
 122 
     | 
    
         
            +
                    cache_key = f"{AGENT_CACHE_PREFIX}{agent_name}:json"
         
     | 
| 
       158 
123 
     | 
    
         | 
| 
       159 
124 
     | 
    
         
             
                    # Check cache first (unless force reload)
         
     | 
| 
       160 
125 
     | 
    
         
             
                    if not force_reload:
         
     | 
| 
         @@ -163,23 +128,40 @@ def load_agent_prompt_from_md(agent_name: str, force_reload: bool = False) -> Op 
     | 
|
| 
       163 
128 
     | 
    
         
             
                            logger.debug(f"Agent prompt for '{agent_name}' loaded from cache")
         
     | 
| 
       164 
129 
     | 
    
         
             
                            return str(cached_content)
         
     | 
| 
       165 
130 
     | 
    
         | 
| 
       166 
     | 
    
         
            -
                    # Get  
     | 
| 
       167 
     | 
    
         
            -
                     
     | 
| 
       168 
     | 
    
         
            -
                    if not  
     | 
| 
       169 
     | 
    
         
            -
                        logger.warning(f"No  
     | 
| 
      
 131 
     | 
    
         
            +
                    # Get JSON file path
         
     | 
| 
      
 132 
     | 
    
         
            +
                    json_filename = AGENT_MAPPINGS.get(agent_name)
         
     | 
| 
      
 133 
     | 
    
         
            +
                    if not json_filename:
         
     | 
| 
      
 134 
     | 
    
         
            +
                        logger.warning(f"No JSON file mapping found for agent: {agent_name}")
         
     | 
| 
       170 
135 
     | 
    
         
             
                        return None
         
     | 
| 
       171 
136 
     | 
    
         | 
| 
       172 
     | 
    
         
            -
                     
     | 
| 
       173 
     | 
    
         
            -
                    framework_agent_roles_dir = _get_framework_agent_roles_dir()
         
     | 
| 
       174 
     | 
    
         
            -
                    md_path = framework_agent_roles_dir / md_filename
         
     | 
| 
      
 137 
     | 
    
         
            +
                    json_path = AGENT_TEMPLATES_DIR / json_filename
         
     | 
| 
       175 
138 
     | 
    
         | 
| 
       176 
139 
     | 
    
         
             
                    # Check if file exists
         
     | 
| 
       177 
     | 
    
         
            -
                    if not  
     | 
| 
       178 
     | 
    
         
            -
                        logger.warning(f"Agent  
     | 
| 
      
 140 
     | 
    
         
            +
                    if not json_path.exists():
         
     | 
| 
      
 141 
     | 
    
         
            +
                        logger.warning(f"Agent JSON file not found: {json_path}")
         
     | 
| 
       179 
142 
     | 
    
         
             
                        return None
         
     | 
| 
       180 
143 
     | 
    
         | 
| 
       181 
     | 
    
         
            -
                    logger.debug(f"Loading agent prompt from: { 
     | 
| 
       182 
     | 
    
         
            -
                     
     | 
| 
      
 144 
     | 
    
         
            +
                    logger.debug(f"Loading agent prompt from: {json_path}")
         
     | 
| 
      
 145 
     | 
    
         
            +
                    
         
     | 
| 
      
 146 
     | 
    
         
            +
                    # Load and parse JSON
         
     | 
| 
      
 147 
     | 
    
         
            +
                    import json
         
     | 
| 
      
 148 
     | 
    
         
            +
                    with open(json_path, 'r', encoding='utf-8') as f:
         
     | 
| 
      
 149 
     | 
    
         
            +
                        data = json.load(f)
         
     | 
| 
      
 150 
     | 
    
         
            +
                    
         
     | 
| 
      
 151 
     | 
    
         
            +
                    # Extract prompt content from JSON
         
     | 
| 
      
 152 
     | 
    
         
            +
                    # Check multiple possible locations for instructions/content
         
     | 
| 
      
 153 
     | 
    
         
            +
                    # Following the same pattern as AgentDeploymentService
         
     | 
| 
      
 154 
     | 
    
         
            +
                    content = (
         
     | 
| 
      
 155 
     | 
    
         
            +
                        data.get('instructions') or
         
     | 
| 
      
 156 
     | 
    
         
            +
                        data.get('narrative_fields', {}).get('instructions') or
         
     | 
| 
      
 157 
     | 
    
         
            +
                        data.get('content') or
         
     | 
| 
      
 158 
     | 
    
         
            +
                        ''
         
     | 
| 
      
 159 
     | 
    
         
            +
                    )
         
     | 
| 
      
 160 
     | 
    
         
            +
                    
         
     | 
| 
      
 161 
     | 
    
         
            +
                    if not content:
         
     | 
| 
      
 162 
     | 
    
         
            +
                        logger.warning(f"No content/instructions found in JSON template: {json_path}")
         
     | 
| 
      
 163 
     | 
    
         
            +
                        logger.debug(f"Checked fields: instructions, narrative_fields.instructions, content")
         
     | 
| 
      
 164 
     | 
    
         
            +
                        return None
         
     | 
| 
       183 
165 
     | 
    
         | 
| 
       184 
166 
     | 
    
         
             
                    # Cache the content with 1 hour TTL
         
     | 
| 
       185 
167 
     | 
    
         
             
                    cache.set(cache_key, content, ttl=3600)
         
     | 
| 
         @@ -188,7 +170,7 @@ def load_agent_prompt_from_md(agent_name: str, force_reload: bool = False) -> Op 
     | 
|
| 
       188 
170 
     | 
    
         
             
                    return content
         
     | 
| 
       189 
171 
     | 
    
         | 
| 
       190 
172 
     | 
    
         
             
                except Exception as e:
         
     | 
| 
       191 
     | 
    
         
            -
                    logger.error(f"Error loading agent prompt from  
     | 
| 
      
 173 
     | 
    
         
            +
                    logger.error(f"Error loading agent prompt from JSON for '{agent_name}': {e}")
         
     | 
| 
       192 
174 
     | 
    
         
             
                    return None
         
     | 
| 
       193 
175 
     | 
    
         | 
| 
       194 
176 
     | 
    
         | 
| 
         @@ -289,7 +271,7 @@ def _get_model_config(agent_name: str, complexity_analysis: Optional[Dict[str, A 
     | 
|
| 
       289 
271 
     | 
    
         | 
| 
       290 
272 
     | 
    
         
             
            def get_agent_prompt(agent_name: str, force_reload: bool = False, return_model_info: bool = False, **kwargs: Any) -> Union[str, Tuple[str, str, Dict[str, Any]]]:
         
     | 
| 
       291 
273 
     | 
    
         
             
                """
         
     | 
| 
       292 
     | 
    
         
            -
                Get agent prompt from  
     | 
| 
      
 274 
     | 
    
         
            +
                Get agent prompt from JSON template with optional dynamic model selection.
         
     | 
| 
       293 
275 
     | 
    
         | 
| 
       294 
276 
     | 
    
         
             
                Args:
         
     | 
| 
       295 
277 
     | 
    
         
             
                    agent_name: Agent name (e.g., 'documentation', 'ticketing')
         
     | 
| 
         @@ -305,11 +287,11 @@ def get_agent_prompt(agent_name: str, force_reload: bool = False, return_model_i 
     | 
|
| 
       305 
287 
     | 
    
         
             
                    str or tuple: Complete agent prompt with base instructions prepended,
         
     | 
| 
       306 
288 
     | 
    
         
             
                                  or tuple of (prompt, selected_model, model_config) if return_model_info=True
         
     | 
| 
       307 
289 
     | 
    
         
             
                """
         
     | 
| 
       308 
     | 
    
         
            -
                # Load from  
     | 
| 
      
 290 
     | 
    
         
            +
                # Load from JSON template
         
     | 
| 
       309 
291 
     | 
    
         
             
                prompt = load_agent_prompt_from_md(agent_name, force_reload)
         
     | 
| 
       310 
292 
     | 
    
         | 
| 
       311 
293 
     | 
    
         
             
                if prompt is None:
         
     | 
| 
       312 
     | 
    
         
            -
                    raise ValueError(f"No agent prompt  
     | 
| 
      
 294 
     | 
    
         
            +
                    raise ValueError(f"No agent prompt JSON template found for: {agent_name}")
         
     | 
| 
       313 
295 
     | 
    
         | 
| 
       314 
296 
     | 
    
         
             
                # Analyze task complexity if task description is provided
         
     | 
| 
       315 
297 
     | 
    
         
             
                complexity_analysis = None
         
     | 
| 
         @@ -452,20 +434,17 @@ def list_available_agents() -> Dict[str, Dict[str, Any]]: 
     | 
|
| 
       452 
434 
     | 
    
         
             
                List all available agents with their sources.
         
     | 
| 
       453 
435 
     | 
    
         | 
| 
       454 
436 
     | 
    
         
             
                Returns:
         
     | 
| 
       455 
     | 
    
         
            -
                    dict: Agent information including  
     | 
| 
      
 437 
     | 
    
         
            +
                    dict: Agent information including JSON template path
         
     | 
| 
       456 
438 
     | 
    
         
             
                """
         
     | 
| 
       457 
439 
     | 
    
         
             
                agents = {}
         
     | 
| 
       458 
440 
     | 
    
         | 
| 
       459 
     | 
    
         
            -
                 
     | 
| 
       460 
     | 
    
         
            -
             
     | 
| 
       461 
     | 
    
         
            -
                
         
     | 
| 
       462 
     | 
    
         
            -
                for agent_name, md_filename in AGENT_MAPPINGS.items():
         
     | 
| 
       463 
     | 
    
         
            -
                    md_path = framework_agent_roles_dir / md_filename
         
     | 
| 
      
 441 
     | 
    
         
            +
                for agent_name, json_filename in AGENT_MAPPINGS.items():
         
     | 
| 
      
 442 
     | 
    
         
            +
                    json_path = AGENT_TEMPLATES_DIR / json_filename
         
     | 
| 
       464 
443 
     | 
    
         | 
| 
       465 
444 
     | 
    
         
             
                    agents[agent_name] = {
         
     | 
| 
       466 
     | 
    
         
            -
                        " 
     | 
| 
       467 
     | 
    
         
            -
                        " 
     | 
| 
       468 
     | 
    
         
            -
                        " 
     | 
| 
      
 445 
     | 
    
         
            +
                        "json_file": json_filename if json_path.exists() else None,
         
     | 
| 
      
 446 
     | 
    
         
            +
                        "json_path": str(json_path) if json_path.exists() else None,
         
     | 
| 
      
 447 
     | 
    
         
            +
                        "has_template": json_path.exists(),
         
     | 
| 
       469 
448 
     | 
    
         
             
                        "default_model": DEFAULT_AGENT_MODELS.get(agent_name, 'claude-sonnet-4-20250514')
         
     | 
| 
       470 
449 
     | 
    
         
             
                    }
         
     | 
| 
       471 
450 
     | 
    
         | 
| 
         @@ -483,13 +462,13 @@ def clear_agent_cache(agent_name: Optional[str] = None) -> None: 
     | 
|
| 
       483 
462 
     | 
    
         
             
                    cache = SharedPromptCache.get_instance()
         
     | 
| 
       484 
463 
     | 
    
         | 
| 
       485 
464 
     | 
    
         
             
                    if agent_name:
         
     | 
| 
       486 
     | 
    
         
            -
                        cache_key = f"{AGENT_CACHE_PREFIX}{agent_name}: 
     | 
| 
      
 465 
     | 
    
         
            +
                        cache_key = f"{AGENT_CACHE_PREFIX}{agent_name}:json"
         
     | 
| 
       487 
466 
     | 
    
         
             
                        cache.invalidate(cache_key)
         
     | 
| 
       488 
467 
     | 
    
         
             
                        logger.debug(f"Cache cleared for agent: {agent_name}")
         
     | 
| 
       489 
468 
     | 
    
         
             
                    else:
         
     | 
| 
       490 
469 
     | 
    
         
             
                        # Clear all agent caches
         
     | 
| 
       491 
470 
     | 
    
         
             
                        for name in AGENT_MAPPINGS:
         
     | 
| 
       492 
     | 
    
         
            -
                            cache_key = f"{AGENT_CACHE_PREFIX}{name}: 
     | 
| 
      
 471 
     | 
    
         
            +
                            cache_key = f"{AGENT_CACHE_PREFIX}{name}:json"
         
     | 
| 
       493 
472 
     | 
    
         
             
                            cache.invalidate(cache_key)
         
     | 
| 
       494 
473 
     | 
    
         
             
                        logger.debug("All agent caches cleared")
         
     | 
| 
       495 
474 
     | 
    
         | 
| 
         @@ -506,14 +485,11 @@ def validate_agent_files() -> Dict[str, Dict[str, Any]]: 
     | 
|
| 
       506 
485 
     | 
    
         
             
                """
         
     | 
| 
       507 
486 
     | 
    
         
             
                results = {}
         
     | 
| 
       508 
487 
     | 
    
         | 
| 
       509 
     | 
    
         
            -
                 
     | 
| 
       510 
     | 
    
         
            -
             
     | 
| 
       511 
     | 
    
         
            -
                
         
     | 
| 
       512 
     | 
    
         
            -
                for agent_name, md_filename in AGENT_MAPPINGS.items():
         
     | 
| 
       513 
     | 
    
         
            -
                    md_path = framework_agent_roles_dir / md_filename
         
     | 
| 
      
 488 
     | 
    
         
            +
                for agent_name, json_filename in AGENT_MAPPINGS.items():
         
     | 
| 
      
 489 
     | 
    
         
            +
                    json_path = AGENT_TEMPLATES_DIR / json_filename
         
     | 
| 
       514 
490 
     | 
    
         
             
                    results[agent_name] = {
         
     | 
| 
       515 
     | 
    
         
            -
                        " 
     | 
| 
       516 
     | 
    
         
            -
                        " 
     | 
| 
      
 491 
     | 
    
         
            +
                        "template_exists": json_path.exists(),
         
     | 
| 
      
 492 
     | 
    
         
            +
                        "template_path": str(json_path)
         
     | 
| 
       517 
493 
     | 
    
         
             
                    }
         
     | 
| 
       518 
494 
     | 
    
         | 
| 
       519 
495 
     | 
    
         
             
                return results
         
     | 
| 
         @@ -36,7 +36,7 @@ logger = logging.getLogger(__name__) 
     | 
|
| 
       36 
36 
     | 
    
         
             
            BASE_AGENT_CACHE_KEY = "base_agent:instructions"
         
     | 
| 
       37 
37 
     | 
    
         | 
| 
       38 
38 
     | 
    
         
             
            def _get_base_agent_file() -> Path:
         
     | 
| 
       39 
     | 
    
         
            -
                """Get the base agent file path 
     | 
| 
      
 39 
     | 
    
         
            +
                """Get the base agent file path."""
         
     | 
| 
       40 
40 
     | 
    
         
             
                # Check if we're running from a wheel installation
         
     | 
| 
       41 
41 
     | 
    
         
             
                try:
         
     | 
| 
       42 
42 
     | 
    
         
             
                    import claude_pm
         
     | 
| 
         @@ -44,27 +44,22 @@ def _get_base_agent_file() -> Path: 
     | 
|
| 
       44 
44 
     | 
    
         
             
                    path_str = str(package_path.resolve())
         
     | 
| 
       45 
45 
     | 
    
         
             
                    if 'site-packages' in path_str or 'dist-packages' in path_str:
         
     | 
| 
       46 
46 
     | 
    
         
             
                        # For wheel installations, check data directory
         
     | 
| 
       47 
     | 
    
         
            -
                        data_base_agent = package_path / "data" / " 
     | 
| 
      
 47 
     | 
    
         
            +
                        data_base_agent = package_path / "data" / "agents" / "BASE_AGENT_TEMPLATE.md"
         
     | 
| 
       48 
48 
     | 
    
         
             
                        if data_base_agent.exists():
         
     | 
| 
       49 
49 
     | 
    
         
             
                            logger.debug(f"Using wheel installation base_agent: {data_base_agent}")
         
     | 
| 
       50 
50 
     | 
    
         
             
                            return data_base_agent
         
     | 
| 
       51 
51 
     | 
    
         
             
                except Exception:
         
     | 
| 
       52 
52 
     | 
    
         
             
                    pass
         
     | 
| 
       53 
53 
     | 
    
         | 
| 
       54 
     | 
    
         
            -
                #  
     | 
| 
       55 
     | 
    
         
            -
                 
     | 
| 
      
 54 
     | 
    
         
            +
                # Use the BASE_AGENT_TEMPLATE.md in the agents directory
         
     | 
| 
      
 55 
     | 
    
         
            +
                base_agent_path = Path(__file__).parent / "BASE_AGENT_TEMPLATE.md"
         
     | 
| 
      
 56 
     | 
    
         
            +
                if base_agent_path.exists():
         
     | 
| 
      
 57 
     | 
    
         
            +
                    logger.debug(f"Using base agent template: {base_agent_path}")
         
     | 
| 
      
 58 
     | 
    
         
            +
                    return base_agent_path
         
     | 
| 
       56 
59 
     | 
    
         | 
| 
       57 
     | 
    
         
            -
                #  
     | 
| 
       58 
     | 
    
         
            -
                 
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
                    if framework_base_agent.exists():
         
     | 
| 
       61 
     | 
    
         
            -
                        logger.debug(f"Using development base_agent: {framework_base_agent}")
         
     | 
| 
       62 
     | 
    
         
            -
                        return framework_base_agent
         
     | 
| 
       63 
     | 
    
         
            -
                
         
     | 
| 
       64 
     | 
    
         
            -
                # Fallback to old behavior (though this shouldn't happen)
         
     | 
| 
       65 
     | 
    
         
            -
                fallback = Path(__file__).parent.parent.parent / "framework" / "agent-roles" / "base_agent.md"
         
     | 
| 
       66 
     | 
    
         
            -
                logger.warning(f"Using fallback base_agent path: {fallback}")
         
     | 
| 
       67 
     | 
    
         
            -
                return fallback
         
     | 
| 
      
 60 
     | 
    
         
            +
                # Fallback error
         
     | 
| 
      
 61 
     | 
    
         
            +
                logger.error("Base agent template file not found")
         
     | 
| 
      
 62 
     | 
    
         
            +
                raise FileNotFoundError("BASE_AGENT_TEMPLATE.md not found in agents directory")
         
     | 
| 
       68 
63 
     | 
    
         | 
| 
       69 
64 
     | 
    
         | 
| 
       70 
65 
     | 
    
         
             
            # Base agent file path (dynamically determined)
         
     | 
    
        claude_mpm/cli.py
    CHANGED
    
    | 
         @@ -1,6 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            """Command-line interface for Claude MPM."""
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            import argparse
         
     | 
| 
      
 4 
     | 
    
         
            +
            import subprocess
         
     | 
| 
       4 
5 
     | 
    
         
             
            import sys
         
     | 
| 
       5 
6 
     | 
    
         
             
            from pathlib import Path
         
     | 
| 
       6 
7 
     | 
    
         
             
            from typing import Optional
         
     | 
| 
         @@ -9,13 +10,11 @@ try: 
     | 
|
| 
       9 
10 
     | 
    
         
             
                # Try relative imports first (when used as package)
         
     | 
| 
       10 
11 
     | 
    
         
             
                from ._version import __version__
         
     | 
| 
       11 
12 
     | 
    
         
             
                from .core.logger import get_logger, setup_logging
         
     | 
| 
       12 
     | 
    
         
            -
                from .services.json_rpc_hook_manager import JSONRPCHookManager
         
     | 
| 
       13 
13 
     | 
    
         
             
                from .constants import CLICommands, CLIPrefix, AgentCommands, LogLevel, CLIFlags
         
     | 
| 
       14 
14 
     | 
    
         
             
            except ImportError:
         
     | 
| 
       15 
15 
     | 
    
         
             
                # Fall back to absolute imports (when run directly)
         
     | 
| 
       16 
16 
     | 
    
         
             
                from claude_mpm._version import __version__
         
     | 
| 
       17 
17 
     | 
    
         
             
                from core.logger import get_logger, setup_logging
         
     | 
| 
       18 
     | 
    
         
            -
                from services.json_rpc_hook_manager import JSONRPCHookManager
         
     | 
| 
       19 
18 
     | 
    
         
             
                from constants import CLICommands, CLIPrefix, AgentCommands, LogLevel, CLIFlags
         
     | 
| 
       20 
19 
     | 
    
         | 
| 
       21 
20 
     | 
    
         | 
| 
         @@ -99,6 +98,12 @@ def main(argv: Optional[list] = None): 
     | 
|
| 
       99 
98 
     | 
    
         
             
                    help="Disable hook service (runs without hooks)"
         
     | 
| 
       100 
99 
     | 
    
         
             
                )
         
     | 
| 
       101 
100 
     | 
    
         | 
| 
      
 101 
     | 
    
         
            +
                parser.add_argument(
         
     | 
| 
      
 102 
     | 
    
         
            +
                    "--intercept-commands",
         
     | 
| 
      
 103 
     | 
    
         
            +
                    action="store_true",
         
     | 
| 
      
 104 
     | 
    
         
            +
                    help="Enable command interception in interactive mode (intercepts /mpm: commands)"
         
     | 
| 
      
 105 
     | 
    
         
            +
                )
         
     | 
| 
      
 106 
     | 
    
         
            +
                
         
     | 
| 
       102 
107 
     | 
    
         
             
                # Add run-specific arguments at top level (for default behavior)
         
     | 
| 
       103 
108 
     | 
    
         
             
                parser.add_argument(
         
     | 
| 
       104 
109 
     | 
    
         
             
                    "--no-tickets",
         
     | 
| 
         @@ -139,6 +144,11 @@ def main(argv: Optional[list] = None): 
     | 
|
| 
       139 
144 
     | 
    
         
             
                    action="store_true",
         
     | 
| 
       140 
145 
     | 
    
         
             
                    help="Disable automatic ticket creation"
         
     | 
| 
       141 
146 
     | 
    
         
             
                )
         
     | 
| 
      
 147 
     | 
    
         
            +
                run_parser.add_argument(
         
     | 
| 
      
 148 
     | 
    
         
            +
                    "--intercept-commands",
         
     | 
| 
      
 149 
     | 
    
         
            +
                    action="store_true",
         
     | 
| 
      
 150 
     | 
    
         
            +
                    help="Enable command interception in interactive mode (intercepts /mpm: commands)"
         
     | 
| 
      
 151 
     | 
    
         
            +
                )
         
     | 
| 
       142 
152 
     | 
    
         
             
                run_parser.add_argument(
         
     | 
| 
       143 
153 
     | 
    
         
             
                    "-i", "--input",
         
     | 
| 
       144 
154 
     | 
    
         
             
                    type=str,
         
     | 
| 
         @@ -244,31 +254,10 @@ def main(argv: Optional[list] = None): 
     | 
|
| 
       244 
254 
     | 
    
         
             
                    logger = logging.getLogger("cli")
         
     | 
| 
       245 
255 
     | 
    
         
             
                    logger.setLevel(logging.WARNING)
         
     | 
| 
       246 
256 
     | 
    
         | 
| 
       247 
     | 
    
         
            -
                #  
     | 
| 
       248 
     | 
    
         
            -
                 
     | 
| 
       249 
     | 
    
         
            -
                 
     | 
| 
       250 
     | 
    
         
            -
             
     | 
| 
       251 
     | 
    
         
            -
                        # Check if hooks are enabled via config
         
     | 
| 
       252 
     | 
    
         
            -
                        try:
         
     | 
| 
       253 
     | 
    
         
            -
                            from .config.hook_config import HookConfig
         
     | 
| 
       254 
     | 
    
         
            -
                        except ImportError:
         
     | 
| 
       255 
     | 
    
         
            -
                            from config.hook_config import HookConfig
         
     | 
| 
       256 
     | 
    
         
            -
                        if HookConfig.is_hooks_enabled():
         
     | 
| 
       257 
     | 
    
         
            -
                            hook_manager = JSONRPCHookManager(log_dir=args.log_dir)
         
     | 
| 
       258 
     | 
    
         
            -
                            if hook_manager.start_service():
         
     | 
| 
       259 
     | 
    
         
            -
                                logger.info("JSON-RPC hook system initialized")
         
     | 
| 
       260 
     | 
    
         
            -
                                if args.logging != LogLevel.OFF.value:
         
     | 
| 
       261 
     | 
    
         
            -
                                    print("✓ JSON-RPC hook system initialized")
         
     | 
| 
       262 
     | 
    
         
            -
                            else:
         
     | 
| 
       263 
     | 
    
         
            -
                                logger.warning("Failed to initialize JSON-RPC hooks, continuing without hooks")
         
     | 
| 
       264 
     | 
    
         
            -
                                if args.logging != LogLevel.OFF.value:
         
     | 
| 
       265 
     | 
    
         
            -
                                    print("⚠️  Failed to initialize JSON-RPC hooks, continuing without hooks")
         
     | 
| 
       266 
     | 
    
         
            -
                                hook_manager = None
         
     | 
| 
       267 
     | 
    
         
            -
                        else:
         
     | 
| 
       268 
     | 
    
         
            -
                            logger.info("Hooks disabled via configuration")
         
     | 
| 
       269 
     | 
    
         
            -
                    except Exception as e:
         
     | 
| 
       270 
     | 
    
         
            -
                        logger.warning(f"Hook service initialization failed: {e}, continuing without hooks")
         
     | 
| 
       271 
     | 
    
         
            -
                        hook_manager = None
         
     | 
| 
      
 257 
     | 
    
         
            +
                # Hook system note: Claude Code hooks are handled externally via the
         
     | 
| 
      
 258 
     | 
    
         
            +
                # hook_handler.py script installed in ~/.claude/settings.json
         
     | 
| 
      
 259 
     | 
    
         
            +
                # The --no-hooks flag is kept for backward compatibility but doesn't affect
         
     | 
| 
      
 260 
     | 
    
         
            +
                # Claude Code hooks which are configured separately.
         
     | 
| 
       272 
261 
     | 
    
         | 
| 
       273 
262 
     | 
    
         
             
                # Default to run command
         
     | 
| 
       274 
263 
     | 
    
         
             
                if not args.command:
         
     | 
| 
         @@ -289,11 +278,11 @@ def main(argv: Optional[list] = None): 
     | 
|
| 
       289 
278 
     | 
    
         | 
| 
       290 
279 
     | 
    
         
             
                try:
         
     | 
| 
       291 
280 
     | 
    
         
             
                    if command in [CLICommands.RUN.value, None]:
         
     | 
| 
       292 
     | 
    
         
            -
                        run_session(args 
     | 
| 
      
 281 
     | 
    
         
            +
                        run_session(args)
         
     | 
| 
       293 
282 
     | 
    
         
             
                    elif command == CLICommands.TICKETS.value:
         
     | 
| 
       294 
283 
     | 
    
         
             
                        list_tickets(args)
         
     | 
| 
       295 
284 
     | 
    
         
             
                    elif command == CLICommands.INFO.value:
         
     | 
| 
       296 
     | 
    
         
            -
                        show_info(args 
     | 
| 
      
 285 
     | 
    
         
            +
                        show_info(args)
         
     | 
| 
       297 
286 
     | 
    
         
             
                    elif command == CLICommands.AGENTS.value:
         
     | 
| 
       298 
287 
     | 
    
         
             
                        manage_agents(args)
         
     | 
| 
       299 
288 
     | 
    
         
             
                    elif command == CLICommands.UI.value:
         
     | 
| 
         @@ -311,9 +300,8 @@ def main(argv: Optional[list] = None): 
     | 
|
| 
       311 
300 
     | 
    
         
             
                        traceback.print_exc()
         
     | 
| 
       312 
301 
     | 
    
         
             
                    return 1
         
     | 
| 
       313 
302 
     | 
    
         
             
                finally:
         
     | 
| 
       314 
     | 
    
         
            -
                    #  
     | 
| 
       315 
     | 
    
         
            -
                     
     | 
| 
       316 
     | 
    
         
            -
                        hook_manager.stop_service()
         
     | 
| 
      
 303 
     | 
    
         
            +
                    # Cleanup handled by individual components
         
     | 
| 
      
 304 
     | 
    
         
            +
                    pass
         
     | 
| 
       317 
305 
     | 
    
         | 
| 
       318 
306 
     | 
    
         
             
                return 0
         
     | 
| 
       319 
307 
     | 
    
         | 
| 
         @@ -337,7 +325,7 @@ def _get_user_input(args, logger): 
     | 
|
| 
       337 
325 
     | 
    
         | 
| 
       338 
326 
     | 
    
         | 
| 
       339 
327 
     | 
    
         | 
| 
       340 
     | 
    
         
            -
            def run_session(args 
     | 
| 
      
 328 
     | 
    
         
            +
            def run_session(args):
         
     | 
| 
       341 
329 
     | 
    
         
             
                """Run a simplified Claude session."""
         
     | 
| 
       342 
330 
     | 
    
         
             
                logger = get_logger("cli")
         
     | 
| 
       343 
331 
     | 
    
         
             
                if args.logging != LogLevel.OFF.value:
         
     | 
| 
         @@ -368,7 +356,17 @@ def run_session(args, hook_manager=None): 
     | 
|
| 
       368 
356 
     | 
    
         
             
                        logger.error("Session failed")
         
     | 
| 
       369 
357 
     | 
    
         
             
                else:
         
     | 
| 
       370 
358 
     | 
    
         
             
                    # Run interactive session
         
     | 
| 
       371 
     | 
    
         
            -
                     
     | 
| 
      
 359 
     | 
    
         
            +
                    if getattr(args, 'intercept_commands', False):
         
     | 
| 
      
 360 
     | 
    
         
            +
                        # Use the interactive wrapper for command interception
         
     | 
| 
      
 361 
     | 
    
         
            +
                        wrapper_path = Path(__file__).parent.parent.parent / "scripts" / "interactive_wrapper.py"
         
     | 
| 
      
 362 
     | 
    
         
            +
                        if wrapper_path.exists():
         
     | 
| 
      
 363 
     | 
    
         
            +
                            print("Starting interactive session with command interception...")
         
     | 
| 
      
 364 
     | 
    
         
            +
                            subprocess.run([sys.executable, str(wrapper_path)])
         
     | 
| 
      
 365 
     | 
    
         
            +
                        else:
         
     | 
| 
      
 366 
     | 
    
         
            +
                            logger.warning("Interactive wrapper not found, falling back to normal mode")
         
     | 
| 
      
 367 
     | 
    
         
            +
                            runner.run_interactive(context)
         
     | 
| 
      
 368 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 369 
     | 
    
         
            +
                        runner.run_interactive(context)
         
     | 
| 
       372 
370 
     | 
    
         | 
| 
       373 
371 
     | 
    
         | 
| 
       374 
372 
     | 
    
         
             
            def list_tickets(args):
         
     | 
| 
         @@ -579,7 +577,7 @@ def run_terminal_ui(args): 
     | 
|
| 
       579 
577 
     | 
    
         
             
                return 0
         
     | 
| 
       580 
578 
     | 
    
         | 
| 
       581 
579 
     | 
    
         | 
| 
       582 
     | 
    
         
            -
            def show_info(args 
     | 
| 
      
 580 
     | 
    
         
            +
            def show_info(args):
         
     | 
| 
       583 
581 
     | 
    
         
             
                """Show framework and configuration information."""
         
     | 
| 
       584 
582 
     | 
    
         
             
                try:
         
     | 
| 
       585 
583 
     | 
    
         
             
                    from .core.framework_loader import FrameworkLoader
         
     | 
| 
         @@ -636,19 +634,15 @@ def show_info(args, hook_manager=None): 
     | 
|
| 
       636 
634 
     | 
    
         
             
                except ImportError:
         
     | 
| 
       637 
635 
     | 
    
         
             
                    print("  ✗ ai-trackdown-pytools: Not installed")
         
     | 
| 
       638 
636 
     | 
    
         | 
| 
       639 
     | 
    
         
            -
                # Check  
     | 
| 
       640 
     | 
    
         
            -
                 
     | 
| 
       641 
     | 
    
         
            -
             
     | 
| 
       642 
     | 
    
         
            -
             
     | 
| 
       643 
     | 
    
         
            -
             
     | 
| 
       644 
     | 
    
         
            -
             
     | 
| 
       645 
     | 
    
         
            -
                            print(f"     Hooks: {', '.join(info['discovered_hooks'][:5])}")
         
     | 
| 
       646 
     | 
    
         
            -
                            if len(info['discovered_hooks']) > 5:
         
     | 
| 
       647 
     | 
    
         
            -
                                print(f"     ... and {len(info['discovered_hooks']) - 5} more")
         
     | 
| 
       648 
     | 
    
         
            -
                    else:
         
     | 
| 
       649 
     | 
    
         
            -
                        print("  ✗ Hook System: Not running")
         
     | 
| 
      
 637 
     | 
    
         
            +
                # Check Claude Code hooks
         
     | 
| 
      
 638 
     | 
    
         
            +
                from pathlib import Path
         
     | 
| 
      
 639 
     | 
    
         
            +
                claude_settings = Path.home() / ".claude" / "settings.json"
         
     | 
| 
      
 640 
     | 
    
         
            +
                if claude_settings.exists():
         
     | 
| 
      
 641 
     | 
    
         
            +
                    print("  ✓ Claude Code Hooks: Installed")
         
     | 
| 
      
 642 
     | 
    
         
            +
                    print("     Use /mpm commands in Claude Code")
         
     | 
| 
       650 
643 
     | 
    
         
             
                else:
         
     | 
| 
       651 
     | 
    
         
            -
                    print("  ✗  
     | 
| 
      
 644 
     | 
    
         
            +
                    print("  ✗ Claude Code Hooks: Not installed")
         
     | 
| 
      
 645 
     | 
    
         
            +
                    print("     Run: python scripts/install_hooks.py")
         
     | 
| 
       652 
646 
     | 
    
         | 
| 
       653 
647 
     | 
    
         | 
| 
       654 
648 
     | 
    
         
             
            if __name__ == "__main__":
         
     |