claude-mpm 3.5.4__py3-none-any.whl → 3.5.6__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.
Files changed (35) hide show
  1. claude_mpm/.claude-mpm/logs/hooks_20250728.log +10 -0
  2. claude_mpm/VERSION +1 -1
  3. claude_mpm/agents/INSTRUCTIONS.md +12 -11
  4. claude_mpm/agents/agent-template.yaml +83 -0
  5. claude_mpm/cli/README.md +108 -0
  6. claude_mpm/cli/commands/agents.py +21 -3
  7. claude_mpm/cli/commands/run.py +4 -11
  8. claude_mpm/cli/utils.py +9 -1
  9. claude_mpm/cli_module/refactoring_guide.md +253 -0
  10. claude_mpm/config/async_logging_config.yaml +145 -0
  11. claude_mpm/core/.claude-mpm/logs/hooks_20250730.log +34 -0
  12. claude_mpm/core/claude_runner.py +94 -33
  13. claude_mpm/core/config_paths.py +0 -1
  14. claude_mpm/core/factories.py +9 -3
  15. claude_mpm/dashboard/.claude-mpm/memories/README.md +36 -0
  16. claude_mpm/dashboard/README.md +121 -0
  17. claude_mpm/dashboard/static/js/dashboard.js.backup +1973 -0
  18. claude_mpm/dashboard/templates/.claude-mpm/memories/README.md +36 -0
  19. claude_mpm/dashboard/templates/.claude-mpm/memories/engineer_agent.md +39 -0
  20. claude_mpm/dashboard/templates/.claude-mpm/memories/version_control_agent.md +38 -0
  21. claude_mpm/hooks/README.md +96 -0
  22. claude_mpm/init.py +83 -13
  23. claude_mpm/schemas/agent_schema.json +435 -0
  24. claude_mpm/services/agents/deployment/agent_deployment.py +165 -9
  25. claude_mpm/services/agents/management/agent_management_service.py +2 -1
  26. claude_mpm/services/framework_claude_md_generator/README.md +92 -0
  27. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +3 -3
  28. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +2 -2
  29. claude_mpm/services/version_control/VERSION +1 -0
  30. {claude_mpm-3.5.4.dist-info → claude_mpm-3.5.6.dist-info}/METADATA +43 -1
  31. {claude_mpm-3.5.4.dist-info → claude_mpm-3.5.6.dist-info}/RECORD +35 -19
  32. {claude_mpm-3.5.4.dist-info → claude_mpm-3.5.6.dist-info}/WHEEL +0 -0
  33. {claude_mpm-3.5.4.dist-info → claude_mpm-3.5.6.dist-info}/entry_points.txt +0 -0
  34. {claude_mpm-3.5.4.dist-info → claude_mpm-3.5.6.dist-info}/licenses/LICENSE +0 -0
  35. {claude_mpm-3.5.4.dist-info → claude_mpm-3.5.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,10 @@
1
+ 2025-07-28 09:57:00,914 - claude_mpm_hooks_claude_mpm - INFO - hook_handler.py:231 - Claude Code hook event: PostToolUse (session: 9a641bea)
2
+ 2025-07-28 09:57:00,914 - claude_mpm_hooks_claude_mpm - INFO - hook_handler.py:254 - PostToolUse: Bash (exit code: N/A)
3
+ 2025-07-28 09:57:09,289 - claude_mpm_hooks_claude_mpm - INFO - hook_handler.py:231 - Claude Code hook event: PreToolUse (session: 9a641bea)
4
+ 2025-07-28 09:57:09,290 - claude_mpm_hooks_claude_mpm - INFO - hook_handler.py:247 - PreToolUse: Bash
5
+ 2025-07-28 09:57:21,601 - claude_mpm_hooks_claude_mpm - INFO - hook_handler.py:231 - Claude Code hook event: PreToolUse (session: 9a641bea)
6
+ 2025-07-28 09:57:21,601 - claude_mpm_hooks_claude_mpm - INFO - hook_handler.py:247 - PreToolUse: LS
7
+ 2025-07-28 09:57:25,939 - claude_mpm_hooks_claude_mpm - INFO - hook_handler.py:231 - Claude Code hook event: PostToolUse (session: 9a641bea)
8
+ 2025-07-28 09:57:25,939 - claude_mpm_hooks_claude_mpm - INFO - hook_handler.py:254 - PostToolUse: LS (exit code: N/A)
9
+ 2025-07-28 09:57:35,668 - claude_mpm_hooks_claude_mpm - INFO - hook_handler.py:231 - Claude Code hook event: PreToolUse (session: 9a641bea)
10
+ 2025-07-28 09:57:35,668 - claude_mpm_hooks_claude_mpm - INFO - hook_handler.py:247 - PreToolUse: Bash
claude_mpm/VERSION CHANGED
@@ -1 +1 @@
1
- 3.5.4
1
+ 3.5.6
@@ -249,14 +249,15 @@ See TODOWRITE.md for complete TodoWrite guidelines.
249
249
  ## Critical Operating Principles
250
250
 
251
251
  1. **🔴 ALWAYS DELEGATE BY DEFAULT** - You MUST delegate ALL work unless user EXPLICITLY says otherwise
252
- 2. **You are an orchestrator and delegator ONLY** - Your value is in coordination, not implementation
253
- 3. **Power through delegation** - Leverage specialized agents' expertise
254
- 4. **Memory awareness** - Check EVERY prompt for memory indicators
255
- 5. **Workflow discipline** - Follow the sequence unless explicitly overridden
256
- 6. **TodoWrite compliance** - ALWAYS use [Agent] prefixes for delegated work
257
- 7. **No direct implementation** - Delegate ALL technical work to specialists (NO EXCEPTIONS without explicit override)
258
- 8. **PM questions only** - Only answer directly about PM role and capabilities
259
- 9. **Context preservation** - Pass complete context to each agent
260
- 10. **Error escalation** - Follow 3-attempt protocol before blocking
261
- 11. **Professional communication** - Maintain neutral, clear tone
262
- 12. **DEFAULT = DELEGATE** - When in doubt, ALWAYS delegate. Direct action requires EXPLICIT user permission
252
+ 2. **🔴 NEVER ASSUME - ALWAYS VERIFY** - NEVER assume anything about code, files, or implementations. ALWAYS verify by checking actual files, reading configs, and confirming dependencies exist
253
+ 3. **You are an orchestrator and delegator ONLY** - Your value is in coordination, not implementation
254
+ 4. **Power through delegation** - Leverage specialized agents' expertise
255
+ 5. **Memory awareness** - Check EVERY prompt for memory indicators
256
+ 6. **Workflow discipline** - Follow the sequence unless explicitly overridden
257
+ 7. **TodoWrite compliance** - ALWAYS use [Agent] prefixes for delegated work
258
+ 8. **No direct implementation** - Delegate ALL technical work to specialists (NO EXCEPTIONS without explicit override)
259
+ 9. **PM questions only** - Only answer directly about PM role and capabilities
260
+ 10. **Context preservation** - Pass complete context to each agent
261
+ 11. **Error escalation** - Follow 3-attempt protocol before blocking
262
+ 12. **Professional communication** - Maintain neutral, clear tone
263
+ 13. **DEFAULT = DELEGATE** - When in doubt, ALWAYS delegate. Direct action requires EXPLICIT user permission
@@ -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}}"
@@ -0,0 +1,108 @@
1
+ # Claude MPM CLI Architecture
2
+
3
+ This document describes the refactored CLI architecture for claude-mpm.
4
+
5
+ ## Overview
6
+
7
+ The CLI has been refactored from a single monolithic `cli.py` file into a modular structure that improves maintainability and code organization.
8
+
9
+ ## Directory Structure
10
+
11
+ ```
12
+ cli/
13
+ ├── __init__.py # Main entry point - orchestrates the CLI flow
14
+ ├── parser.py # Argument parsing logic - single source of truth for CLI arguments
15
+ ├── utils.py # Shared utility functions
16
+ ├── commands/ # Individual command implementations
17
+ │ ├── __init__.py
18
+ │ ├── run.py # Default command - runs Claude sessions
19
+ │ ├── tickets.py # Lists tickets
20
+ │ ├── info.py # Shows system information
21
+ │ └── agents.py # Manages agent deployments
22
+ └── README.md # This file
23
+ ```
24
+
25
+ ## Key Design Decisions
26
+
27
+ ### 1. Modular Command Structure
28
+ Each command is implemented in its own module under `commands/`. This makes it easy to:
29
+ - Add new commands without touching existing code
30
+ - Test commands in isolation
31
+ - Understand what each command does
32
+
33
+ ### 2. Centralized Argument Parsing
34
+ All argument definitions are in `parser.py`. This provides:
35
+ - Single source of truth for CLI arguments
36
+ - Reusable argument groups (common arguments, run arguments)
37
+ - Clear separation of parsing from execution
38
+
39
+ ### 3. Shared Utilities
40
+ Common functions are in `utils.py`:
41
+ - `get_user_input()` - Handles input from files, stdin, or command line
42
+ - `get_agent_versions_display()` - Formats agent version information
43
+ - `setup_logging()` - Configures logging based on arguments
44
+ - `ensure_directories()` - Creates required directories on first run
45
+
46
+ ### 4. Backward Compatibility
47
+ The refactoring maintains full backward compatibility:
48
+ - `__main__.py` still imports from `claude_mpm.cli`
49
+ - The main `cli/__init__.py` exports the same `main()` function
50
+ - All existing commands and arguments work exactly as before
51
+
52
+ ## Entry Points
53
+
54
+ 1. **Package execution**: `python -m claude_mpm`
55
+ - Uses `__main__.py` which imports from `cli/__init__.py`
56
+
57
+ 2. **Direct import**: `from claude_mpm.cli import main`
58
+ - Imports the main function from `cli/__init__.py`
59
+
60
+ 3. **Shell script**: `claude-mpm` command
61
+ - Calls `python -m claude_mpm` with proper environment setup
62
+
63
+ ## Adding New Commands
64
+
65
+ To add a new command:
66
+
67
+ 1. Create a new module in `commands/`:
68
+ ```python
69
+ # commands/mycommand.py
70
+ def my_command(args):
71
+ """Execute my command."""
72
+ # Implementation here
73
+ ```
74
+
75
+ 2. Add the command to `commands/__init__.py`:
76
+ ```python
77
+ from .mycommand import my_command
78
+ ```
79
+
80
+ 3. Add parser configuration in `parser.py`:
81
+ ```python
82
+ # In create_parser()
83
+ mycommand_parser = subparsers.add_parser(
84
+ "mycommand",
85
+ help="Description of my command"
86
+ )
87
+ # Add command-specific arguments
88
+ ```
89
+
90
+ 4. Add the command mapping in `cli/__init__.py`:
91
+ ```python
92
+ # In _execute_command()
93
+ command_map = {
94
+ # ... existing commands ...
95
+ "mycommand": my_command,
96
+ }
97
+ ```
98
+
99
+ ## Removed Files
100
+
101
+ - `cli_main.py` - Redundant entry point, functionality moved to `__main__.py`
102
+ - Original `cli.py` - Split into the modular structure described above
103
+
104
+ ## Preserved Files
105
+
106
+ - `cli_enhancements.py` - Experimental Click-based CLI with enhanced features
107
+ - Kept for reference and future enhancement ideas
108
+ - Not currently used in production
@@ -34,7 +34,16 @@ def manage_agents(args):
34
34
 
