claude-mpm 0.3.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.

Potentially problematic release.


This version of claude-mpm might be problematic. Click here for more details.

Files changed (159) hide show
  1. claude_mpm/__init__.py +17 -0
  2. claude_mpm/__main__.py +14 -0
  3. claude_mpm/_version.py +32 -0
  4. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +88 -0
  5. claude_mpm/agents/INSTRUCTIONS.md +375 -0
  6. claude_mpm/agents/__init__.py +118 -0
  7. claude_mpm/agents/agent_loader.py +621 -0
  8. claude_mpm/agents/agent_loader_integration.py +229 -0
  9. claude_mpm/agents/agents_metadata.py +204 -0
  10. claude_mpm/agents/base_agent.json +27 -0
  11. claude_mpm/agents/base_agent_loader.py +519 -0
  12. claude_mpm/agents/schema/agent_schema.json +160 -0
  13. claude_mpm/agents/system_agent_config.py +587 -0
  14. claude_mpm/agents/templates/__init__.py +101 -0
  15. claude_mpm/agents/templates/data_engineer_agent.json +46 -0
  16. claude_mpm/agents/templates/documentation_agent.json +45 -0
  17. claude_mpm/agents/templates/engineer_agent.json +49 -0
  18. claude_mpm/agents/templates/ops_agent.json +46 -0
  19. claude_mpm/agents/templates/qa_agent.json +45 -0
  20. claude_mpm/agents/templates/research_agent.json +49 -0
  21. claude_mpm/agents/templates/security_agent.json +46 -0
  22. claude_mpm/agents/templates/update-optimized-specialized-agents.json +374 -0
  23. claude_mpm/agents/templates/version_control_agent.json +46 -0
  24. claude_mpm/agents/test_fix_deployment/.claude-pm/config/project.json +6 -0
  25. claude_mpm/cli.py +655 -0
  26. claude_mpm/cli_main.py +13 -0
  27. claude_mpm/cli_module/__init__.py +15 -0
  28. claude_mpm/cli_module/args.py +222 -0
  29. claude_mpm/cli_module/commands.py +203 -0
  30. claude_mpm/cli_module/migration_example.py +183 -0
  31. claude_mpm/cli_module/refactoring_guide.md +253 -0
  32. claude_mpm/cli_old/__init__.py +1 -0
  33. claude_mpm/cli_old/ticket_cli.py +102 -0
  34. claude_mpm/config/__init__.py +5 -0
  35. claude_mpm/config/hook_config.py +42 -0
  36. claude_mpm/constants.py +150 -0
  37. claude_mpm/core/__init__.py +45 -0
  38. claude_mpm/core/agent_name_normalizer.py +248 -0
  39. claude_mpm/core/agent_registry.py +627 -0
  40. claude_mpm/core/agent_registry.py.bak +312 -0
  41. claude_mpm/core/agent_session_manager.py +273 -0
  42. claude_mpm/core/base_service.py +747 -0
  43. claude_mpm/core/base_service.py.bak +406 -0
  44. claude_mpm/core/config.py +334 -0
  45. claude_mpm/core/config_aliases.py +292 -0
  46. claude_mpm/core/container.py +347 -0
  47. claude_mpm/core/factories.py +281 -0
  48. claude_mpm/core/framework_loader.py +472 -0
  49. claude_mpm/core/injectable_service.py +206 -0
  50. claude_mpm/core/interfaces.py +539 -0
  51. claude_mpm/core/logger.py +468 -0
  52. claude_mpm/core/minimal_framework_loader.py +107 -0
  53. claude_mpm/core/mixins.py +150 -0
  54. claude_mpm/core/service_registry.py +299 -0
  55. claude_mpm/core/session_manager.py +190 -0
  56. claude_mpm/core/simple_runner.py +511 -0
  57. claude_mpm/core/tool_access_control.py +173 -0
  58. claude_mpm/hooks/README.md +243 -0
  59. claude_mpm/hooks/__init__.py +5 -0
  60. claude_mpm/hooks/base_hook.py +154 -0
  61. claude_mpm/hooks/builtin/__init__.py +1 -0
  62. claude_mpm/hooks/builtin/logging_hook_example.py +165 -0
  63. claude_mpm/hooks/builtin/post_delegation_hook_example.py +124 -0
  64. claude_mpm/hooks/builtin/pre_delegation_hook_example.py +125 -0
  65. claude_mpm/hooks/builtin/submit_hook_example.py +100 -0
  66. claude_mpm/hooks/builtin/ticket_extraction_hook_example.py +237 -0
  67. claude_mpm/hooks/builtin/todo_agent_prefix_hook.py +239 -0
  68. claude_mpm/hooks/builtin/workflow_start_hook.py +181 -0
  69. claude_mpm/hooks/hook_client.py +264 -0
  70. claude_mpm/hooks/hook_runner.py +370 -0
  71. claude_mpm/hooks/json_rpc_executor.py +259 -0
  72. claude_mpm/hooks/json_rpc_hook_client.py +319 -0
  73. claude_mpm/hooks/tool_call_interceptor.py +204 -0
  74. claude_mpm/init.py +246 -0
  75. claude_mpm/orchestration/SUBPROCESS_DESIGN.md +66 -0
  76. claude_mpm/orchestration/__init__.py +6 -0
  77. claude_mpm/orchestration/archive/direct_orchestrator.py +195 -0
  78. claude_mpm/orchestration/archive/factory.py +215 -0
  79. claude_mpm/orchestration/archive/hook_enabled_orchestrator.py +188 -0
  80. claude_mpm/orchestration/archive/hook_integration_example.py +178 -0
  81. claude_mpm/orchestration/archive/interactive_subprocess_orchestrator.py +826 -0
  82. claude_mpm/orchestration/archive/orchestrator.py +501 -0
  83. claude_mpm/orchestration/archive/pexpect_orchestrator.py +252 -0
  84. claude_mpm/orchestration/archive/pty_orchestrator.py +270 -0
  85. claude_mpm/orchestration/archive/simple_orchestrator.py +82 -0
  86. claude_mpm/orchestration/archive/subprocess_orchestrator.py +801 -0
  87. claude_mpm/orchestration/archive/system_prompt_orchestrator.py +278 -0
  88. claude_mpm/orchestration/archive/wrapper_orchestrator.py +187 -0
  89. claude_mpm/scripts/__init__.py +1 -0
  90. claude_mpm/scripts/ticket.py +269 -0
  91. claude_mpm/services/__init__.py +10 -0
  92. claude_mpm/services/agent_deployment.py +955 -0
  93. claude_mpm/services/agent_lifecycle_manager.py +948 -0
  94. claude_mpm/services/agent_management_service.py +596 -0
  95. claude_mpm/services/agent_modification_tracker.py +841 -0
  96. claude_mpm/services/agent_profile_loader.py +606 -0
  97. claude_mpm/services/agent_registry.py +677 -0
  98. claude_mpm/services/base_agent_manager.py +380 -0
  99. claude_mpm/services/framework_agent_loader.py +337 -0
  100. claude_mpm/services/framework_claude_md_generator/README.md +92 -0
  101. claude_mpm/services/framework_claude_md_generator/__init__.py +206 -0
  102. claude_mpm/services/framework_claude_md_generator/content_assembler.py +151 -0
  103. claude_mpm/services/framework_claude_md_generator/content_validator.py +126 -0
  104. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +137 -0
  105. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +106 -0
  106. claude_mpm/services/framework_claude_md_generator/section_generators/agents.py +582 -0
  107. claude_mpm/services/framework_claude_md_generator/section_generators/claude_pm_init.py +97 -0
  108. claude_mpm/services/framework_claude_md_generator/section_generators/core_responsibilities.py +27 -0
  109. claude_mpm/services/framework_claude_md_generator/section_generators/delegation_constraints.py +23 -0
  110. claude_mpm/services/framework_claude_md_generator/section_generators/environment_config.py +23 -0
  111. claude_mpm/services/framework_claude_md_generator/section_generators/footer.py +20 -0
  112. claude_mpm/services/framework_claude_md_generator/section_generators/header.py +26 -0
  113. claude_mpm/services/framework_claude_md_generator/section_generators/orchestration_principles.py +30 -0
  114. claude_mpm/services/framework_claude_md_generator/section_generators/role_designation.py +37 -0
  115. claude_mpm/services/framework_claude_md_generator/section_generators/subprocess_validation.py +111 -0
  116. claude_mpm/services/framework_claude_md_generator/section_generators/todo_task_tools.py +89 -0
  117. claude_mpm/services/framework_claude_md_generator/section_generators/troubleshooting.py +39 -0
  118. claude_mpm/services/framework_claude_md_generator/section_manager.py +106 -0
  119. claude_mpm/services/framework_claude_md_generator/version_manager.py +121 -0
  120. claude_mpm/services/framework_claude_md_generator.py +621 -0
  121. claude_mpm/services/hook_service.py +388 -0
  122. claude_mpm/services/hook_service_manager.py +223 -0
  123. claude_mpm/services/json_rpc_hook_manager.py +92 -0
  124. claude_mpm/services/parent_directory_manager/README.md +83 -0
  125. claude_mpm/services/parent_directory_manager/__init__.py +577 -0
  126. claude_mpm/services/parent_directory_manager/backup_manager.py +258 -0
  127. claude_mpm/services/parent_directory_manager/config_manager.py +210 -0
  128. claude_mpm/services/parent_directory_manager/deduplication_manager.py +279 -0
  129. claude_mpm/services/parent_directory_manager/framework_protector.py +143 -0
  130. claude_mpm/services/parent_directory_manager/operations.py +186 -0
  131. claude_mpm/services/parent_directory_manager/state_manager.py +624 -0
  132. claude_mpm/services/parent_directory_manager/template_deployer.py +579 -0
  133. claude_mpm/services/parent_directory_manager/validation_manager.py +378 -0
  134. claude_mpm/services/parent_directory_manager/version_control_helper.py +339 -0
  135. claude_mpm/services/parent_directory_manager/version_manager.py +222 -0
  136. claude_mpm/services/shared_prompt_cache.py +819 -0
  137. claude_mpm/services/ticket_manager.py +213 -0
  138. claude_mpm/services/ticket_manager_di.py +318 -0
  139. claude_mpm/services/ticketing_service_original.py +508 -0
  140. claude_mpm/services/version_control/VERSION +1 -0
  141. claude_mpm/services/version_control/__init__.py +70 -0
  142. claude_mpm/services/version_control/branch_strategy.py +670 -0
  143. claude_mpm/services/version_control/conflict_resolution.py +744 -0
  144. claude_mpm/services/version_control/git_operations.py +784 -0
  145. claude_mpm/services/version_control/semantic_versioning.py +703 -0
  146. claude_mpm/ui/__init__.py +1 -0
  147. claude_mpm/ui/rich_terminal_ui.py +295 -0
  148. claude_mpm/ui/terminal_ui.py +328 -0
  149. claude_mpm/utils/__init__.py +16 -0
  150. claude_mpm/utils/config_manager.py +468 -0
  151. claude_mpm/utils/import_migration_example.py +80 -0
  152. claude_mpm/utils/imports.py +182 -0
  153. claude_mpm/utils/path_operations.py +357 -0
  154. claude_mpm/utils/paths.py +289 -0
  155. claude_mpm-0.3.0.dist-info/METADATA +290 -0
  156. claude_mpm-0.3.0.dist-info/RECORD +159 -0
  157. claude_mpm-0.3.0.dist-info/WHEEL +5 -0
  158. claude_mpm-0.3.0.dist-info/entry_points.txt +4 -0
  159. claude_mpm-0.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,222 @@
