claude-mpm 4.13.2__py3-none-any.whl → 4.18.2__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 (250) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_ENGINEER.md +286 -0
  3. claude_mpm/agents/BASE_PM.md +48 -17
  4. claude_mpm/agents/OUTPUT_STYLE.md +329 -11
  5. claude_mpm/agents/PM_INSTRUCTIONS.md +227 -8
  6. claude_mpm/agents/agent_loader.py +17 -5
  7. claude_mpm/agents/frontmatter_validator.py +284 -253
  8. claude_mpm/agents/templates/agentic-coder-optimizer.json +9 -2
  9. claude_mpm/agents/templates/api_qa.json +7 -1
  10. claude_mpm/agents/templates/clerk-ops.json +8 -1
  11. claude_mpm/agents/templates/code_analyzer.json +4 -1
  12. claude_mpm/agents/templates/dart_engineer.json +11 -1
  13. claude_mpm/agents/templates/data_engineer.json +11 -1
  14. claude_mpm/agents/templates/documentation.json +6 -1
  15. claude_mpm/agents/templates/engineer.json +18 -1
  16. claude_mpm/agents/templates/gcp_ops_agent.json +8 -1
  17. claude_mpm/agents/templates/golang_engineer.json +11 -1
  18. claude_mpm/agents/templates/java_engineer.json +12 -2
  19. claude_mpm/agents/templates/local_ops_agent.json +1217 -6
  20. claude_mpm/agents/templates/nextjs_engineer.json +11 -1
  21. claude_mpm/agents/templates/ops.json +8 -1
  22. claude_mpm/agents/templates/php-engineer.json +11 -1
  23. claude_mpm/agents/templates/project_organizer.json +10 -3
  24. claude_mpm/agents/templates/prompt-engineer.json +5 -1
  25. claude_mpm/agents/templates/python_engineer.json +11 -1
  26. claude_mpm/agents/templates/qa.json +7 -1
  27. claude_mpm/agents/templates/react_engineer.json +11 -1
  28. claude_mpm/agents/templates/refactoring_engineer.json +8 -1
  29. claude_mpm/agents/templates/research.json +4 -1
  30. claude_mpm/agents/templates/ruby-engineer.json +11 -1
  31. claude_mpm/agents/templates/rust_engineer.json +11 -1
  32. claude_mpm/agents/templates/security.json +6 -1
  33. claude_mpm/agents/templates/svelte-engineer.json +225 -0
  34. claude_mpm/agents/templates/ticketing.json +6 -1
  35. claude_mpm/agents/templates/typescript_engineer.json +11 -1
  36. claude_mpm/agents/templates/vercel_ops_agent.json +8 -1
  37. claude_mpm/agents/templates/version_control.json +8 -1
  38. claude_mpm/agents/templates/web_qa.json +7 -1
  39. claude_mpm/agents/templates/web_ui.json +11 -1
  40. claude_mpm/cli/__init__.py +34 -706
  41. claude_mpm/cli/commands/agent_manager.py +25 -12
  42. claude_mpm/cli/commands/agent_state_manager.py +186 -0
  43. claude_mpm/cli/commands/agents.py +204 -148
  44. claude_mpm/cli/commands/aggregate.py +7 -3
  45. claude_mpm/cli/commands/analyze.py +9 -4
  46. claude_mpm/cli/commands/analyze_code.py +7 -2
  47. claude_mpm/cli/commands/auto_configure.py +7 -9
  48. claude_mpm/cli/commands/config.py +47 -13
  49. claude_mpm/cli/commands/configure.py +294 -1788
  50. claude_mpm/cli/commands/configure_agent_display.py +261 -0
  51. claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
  52. claude_mpm/cli/commands/configure_hook_manager.py +225 -0
  53. claude_mpm/cli/commands/configure_models.py +18 -0
  54. claude_mpm/cli/commands/configure_navigation.py +167 -0
  55. claude_mpm/cli/commands/configure_paths.py +104 -0
  56. claude_mpm/cli/commands/configure_persistence.py +254 -0
  57. claude_mpm/cli/commands/configure_startup_manager.py +646 -0
  58. claude_mpm/cli/commands/configure_template_editor.py +497 -0
  59. claude_mpm/cli/commands/configure_validators.py +73 -0
  60. claude_mpm/cli/commands/local_deploy.py +537 -0
  61. claude_mpm/cli/commands/memory.py +54 -20
  62. claude_mpm/cli/commands/mpm_init.py +39 -25
  63. claude_mpm/cli/commands/mpm_init_handler.py +8 -3
  64. claude_mpm/cli/executor.py +202 -0
  65. claude_mpm/cli/helpers.py +105 -0
  66. claude_mpm/cli/interactive/__init__.py +3 -0
  67. claude_mpm/cli/interactive/skills_wizard.py +491 -0
  68. claude_mpm/cli/parsers/__init__.py +7 -1
  69. claude_mpm/cli/parsers/base_parser.py +98 -3
  70. claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
  71. claude_mpm/cli/shared/output_formatters.py +28 -19
  72. claude_mpm/cli/startup.py +481 -0
  73. claude_mpm/cli/utils.py +52 -1
  74. claude_mpm/commands/mpm-help.md +3 -0
  75. claude_mpm/commands/mpm-version.md +113 -0
  76. claude_mpm/commands/mpm.md +1 -0
  77. claude_mpm/config/agent_config.py +2 -2
  78. claude_mpm/config/model_config.py +428 -0
  79. claude_mpm/core/base_service.py +13 -12
  80. claude_mpm/core/enums.py +452 -0
  81. claude_mpm/core/factories.py +1 -1
  82. claude_mpm/core/instruction_reinforcement_hook.py +2 -1
  83. claude_mpm/core/interactive_session.py +9 -3
  84. claude_mpm/core/logging_config.py +6 -2
  85. claude_mpm/core/oneshot_session.py +8 -4
  86. claude_mpm/core/optimized_agent_loader.py +3 -3
  87. claude_mpm/core/output_style_manager.py +12 -192
  88. claude_mpm/core/service_registry.py +5 -1
  89. claude_mpm/core/types.py +2 -9
  90. claude_mpm/core/typing_utils.py +7 -6
  91. claude_mpm/dashboard/static/js/dashboard.js +0 -14
  92. claude_mpm/dashboard/templates/index.html +3 -41
  93. claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
  94. claude_mpm/hooks/instruction_reinforcement.py +7 -2
  95. claude_mpm/models/resume_log.py +340 -0
  96. claude_mpm/services/agents/auto_config_manager.py +10 -11
  97. claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
  98. claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
  99. claude_mpm/services/agents/deployment/agent_validator.py +17 -1
  100. claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
  101. claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
  102. claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
  103. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +7 -6
  104. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
  105. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
  106. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +5 -3
  107. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
  108. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +9 -6
  109. claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
  110. claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
  111. claude_mpm/services/agents/local_template_manager.py +1 -1
  112. claude_mpm/services/agents/memory/agent_memory_manager.py +5 -2
  113. claude_mpm/services/agents/registry/modification_tracker.py +5 -2
  114. claude_mpm/services/command_handler_service.py +11 -5
  115. claude_mpm/services/core/interfaces/__init__.py +74 -2
  116. claude_mpm/services/core/interfaces/health.py +172 -0
  117. claude_mpm/services/core/interfaces/model.py +281 -0
  118. claude_mpm/services/core/interfaces/process.py +372 -0
  119. claude_mpm/services/core/interfaces/restart.py +307 -0
  120. claude_mpm/services/core/interfaces/stability.py +260 -0
  121. claude_mpm/services/core/models/__init__.py +33 -0
  122. claude_mpm/services/core/models/agent_config.py +12 -28
  123. claude_mpm/services/core/models/health.py +162 -0
  124. claude_mpm/services/core/models/process.py +235 -0
  125. claude_mpm/services/core/models/restart.py +302 -0
  126. claude_mpm/services/core/models/stability.py +264 -0
  127. claude_mpm/services/core/path_resolver.py +23 -7
  128. claude_mpm/services/diagnostics/__init__.py +2 -2
  129. claude_mpm/services/diagnostics/checks/agent_check.py +25 -24
  130. claude_mpm/services/diagnostics/checks/claude_code_check.py +24 -23
  131. claude_mpm/services/diagnostics/checks/common_issues_check.py +25 -24
  132. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -23
  133. claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
  134. claude_mpm/services/diagnostics/checks/installation_check.py +30 -29
  135. claude_mpm/services/diagnostics/checks/instructions_check.py +20 -19
  136. claude_mpm/services/diagnostics/checks/mcp_check.py +50 -36
  137. claude_mpm/services/diagnostics/checks/mcp_services_check.py +36 -31
  138. claude_mpm/services/diagnostics/checks/monitor_check.py +23 -22
  139. claude_mpm/services/diagnostics/checks/startup_log_check.py +9 -8
  140. claude_mpm/services/diagnostics/diagnostic_runner.py +6 -5
  141. claude_mpm/services/diagnostics/doctor_reporter.py +28 -25
  142. claude_mpm/services/diagnostics/models.py +19 -24
  143. claude_mpm/services/infrastructure/monitoring/__init__.py +1 -1
  144. claude_mpm/services/infrastructure/monitoring/aggregator.py +12 -12
  145. claude_mpm/services/infrastructure/monitoring/base.py +5 -13
  146. claude_mpm/services/infrastructure/monitoring/network.py +7 -6
  147. claude_mpm/services/infrastructure/monitoring/process.py +13 -12
  148. claude_mpm/services/infrastructure/monitoring/resources.py +7 -6
  149. claude_mpm/services/infrastructure/monitoring/service.py +16 -15
  150. claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
  151. claude_mpm/services/local_ops/__init__.py +163 -0
  152. claude_mpm/services/local_ops/crash_detector.py +257 -0
  153. claude_mpm/services/local_ops/health_checks/__init__.py +28 -0
  154. claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
  155. claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
  156. claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
  157. claude_mpm/services/local_ops/health_manager.py +430 -0
  158. claude_mpm/services/local_ops/log_monitor.py +396 -0
  159. claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
  160. claude_mpm/services/local_ops/process_manager.py +595 -0
  161. claude_mpm/services/local_ops/resource_monitor.py +331 -0
  162. claude_mpm/services/local_ops/restart_manager.py +401 -0
  163. claude_mpm/services/local_ops/restart_policy.py +387 -0
  164. claude_mpm/services/local_ops/state_manager.py +372 -0
  165. claude_mpm/services/local_ops/unified_manager.py +600 -0
  166. claude_mpm/services/mcp_config_manager.py +9 -4
  167. claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
  168. claude_mpm/services/mcp_gateway/core/base.py +18 -31
  169. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +71 -24
  170. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +30 -28
  171. claude_mpm/services/memory_hook_service.py +4 -1
  172. claude_mpm/services/model/__init__.py +147 -0
  173. claude_mpm/services/model/base_provider.py +365 -0
  174. claude_mpm/services/model/claude_provider.py +412 -0
  175. claude_mpm/services/model/model_router.py +453 -0
  176. claude_mpm/services/model/ollama_provider.py +415 -0
  177. claude_mpm/services/monitor/daemon_manager.py +3 -2
  178. claude_mpm/services/monitor/handlers/dashboard.py +2 -1
  179. claude_mpm/services/monitor/handlers/hooks.py +2 -1
  180. claude_mpm/services/monitor/management/lifecycle.py +3 -2
  181. claude_mpm/services/monitor/server.py +2 -1
  182. claude_mpm/services/session_management_service.py +3 -2
  183. claude_mpm/services/session_manager.py +205 -1
  184. claude_mpm/services/shared/async_service_base.py +16 -27
  185. claude_mpm/services/shared/lifecycle_service_base.py +1 -14
  186. claude_mpm/services/socketio/handlers/__init__.py +5 -2
  187. claude_mpm/services/socketio/handlers/hook.py +13 -2
  188. claude_mpm/services/socketio/handlers/registry.py +4 -2
  189. claude_mpm/services/socketio/server/main.py +10 -8
  190. claude_mpm/services/subprocess_launcher_service.py +14 -5
  191. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +8 -7
  192. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +6 -5
  193. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +8 -7
  194. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +7 -6
  195. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +5 -4
  196. claude_mpm/services/unified/config_strategies/validation_strategy.py +13 -9
  197. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +10 -3
  198. claude_mpm/services/unified/deployment_strategies/local.py +6 -5
  199. claude_mpm/services/unified/deployment_strategies/utils.py +6 -5
  200. claude_mpm/services/unified/deployment_strategies/vercel.py +7 -6
  201. claude_mpm/services/unified/interfaces.py +3 -1
  202. claude_mpm/services/unified/unified_analyzer.py +14 -10
  203. claude_mpm/services/unified/unified_config.py +2 -1
  204. claude_mpm/services/unified/unified_deployment.py +9 -4
  205. claude_mpm/services/version_service.py +104 -1
  206. claude_mpm/skills/__init__.py +21 -0
  207. claude_mpm/skills/bundled/__init__.py +6 -0
  208. claude_mpm/skills/bundled/api-documentation.md +393 -0
  209. claude_mpm/skills/bundled/async-testing.md +571 -0
  210. claude_mpm/skills/bundled/code-review.md +143 -0
  211. claude_mpm/skills/bundled/database-migration.md +199 -0
  212. claude_mpm/skills/bundled/docker-containerization.md +194 -0
  213. claude_mpm/skills/bundled/express-local-dev.md +1429 -0
  214. claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
  215. claude_mpm/skills/bundled/git-workflow.md +414 -0
  216. claude_mpm/skills/bundled/imagemagick.md +204 -0
  217. claude_mpm/skills/bundled/json-data-handling.md +223 -0
  218. claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
  219. claude_mpm/skills/bundled/pdf.md +141 -0
  220. claude_mpm/skills/bundled/performance-profiling.md +567 -0
  221. claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
  222. claude_mpm/skills/bundled/security-scanning.md +327 -0
  223. claude_mpm/skills/bundled/systematic-debugging.md +473 -0
  224. claude_mpm/skills/bundled/test-driven-development.md +378 -0
  225. claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
  226. claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
  227. claude_mpm/skills/bundled/xlsx.md +157 -0
  228. claude_mpm/skills/registry.py +286 -0
  229. claude_mpm/skills/skill_manager.py +310 -0
  230. claude_mpm/tools/code_tree_analyzer.py +177 -141
  231. claude_mpm/tools/code_tree_events.py +4 -2
  232. claude_mpm/utils/agent_dependency_loader.py +2 -2
  233. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/METADATA +117 -8
  234. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/RECORD +238 -174
  235. claude_mpm/dashboard/static/css/code-tree.css +0 -1639
  236. claude_mpm/dashboard/static/js/components/code-tree/tree-breadcrumb.js +0 -353
  237. claude_mpm/dashboard/static/js/components/code-tree/tree-constants.js +0 -235
  238. claude_mpm/dashboard/static/js/components/code-tree/tree-search.js +0 -409
  239. claude_mpm/dashboard/static/js/components/code-tree/tree-utils.js +0 -435
  240. claude_mpm/dashboard/static/js/components/code-tree.js +0 -5869
  241. claude_mpm/dashboard/static/js/components/code-viewer.js +0 -1386
  242. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
  243. claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1041
  244. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
  245. claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
  246. claude_mpm/services/project/analyzer_refactored.py +0 -450
  247. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/WHEEL +0 -0
  248. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/entry_points.txt +0 -0
  249. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/licenses/LICENSE +0 -0
  250. {claude_mpm-4.13.2.dist-info → claude_mpm-4.18.2.dist-info}/top_level.txt +0 -0