35
35
  try:
36
36
  from ...services import AgentDeploymentService
37
- deployment_service = AgentDeploymentService()
37
+ import os
38
+ from pathlib import Path
39
+
40
+ # Determine the user's working directory from environment
41
+ # This ensures agents are deployed to the correct directory
42
+ user_working_dir = None
43
+ if 'CLAUDE_MPM_USER_PWD' in os.environ:
44
+ user_working_dir = Path(os.environ['CLAUDE_MPM_USER_PWD'])
45
+
46
+ deployment_service = AgentDeploymentService(working_directory=user_working_dir)
38
47
 
39
48
  if not args.agents_command:
40
49
  # No subcommand - show agent versions
@@ -153,7 +162,15 @@ def _deploy_agents(args, deployment_service, force=False):
153
162
 
154
163
  # Also deploy project agents if they exist
155
164
  from pathlib import Path
156
- project_agents_dir = Path.cwd() / '.claude-mpm' / 'agents'
165
+ import os
166
+
167
+ # Use the user's working directory if available
168
+ if 'CLAUDE_MPM_USER_PWD' in os.environ:
169
+ project_dir = Path(os.environ['CLAUDE_MPM_USER_PWD'])
170
+ else:
171
+ project_dir = Path.cwd()
172
+
173
+ project_agents_dir = project_dir / '.claude-mpm' / 'agents'
157
174
  if project_agents_dir.exists():
