claude-mpm 4.5.8__py3-none-any.whl → 4.5.12__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 (226) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +20 -5
  3. claude_mpm/agents/agent_loader.py +19 -2
  4. claude_mpm/agents/base_agent_loader.py +5 -5
  5. claude_mpm/agents/frontmatter_validator.py +4 -4
  6. claude_mpm/agents/templates/agent-manager.json +3 -3
  7. claude_mpm/agents/templates/agentic-coder-optimizer.json +3 -3
  8. claude_mpm/agents/templates/api_qa.json +1 -1
  9. claude_mpm/agents/templates/clerk-ops.json +3 -3
  10. claude_mpm/agents/templates/code_analyzer.json +3 -3
  11. claude_mpm/agents/templates/dart_engineer.json +294 -0
  12. claude_mpm/agents/templates/data_engineer.json +3 -3
  13. claude_mpm/agents/templates/documentation.json +2 -2
  14. claude_mpm/agents/templates/engineer.json +2 -2
  15. claude_mpm/agents/templates/gcp_ops_agent.json +2 -2
  16. claude_mpm/agents/templates/imagemagick.json +1 -1
  17. claude_mpm/agents/templates/local_ops_agent.json +319 -41
  18. claude_mpm/agents/templates/memory_manager.json +2 -2
  19. claude_mpm/agents/templates/nextjs_engineer.json +2 -2
  20. claude_mpm/agents/templates/ops.json +2 -2
  21. claude_mpm/agents/templates/php-engineer.json +1 -1
  22. claude_mpm/agents/templates/project_organizer.json +1 -1
  23. claude_mpm/agents/templates/prompt-engineer.json +6 -4
  24. claude_mpm/agents/templates/python_engineer.json +2 -2
  25. claude_mpm/agents/templates/qa.json +1 -1
  26. claude_mpm/agents/templates/react_engineer.json +3 -3
  27. claude_mpm/agents/templates/refactoring_engineer.json +3 -3
  28. claude_mpm/agents/templates/research.json +2 -2
  29. claude_mpm/agents/templates/security.json +2 -2
  30. claude_mpm/agents/templates/ticketing.json +2 -2
  31. claude_mpm/agents/templates/typescript_engineer.json +2 -2
  32. claude_mpm/agents/templates/vercel_ops_agent.json +2 -2
  33. claude_mpm/agents/templates/version_control.json +2 -2
  34. claude_mpm/agents/templates/web_qa.json +6 -6
  35. claude_mpm/agents/templates/web_ui.json +3 -3
  36. claude_mpm/cli/__init__.py +49 -19
  37. claude_mpm/cli/commands/agent_manager.py +3 -3
  38. claude_mpm/cli/commands/agents.py +6 -6
  39. claude_mpm/cli/commands/aggregate.py +4 -4
  40. claude_mpm/cli/commands/analyze.py +2 -2
  41. claude_mpm/cli/commands/analyze_code.py +1 -1
  42. claude_mpm/cli/commands/cleanup.py +3 -3
  43. claude_mpm/cli/commands/config.py +2 -2
  44. claude_mpm/cli/commands/configure.py +605 -21
  45. claude_mpm/cli/commands/dashboard.py +1 -1
  46. claude_mpm/cli/commands/debug.py +3 -3
  47. claude_mpm/cli/commands/doctor.py +1 -1
  48. claude_mpm/cli/commands/mcp.py +7 -7
  49. claude_mpm/cli/commands/mcp_command_router.py +1 -1
  50. claude_mpm/cli/commands/mcp_config.py +2 -2
  51. claude_mpm/cli/commands/mcp_external_commands.py +2 -2
  52. claude_mpm/cli/commands/mcp_install_commands.py +3 -3
  53. claude_mpm/cli/commands/mcp_pipx_config.py +2 -2
  54. claude_mpm/cli/commands/mcp_setup_external.py +3 -3
  55. claude_mpm/cli/commands/monitor.py +1 -1
  56. claude_mpm/cli/commands/mpm_init_handler.py +1 -1
  57. claude_mpm/cli/interactive/agent_wizard.py +1 -1
  58. claude_mpm/cli/parsers/configure_parser.py +5 -0
  59. claude_mpm/cli/parsers/search_parser.py +1 -1
  60. claude_mpm/cli/shared/argument_patterns.py +2 -2
  61. claude_mpm/cli/shared/base_command.py +1 -1
  62. claude_mpm/cli/startup_logging.py +4 -4
  63. claude_mpm/config/experimental_features.py +4 -4
  64. claude_mpm/config/socketio_config.py +2 -2
  65. claude_mpm/core/__init__.py +53 -17
  66. claude_mpm/core/agent_session_manager.py +2 -2
  67. claude_mpm/core/api_validator.py +3 -3
  68. claude_mpm/core/base_service.py +10 -1
  69. claude_mpm/core/cache.py +2 -2
  70. claude_mpm/core/config.py +5 -5
  71. claude_mpm/core/config_aliases.py +4 -4
  72. claude_mpm/core/config_constants.py +1 -1
  73. claude_mpm/core/error_handler.py +1 -1
  74. claude_mpm/core/file_utils.py +5 -5
  75. claude_mpm/core/framework/formatters/capability_generator.py +5 -5
  76. claude_mpm/core/framework/loaders/agent_loader.py +1 -1
  77. claude_mpm/core/framework/processors/metadata_processor.py +1 -1
  78. claude_mpm/core/framework/processors/template_processor.py +3 -3
  79. claude_mpm/core/framework_loader.py +2 -2
  80. claude_mpm/core/log_manager.py +11 -4
  81. claude_mpm/core/logger.py +2 -2
  82. claude_mpm/core/optimized_startup.py +1 -1
  83. claude_mpm/core/output_style_manager.py +1 -1
  84. claude_mpm/core/service_registry.py +2 -2
  85. claude_mpm/core/session_manager.py +3 -3
  86. claude_mpm/core/shared/config_loader.py +1 -1
  87. claude_mpm/core/socketio_pool.py +2 -2
  88. claude_mpm/core/unified_agent_registry.py +2 -2
  89. claude_mpm/core/unified_config.py +6 -6
  90. claude_mpm/core/unified_paths.py +2 -2
  91. claude_mpm/dashboard/api/simple_directory.py +1 -1
  92. claude_mpm/generators/agent_profile_generator.py +1 -1
  93. claude_mpm/hooks/claude_hooks/event_handlers.py +2 -2
  94. claude_mpm/hooks/claude_hooks/installer.py +9 -9
  95. claude_mpm/hooks/claude_hooks/response_tracking.py +16 -11
  96. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +16 -13
  97. claude_mpm/hooks/claude_hooks/tool_analysis.py +2 -2
  98. claude_mpm/hooks/memory_integration_hook.py +1 -1
  99. claude_mpm/hooks/validation_hooks.py +1 -1
  100. claude_mpm/init.py +4 -4
  101. claude_mpm/models/agent_session.py +1 -1
  102. claude_mpm/scripts/socketio_daemon.py +5 -5
  103. claude_mpm/services/__init__.py +145 -161
  104. claude_mpm/services/agent_capabilities_service.py +1 -1
  105. claude_mpm/services/agents/agent_builder.py +4 -4
  106. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +1 -1
  107. claude_mpm/services/agents/deployment/agent_metrics_collector.py +1 -1
  108. claude_mpm/services/agents/deployment/agent_record_service.py +3 -3
  109. claude_mpm/services/agents/deployment/deployment_config_loader.py +21 -0
  110. claude_mpm/services/agents/deployment/deployment_wrapper.py +1 -1
  111. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +2 -2
  112. claude_mpm/services/agents/loading/agent_profile_loader.py +2 -2
  113. claude_mpm/services/agents/loading/base_agent_manager.py +12 -2
  114. claude_mpm/services/agents/local_template_manager.py +5 -5
  115. claude_mpm/services/agents/registry/deployed_agent_discovery.py +1 -1
  116. claude_mpm/services/agents/registry/modification_tracker.py +19 -11
  117. claude_mpm/services/async_session_logger.py +3 -3
  118. claude_mpm/services/claude_session_logger.py +4 -4
  119. claude_mpm/services/cli/agent_listing_service.py +3 -3
  120. claude_mpm/services/cli/agent_validation_service.py +1 -1
  121. claude_mpm/services/cli/session_manager.py +2 -2
  122. claude_mpm/services/core/path_resolver.py +1 -1
  123. claude_mpm/services/diagnostics/checks/agent_check.py +1 -1
  124. claude_mpm/services/diagnostics/checks/claude_code_check.py +2 -2
  125. claude_mpm/services/diagnostics/checks/common_issues_check.py +3 -3
  126. claude_mpm/services/diagnostics/checks/configuration_check.py +2 -2
  127. claude_mpm/services/diagnostics/checks/installation_check.py +1 -1
  128. claude_mpm/services/diagnostics/checks/mcp_check.py +1 -1
  129. claude_mpm/services/diagnostics/checks/mcp_services_check.py +9 -9
  130. claude_mpm/services/diagnostics/checks/monitor_check.py +1 -1
  131. claude_mpm/services/diagnostics/doctor_reporter.py +1 -1
  132. claude_mpm/services/event_aggregator.py +1 -1
  133. claude_mpm/services/event_bus/event_bus.py +7 -2
  134. claude_mpm/services/events/consumers/dead_letter.py +2 -2
  135. claude_mpm/services/framework_claude_md_generator/__init__.py +1 -1
  136. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +3 -3
  137. claude_mpm/services/framework_claude_md_generator/version_manager.py +1 -1
  138. claude_mpm/services/hook_installer_service.py +7 -7
  139. claude_mpm/services/infrastructure/context_preservation.py +7 -7
  140. claude_mpm/services/infrastructure/daemon_manager.py +5 -5
  141. claude_mpm/services/mcp_config_manager.py +169 -48
  142. claude_mpm/services/mcp_gateway/__init__.py +98 -94
  143. claude_mpm/services/mcp_gateway/auto_configure.py +5 -5
  144. claude_mpm/services/mcp_gateway/config/config_loader.py +2 -2
  145. claude_mpm/services/mcp_gateway/config/configuration.py +3 -3
  146. claude_mpm/services/mcp_gateway/core/process_pool.py +3 -3
  147. claude_mpm/services/mcp_gateway/core/singleton_manager.py +2 -2
  148. claude_mpm/services/mcp_gateway/core/startup_verification.py +1 -1
  149. claude_mpm/services/mcp_gateway/main.py +1 -1
  150. claude_mpm/services/mcp_gateway/registry/service_registry.py +4 -2
  151. claude_mpm/services/mcp_gateway/registry/tool_registry.py +2 -1
  152. claude_mpm/services/mcp_gateway/server/stdio_handler.py +1 -1
  153. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +1 -1
  154. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +1 -1
  155. claude_mpm/services/mcp_gateway/tools/hello_world.py +1 -1
  156. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +5 -5
  157. claude_mpm/services/mcp_gateway/utils/update_preferences.py +2 -2
  158. claude_mpm/services/mcp_service_verifier.py +1 -1
  159. claude_mpm/services/memory/builder.py +1 -1
  160. claude_mpm/services/memory/cache/shared_prompt_cache.py +2 -1
  161. claude_mpm/services/memory/indexed_memory.py +3 -3
  162. claude_mpm/services/monitor/daemon.py +1 -1
  163. claude_mpm/services/monitor/daemon_manager.py +9 -9
  164. claude_mpm/services/monitor/event_emitter.py +1 -1
  165. claude_mpm/services/monitor/handlers/file.py +1 -1
  166. claude_mpm/services/monitor/handlers/hooks.py +3 -3
  167. claude_mpm/services/monitor/management/lifecycle.py +7 -7
  168. claude_mpm/services/monitor/server.py +2 -2
  169. claude_mpm/services/orphan_detection.py +788 -0
  170. claude_mpm/services/port_manager.py +2 -2
  171. claude_mpm/services/project/analyzer.py +3 -3
  172. claude_mpm/services/project/archive_manager.py +13 -13
  173. claude_mpm/services/project/dependency_analyzer.py +4 -4
  174. claude_mpm/services/project/documentation_manager.py +4 -4
  175. claude_mpm/services/project/enhanced_analyzer.py +8 -8
  176. claude_mpm/services/project/registry.py +4 -4
  177. claude_mpm/services/project_port_allocator.py +597 -0
  178. claude_mpm/services/response_tracker.py +1 -1
  179. claude_mpm/services/session_management_service.py +1 -1
  180. claude_mpm/services/session_manager.py +6 -4
  181. claude_mpm/services/socketio/event_normalizer.py +1 -1
  182. claude_mpm/services/socketio/handlers/code_analysis.py +14 -12
  183. claude_mpm/services/socketio/handlers/file.py +1 -1
  184. claude_mpm/services/socketio/migration_utils.py +1 -1
  185. claude_mpm/services/socketio/server/core.py +1 -1
  186. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +1 -1
  187. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +4 -4
  188. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +1 -1
  189. claude_mpm/services/unified/config_strategies/config_schema.py +4 -4
  190. claude_mpm/services/unified/config_strategies/context_strategy.py +6 -6
  191. claude_mpm/services/unified/config_strategies/error_handling_strategy.py +10 -10
  192. claude_mpm/services/unified/config_strategies/file_loader_strategy.py +5 -5
  193. claude_mpm/services/unified/config_strategies/unified_config_service.py +8 -8
  194. claude_mpm/services/unified/config_strategies/validation_strategy.py +15 -15
  195. claude_mpm/services/unified/deployment_strategies/base.py +4 -4
  196. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +15 -15
  197. claude_mpm/services/unified/deployment_strategies/local.py +9 -9
  198. claude_mpm/services/unified/deployment_strategies/utils.py +9 -9
  199. claude_mpm/services/unified/deployment_strategies/vercel.py +7 -7
  200. claude_mpm/services/unified/unified_config.py +5 -5
  201. claude_mpm/services/unified/unified_deployment.py +2 -2
  202. claude_mpm/services/utility_service.py +1 -1
  203. claude_mpm/services/version_control/conflict_resolution.py +2 -2
  204. claude_mpm/services/version_control/git_operations.py +3 -3
  205. claude_mpm/services/version_control/semantic_versioning.py +13 -13
  206. claude_mpm/services/version_control/version_parser.py +1 -1
  207. claude_mpm/storage/state_storage.py +12 -13
  208. claude_mpm/tools/code_tree_analyzer.py +5 -5
  209. claude_mpm/tools/code_tree_builder.py +4 -4
  210. claude_mpm/tools/socketio_debug.py +1 -1
  211. claude_mpm/utils/agent_dependency_loader.py +4 -4
  212. claude_mpm/utils/common.py +2 -2
  213. claude_mpm/utils/config_manager.py +3 -3
  214. claude_mpm/utils/dependency_cache.py +2 -2
  215. claude_mpm/utils/dependency_strategies.py +6 -6
  216. claude_mpm/utils/file_utils.py +11 -11
  217. claude_mpm/utils/log_cleanup.py +1 -1
  218. claude_mpm/utils/path_operations.py +1 -1
  219. claude_mpm/validation/agent_validator.py +2 -2
  220. claude_mpm/validation/frontmatter_validator.py +1 -1
  221. {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/METADATA +1 -1
  222. {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/RECORD +226 -223
  223. {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/WHEEL +0 -0
  224. {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/entry_points.txt +0 -0
  225. {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/licenses/LICENSE +0 -0
  226. {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/top_level.txt +0 -0
@@ -65,7 +65,7 @@ class DashboardCommand(BaseCommand):
65
65
  self.logger.error(f"Error executing dashboard command: {e}", exc_info=True)
66
66
  return CommandResult.error_result(f"Error executing dashboard command: {e}")
67
67
 
68
- def _start_dashboard(self, args) -> CommandResult: # noqa: PLR0911
68
+ def _start_dashboard(self, args) -> CommandResult:
69
69
  """Start the dashboard server."""
70
70
  port = getattr(args, "port", 8765)
71
71
  host = getattr(args, "host", "localhost")
@@ -21,7 +21,7 @@ from typing import Any, Dict
21
21
  from ...core.logger import get_logger
22
22
 
23
23
 
24
- def manage_debug(args): # noqa: PLR0911
24
+ def manage_debug(args):
25
25
  """
26
26
  Main entry point for debug commands.
27
27
 
@@ -446,7 +446,7 @@ def debug_agents(args, logger):
446
446
 
447
447
  # Try to load and analyze memory
448
448
  try:
449
- with open(mem_file) as f:
449
+ with mem_file.open() as f:
450
450
  memory_data = json.load(f)
451
451
 
452
452
  entry_count = (
@@ -509,7 +509,7 @@ def debug_agents(args, logger):
509
509
  print(f" Size: {agent_file.stat().st_size:,} bytes")
510
510
 
511
511
  # Read first few lines for type detection
512
- with open(agent_file) as f:
512
+ with agent_file.open() as f:
513
513
  lines = f.readlines()[:10]
514
514
  for line in lines:
515
515
  if "role:" in line.lower():
@@ -181,7 +181,7 @@ def doctor_command(args):
181
181
  import sys
182
182
 
183
183
  original_stdout = sys.stdout
184
- with open(output_file, "w") as f:
184
+ with output_file.open("w") as f:
185
185
  sys.stdout = f
186
186
  reporter.report(summary, format=output_format)
187
187
  sys.stdout = original_stdout
@@ -61,10 +61,10 @@ def manage_mcp(args):
61
61
 
62
62
  # Allow install command to proceed
63
63
  if args.mcp_command == MCPCommands.INSTALL.value:
64
- MCPConfiguration = None # noqa: N806
65
- MCPServiceRegistry = None # noqa: N806
66
- ToolRegistry = None # noqa: N806
67
- MCPGateway = None # noqa: N806
64
+ MCPConfiguration = None
65
+ MCPServiceRegistry = None
66
+ ToolRegistry = None
67
+ MCPGateway = None
68
68
  else:
69
69
  print(
70
70
  "\nError: MCP Gateway services not fully available",
@@ -120,7 +120,7 @@ def _show_status(
120
120
  MCPConfiguration,
121
121
  MCPServiceRegistry,
122
122
  ToolRegistry,
123
- MCPGateway, # noqa: N803
123
+ MCPGateway,
124
124
  ):
125
125
  """
126
126
  Show MCP Gateway status when no subcommand is provided.
@@ -151,7 +151,7 @@ def _show_status(
151
151
  if config_path.exists():
152
152
  print(f" Config file: {config_path}")
153
153
  try:
154
- with open(config_path) as f:
154
+ with config_path.open() as f:
155
155
  config = json.load(f)
156
156
  if "servers" in config:
157
157
  print(f" Configured servers: {len(config.get('servers', {}))}")
@@ -171,7 +171,7 @@ def _show_status(
171
171
  if claude_config.exists():
172
172
  print(f"\n🖥️ Claude Code Config: {claude_config}")
173
173
  try:
174
- with open(claude_config) as f:
174
+ with claude_config.open() as f:
175
175
  config = json.load(f)
176
176
  mcp_servers = config.get("mcpServers", {})
177
177
  if "claude-mpm" in mcp_servers:
@@ -16,7 +16,7 @@ class MCPCommandRouter:
16
16
  """Initialize the command router."""
17
17
  self.logger = logger
18
18
 
19
- def route_command(self, args) -> int: # noqa: PLR0911
19
+ def route_command(self, args) -> int:
20
20
  """Route command to appropriate handler."""
21
21
  if args.mcp_command == MCPCommands.START.value:
22
22
  return asyncio.run(self._start_server(args))
@@ -62,7 +62,7 @@ class MCPConfigCommand(BaseCommand):
62
62
  # Show the updated configuration
63
63
  config_path = Path.cwd() / ".mcp.json"
64
64
  if config_path.exists():
65
- with open(config_path) as f:
65
+ with config_path.open() as f:
66
66
  config = json.load(f)
67
67
  return CommandResult(
68
68
  success=True,
@@ -120,7 +120,7 @@ class MCPConfigCommand(BaseCommand):
120
120
  current_config = {}
121
121
  if config_path.exists():
122
122
  try:
123
- with open(config_path) as f:
123
+ with config_path.open() as f:
124
124
  current_config = json.load(f)
125
125
  except Exception:
126
126
  pass
@@ -12,7 +12,7 @@ class MCPExternalCommands:
12
12
  """Initialize the MCP external commands handler."""
13
13
  self.logger = logger
14
14
 
15
- def manage_external(self, args): # noqa: PLR0911
15
+ def manage_external(self, args):
16
16
  """Manage external MCP services.
17
17
 
18
18
  Args:
@@ -135,7 +135,7 @@ class MCPExternalCommands:
135
135
  print(f"\n📄 Found config: {config_path}")
136
136
 
137
137
  try:
138
- with open(config_path) as f:
138
+ with config_path.open() as f:
139
139
  config = json.load(f)
140
140
 
141
141
  mcp_servers = config.get("mcpServers", {})
@@ -270,7 +270,7 @@ class MCPInstallCommands:
270
270
  if not force:
271
271
  # Check if claude-mpm-gateway already exists
272
272
  try:
273
- with open(config_path) as f:
273
+ with config_path.open() as f:
274
274
  existing_config = json.load(f)
275
275
 
276
276
  if (
@@ -305,7 +305,7 @@ class MCPInstallCommands:
305
305
  else:
306
306
  # Force mode - create backup but proceed
307
307
  try:
308
- with open(config_path) as f:
308
+ with config_path.open() as f:
309
309
  existing_config = json.load(f)
310
310
  config = existing_config
311
311
  print(" Force mode: Overwriting existing configuration")
@@ -335,7 +335,7 @@ class MCPInstallCommands:
335
335
  config_path.parent.mkdir(parents=True, exist_ok=True)
336
336
 
337
337
  # Write configuration with nice formatting
338
- with open(config_path, "w") as f:
338
+ with config_path.open("w") as f:
339
339
  json.dump(config, f, indent=2)
340
340
 
341
341
  print(f"\n✅ Configuration saved to {config_path}")
@@ -110,7 +110,7 @@ def configure_mcp_for_pipx(args) -> int:
110
110
  existing_config = {}
111
111
  if config_path.exists():
112
112
  try:
113
- with open(config_path) as f:
113
+ with config_path.open() as f:
114
114
  existing_config = json.load(f)
115
115
  print("✅ Existing config loaded")
116
116
  except json.JSONDecodeError:
@@ -145,7 +145,7 @@ def configure_mcp_for_pipx(args) -> int:
145
145
  # Write config
146
146
  if not args.dry_run:
147
147
  try:
148
- with open(config_path, "w") as f:
148
+ with config_path.open("w") as f:
149
149
  json.dump(existing_config, f, indent=2)
150
150
  print(f"\n✅ Configuration written to: {config_path}")
151
151
  except Exception as e:
@@ -621,7 +621,7 @@ class MCPExternalServicesSetup:
621
621
  """
622
622
  try:
623
623
  if config_path.exists():
624
- with open(config_path) as f:
624
+ with config_path.open() as f:
625
625
  config = json.load(f)
626
626
  # Ensure mcpServers key exists
627
627
  if "mcpServers" not in config:
@@ -664,7 +664,7 @@ class MCPExternalServicesSetup:
664
664
  print(f" 📁 Created backup: {backup_path}")
665
665
 
666
666
  # Write configuration with proper formatting
667
- with open(config_path, "w") as f:
667
+ with config_path.open("w") as f:
668
668
  json.dump(config, f, indent=2)
669
669
  f.write("\n") # Add newline at end of file
670
670
 
@@ -830,7 +830,7 @@ class MCPExternalServicesSetup:
830
830
 
831
831
  if mcp_config_path.exists():
832
832
  try:
833
- with open(mcp_config_path) as f:
833
+ with mcp_config_path.open() as f:
834
834
  mcp_config = json.load(f)
835
835
  print(f"\n📁 Project MCP config: {mcp_config_path}")
836
836
  except Exception:
@@ -37,7 +37,7 @@ class MonitorCommand(BaseCommand):
37
37
 
38
38
  return None
39
39
 
40
- def run(self, args) -> CommandResult: # noqa: PLR0911
40
+ def run(self, args) -> CommandResult:
41
41
  """Execute the monitor command using unified monitoring daemon."""
42
42
  try:
43
43
  self.logger.info("Monitor command using unified monitoring daemon")
@@ -11,7 +11,7 @@ from rich.console import Console
11
11
  console = Console()
12
12
 
13
13
 
14
- def manage_mpm_init(args): # noqa: PLR0911
14
+ def manage_mpm_init(args):
15
15
  """
16
16
  Handle mpm-init command execution.
17
17
 
@@ -769,7 +769,7 @@ class AgentWizard:
769
769
 
770
770
  output_file = output_dir / f"{template.agent_id}.json"
771
771
 
772
- with open(output_file, "w") as f:
772
+ with output_file.open("w") as f:
773
773
  json.dump(template.to_json(), f, indent=2)
774
774
 
775
775
  return True, f"Agent exported to {output_file}"
@@ -58,6 +58,11 @@ def add_configure_subparser(subparsers) -> argparse.ArgumentParser:
58
58
  action="store_true",
59
59
  help="Jump directly to behavior file management",
60
60
  )
61
+ nav_group.add_argument(
62
+ "--startup",
63
+ action="store_true",
64
+ help="Configure startup services and agents",
65
+ )
61
66
  nav_group.add_argument(
62
67
  "--version-info",
63
68
  action="store_true",
@@ -190,7 +190,7 @@ Examples:
190
190
  return search_parser
191
191
 
192
192
 
193
- def validate_search_args(args: argparse.Namespace) -> Optional[str]: # noqa: PLR0911
193
+ def validate_search_args(args: argparse.Namespace) -> Optional[str]:
194
194
  """
195
195
  Validate search command arguments.
196
196
 
@@ -7,14 +7,14 @@ across multiple CLI commands.
7
7
 
8
8
  import argparse
9
9
  from pathlib import Path
10
- from typing import Any, Dict, List, Optional
10
+ from typing import Any, ClassVar, Dict, List, Optional
11
11
 
12
12
 
13
13
  class CommonArguments:
14
14
  """Registry of common argument patterns used across CLI commands."""
15
15
 
16
16
  # Logging arguments
17
- VERBOSE = {
17
+ VERBOSE: ClassVar[Dict[str, Any]] = {
18
18
  "flags": ["-v", "--verbose"],
19
19
  "action": "store_true",
20
20
  "help": "Enable verbose output",
@@ -183,7 +183,7 @@ class BaseCommand(ABC):
183
183
 
184
184
  if hasattr(args, "output") and args.output:
185
185
  # Write to file
186
- with open(args.output, "w") as f:
186
+ with args.output.open("w") as f:
187
187
  f.write(formatted_output)
188
188
  self.logger.info(f"Output written to {args.output}")
189
189
  else:
@@ -303,7 +303,7 @@ class StartupStatusLogger:
303
303
 
304
304
  import json
305
305
 
306
- with open(claude_json_path) as f:
306
+ with claude_json_path.open() as f:
307
307
  config = json.load(f)
308
308
 
309
309
  result["found"] = True
@@ -716,9 +716,9 @@ def start_vector_search_indexing(project_root: Optional[Path] = None) -> None:
716
716
  try:
717
717
  # Try to get the current event loop
718
718
  loop = asyncio.get_running_loop()
719
- # If we're in an event loop, create a task
720
- # Store reference to avoid RUF006 warning
721
- _ = loop.create_task(trigger_vector_search_indexing(project_root))
719
+ # If we're in an event loop, create a task (fire-and-forget)
720
+ _task = loop.create_task(trigger_vector_search_indexing(project_root)) # noqa: RUF006
721
+ # Fire-and-forget: task will complete in background
722
722
  except RuntimeError:
723
723
  # No event loop running - use subprocess directly to avoid event loop lifecycle issues
724
724
  # The async approach with asyncio.run() creates and closes a loop which causes
@@ -64,7 +64,7 @@ class ExperimentalFeatures:
64
64
  """
65
65
  if self._config_file and self._config_file.exists():
66
66
  try:
67
- with open(self._config_file) as f:
67
+ with self._config_file.open() as f:
68
68
  config = json.load(f)
69
69
  experimental = config.get("experimental_features", {})
70
70
  self._features.update(experimental)
@@ -125,7 +125,7 @@ class ExperimentalFeatures:
125
125
  accepted_file = Path.home() / ".claude-mpm" / ".experimental_accepted"
126
126
  if accepted_file.exists():
127
127
  try:
128
- with open(accepted_file) as f:
128
+ with accepted_file.open() as f:
129
129
  accepted = json.load(f)
130
130
  if feature in accepted.get("features", []):
131
131
  return False
@@ -148,7 +148,7 @@ class ExperimentalFeatures:
148
148
 
149
149
  try:
150
150
  if accepted_file.exists():
151
- with open(accepted_file) as f:
151
+ with accepted_file.open() as f:
152
152
  data = json.load(f)
153
153
  else:
154
154
  data = {"features": [], "timestamp": {}}
@@ -159,7 +159,7 @@ class ExperimentalFeatures:
159
159
  "CLAUDE_MPM_TIMESTAMP", str(Path.cwd())
160
160
  )
161
161
 
162
- with open(accepted_file, "w") as f:
162
+ with accepted_file.open("w") as f:
163
163
  json.dump(data, f, indent=2)
164
164
  except Exception:
165
165
  # Silently ignore errors in acceptance tracking
@@ -249,7 +249,7 @@ class ConfigManager:
249
249
  for config_path in self.config_search_paths:
250
250
  if config_path.exists():
251
251
  try:
252
- with open(config_path) as f:
252
+ with config_path.open() as f:
253
253
  return json.load(f)
254
254
  except Exception as e:
255
255
  print(f"Warning: Failed to load config from {config_path}: {e}")
@@ -267,7 +267,7 @@ class ConfigManager:
267
267
  path = config_dir / self.config_file_name
268
268
 
269
269
  try:
270
- with open(path, "w") as f:
270
+ with path.open("w") as f:
271
271
  json.dump(config.to_dict(), f, indent=2)
272
272
  return True
273
273
  except Exception as e:
@@ -1,20 +1,56 @@
1
- """Core components for Claude MPM."""
2
-
3
- from .claude_runner import ClaudeRunner
4
- from .config import Config
5
-
6
- # Import DI components
7
- from .container import DIContainer, ServiceLifetime, get_container
8
- from .factories import (
9
- AgentServiceFactory,
10
- ConfigurationFactory,
11
- ServiceFactory,
12
- SessionManagerFactory,
13
- get_factory_registry,
14
- )
15
- from .injectable_service import InjectableService
16
- from .mixins import LoggerMixin
17
- from .service_registry import ServiceRegistry, get_service_registry, initialize_services
1
+ """Core components for Claude MPM.
2
+
3
+ Lazy imports for all components to avoid loading heavy dependencies
4
+ when only importing lightweight utilities (constants, logging_utils, etc).
5
+ """
6
+
7
+
8
+ # Lazy imports via __getattr__ to prevent loading heavy dependencies
9
+ # when hooks only need lightweight utilities
10
+ def __getattr__(name):
11
+ """Lazy load core components only when accessed using dictionary-based mapping."""
12
+ from importlib import import_module
13
+
14
+ # Dictionary mapping: name -> (module_path, attribute_name)
15
+ _LAZY_IMPORTS = {
16
+ # Core components
17
+ "ClaudeRunner": ("claude_mpm.core.claude_runner", "ClaudeRunner"),
18
+ "Config": ("claude_mpm.core.config", "Config"),
19
+ # Dependency injection
20
+ "DIContainer": ("claude_mpm.core.container", "DIContainer"),
21
+ "ServiceLifetime": ("claude_mpm.core.container", "ServiceLifetime"),
22
+ "get_container": ("claude_mpm.core.container", "get_container"),
23
+ # Factories
24
+ "AgentServiceFactory": ("claude_mpm.core.factories", "AgentServiceFactory"),
25
+ "ConfigurationFactory": ("claude_mpm.core.factories", "ConfigurationFactory"),
26
+ "ServiceFactory": ("claude_mpm.core.factories", "ServiceFactory"),
27
+ "SessionManagerFactory": ("claude_mpm.core.factories", "SessionManagerFactory"),
28
+ "get_factory_registry": ("claude_mpm.core.factories", "get_factory_registry"),
29
+ # Services and utilities
30
+ "InjectableService": (
31
+ "claude_mpm.core.injectable_service",
32
+ "InjectableService",
33
+ ),
34
+ "LoggerMixin": ("claude_mpm.core.mixins", "LoggerMixin"),
35
+ # Service registry
36
+ "ServiceRegistry": ("claude_mpm.core.service_registry", "ServiceRegistry"),
37
+ "get_service_registry": (
38
+ "claude_mpm.core.service_registry",
39
+ "get_service_registry",
40
+ ),
41
+ "initialize_services": (
42
+ "claude_mpm.core.service_registry",
43
+ "initialize_services",
44
+ ),
45
+ }
46
+
47
+ if name in _LAZY_IMPORTS:
48
+ module_path, attr_name = _LAZY_IMPORTS[name]
49
+ module = import_module(module_path)
50
+ return getattr(module, attr_name)
51
+
52
+ raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
53
+
18
54
 
19
55
  __all__ = [
20
56
  "AgentServiceFactory",
@@ -209,7 +209,7 @@ class AgentSessionManager:
209
209
  "agent_sessions": dict(self.agent_sessions),
210
210
  "updated_at": datetime.now(timezone.utc).isoformat(),
211
211
  }
212
- with open(session_file, "w") as f:
212
+ with session_file.open("w") as f:
213
213
  json.dump(data, f, indent=2)
214
214
  except Exception as e:
215
215
  logger.error(f"Failed to save agent sessions: {e}")
@@ -219,7 +219,7 @@ class AgentSessionManager:
219
219
  session_file = self.session_dir / "agent_sessions.json"
220
220
  if session_file.exists():
221
221
  try:
222
- with open(session_file) as f:
222
+ with session_file.open() as f:
223
223
  data = json.load(f)
224
224
  self.agent_sessions = defaultdict(
225
225
  dict, data.get("agent_sessions", {})
@@ -84,7 +84,7 @@ class APIKeyValidator:
84
84
 
85
85
  return not bool(self.errors), self.errors, self.warnings
86
86
 
87
- def _validate_openai_key(self, api_key: str) -> bool: # noqa: PLR0911
87
+ def _validate_openai_key(self, api_key: str) -> bool:
88
88
  """Validate OpenAI API key.
89
89
 
90
90
  Args:
@@ -133,7 +133,7 @@ class APIKeyValidator:
133
133
  self.errors.append(f"❌ OpenAI API validation failed with error: {e}")
134
134
  return False
135
135
 
136
- def _validate_anthropic_key(self, api_key: str) -> bool: # noqa: PLR0911
136
+ def _validate_anthropic_key(self, api_key: str) -> bool:
137
137
  """Validate Anthropic API key.
138
138
 
139
139
  Args:
@@ -196,7 +196,7 @@ class APIKeyValidator:
196
196
  self.errors.append(f"❌ Anthropic API validation failed with error: {e}")
197
197
  return False
198
198
 
199
- def _validate_github_token(self, token: str) -> bool: # noqa: PLR0911
199
+ def _validate_github_token(self, token: str) -> bool:
200
200
  """Validate GitHub personal access token.
201
201
 
202
202
  Args:
@@ -428,7 +428,16 @@ class BaseService(LoggerMixin, ABC):
428
428
  self.logger.info(
429
429
  f"Received signal {signum}, initiating graceful shutdown..."
430
430
  )
431
- asyncio.create_task(self.stop())
431
+ # Get the event loop and create a tracked shutdown task
432
+ try:
433
+ loop = asyncio.get_event_loop()
434
+ task = loop.create_task(self.stop())
435
+ # Store reference to prevent GC during shutdown
436
+ if not hasattr(self, '_shutdown_task'):
437
+ self._shutdown_task = task
438
+ except RuntimeError:
439
+ # No event loop, call stop synchronously
440
+ self.logger.warning("No event loop available for graceful shutdown")
432
441
 
433
442
  signal.signal(signal.SIGINT, signal_handler)
434
443
  signal.signal(signal.SIGTERM, signal_handler)
claude_mpm/core/cache.py CHANGED
@@ -391,7 +391,7 @@ class FileSystemCache:
391
391
 
392
392
  try:
393
393
  self.persist_path.parent.mkdir(parents=True, exist_ok=True)
394
- with open(self.persist_path, "wb") as f:
394
+ with self.persist_path.open("wb") as f:
395
395
  pickle.dump(self._cache, f)
396
396
  self._logger.debug(f"Cache persisted to {self.persist_path}")
397
397
  except Exception as e:
@@ -403,7 +403,7 @@ class FileSystemCache:
403
403
  return
404
404
 
405
405
  try:
406
- with open(self.persist_path, "rb") as f:
406
+ with self.persist_path.open("rb") as f:
407
407
  loaded_cache = pickle.load(f)
408
408
 
409
409
  # Rebuild cache with validation
claude_mpm/core/config.py CHANGED
@@ -201,7 +201,7 @@ class Config:
201
201
  # Set flag IMMEDIATELY before logging to prevent any possibility of duplicate
202
202
  # messages. No lock needed here since we're already inside __init__ lock
203
203
  Config._success_logged = True
204
- logger.info(
204
+ logger.debug(
205
205
  f"✓ Successfully loaded configuration from {file_path}"
206
206
  )
207
207
  else:
@@ -253,7 +253,7 @@ class Config:
253
253
  "operation": "read",
254
254
  "error_type": type(e).__name__,
255
255
  },
256
- )
256
+ ) from e
257
257
  except Exception as e:
258
258
  # Catch any remaining unexpected errors and wrap them as configuration errors
259
259
  raise ConfigurationError(
@@ -263,7 +263,7 @@ class Config:
263
263
  "error_type": type(e).__name__,
264
264
  "original_error": str(e),
265
265
  },
266
- )
266
+ ) from e
267
267
 
268
268
  def _load_env_vars(self) -> None:
269
269
  """Load configuration from environment variables."""
@@ -651,7 +651,7 @@ class Config:
651
651
  "format": format,
652
652
  "error_type": type(e).__name__,
653
653
  },
654
- )
654
+ ) from e
655
655
  except Exception as e:
656
656
  # Re-raise ConfigurationError as-is, wrap others
657
657
  if isinstance(e, ConfigurationError):
@@ -663,7 +663,7 @@ class Config:
663
663
  "format": format,
664
664
  "error_type": type(e).__name__,
665
665
  },
666
- )
666
+ ) from e
667
667
 
668
668
  def validate(self, schema: Dict[str, Any]) -> bool:
669
669
  """
@@ -88,7 +88,7 @@ class ConfigAliasManager:
88
88
  self.config_mgr.save_json(aliases, self.aliases_file, sort_keys=True)
89
89
  except Exception as e:
90
90
  logger.error(f"Failed to save aliases: {e}")
91
- raise ConfigAliasError(f"Failed to save aliases: {e}")
91
+ raise ConfigAliasError(f"Failed to save aliases: {e}") from e
92
92
 
93
93
  def create_alias(self, alias_name: str, directory_path: str) -> None:
94
94
  """
@@ -211,7 +211,7 @@ class ConfigAliasManager:
211
211
  except Exception as e:
212
212
  raise InvalidDirectoryError(
213
213
  f"Cannot create directory '{directory_path}': {e}"
214
- )
214
+ ) from e
215
215
 