@@ -1,290 +1,85 @@
1
- from pathlib import Path
2
- from typing import Optional
3
-
4
1
  """
5
2
  Claude MPM Command-Line Interface.
6
3
 
7
- WHY: This module serves as the main entry point for the CLI, coordinating
8
- argument parsing and command execution. It replaces the monolithic cli.py
9
- with a more modular structure.
4
+ Main entry point for CLI. Implementation details extracted to:
5
+ - cli/helpers.py: Configuration checks and prompts
6
+ - cli/startup.py: Initialization (registry, MCP, updates)
7
+ - cli/executor.py: Command execution routing
10
8
 
11
- DESIGN DECISION: We maintain backward compatibility by keeping the same
12
- interface while organizing code into logical modules. The main() function
13
- remains the primary entry point for both direct execution and package imports.
9
+ Refactored from 803 lines to <130 lines (TSK-0053).
14
10
  """
15
11
 
12
+ import os
16
13
  import sys
14
+ from pathlib import Path
15
+ from typing import Optional
17
16
 
18
17
  from claude_mpm.config.paths import paths
19
18
 
20
- from ..constants import CLICommands, LogLevel
21
- from .commands import ( # run_guarded_session is imported lazily to avoid loading experimental code
22
- aggregate_command,
23
- cleanup_memory,
24
- manage_agent_manager,
25
- manage_agents,
26
- manage_config,
27
- manage_configure,
28
- manage_debug,
29
- manage_mcp,
30
- manage_memory,
31
- manage_monitor,
32
- manage_tickets,
33
- run_doctor,
34
- run_session,
35
- show_info,
19
+ from ..constants import CLICommands
20
+ from .executor import ensure_run_attributes, execute_command
21
+ from .helpers import (
22
+ handle_missing_configuration,
23
+ has_configuration_file,
24
+ should_skip_config_check,
36
25
  )
37
- from .commands.analyze_code import manage_analyze_code
38
- from .commands.dashboard import manage_dashboard
39
- from .commands.upgrade import upgrade
40
26
  from .parser import create_parser, preprocess_args
27
+ from .startup import (
28
+ run_background_services,
29
+ setup_configure_command_environment,
30
+ setup_early_environment,
31
+ setup_mcp_server_logging,
32
+ should_skip_background_services,
33
+ )
41
34
  from .utils import ensure_directories, setup_logging
42
35
 
43
- # Get version using centralized path management
44
- # Try package VERSION file first (for installed packages)
36
+ # Version resolution
45
37
  package_version_file = Path(__file__).parent.parent / "VERSION"
46
38
  if package_version_file.exists():
47
39
  __version__ = package_version_file.read_text().strip()
48
- # Use centralized path management for VERSION file
49
40
  elif paths.version_file.exists():
50
41
  __version__ = paths.version_file.read_text().strip()
51
42
  else:
52
- # Try to import from package as fallback
53
43
  try:
54
44
  from .. import __version__
55
45
  except ImportError:
56
- # Default version if all else fails
57
46
  __version__ = "0.0.0"
58
47
 
59
48
 
60
- def _has_configuration_file() -> bool:
61
- """Check if any configuration file exists in standard locations."""
62
- config_paths = [
63
- Path.cwd() / ".claude-mpm" / "configuration.yaml",
64
- Path.cwd() / ".claude-mpm" / "configuration.yml",
65
- Path.home() / ".claude-mpm" / "configuration.yaml",
66
- Path.home() / ".claude-mpm" / "configuration.yml",
67
- ]
68
- return any(path.exists() for path in config_paths)
69
-
70
-
71
- def _is_interactive_session() -> bool:
72
- """Check if running in an interactive terminal."""
73
- return sys.stdin.isatty() and sys.stdout.isatty()
74
-
75
-
76
- def _should_skip_config_check(command: Optional[str]) -> bool:
77
- """Check if command should skip configuration check."""
78
- skip_commands = ["configure", "doctor", "info", "mcp", "config"]
79
- return command in skip_commands if command else False
80
-
81
-
82
- def _prompt_for_configuration() -> bool:
83
- """Prompt user to run configuration wizard.
84
-
85
- Returns:
86
- bool: True if user wants to configure, False otherwise
87
- """
88
- from rich.console import Console as RichConsole
89
- from rich.prompt import Confirm
90
-
91
- console = RichConsole()
92
-
93
- console.print()
94
- console.print("[yellow]⚙️ Configuration Recommended[/yellow]")
95
- console.print()
96
- console.print("Claude MPM works best with proper configuration.")
97
- console.print("The configurator helps you:")
98
- console.print(" • Enable/disable MCP services (ticketer, browser, vector-search)")
99
- console.print(" • Configure hook services (monitor, dashboard)")
100
- console.print(" • Select system agents to use")
101
- console.print()
102
-
103
- return Confirm.ask("Would you like to run the configurator now?", default=True)
104
-
105
-
106
49
  def main(argv: Optional[list] = None):
107
- """
108
- Main CLI entry point.
109
-
110
- WHY: This function orchestrates the entire CLI flow:
111
- 1. Ensures directories exist
112
- 2. Preprocesses arguments (handling --mpm: prefix)
113
- 3. Parses arguments
114
- 4. Sets up logging
115
- 5. Executes the appropriate command
116
-
117
- DESIGN DECISION: We keep error handling at this level to provide consistent
118
- error messages and exit codes across all commands.
119
-
120
- Args:
121
- argv: Optional list of command line arguments for testing
122
-
123
- Returns:
124
- Exit code (0 for success, non-zero for errors)
125
- """
126
- # Disable telemetry by default (set early in case any imported modules check it)
127
- import os
128
-
129
- os.environ.setdefault("DISABLE_TELEMETRY", "1")
130
-
131
- # Set environment variable BEFORE parsing args to prevent cleanup noise during configure
132
- # This will be checked later after we know the command
133
- os.environ.setdefault("CLAUDE_MPM_SKIP_CLEANUP", "0")
134
-
135
- # EARLY CHECK: If 'configure' is in argv, suppress logging IMMEDIATELY
136
- # This must happen BEFORE any imports that trigger logging
137
- if argv is None:
138
- argv = sys.argv[1:]
139
- if "configure" in argv or (len(argv) > 0 and argv[0] == "configure"):
140
- import logging
141
-
142
- logging.getLogger("claude_mpm").setLevel(logging.WARNING)
143
- os.environ["CLAUDE_MPM_SKIP_CLEANUP"] = "1"
50
+ """Main CLI entry point orchestrating argument parsing and command execution."""
51
+ argv = setup_early_environment(argv)
144
52
 
145
- # Parse args early to check if we should skip background services
146
- # (for commands like --version, --help, configure, etc.)
147
53
  parser = create_parser(version=__version__)
148
54
  processed_argv = preprocess_args(argv)
149
55
  args = parser.parse_args(processed_argv)
150
56
 
151
- # Check for configuration file on startup
152
- # Skip for help/version flags or specific commands
153
57
  help_version_flags = ["--version", "-v", "--help", "-h"]
154
58
  is_help_or_version = any(
155
59
  flag in (processed_argv or sys.argv[1:]) for flag in help_version_flags
156
60
  )
157
61
 
158
- if not _has_configuration_file() and not is_help_or_version:
159
- # Skip check for certain commands
160
- if not _should_skip_config_check(getattr(args, "command", None)):
161
- if _is_interactive_session():
162
- # Interactive: Offer to run configurator
163
- if _prompt_for_configuration():
164
- # User wants to configure - run configure command
165
- from rich.console import Console as RichConsole
166
-
167
- from .commands.configure import ConfigureCommand
168
-
169
- console = RichConsole()
170
- config_cmd = ConfigureCommand()
171
- try:
172
- config_cmd.execute({})
173
- console.print(
174
- "\n[green]✓[/green] Configuration complete! "
175
- "Continuing with command...\n"
176
- )
177
- except Exception as e:
178
- console.print(f"\n[yellow]⚠[/yellow] Configuration error: {e}")
179
- console.print(
180
- "[yellow]Continuing with default "
181
- "configuration...\n[/yellow]"
182
- )
183
- else:
184
- from rich.console import Console as RichConsole
185
-
186
- console = RichConsole()
187
- console.print("\n[dim]Using default configuration.[/dim]")
188
- console.print(
189
- "[dim]Run 'claude-mpm configure' anytime to set up your "
190
- "preferences.\n[/dim]"
191
- )
192
- else:
193
- # Non-interactive: Log message only
194
- import logging
195
-
196
- logger = logging.getLogger(__name__)
197
- logger.info(
198
- "Configuration file not found, using defaults. "
199
- "Run 'claude-mpm configure' to customize."
200
- )
201
-
202
- # Skip background services for configure command - it needs clean state
203
- # Also skip for help/version/diagnostic commands
204
- skip_background_services_commands = ["--version", "-v", "--help", "-h"]
205
- should_skip_background = any(
206
- cmd in (processed_argv or sys.argv[1:])
207
- for cmd in skip_background_services_commands
208
- ) or (
209
- hasattr(args, "command")
210
- and args.command in ["info", "doctor", "config", "mcp", "configure"]
211
- )
212
-
213
- # Set environment variable to skip cleanup for configure command
214
- # Also suppress INFO logging to prevent noise during configure
215
- if hasattr(args, "command") and args.command == "configure":
216
- os.environ["CLAUDE_MPM_SKIP_CLEANUP"] = "1"
217
- # Suppress INFO logs for configure command
218
- import logging
219
-
220
- logging.getLogger("claude_mpm").setLevel(logging.WARNING)
62
+ if not has_configuration_file() and not is_help_or_version:
63
+ if not should_skip_config_check(getattr(args, "command", None)):
64
+ handle_missing_configuration()
221
65
 
222
- # Only initialize background services if not skipping
223
- if not should_skip_background:
224
- # Ensure directories are initialized on first run
225
- ensure_directories()
66
+ setup_configure_command_environment(args)
226
67
 
227
- # Initialize or update project registry
228
- _initialize_project_registry()
68
+ ensure_directories()
69
+ if not should_skip_background_services(args, processed_argv):
70
+ run_background_services()
71
+ logger = setup_mcp_server_logging(args)
229
72
 
230
- # Check for MCP auto-configuration (pipx installations)
231
- _check_mcp_auto_configuration()
232
-
233
- # Re-enabled: MCP pre-warming is safe with subprocess daemon (v4.2.40)
234
- # The subprocess approach avoids fork() issues entirely
235
- _verify_mcp_gateway_startup()
236
-
237
- # Check for updates (non-blocking, async)
238
- _check_for_updates_async()
239
- else:
240
- # Still need directories for configure command to work
241
- ensure_directories()
242
-
243
- # Set up logging
244
- # Special case: For MCP start command, we need minimal logging to avoid stdout interference
245
- if (
246
- args.command == CLICommands.MCP.value
247
- and getattr(args, "mcp_command", None) == "start"
248
- ):
249
- # For MCP server, configure minimal stderr-only logging
250
- import logging
251
-
252
- # sys is already imported at module level
253
- # Only log errors to stderr for MCP server
254
- if not getattr(args, "test", False) and not getattr(
255
- args, "instructions", False
256
- ):
257
- # Production MCP mode - minimal logging
258
- logging.basicConfig(
259
- level=logging.ERROR, format="%(message)s", stream=sys.stderr, force=True
260
- )
261
- logger = logging.getLogger("claude_mpm")
262
- else:
263
- # Test or instructions mode - normal logging
264
- logger = setup_logging(args)
265
- else:
266
- # Normal logging for all other commands
267
- logger = setup_logging(args)
268
-
269
- # Debug output if requested
270
73
  if hasattr(args, "debug") and args.debug:
271
74
  logger.debug(f"Command: {args.command}")
272
75
  logger.debug(f"Arguments: {args}")
273
76
 
274
- # Hook system note: Claude Code hooks are handled externally via the
275
- # hook_handler.py script installed in ~/.claude/settings.json
276
- # The --no-hooks flag is kept for backward compatibility but doesn't affect
277
- # Claude Code hooks which are configured separately.
278
-
279
- # Default to run command if no command specified
280
77
  if not args.command:
281
78
  args.command = CLICommands.RUN.value
282
- # Ensure run-specific attributes exist when defaulting to run
283
- _ensure_run_attributes(args)
79
+ ensure_run_attributes(args)
284
80
 
285
- # Execute command
286
81
  try:
287
- return _execute_command(args.command, args)
82
+ return execute_command(args.command, args)
288
83
  except KeyboardInterrupt:
289
84
  logger.info("Session interrupted by user")
290
85
  return 0
@@ -297,473 +92,6 @@ def main(argv: Optional[list] = None):
297
92
  return 1
298
93
 
299
94
 
300
- def _initialize_project_registry():
301
- """
302
- Initialize or update the project registry for the current session.
303
-
304
- WHY: The project registry tracks all claude-mpm projects and their metadata
305
- across sessions. This function ensures the current project is properly
306
- registered and updates session information.
307
-
308
- DESIGN DECISION: Registry failures are logged but don't prevent startup
309
- to ensure claude-mpm remains functional even if registry operations fail.
310
- """
311
- try:
312
- from ..services.project.registry import ProjectRegistry
313
-
314
- registry = ProjectRegistry()
315
- registry.get_or_create_project_entry()
316
- except Exception as e:
317
- # Import logger here to avoid circular imports
318
- from ..core.logger import get_logger
319
-
320
- logger = get_logger("cli")
321
- logger.debug(f"Failed to initialize project registry: {e}")
322
- # Continue execution - registry failure shouldn't block startup
323
-
324
-
325
- def _check_mcp_auto_configuration():
326
- """
327
- Check and potentially auto-configure MCP for pipx installations.
328
-
329
- WHY: Users installing via pipx should have MCP work out-of-the-box with
330
- minimal friction. This function offers one-time auto-configuration with
331
- user consent.
332
-
333
- DESIGN DECISION: This is blocking but quick - it only runs once and has
334
- a 10-second timeout. We want to catch users on first run for the best
335
- experience.
336
- """
337
- try:
338
- from ..services.mcp_gateway.auto_configure import check_and_configure_mcp
339
-
340
- # This function handles all the logic:
341
- # - Checks if already configured
342
- # - Checks if pipx installation
343
- # - Checks if already asked before
344
- # - Prompts user if needed
345
- # - Configures if user agrees
346
- check_and_configure_mcp()
347
-
348
- except Exception as e:
349
- # Non-critical - log but don't fail
350
- from ..core.logger import get_logger
351
-
352
- logger = get_logger("cli")
353
- logger.debug(f"MCP auto-configuration check failed: {e}")
354
-
355
- # Skip MCP service fixes for the doctor and configure commands
356
- # The doctor command performs its own comprehensive MCP service check
357
- # The configure command allows users to configure which services to enable
358
- # Running both would cause duplicate checks and log messages (9 seconds apart)
359
- import sys
360
-
361
- if len(sys.argv) > 1 and sys.argv[1] in ("doctor", "configure"):
362
- return
363
-
364
- # Also ensure MCP services are properly configured in ~/.claude.json
365
- # This fixes incorrect paths and adds missing services
366
- try:
367
- from ..core.logger import get_logger
368
- from ..services.mcp_config_manager import MCPConfigManager
369
-
370
- logger = get_logger("cli")
371
- mcp_manager = MCPConfigManager()
372
-
373
- # Fix any corrupted installations first
374
- fix_success, fix_message = mcp_manager.fix_mcp_service_issues()
375
- if fix_message and "Fixed:" in fix_message:
376
- logger.info(f"MCP service fixes applied: {fix_message}")
377
-
378
- # Ensure all services are configured correctly
379
- config_success, config_message = mcp_manager.ensure_mcp_services_configured()
380
- if config_message and "Added MCP services" in config_message:
381
- logger.info(f"MCP services configured: {config_message}")
382
-
383
- except Exception as e:
384
- # Non-critical - log but don't fail
385
- from ..core.logger import get_logger
386
-
387
- logger = get_logger("cli")
388
- logger.debug(f"MCP services configuration update failed: {e}")
389
-
390
-
391
- def _verify_mcp_gateway_startup():
392
- """
393
- Verify MCP Gateway configuration on startup and pre-warm MCP services.
394
-
395
- WHY: The MCP gateway should be automatically configured and verified on startup
396
- to provide a seamless experience with diagnostic tools, file summarizer, and
397
- ticket service. Pre-warming MCP services eliminates the 11.9s delay on first use.
398
-
399
- DESIGN DECISION: This is non-blocking - failures are logged but don't prevent
400
- startup to ensure claude-mpm remains functional even if MCP gateway has issues.
401
- """
402
- # Quick verification of MCP services installation
403
- try:
404
- from ..core.logger import get_logger
405
- from ..services.mcp_service_verifier import verify_mcp_services_on_startup
406
-
407
- logger = get_logger("mcp_verify")
408
- all_ok, message = verify_mcp_services_on_startup()
409
- if not all_ok:
410
- logger.warning(message)
411
- except Exception:
412
- # Non-critical - continue with startup
413
- pass
414
-
415
- try:
416
- import asyncio
417
- import time
418
-
419
- from ..core.logger import get_logger
420
- from ..services.mcp_gateway.core.process_pool import pre_warm_mcp_servers
421
- from ..services.mcp_gateway.core.startup_verification import (
422
- is_mcp_gateway_configured,
423
- verify_mcp_gateway_on_startup,
424
- )
425
-
426
- logger = get_logger("mcp_prewarm")
427
-
428
- # Quick check first - if already configured, skip detailed verification
429
- gateway_configured = is_mcp_gateway_configured()
430
-
431
- # DISABLED: Pre-warming MCP servers can interfere with Claude Code's MCP management
432
- # This was causing issues with MCP server initialization and stderr handling
433
- # def run_pre_warming():
434
- # loop = None
435
- # try:
436
- # start_time = time.time()
437
- # loop = asyncio.new_event_loop()
438
- # asyncio.set_event_loop(loop)
439
- #
440
- # # Pre-warm MCP servers (especially vector search)
441
- # logger.info("Pre-warming MCP servers to eliminate startup delay...")
442
- # loop.run_until_complete(pre_warm_mcp_servers())
443
- #
444
- # pre_warm_time = time.time() - start_time
445
- # if pre_warm_time > 1.0:
446
- # logger.info(f"MCP servers pre-warmed in {pre_warm_time:.2f}s")
447
-
448
- # Dummy function to maintain structure
449
- def run_pre_warming():
450
- loop = None
451
- try:
452
- time.time()
453
- loop = asyncio.new_event_loop()
454
- asyncio.set_event_loop(loop)
455
-
456
- # Also run gateway verification if needed
457
- if not gateway_configured:
458
- loop.run_until_complete(verify_mcp_gateway_on_startup())
459
-
460
- except Exception as e:
461
- # Non-blocking - log but don't fail
462
- logger.debug(f"MCP pre-warming error (non-critical): {e}")
463
- finally:
464
- # Properly clean up event loop to prevent kqueue warnings
465
- if loop is not None:
466
- try:
467
- # Cancel all running tasks
468
- pending = asyncio.all_tasks(loop)
469
- for task in pending:
470
- task.cancel()
471
- # Wait for tasks to complete cancellation
472
- if pending:
473
- loop.run_until_complete(
474
- asyncio.gather(*pending, return_exceptions=True)
475
- )
476
- except Exception:
477
- pass # Ignore cleanup errors
478
- finally:
479
- loop.close()
480
- # Clear the event loop reference to help with cleanup
481
- asyncio.set_event_loop(None)
482
-
483
- # Run pre-warming in background thread
484
- import threading
485
-
486
- pre_warm_thread = threading.Thread(target=run_pre_warming, daemon=True)
487
- pre_warm_thread.start()
488
-
489
- return
490
-
491
- # Run detailed verification in background if not configured
492
- if not gateway_configured:
493
- # Note: We don't await this to avoid blocking startup
494
- def run_verification():
495
- loop = None
496
- try:
497
- loop = asyncio.new_event_loop()
498
- asyncio.set_event_loop(loop)
499
- results = loop.run_until_complete(verify_mcp_gateway_on_startup())
500
-
501
- # Log results but don't block
502
- from ..core.logger import get_logger
503
-
504
- logger = get_logger("cli")
505
-
506
- if results.get("gateway_configured"):
507
- logger.debug("MCP Gateway verification completed successfully")
508
- else:
509
- logger.debug("MCP Gateway verification completed with warnings")
510
-
511
- except Exception as e:
512
- from ..core.logger import get_logger
513
-
514
- logger = get_logger("cli")
515
- logger.debug(f"MCP Gateway verification failed: {e}")
516
- finally:
517
- # Properly clean up event loop to prevent kqueue warnings
518
- if loop is not None:
519
- try:
520
- # Cancel all running tasks
521
- pending = asyncio.all_tasks(loop)
522
- for task in pending:
523
- task.cancel()
524
- # Wait for tasks to complete cancellation
525
- if pending:
526
- loop.run_until_complete(
527
- asyncio.gather(*pending, return_exceptions=True)
528
- )
529
- except Exception:
530
- pass # Ignore cleanup errors
531
- finally:
532
- loop.close()
533
- # Clear the event loop reference to help with cleanup
534
- asyncio.set_event_loop(None)
535
-
536
- # Run in background thread to avoid blocking startup
537
- import threading
538
-
539
- verification_thread = threading.Thread(target=run_verification, daemon=True)
540
- verification_thread.start()
541
-
542
- except Exception as e:
543
- # Import logger here to avoid circular imports
544
- from ..core.logger import get_logger
545
-
546
- logger = get_logger("cli")
547
- logger.debug(f"Failed to start MCP Gateway verification: {e}")
548
- # Continue execution - MCP gateway issues shouldn't block startup
549
-
550
-
551
- def _check_for_updates_async():
552
- """
553
- Check for updates in background thread (non-blocking).
554
-
555
- WHY: Users should be notified of new versions and have an easy way to upgrade
556
- without manually checking PyPI/npm. This runs asynchronously on startup to avoid
557
- blocking the CLI.
558
-
559
- DESIGN DECISION: This is non-blocking and non-critical - failures are logged
560
- but don't prevent startup. Only runs for pip/pipx/npm installations, skips
561
- editable/development installations.
562
- """
563
-
564
- def run_update_check():
565
- """Inner function to run in background thread."""
566
- loop = None
567
- try:
568
- import asyncio
569
-
570
- from ..core.logger import get_logger
571
- from ..services.self_upgrade_service import SelfUpgradeService
572
-
573
- logger = get_logger("upgrade_check")
574
-
575
- # Create new event loop for this thread
576
- loop = asyncio.new_event_loop()
577
- asyncio.set_event_loop(loop)
578
-
579
- # Create upgrade service and check for updates
580
- upgrade_service = SelfUpgradeService()
581
-
582
- # Skip for editable installs (development mode)
583
- from ..services.self_upgrade_service import InstallationMethod
584
-
585
- if upgrade_service.installation_method == InstallationMethod.EDITABLE:
586
- logger.debug("Skipping version check for editable installation")
587
- return
588
-
589
- # Check and prompt for upgrade if available (non-blocking)
590
- loop.run_until_complete(upgrade_service.check_and_prompt_on_startup())
591
-
592
- except Exception as e:
593
- # Non-critical - log but don't fail startup
594
- try:
595
- from ..core.logger import get_logger
596
-
597
- logger = get_logger("upgrade_check")
598
- logger.debug(f"Update check failed (non-critical): {e}")
599
- except Exception:
600
- pass # Avoid any errors in error handling
601
- finally:
602
- # Properly clean up event loop
603
- if loop is not None:
604
- try:
605
- # Cancel all running tasks
606
- pending = asyncio.all_tasks(loop)
607
- for task in pending:
608
- task.cancel()
609
- # Wait for tasks to complete cancellation
610
- if pending:
611
- loop.run_until_complete(
612
- asyncio.gather(*pending, return_exceptions=True)
613
- )
614
- except Exception:
615
- pass # Ignore cleanup errors
616
- finally:
617
- loop.close()
618
- # Clear the event loop reference to help with cleanup
619
- asyncio.set_event_loop(None)
620
-
621
- # Run update check in background thread to avoid blocking startup
622
- import threading
623
-
624
- update_check_thread = threading.Thread(target=run_update_check, daemon=True)
625
- update_check_thread.start()
626
-
627
-
628
- def _ensure_run_attributes(args):
629
- """
630
- Ensure run command attributes exist when defaulting to run.
631
-
632
- WHY: When no command is specified, we default to 'run' but the args object
633
- won't have run-specific attributes from the subparser. This function ensures
634
- they exist with sensible defaults.
635
-
636
- Args:
637
- args: Parsed arguments object to update
638
- """
639
- # Set defaults for run command attributes
640
- args.no_tickets = getattr(args, "no_tickets", False)
641
- args.no_hooks = getattr(args, "no_hooks", False)
642
- args.intercept_commands = getattr(args, "intercept_commands", False)
643
- args.input = getattr(args, "input", None)
644
- args.non_interactive = getattr(args, "non_interactive", False)
645
- args.no_native_agents = getattr(args, "no_native_agents", False)
646
-
647
- # Handle claude_args - if --resume flag is set, add it to claude_args
648
- claude_args = getattr(args, "claude_args", [])
649
- if getattr(args, "resume", False):
650
- # Add --resume to claude_args if not already present
651
- if "--resume" not in claude_args:
652
- claude_args = ["--resume", *claude_args]
653
- args.claude_args = claude_args
654
-
655
- args.launch_method = getattr(args, "launch_method", "exec")
656
- args.websocket = getattr(args, "websocket", False)
657
- args.websocket_port = getattr(args, "websocket_port", 8765)
658
- # CRITICAL: Include mpm_resume attribute for session resumption
659
- args.mpm_resume = getattr(args, "mpm_resume", None)
660
- # Also include monitor and force attributes
661
- args.monitor = getattr(args, "monitor", False)
662
- args.force = getattr(args, "force", False)
663
- args.reload_agents = getattr(args, "reload_agents", False)
664
- # Include dependency checking attributes
665
- args.check_dependencies = getattr(args, "check_dependencies", True)
666
- args.force_check_dependencies = getattr(args, "force_check_dependencies", False)
667
- args.no_prompt = getattr(args, "no_prompt", False)
668
- args.force_prompt = getattr(args, "force_prompt", False)
669
-
670
-
671
- def _execute_command(command: str, args) -> int:
672
- """
673
- Execute the specified command.
674
-
675
- WHY: This function maps command names to their implementations, providing
676
- a single place to manage command routing. Experimental commands are imported
677
- lazily to avoid loading unnecessary code.
678
-
679
- DESIGN DECISION: run_guarded is imported only when needed to maintain
680
- separation between stable and experimental features.
681
-
682
- Args:
683
- command: The command name to execute
684
- args: Parsed command line arguments
685
-
686
- Returns:
687
- Exit code from the command
688
- """
689
- # Handle experimental run-guarded command separately with lazy import
690
- if command == "run-guarded":
691
- # Lazy import to avoid loading experimental code unless needed
692
- from .commands.run_guarded import execute_run_guarded
693
-
694
- result = execute_run_guarded(args)
695
- return result if result is not None else 0
696
-
697
- # Handle mpm-init command with lazy import
698
- if command == "mpm-init":
699
- # Lazy import to avoid loading unless needed
700
- from .commands.mpm_init_handler import manage_mpm_init
701
-
702
- result = manage_mpm_init(args)
703
- return result if result is not None else 0
704
-
705
- # Handle uninstall command with lazy import
706
- if command == "uninstall":
707
- # Lazy import to avoid loading unless needed
708
- from .commands.uninstall import UninstallCommand
709
-
710
- cmd = UninstallCommand()
711
- result = cmd.execute(args)
712
- # Convert CommandResult to exit code
713
- return result.exit_code if result else 0
714
-
715
- # Handle verify command with lazy import
716
- if command == "verify":
717
- # Lazy import to avoid loading unless needed
718
- from .commands.verify import handle_verify
719
-
720
- result = handle_verify(args)
721
- return result if result is not None else 0
722
-
723
- # Handle auto-configure command with lazy import
724
- if command == "auto-configure":
725
- # Lazy import to avoid loading unless needed
726
- from .commands.auto_configure import AutoConfigureCommand
727
-
728
- cmd = AutoConfigureCommand()
729
- result = cmd.run(args)
730
- # Convert CommandResult to exit code
731
- return result.exit_code if result else 0
732
-
733
- # Map stable commands to their implementations
734
- command_map = {
735
- CLICommands.RUN.value: run_session,
736
- # CLICommands.RUN_GUARDED.value is handled above
737
- CLICommands.TICKETS.value: manage_tickets,
738
- CLICommands.INFO.value: show_info,
739
- CLICommands.AGENTS.value: manage_agents,
740
- CLICommands.AGENT_MANAGER.value: manage_agent_manager,
741
- CLICommands.MEMORY.value: manage_memory,
742
- CLICommands.MONITOR.value: manage_monitor,
743
- CLICommands.DASHBOARD.value: manage_dashboard,
744
- CLICommands.CONFIG.value: manage_config,
745
- CLICommands.CONFIGURE.value: manage_configure,
746
- CLICommands.AGGREGATE.value: aggregate_command,
747
- CLICommands.ANALYZE_CODE.value: manage_analyze_code,
748
- CLICommands.CLEANUP.value: cleanup_memory,
749
- CLICommands.MCP.value: manage_mcp,
750
- CLICommands.DOCTOR.value: run_doctor,
751
- CLICommands.UPGRADE.value: upgrade,
752
- "debug": manage_debug, # Add debug command
753
- "mpm-init": None, # Will be handled separately with lazy import
754
- }
755
-
756
- # Execute command if found
757
- if command in command_map:
758
- result = command_map[command](args)
759
- # Commands may return None (success) or an exit code
760
- return result if result is not None else 0
761
- # Unknown command - this shouldn't happen with argparse
762
- # but we handle it for completeness
763
- print(f"Unknown command: {command}")
764
- return 1
765
-
766
-
767
95
  # For backward compatibility - export main
768
96
  if __name__ == "__main__":
769
97
  sys.exit(main())