1
+ """Argument registry for consolidating CLI argument definitions."""
2
+
3
+ import argparse
4
+ from pathlib import Path
5
+ from typing import Dict, Any, Optional, Callable
6
+
7
+
8
+ class ArgumentRegistry:
9
+ """Registry for managing CLI argument definitions.
10
+
11
+ This class consolidates argument definitions to reduce duplication
12
+ and simplify the main CLI function.
13
+ """
14
+
15
+ def __init__(self):
16
+ """Initialize the registry with common argument groups."""
17
+ self._arguments: Dict[str, Dict[str, Any]] = {}
18
+ self._groups: Dict[str, list] = {
19
+ 'global': [],
20
+ 'run': [],
21
+ 'logging': [],
22
+ 'framework': [],
23
+ 'orchestration': []
24
+ }
25
+ self._register_common_arguments()
26
+
27
+ def _register_common_arguments(self):
28
+ """Register all common argument definitions."""
29
+ # Version argument
30
+ self.register('version', {
31
+ 'flags': ['--version'],
32
+ 'action': 'version',
33
+ 'help': 'Show version and exit',
34
+ 'groups': ['global']
35
+ })
36
+
37
+ # Logging arguments
38
+ self.register('debug', {
39
+ 'flags': ['-d', '--debug'],
40
+ 'action': 'store_true',
41
+ 'help': 'Enable debug logging (deprecated, use --logging DEBUG)',
42
+ 'groups': ['logging', 'global']
43
+ })
44
+
45
+ self.register('logging', {
46
+ 'flags': ['--logging'],
47
+ 'choices': ['OFF', 'INFO', 'DEBUG'],
48
+ 'default': 'OFF',
49
+ 'help': 'Logging level (default: OFF)',
50
+ 'groups': ['logging', 'global']
51
+ })
52
+
53
+ self.register('log_dir', {
54
+ 'flags': ['--log-dir'],
55
+ 'type': Path,
56
+ 'help': 'Custom log directory (default: ~/.claude-mpm/logs)',
57
+ 'groups': ['logging', 'global']
58
+ })
59
+
60
+ # Framework arguments
61
+ self.register('framework_path', {
62
+ 'flags': ['--framework-path'],
63
+ 'type': Path,
64
+ 'help': 'Path to claude-mpm framework',
65
+ 'groups': ['framework', 'global']
66
+ })
67
+
68
+ self.register('agents_dir', {
69
+ 'flags': ['--agents-dir'],
70
+ 'type': Path,
71
+ 'help': 'Custom agents directory to use',
72
+ 'groups': ['framework', 'global']
73
+ })
74
+
75
+ self.register('no_hooks', {
76
+ 'flags': ['--no-hooks'],
77
+ 'action': 'store_true',
78
+ 'help': 'Disable hook service (runs without hooks)',
79
+ 'groups': ['framework', 'global', 'run']
80
+ })
81
+
82
+ # Run command arguments
83
+ self.register('no_tickets', {
84
+ 'flags': ['--no-tickets'],
85
+ 'action': 'store_true',
86
+ 'help': 'Disable automatic ticket creation',
87
+ 'groups': ['run']
88
+ })
89
+
90
+ self.register('input', {
91
+ 'flags': ['-i', '--input'],
92
+ 'type': str,
93
+ 'help': 'Input text or file path (for non-interactive mode)',
94
+ 'groups': ['run']
95
+ })
96
+
97
+ self.register('non_interactive', {
98
+ 'flags': ['--non-interactive'],
99
+ 'action': 'store_true',
100
+ 'help': 'Run in non-interactive mode (read from stdin or --input)',
101
+ 'groups': ['run']
102
+ })
103
+
104
+ # Orchestration arguments
105
+ self.register('subprocess', {
106
+ 'flags': ['--subprocess'],
107
+ 'action': 'store_true',
108
+ 'help': 'Use subprocess orchestration for agent delegations',
109
+ 'groups': ['orchestration', 'run']
110
+ })
111
+
112
+ self.register('interactive_subprocess', {
113
+ 'flags': ['--interactive-subprocess'],
114
+ 'action': 'store_true',
115
+ 'help': 'Use interactive subprocess orchestration with pexpect control',
116
+ 'groups': ['orchestration', 'run']
117
+ })
118
+
119
+ self.register('todo_hijack', {
120
+ 'flags': ['--todo-hijack'],
121
+ 'action': 'store_true',
122
+ 'help': 'Enable TODO hijacking to transform Claude\'s TODOs into agent delegations',
123
+ 'groups': ['orchestration', 'run']
124
+ })
125
+
126
+ # Ticket list arguments
127
+ self.register('limit', {
128
+ 'flags': ['-n', '--limit'],
129
+ 'type': int,
130
+ 'default': 10,
131
+ 'help': 'Number of tickets to show',
132
+ 'groups': ['tickets']
133
+ })
134
+
135
+ def register(self, name: str, definition: Dict[str, Any]):
136
+ """Register a new argument definition.
137
+
138
+ Args:
139
+ name: Internal name for the argument
140
+ definition: Dictionary containing argument configuration:
141
+ - flags: List of flag strings (e.g., ['-d', '--debug'])
142
+ - groups: List of group names this argument belongs to
143
+ - All other kwargs are passed to add_argument()
144
+ """
145
+ self._arguments[name] = definition
146
+
147
+ # Add to groups
148
+ for group in definition.get('groups', []):
149
+ if group not in self._groups:
150
+ self._groups[group] = []
151
+ self._groups[group].append(name)
152
+
153
+ def apply_arguments(self, parser: argparse.ArgumentParser,
154
+ groups: Optional[list] = None,
155
+ exclude: Optional[list] = None):
156
+ """Apply argument definitions to a parser.
157
+
158
+ Args:
159
+ parser: ArgumentParser or subparser to add arguments to
160
+ groups: List of group names to include (None = all)
161
+ exclude: List of argument names to exclude
162
+ """
163
+ # Determine which arguments to add
164
+ if groups is None:
165
+ arg_names = list(self._arguments.keys())
166
+ else:
167
+ arg_names = []
168
+ for group in groups:
169
+ arg_names.extend(self._groups.get(group, []))
170
+ # Remove duplicates while preserving order
171
+ seen = set()
172
+ arg_names = [x for x in arg_names if not (x in seen or seen.add(x))]
173
+
174
+ # Apply exclusions
175
+ if exclude:
176
+ arg_names = [n for n in arg_names if n not in exclude]
177
+
178
+ # Add arguments to parser
179
+ for name in arg_names:
180
+ definition = self._arguments[name].copy()
181
+ flags = definition.pop('flags')
182
+ definition.pop('groups', None) # Remove groups from kwargs
183
+
184
+ # Special handling for version
185
+ if name == 'version' and hasattr(parser, '_version'):
186
+ definition['version'] = parser._version
187
+
188
+ parser.add_argument(*flags, **definition)
189
+
190
+ def get_argument_groups(self) -> Dict[str, list]:
191
+ """Get all argument groups and their members."""
192
+ return self._groups.copy()
193
+
194
+ def get_argument_definition(self, name: str) -> Optional[Dict[str, Any]]:
195
+ """Get the definition for a specific argument."""
196
+ return self._arguments.get(name, {}).copy()
197
+
198
+ def create_argument_group(self, parser: argparse.ArgumentParser,
199
+ group_name: str,
200
+ title: str,
201
+ description: Optional[str] = None):
202
+ """Create an argument group and add arguments from a named group.
203
+
204
+ Args:
205
+ parser: Parser to add the group to
206
+ group_name: Name of the argument group in registry
207
+ title: Title for the argument group
208
+ description: Optional description for the group
209
+ """
210
+ group = parser.add_argument_group(title, description)
211
+
212
+ # Get arguments for this group
213
+ arg_names = self._groups.get(group_name, [])
214
+
215
+ for name in arg_names:
216
+ definition = self._arguments[name].copy()
217
+ flags = definition.pop('flags')
218
+ definition.pop('groups', None)
219
+
220
+ group.add_argument(*flags, **definition)
221
+
222
+ return group
@@ -0,0 +1,203 @@
1
+ """Command registry for managing CLI subcommands."""
2
+
3
+ import argparse
4
+ from typing import Dict, Any, Optional, Callable, List
5
+ from .args import ArgumentRegistry
6
+
7
+
8
+ class CommandDefinition:
9
+ """Definition for a CLI command."""
10
+
11
+ def __init__(self, name: str, help_text: str,
12
+ handler: Callable,
13
+ argument_groups: Optional[List[str]] = None,
14
+ extra_args: Optional[Dict[str, Dict[str, Any]]] = None):
15
+ """Initialize command definition.
16
+
17
+ Args:
18
+ name: Command name
19
+ help_text: Help text for the command
20
+ handler: Function to call when command is executed
21
+ argument_groups: List of argument group names to include
22
+ extra_args: Additional command-specific arguments
23
+ """
24
+ self.name = name
25
+ self.help_text = help_text
26
+ self.handler = handler
27
+ self.argument_groups = argument_groups or []
28
+ self.extra_args = extra_args or {}
29
+
30
+
31
+ class CommandRegistry:
32
+ """Registry for managing CLI subcommands.
33
+
34
+ This class simplifies subcommand management and reduces
35
+ complexity in the main CLI function.
36
+ """
37
+
38
+ def __init__(self, arg_registry: Optional[ArgumentRegistry] = None):
39
+ """Initialize the command registry.
40
+
41
+ Args:
42
+ arg_registry: ArgumentRegistry instance for applying arguments
43
+ """
44
+ self._commands: Dict[str, CommandDefinition] = {}
45
+ self._arg_registry = arg_registry or ArgumentRegistry()
46
+ self._default_command: Optional[str] = None
47
+
48
+ def register(self, name: str, help_text: str, handler: Callable,
49
+ argument_groups: Optional[List[str]] = None,
50
+ extra_args: Optional[Dict[str, Dict[str, Any]]] = None,
51
+ is_default: bool = False):
52
+ """Register a new command.
53
+
54
+ Args:
55
+ name: Command name
56
+ help_text: Help text for the command
57
+ handler: Function to call when command is executed
58
+ argument_groups: List of argument group names from ArgumentRegistry
59
+ extra_args: Additional command-specific arguments
60
+ is_default: Whether this is the default command
61
+ """
62
+ command = CommandDefinition(
63
+ name=name,
64
+ help_text=help_text,
65
+ handler=handler,
66
+ argument_groups=argument_groups,
67
+ extra_args=extra_args
68
+ )
69
+
70
+ self._commands[name] = command
71
+
72
+ if is_default:
73
+ self._default_command = name
74
+
75
+ def setup_subcommands(self, parser: argparse.ArgumentParser) -> argparse._SubParsersAction:
76
+ """Set up all registered subcommands on the parser.
77
+
78
+ Args:
79
+ parser: Main argument parser
80
+
81
+ Returns:
82
+ The subparsers object
83
+ """
84
+ subparsers = parser.add_subparsers(
85
+ dest="command",
86
+ help="Available commands"
87
+ )
88
+
89
+ for name, command in self._commands.items():
90
+ # Create subparser
91
+ subparser = subparsers.add_parser(
92
+ name,
93
+ help=command.help_text
94
+ )
95
+
96
+ # Apply argument groups
97
+ if command.argument_groups:
98
+ self._arg_registry.apply_arguments(
99
+ subparser,
100
+ groups=command.argument_groups
101
+ )
102
+
103
+ # Apply extra arguments
104
+ for arg_name, arg_config in command.extra_args.items():
105
+ flags = arg_config.pop('flags', [f'--{arg_name.replace("_", "-")}'])
106
+ subparser.add_argument(*flags, **arg_config)
107
+
108
+ # Store handler reference
109
+ subparser.set_defaults(_handler=command.handler)
110
+
111
+ return subparsers
112
+
113
+ def execute_command(self, args: argparse.Namespace, **kwargs) -> Any:
114
+ """Execute the appropriate command handler.
115
+
116
+ Args:
117
+ args: Parsed command line arguments
118
+ **kwargs: Additional keyword arguments to pass to handler
119
+
120
+ Returns:
121
+ Result from the command handler
122
+ """
123
+ # Handle default command
124
+ command_name = args.command
125
+ if not command_name and self._default_command:
126
+ command_name = self._default_command
127
+ args.command = command_name
128
+
129
+ # Apply default command's argument defaults
130
+ command = self._commands.get(command_name)
131
+ if command:
132
+ # Set defaults for arguments that might not be present
133
+ for group in command.argument_groups:
134
+ for arg_name in self._arg_registry._groups.get(group, []):
135
+ if not hasattr(args, arg_name):
136
+ definition = self._arg_registry.get_argument_definition(arg_name)
137
+ if definition and 'default' in definition:
138
+ setattr(args, arg_name, definition['default'])
139
+ elif definition and definition.get('action') == 'store_true':
140
+ setattr(args, arg_name, False)
141
+
142
+ # Get command handler
143
+ if hasattr(args, '_handler'):
144
+ handler = args._handler
145
+ else:
146
+ command = self._commands.get(command_name)
147
+ if command:
148
+ handler = command.handler
149
+ else:
150
+ return None
151
+
152
+ # Execute handler
153
+ return handler(args, **kwargs)
154
+
155
+ def get_command_names(self) -> List[str]:
156
+ """Get list of all registered command names."""
157
+ return list(self._commands.keys())
158
+
159
+ def get_default_command(self) -> Optional[str]:
160
+ """Get the default command name."""
161
+ return self._default_command
162
+
163
+ def has_command(self, name: str) -> bool:
164
+ """Check if a command is registered."""
165
+ return name in self._commands
166
+
167
+
168
+ def register_standard_commands(registry: CommandRegistry):
169
+ """Register the standard claude-mpm commands.
170
+
171
+ This function registers all the standard commands that claude-mpm
172
+ supports, reducing the setup code in main().
173
+
174
+ Args:
175
+ registry: CommandRegistry instance to register commands with
176
+ """
177
+ # Import handlers (these would be refactored from cli.py)
178
+ from ..cli import run_session, list_tickets, show_info
179
+
180
+ # Register run command (default)
181
+ registry.register(
182
+ name='run',
183
+ help_text='Run orchestrated Claude session (default)',
184
+ handler=run_session,
185
+ argument_groups=['run', 'orchestration'],
186
+ is_default=True
187
+ )
188
+
189
+ # Register tickets command
190
+ registry.register(
191
+ name='tickets',
192
+ help_text='List recent tickets',
193
+ handler=list_tickets,
194
+ argument_groups=['tickets']
195
+ )
196
+
197
+ # Register info command
198
+ registry.register(
199
+ name='info',
200
+ help_text='Show framework and configuration info',
201
+ handler=show_info,
202
+ argument_groups=[] # No specific arguments needed
203
+ )
@@ -0,0 +1,183 @@
1
+ """Example migration showing how to refactor main() using registries.
2
+
3
+ This file demonstrates how to reduce the complexity of the main() function
4
+ from 16 to under 10 by using ArgumentRegistry and CommandRegistry.
5
+ """
6
+
7
+ import argparse
8
+ import sys
9
+ from pathlib import Path
10
+ from typing import Optional
11
+
12
+ from claude_mpm._version import __version__
13
+ from claude_mpm.core.logger import get_logger, setup_logging
14
+ from claude_mpm.services.hook_service_manager import HookServiceManager
15
+ from claude_mpm.cli import ArgumentRegistry, CommandRegistry, register_standard_commands
16
+
17
+
18
+ def main_refactored(argv: Optional[list] = None):
19
+ """Refactored main CLI entry point with reduced complexity.
20
+
21
+ This version uses registries to manage arguments and commands,
22
+ reducing cyclomatic complexity from 16 to approximately 8.
23
+ """
24
+ # Initialize registries
25
+ arg_registry = ArgumentRegistry()
26
+ cmd_registry = CommandRegistry(arg_registry)
27
+
28
+ # Register standard commands
29
+ register_standard_commands(cmd_registry)
30
+
31
+ # Create parser with basic info
32
+ parser = argparse.ArgumentParser(
33
+ prog="claude-mpm",
34
+ description=f"Claude Multi-Agent Project Manager v{__version__} - Orchestrate Claude with agent delegation and ticket tracking",
35
+ epilog="By default, runs an orchestrated Claude session. Use 'claude-mpm' for interactive mode or 'claude-mpm -i \"prompt\"' for non-interactive mode."
36
+ )
37
+
38
+ # Store version for ArgumentRegistry
39
+ parser._version = f"claude-mpm {__version__}"
40
+
41
+ # Apply global arguments
42
+ arg_registry.apply_arguments(parser, groups=['global'])
43
+
44
+ # Apply run arguments at top level (for default behavior)
45
+ arg_registry.apply_arguments(parser, groups=['run'], exclude=['no_hooks'])
46
+
47
+ # Set up subcommands
48
+ cmd_registry.setup_subcommands(parser)
49
+
50
+ # Parse arguments
51
+ args = parser.parse_args(argv)
52
+
53
+ # Set up logging
54
+ _setup_logging(args)
55
+
56
+ # Initialize hook service
57
+ hook_manager = _initialize_hook_service(args)
58
+
59
+ try:
60
+ # Execute command
61
+ result = cmd_registry.execute_command(args, hook_manager=hook_manager)
62
+ if result is None and not args.command:
63
+ parser.print_help()
64
+ return 1
65
+ return result or 0
66
+
67
+ except KeyboardInterrupt:
68
+ get_logger("cli").info("Session interrupted by user")
69
+ return 0
70
+ except Exception as e:
71
+ logger = get_logger("cli")
72
+ logger.error(f"Error: {e}")
73
+ if args.debug:
74
+ import traceback
75
+ traceback.print_exc()
76
+ return 1
77
+ finally:
78
+ # Clean up hook service
79
+ if hook_manager:
80
+ hook_manager.stop_service()
81
+
82
+
83
+ def _setup_logging(args):
84
+ """Set up logging based on arguments (extracted helper)."""
85
+ # Handle deprecated --debug flag
86
+ if args.debug and args.logging == "OFF":
87
+ args.logging = "DEBUG"
88
+
89
+ # Only setup logging if not OFF
90
+ if args.logging != "OFF":
91
+ setup_logging(level=args.logging, log_dir=args.log_dir)
92
+ else:
93
+ # Minimal logger for CLI feedback
94
+ import logging
95
+ logger = logging.getLogger("cli")
96
+ logger.setLevel(logging.WARNING)
97
+
98
+
99
+ def _initialize_hook_service(args):
100
+ """Initialize hook service if enabled (extracted helper)."""
101
+ if getattr(args, 'no_hooks', False):
102
+ return None
103
+
104
+ try:
105
+ # Check if hooks are enabled via config
106
+ from claude_mpm.config.hook_config import HookConfig
107
+
108
+ if not HookConfig.is_hooks_enabled():
109
+ get_logger("cli").info("Hooks disabled via configuration")
110
+ return None
111
+
112
+ hook_manager = HookServiceManager(log_dir=args.log_dir)
113
+ if hook_manager.start_service():
114
+ logger = get_logger("cli")
115
+ logger.info(f"Hook service started on port {hook_manager.port}")
116
+ print(f"Hook service started on port {hook_manager.port}")
117
+ return hook_manager
118
+ else:
119
+ logger = get_logger("cli")
120
+ logger.warning("Failed to start hook service, continuing without hooks")
121
+ print("Failed to start hook service, continuing without hooks")
122
+ return None
123
+
124
+ except Exception as e:
125
+ get_logger("cli").warning(f"Hook service initialization failed: {e}, continuing without hooks")
126
+ return None
127
+
128
+
129
+ # Example of how to add custom commands
130
+ def extend_with_custom_commands(cmd_registry: CommandRegistry):
131
+ """Example of adding custom commands to the registry."""
132
+
133
+ def validate_command(args, **kwargs):
134
+ """Custom validation command."""
135
+ print("Running validation...")
136
+ # Implementation here
137
+ return 0
138
+
139
+ # Register custom command
140
+ cmd_registry.register(
141
+ name='validate',
142
+ help_text='Validate project configuration and agent setup',
143
+ handler=validate_command,
144
+ argument_groups=['framework'],
145
+ extra_args={
146
+ 'strict': {
147
+ 'flags': ['--strict'],
148
+ 'action': 'store_true',
149
+ 'help': 'Enable strict validation mode'
150
+ }
151
+ }
152
+ )
153
+
154
+
155
+ # Comparison with original main() function
156
+ def complexity_comparison():
157
+ """Show the complexity reduction achieved.
158
+
159
+ Original main() function:
160
+ - Cyclomatic complexity: 16
161
+ - Lines of code: ~230
162
+ - Duplicate argument definitions: 6 (for 'run' command)
163
+ - Mixed concerns: argument parsing, logging, hook service, command dispatch
164
+
165
+ Refactored main_refactored() function:
166
+ - Cyclomatic complexity: ~8
167
+ - Lines of code: ~50
168
+ - Duplicate argument definitions: 0
169
+ - Separated concerns: registries handle definitions, helpers handle setup
170
+
171
+ Benefits:
172
+ 1. Centralized argument definitions eliminate duplication
173
+ 2. Command registry simplifies adding new commands
174
+ 3. Extracted helpers reduce nesting and conditionals
175
+ 4. Easier to test individual components
176
+ 5. Clear separation of concerns
177
+ """
178
+ pass
179
+
180
+
181
+ if __name__ == "__main__":
182
+ # Example usage of refactored main
183
+ sys.exit(main_refactored())