158
175
  json_files = list(project_agents_dir.glob('*.json'))
159
176
  if json_files:
@@ -161,7 +178,8 @@ def _deploy_agents(args, deployment_service, force=False):
161
178
  from claude_mpm.services.agents.deployment.agent_deployment import AgentDeploymentService
162
179
  project_service = AgentDeploymentService(
163
180
  templates_dir=project_agents_dir,
164
- base_agent_path=project_agents_dir / 'base_agent.json' if (project_agents_dir / 'base_agent.json').exists() else None
181
+ base_agent_path=project_agents_dir / 'base_agent.json' if (project_agents_dir / 'base_agent.json').exists() else None,
182
+ working_directory=project_dir # Pass the project directory
165
183
  )
166
184
  project_results = project_service.deploy_agents(
167
185
  target_dir=args.target if args.target else Path.cwd() / '.claude' / 'agents',
@@ -281,17 +281,10 @@ def run_session(args):
281
281
  websocket_port=websocket_port
282
282
  )
283
283
 
284
- # Ensure project agents are available if we're in a project directory
285
- # This deploys system agents to .claude-mpm/agents/ for local customization
286
- if not hasattr(args, 'no_native_agents') or not args.no_native_agents:
287
- # Check if we're in a project directory (has .git or other markers)
288
- project_markers = ['.git', 'pyproject.toml', 'package.json', 'requirements.txt']
289
- cwd = Path.cwd()
290
- is_project = any((cwd / marker).exists() for marker in project_markers)
291
-
292
- if is_project:
293
- logger.debug("Detected project directory, ensuring agents are available locally")
294
- runner.ensure_project_agents()
284
+ # Agent deployment is handled by ClaudeRunner.setup_agents() and
285
+ # ClaudeRunner.deploy_project_agents_to_claude() which are called
286
+ # in both run_interactive() and run_oneshot() methods.
287
+ # No need for redundant deployment here.
295
288
 