216
216
  # Verify we can write to the directory
217
217
  test_file = directory_path / ".claude_pm_test"
@@ -221,14 +221,14 @@ class ConfigAliasManager:
221
221
  except Exception as e:
222
222
  raise InvalidDirectoryError(
223
223
  f"Directory '{directory_path}' is not writable: {e}"
224
- )
224
+ ) from e
225
225
 
226
226
  return directory_path
227
227
 
228
228
  except InvalidDirectoryError:
229
229
  raise
230
230
  except Exception as e:
231
- raise InvalidDirectoryError(f"Invalid directory path '{path}': {e}")
231
+ raise InvalidDirectoryError(f"Invalid directory path '{path}': {e}") from e
232
232
 
233
233
  def get_alias(self, alias_name: str) -> Optional[str]:
234
234
  """
@@ -99,7 +99,7 @@ class ConfigConstants:
99
99
  cls._config_service = config_service
100
100
 
101
101
  @classmethod
102
- def get_timeout(cls, timeout_type: str) -> int: # noqa: PLR0911
102
+ def get_timeout(cls, timeout_type: str) -> int:
103
103
  """
104
104
  Get timeout value by type.
105
105
 
@@ -218,7 +218,7 @@ class ErrorHandler:
218
218
  return handler(error, error_context)
219
219
  except Exception as recovery_error:
220
220
  self.logger.error(f"Recovery failed: {recovery_error}")
221
- raise error
221
+ raise error from recovery_error
222
222
 
223
223
  # No recovery handler available
224
224
  self.logger.warning(f"No recovery handler for {error_type.__name__}")