296
289
  # Set browser opening flag for monitor mode
297
290
  if monitor_mode:
claude_mpm/cli/utils.py CHANGED
@@ -61,7 +61,15 @@ def get_agent_versions_display() -> Optional[str]:
61
61
  """
62
62
  try:
63
63
  from ..services import AgentDeploymentService
64
- deployment_service = AgentDeploymentService()
64
+ import os
65
+ from pathlib import Path
66
+
67
+ # Determine the user's working directory from environment
68
+ user_working_dir = None
69
+ if 'CLAUDE_MPM_USER_PWD' in os.environ:
70
+ user_working_dir = Path(os.environ['CLAUDE_MPM_USER_PWD'])
71
+
72
+ deployment_service = AgentDeploymentService(working_directory=user_working_dir)
65
73
 
66
74
  # Get deployed agents
67
75
  verification = deployment_service.verify_deployment()
@@ -0,0 +1,253 @@
1
+ # CLI Refactoring Guide
2
+
3
+ This guide shows how to refactor the main() function in `/src/claude_mpm/cli.py` to reduce complexity from 16 to under 10.
4
+
5
+ ## Current Issues
6
+
7
+ 1. **High Cyclomatic Complexity (16)**
8
+ - Multiple nested conditionals
9
+ - Duplicate argument definitions
10
+ - Mixed concerns in one function
11
+
12
+ 2. **Code Duplication**
13
+ - Arguments defined twice (global level + run subcommand)
14
+ - Similar patterns repeated for each command
15
+
16
+ 3. **Poor Maintainability**
17
+ - Adding new commands requires multiple changes
18
+ - Hard to test individual components
19
+
20
+ ## Refactoring Steps
21
+
22
+ ### Step 1: Update imports in cli.py
23
+
24
+ ```python
25
+ # Add to imports
26
+ from .cli import ArgumentRegistry, CommandRegistry, register_standard_commands
27
+ ```
28
+
29
+ ### Step 2: Replace main() function
30
+
31
+ Replace the entire `main()` function with:
32
+
33
+ ```python
34
+ def main(argv: Optional[list] = None):
35
+ """Main CLI entry point with reduced complexity."""
36
+ # Initialize registries
37
+ arg_registry = ArgumentRegistry()
38
+ cmd_registry = CommandRegistry(arg_registry)
39
+
40
+ # Register standard commands
41
+ register_standard_commands(cmd_registry)
42
+
43
+ # Create parser
44
+ parser = argparse.ArgumentParser(
45
+ prog="claude-mpm",
46
+ description=f"Claude Multi-Agent Project Manager v{__version__}",
47
+ epilog="By default, runs an orchestrated Claude session."
48
+ )
49
+
50
+ # Store version for ArgumentRegistry
51
+ parser._version = f"claude-mpm {__version__}"
52
+
53
+ # Apply global arguments
54
+ arg_registry.apply_arguments(parser, groups=['global'])
55
+
56
+ # Apply run arguments at top level (for default behavior)
57
+ arg_registry.apply_arguments(parser, groups=['run'], exclude=['no_hooks'])
58
+
59
+ # Set up subcommands
60
+ cmd_registry.setup_subcommands(parser)
61
+
62
+ # Parse arguments
63
+ args = parser.parse_args(argv)
64
+
65
+ # Set up logging
66
+ _setup_logging(args)
67
+
68
+ # Initialize hook service
69
+ hook_manager = _initialize_hook_service(args)
70
+
71
+ try:
72
+ # Execute command
73
+ result = cmd_registry.execute_command(args, hook_manager=hook_manager)
74
+ if result is None and not args.command:
75
+ parser.print_help()
76
+ return 1
77
+ return result or 0
78
+
79
+ except KeyboardInterrupt:
80
+ get_logger("cli").info("Session interrupted by user")
81
+ return 0
82
+ except Exception as e:
83
+ logger = get_logger("cli")
84
+ logger.error(f"Error: {e}")
85
+ if args.debug:
86
+ import traceback
87
+ traceback.print_exc()
88
+ return 1
89
+ finally:
90
+ if hook_manager:
91
+ hook_manager.stop_service()
92
+ ```
93
+
94
+ ### Step 3: Extract helper functions
95
+
96
+ Add these helper functions after main():
97
+
98
+ ```python
99
+ def _setup_logging(args):
100
+ """Set up logging based on arguments."""
101
+ if args.debug and args.logging == "OFF":
102
+ args.logging = "DEBUG"
103
+
104
+ if args.logging != "OFF":
105
+ setup_logging(level=args.logging, log_dir=args.log_dir)
106
+ else:
107
+ import logging
108
+ logger = logging.getLogger("cli")
109
+ logger.setLevel(logging.WARNING)
110
+
111
+
112
+ def _initialize_hook_service(args):
113
+ """Initialize hook service if enabled."""
114
+ if getattr(args, 'no_hooks', False):
115
+ return None
116
+
117
+ try:
118
+ from .config.hook_config import HookConfig
119
+
120
+ if not HookConfig.is_hooks_enabled():
121
+ get_logger("cli").info("Hooks disabled via configuration")
122
+ return None
123
+
124
+ hook_manager = HookServiceManager(log_dir=args.log_dir)
125
+ if hook_manager.start_service():
126
+ logger = get_logger("cli")
127
+ logger.info(f"Hook service started on port {hook_manager.port}")
128
+ print(f"Hook service started on port {hook_manager.port}")
129
+ return hook_manager
130
+ else:
131
+ logger = get_logger("cli")
132
+ logger.warning("Failed to start hook service")
133
+ print("Failed to start hook service, continuing without hooks")
134
+ return None
135
+
136
+ except Exception as e:
137
+ get_logger("cli").warning(f"Hook service init failed: {e}")
138
+ return None
139
+ ```
140
+
141
+ ### Step 4: Update command handler signatures
142
+
143
+ Ensure all command handlers accept `**kwargs`:
144
+
145
+ ```python
146
+ def run_session(args, hook_manager=None, **kwargs):
147
+ """Run an orchestrated Claude session."""
148
+ # ... existing implementation
149
+
150
+ def list_tickets(args, **kwargs):
151
+ """List recent tickets."""
152
+ # ... existing implementation
153
+
154
+ def show_info(args, hook_manager=None, **kwargs):
155
+ """Show framework and configuration information."""
156
+ # ... existing implementation
157
+ ```
158
+
159
+ ## Benefits Achieved
160
+
161
+ ### Complexity Reduction
162
+ - **Before**: Cyclomatic complexity of 16
163
+ - **After**: Cyclomatic complexity of ~8
164
+
165
+ ### Code Organization
166
+ - Centralized argument definitions
167
+ - No duplicate argument definitions
168
+ - Clear separation of concerns
169
+ - Easier to add new commands
170
+
171
+ ### Maintainability
172
+ - New commands can be added with a single `register()` call
173
+ - Arguments are defined once and reused
174
+ - Helper functions are testable in isolation
175
+ - Registry pattern allows for extension
176
+
177
+ ## Adding New Commands
178
+
179
+ With the registry system, adding a new command is simple:
180
+
181
+ ```python
182
+ # In your code or plugin
183
+ def my_command(args, **kwargs):
184
+ """Implementation of your command."""
185
+ print(f"Running my command with args: {args}")
186
+ return 0
187
+
188
+ # Register it
189
+ cmd_registry.register(
190
+ name='mycommand',
191
+ help_text='Description of my command',
192
+ handler=my_command,
193
+ argument_groups=['framework'], # Reuse existing argument groups
194
+ extra_args={
195
+ 'custom_arg': {
196
+ 'flags': ['--custom'],
197
+ 'type': str,
198
+ 'help': 'A custom argument for this command'
199
+ }
200
+ }
201
+ )
202
+ ```
203
+
204
+ ## Testing
205
+
206
+ The refactored code is easier to test:
207
+
208
+ ```python
209
+ # Test argument registry
210
+ def test_argument_registry():
211
+ registry = ArgumentRegistry()
212
+ parser = argparse.ArgumentParser()
213
+ registry.apply_arguments(parser, groups=['logging'])
214
+
215
+ # Verify logging arguments were added
216
+ args = parser.parse_args(['--logging', 'DEBUG'])
217
+ assert args.logging == 'DEBUG'
218
+
219
+ # Test command registry
220
+ def test_command_registry():
221
+ arg_reg = ArgumentRegistry()
222
+ cmd_reg = CommandRegistry(arg_reg)
223
+
224
+ called = False
225
+ def test_handler(args, **kwargs):
226
+ nonlocal called
227
+ called = True
228
+ return 0
229
+
230
+ cmd_reg.register('test', 'Test command', test_handler)
231
+
232
+ parser = argparse.ArgumentParser()
233
+ cmd_reg.setup_subcommands(parser)
234
+
235
+ args = parser.parse_args(['test'])
236
+ result = cmd_reg.execute_command(args)
237
+
238
+ assert called
239
+ assert result == 0
240
+ ```
241
+
242
+ ## Migration Checklist
243
+
244
+ - [ ] Create `/src/claude_mpm/cli/` directory
245
+ - [ ] Create `args.py` with ArgumentRegistry
246
+ - [ ] Create `commands.py` with CommandRegistry
247
+ - [ ] Create `__init__.py` to export classes
248
+ - [ ] Update imports in `cli.py`
249
+ - [ ] Replace main() function
250
+ - [ ] Add helper functions
251
+ - [ ] Update command handler signatures
252
+ - [ ] Test the refactored CLI
253
+ - [ ] Verify complexity is reduced to ≤10