claude-mpm 4.5.11__py3-none-any.whl → 4.5.13__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 (190) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_ENGINEER.md +47 -0
  3. claude_mpm/agents/BASE_QA.md +60 -0
  4. claude_mpm/agents/frontmatter_validator.py +4 -4
  5. claude_mpm/agents/templates/nextjs_engineer.json +2 -2
  6. claude_mpm/agents/templates/qa.json +13 -3
  7. claude_mpm/agents/templates/react_engineer.json +2 -2
  8. claude_mpm/agents/templates/typescript_engineer.json +2 -2
  9. claude_mpm/agents/templates/web_qa.json +14 -3
  10. claude_mpm/cli/commands/agent_manager.py +3 -3
  11. claude_mpm/cli/commands/agents.py +6 -6
  12. claude_mpm/cli/commands/aggregate.py +4 -4
  13. claude_mpm/cli/commands/analyze.py +2 -2
  14. claude_mpm/cli/commands/analyze_code.py +1 -1
  15. claude_mpm/cli/commands/cleanup.py +3 -3
  16. claude_mpm/cli/commands/config.py +2 -2
  17. claude_mpm/cli/commands/configure.py +14 -14
  18. claude_mpm/cli/commands/dashboard.py +1 -1
  19. claude_mpm/cli/commands/debug.py +3 -3
  20. claude_mpm/cli/commands/doctor.py +1 -1
  21. claude_mpm/cli/commands/mcp.py +7 -7
  22. claude_mpm/cli/commands/mcp_command_router.py +1 -1
  23. claude_mpm/cli/commands/mcp_config.py +2 -2
  24. claude_mpm/cli/commands/mcp_external_commands.py +2 -2
  25. claude_mpm/cli/commands/mcp_install_commands.py +3 -3
  26. claude_mpm/cli/commands/mcp_pipx_config.py +2 -2
  27. claude_mpm/cli/commands/mcp_setup_external.py +3 -3
  28. claude_mpm/cli/commands/monitor.py +1 -1
  29. claude_mpm/cli/commands/mpm_init_handler.py +1 -1
  30. claude_mpm/cli/interactive/agent_wizard.py +1 -1
  31. claude_mpm/cli/parsers/search_parser.py +1 -1
  32. claude_mpm/cli/shared/argument_patterns.py +2 -2
  33. claude_mpm/cli/shared/base_command.py +1 -1
  34. claude_mpm/cli/startup_logging.py +6 -4
  35. claude_mpm/config/experimental_features.py +4 -4
  36. claude_mpm/config/socketio_config.py +2 -2
  37. claude_mpm/core/agent_session_manager.py +2 -2
  38. claude_mpm/core/api_validator.py +3 -3
  39. claude_mpm/core/base_service.py +10 -1
  40. claude_mpm/core/cache.py +2 -2
  41. claude_mpm/core/config.py +4 -4
  42. claude_mpm/core/config_aliases.py +4 -4
  43. claude_mpm/core/config_constants.py +1 -1
  44. claude_mpm/core/error_handler.py +1 -1
  45. claude_mpm/core/file_utils.py +5 -5
  46. claude_mpm/core/framework/formatters/capability_generator.py +5 -5
  47. claude_mpm/core/framework/loaders/agent_loader.py +1 -1
  48. claude_mpm/core/framework/processors/metadata_processor.py +1 -1
  49. claude_mpm/core/framework/processors/template_processor.py +3 -3
  50. claude_mpm/core/framework_loader.py +2 -2
  51. claude_mpm/core/log_manager.py +4 -4
  52. claude_mpm/core/logger.py +2 -2
  53. claude_mpm/core/optimized_startup.py +1 -1
  54. claude_mpm/core/output_style_manager.py +1 -1
  55. claude_mpm/core/service_registry.py +2 -2
  56. claude_mpm/core/session_manager.py +3 -3
  57. claude_mpm/core/shared/config_loader.py +1 -1
  58. claude_mpm/core/socketio_pool.py +2 -2
  59. claude_mpm/core/unified_agent_registry.py +2 -2
  60. claude_mpm/core/unified_config.py +6 -6
  61. claude_mpm/core/unified_paths.py +2 -2
  62. claude_mpm/dashboard/api/simple_directory.py +1 -1
  63. claude_mpm/generators/agent_profile_generator.py +1 -1
  64. claude_mpm/hooks/claude_hooks/event_handlers.py +2 -2
  65. claude_mpm/hooks/claude_hooks/installer.py +9 -9
  66. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +7 -2
  67. claude_mpm/hooks/claude_hooks/tool_analysis.py +2 -2
  68. claude_mpm/hooks/memory_integration_hook.py +1 -1
  69. claude_mpm/hooks/validation_hooks.py +1 -1
  70. claude_mpm/init.py +4 -4
  71. claude_mpm/models/agent_session.py +1 -1
  72. claude_mpm/scripts/socketio_daemon.py +5 -5
  73. claude_mpm/services/__init__.py +2 -2
  74. claude_mpm/services/agent_capabilities_service.py +1 -1
  75. claude_mpm/services/agents/agent_builder.py +6 -4
  76. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +1 -1
  77. claude_mpm/services/agents/deployment/agent_metrics_collector.py +1 -1
  78. claude_mpm/services/agents/deployment/agent_record_service.py +3 -3
  79. claude_mpm/services/agents/deployment/deployment_wrapper.py +1 -1
  80. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +2 -2
  81. claude_mpm/services/agents/loading/agent_profile_loader.py +2 -2
  82. claude_mpm/services/agents/local_template_manager.py +5 -5
  83. claude_mpm/services/agents/registry/deployed_agent_discovery.py +1 -1
  84. claude_mpm/services/agents/registry/modification_tracker.py +19 -11
  85. claude_mpm/services/async_session_logger.py +1 -1
  86. claude_mpm/services/claude_session_logger.py +1 -1
  87. claude_mpm/services/cli/agent_listing_service.py +3 -3
  88. claude_mpm/services/cli/agent_validation_service.py +1 -1
  89. claude_mpm/services/cli/session_manager.py +2 -2
  90. claude_mpm/services/core/path_resolver.py +1 -1
  91. claude_mpm/services/diagnostics/checks/agent_check.py +1 -1
  92. claude_mpm/services/diagnostics/checks/claude_code_check.py +2 -2
  93. claude_mpm/services/diagnostics/checks/common_issues_check.py +3 -3
  94. claude_mpm/services/diagnostics/checks/configuration_check.py +2 -2
  95. claude_mpm/services/diagnostics/checks/installation_check.py +1 -1
  96. claude_mpm/services/diagnostics/checks/mcp_check.py +1 -1
  97. claude_mpm/services/diagnostics/checks/mcp_services_check.py +9 -9
  98. claude_mpm/services/diagnostics/checks/monitor_check.py +1 -1
  99. claude_mpm/services/diagnostics/doctor_reporter.py +1 -1
  100. claude_mpm/services/event_aggregator.py +1 -1
  101. claude_mpm/services/event_bus/event_bus.py +9 -2
  102. claude_mpm/services/events/consumers/dead_letter.py +2 -2
  103. claude_mpm/services/framework_claude_md_generator/__init__.py +1 -1
  104. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +3 -3
  105. claude_mpm/services/framework_claude_md_generator/version_manager.py +1 -1
  106. claude_mpm/services/hook_installer_service.py +7 -7
  107. claude_mpm/services/infrastructure/context_preservation.py +7 -7
  108. claude_mpm/services/infrastructure/daemon_manager.py +5 -5
  109. claude_mpm/services/mcp_config_manager.py +10 -10
  110. claude_mpm/services/mcp_gateway/auto_configure.py +5 -5
  111. claude_mpm/services/mcp_gateway/config/config_loader.py +2 -2
  112. claude_mpm/services/mcp_gateway/config/configuration.py +5 -3
  113. claude_mpm/services/mcp_gateway/core/process_pool.py +3 -3
  114. claude_mpm/services/mcp_gateway/core/singleton_manager.py +2 -2
  115. claude_mpm/services/mcp_gateway/core/startup_verification.py +1 -1
  116. claude_mpm/services/mcp_gateway/main.py +1 -1
  117. claude_mpm/services/mcp_gateway/registry/service_registry.py +4 -2
  118. claude_mpm/services/mcp_gateway/registry/tool_registry.py +2 -1
  119. claude_mpm/services/mcp_gateway/server/stdio_handler.py +1 -1
  120. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +1 -1
  121. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +1 -1
  122. claude_mpm/services/mcp_gateway/tools/hello_world.py +1 -1
  123. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +5 -5
  124. claude_mpm/services/mcp_gateway/utils/update_preferences.py +2 -2
  125. claude_mpm/services/mcp_service_verifier.py +1 -1
  126. claude_mpm/services/memory/builder.py +1 -1
  127. claude_mpm/services/memory/cache/shared_prompt_cache.py +2 -1
  128. claude_mpm/services/memory/indexed_memory.py +3 -3
  129. claude_mpm/services/monitor/daemon.py +1 -1
  130. claude_mpm/services/monitor/daemon_manager.py +9 -9
  131. claude_mpm/services/monitor/handlers/file.py +1 -1
  132. claude_mpm/services/monitor/handlers/hooks.py +3 -3
  133. claude_mpm/services/monitor/management/lifecycle.py +7 -7
  134. claude_mpm/services/monitor/server.py +2 -2
  135. claude_mpm/services/orphan_detection.py +11 -16
  136. claude_mpm/services/port_manager.py +2 -2
  137. claude_mpm/services/project/analyzer.py +3 -3
  138. claude_mpm/services/project/archive_manager.py +17 -13
  139. claude_mpm/services/project/dependency_analyzer.py +4 -4
  140. claude_mpm/services/project/documentation_manager.py +4 -4
  141. claude_mpm/services/project/enhanced_analyzer.py +19 -8
  142. claude_mpm/services/project/registry.py +4 -4
  143. claude_mpm/services/project_port_allocator.py +7 -12
  144. claude_mpm/services/session_management_service.py +1 -1
  145. claude_mpm/services/socketio/event_normalizer.py +1 -1
  146. claude_mpm/services/socketio/handlers/code_analysis.py +14 -12
  147. claude_mpm/services/socketio/handlers/file.py +1 -1
  148. claude_mpm/services/socketio/migration_utils.py +1 -1
  149. claude_mpm/services/socketio/server/core.py +1 -1
  150. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +1 -1
  151. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +4 -4
  152. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +1 -1
  153. claude_mpm/services/unified/config_strategies/config_schema.py +4 -4
  154. claude_mpm/services/unified/config_strategies/context_strategy.py +8 -6
  155. claude_mpm/services/unified/config_strategies/error_handling_strategy.py +10 -10
  156. claude_mpm/services/unified/config_strategies/file_loader_strategy.py +5 -5
  157. claude_mpm/services/unified/config_strategies/unified_config_service.py +8 -8
  158. claude_mpm/services/unified/config_strategies/validation_strategy.py +15 -15
  159. claude_mpm/services/unified/deployment_strategies/base.py +4 -4
  160. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +15 -15
  161. claude_mpm/services/unified/deployment_strategies/local.py +11 -11
  162. claude_mpm/services/unified/deployment_strategies/utils.py +11 -9
  163. claude_mpm/services/unified/deployment_strategies/vercel.py +7 -9
  164. claude_mpm/services/unified/unified_config.py +5 -5
  165. claude_mpm/services/unified/unified_deployment.py +2 -2
  166. claude_mpm/services/utility_service.py +1 -1
  167. claude_mpm/services/version_control/conflict_resolution.py +2 -2
  168. claude_mpm/services/version_control/git_operations.py +3 -3
  169. claude_mpm/services/version_control/semantic_versioning.py +13 -13
  170. claude_mpm/services/version_control/version_parser.py +1 -1
  171. claude_mpm/storage/state_storage.py +12 -13
  172. claude_mpm/tools/code_tree_analyzer.py +5 -5
  173. claude_mpm/tools/code_tree_builder.py +4 -4
  174. claude_mpm/tools/socketio_debug.py +1 -1
  175. claude_mpm/utils/agent_dependency_loader.py +4 -4
  176. claude_mpm/utils/common.py +2 -2
  177. claude_mpm/utils/config_manager.py +3 -3
  178. claude_mpm/utils/dependency_cache.py +2 -2
  179. claude_mpm/utils/dependency_strategies.py +6 -6
  180. claude_mpm/utils/file_utils.py +11 -11
  181. claude_mpm/utils/log_cleanup.py +1 -1
  182. claude_mpm/utils/path_operations.py +1 -1
  183. claude_mpm/validation/agent_validator.py +2 -2
  184. claude_mpm/validation/frontmatter_validator.py +1 -1
  185. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/METADATA +1 -1
  186. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/RECORD +190 -190
  187. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/WHEEL +0 -0
  188. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/entry_points.txt +0 -0
  189. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/licenses/LICENSE +0 -0
  190. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/top_level.txt +0 -0
@@ -64,14 +64,14 @@ class SimpleAgentManager:
64
64
  def _load_states(self):
65
65
  """Load agent states from file."""
66
66
  if self.config_file.exists():
67
- with open(self.config_file) as f:
67
+ with self.config_file.open() as f:
68
68
  self.states = json.load(f)
69
69
  else:
70
70
  self.states = {}
71
71
 
72
72
  def _save_states(self):
73
73
  """Save agent states to file."""
74
- with open(self.config_file, "w") as f:
74
+ with self.config_file.open("w") as f:
75
75
  json.dump(self.states, f, indent=2)
76
76
 
77
77
  def is_agent_enabled(self, agent_name: str) -> bool:
@@ -105,7 +105,7 @@ class SimpleAgentManager:
105
105
  continue
106
106
 
107
107
  try:
108
- with open(template_file) as f:
108
+ with template_file.open() as f:
109
109
  template_data = json.load(f)
110
110
 
111
111
  # Extract agent information from template
@@ -205,7 +205,7 @@ class ConfigureCommand(BaseCommand):
205
205
 
206
206
  return None
207
207
 
208
- def run(self, args) -> CommandResult: # noqa: PLR0911
208
+ def run(self, args) -> CommandResult:
209
209
  """Execute the configure command."""
210
210
  # Set configuration scope
211
211
  self.current_scope = getattr(args, "scope", "project")
@@ -447,7 +447,7 @@ class ConfigureCommand(BaseCommand):
447
447
  try:
448
448
  template_path = self._get_agent_template_path(agent.name)
449
449
  if template_path.exists():
450
- with open(template_path) as f:
450
+ with template_path.open() as f:
451
451
  template = json.load(f)
452
452
  model = template.get("capabilities", {}).get("model", "default")
453
453
  tools_display = f"Model: {model}"
@@ -542,7 +542,7 @@ class ConfigureCommand(BaseCommand):
542
542
  template_path = self._get_agent_template_path(agent.name)
543
543
 
544
544
  if template_path.exists():
545
- with open(template_path) as f:
545
+ with template_path.open() as f:
546
546
  template = json.load(f)
547
547
  is_system = str(template_path).startswith(
548
548
  str(self.agent_manager.templates_dir)
@@ -693,11 +693,11 @@ class ConfigureCommand(BaseCommand):
693
693
  subprocess.call([editor, temp_path])
694
694
 
695
695
  # Read back the edited content
696
- with open(temp_path) as f:
696
+ with temp_path.open() as f:
697
697
  new_template = json.load(f)
698
698
 
699
699
  # Save to actual template path
700
- with open(template_path, "w") as f:
700
+ with template_path.open("w") as f:
701
701
  json.dump(new_template, f, indent=2)
702
702
 
703
703
  self.console.print("[green]Template updated successfully![/green]")
@@ -732,7 +732,7 @@ class ConfigureCommand(BaseCommand):
732
732
  current[parts[-1]] = value
733
733
 
734
734
  # Save the template
735
- with open(template_path, "w") as f:
735
+ with template_path.open("w") as f:
736
736
  json.dump(template, f, indent=2)
737
737
 
738
738
  self.console.print(
@@ -765,7 +765,7 @@ class ConfigureCommand(BaseCommand):
765
765
  del current[parts[-1]]
766
766
 
767
767
  # Save the template
768
- with open(template_path, "w") as f:
768
+ with template_path.open("w") as f:
769
769
  json.dump(template, f, indent=2)
770
770
 
771
771
  self.console.print(
@@ -802,7 +802,7 @@ class ConfigureCommand(BaseCommand):
802
802
  return
803
803
 
804
804
  # Save the template copy
805
- with open(custom_path, "w") as f:
805
+ with custom_path.open("w") as f:
806
806
  json.dump(template, f, indent=2)
807
807
 
808
808
  self.console.print(f"[green]Created custom template at: {custom_path}[/green]")
@@ -839,7 +839,7 @@ class ConfigureCommand(BaseCommand):
839
839
 
840
840
  if template_path.exists():
841
841
  try:
842
- with open(template_path) as f:
842
+ with template_path.open() as f:
843
843
  template = json.load(f)
844
844
 
845
845
  # Extract additional information
@@ -1675,7 +1675,7 @@ Directory: {self.project_dir}
1675
1675
 
1676
1676
  # Write to file
1677
1677
  output_path = Path(file_path)
1678
- with open(output_path, "w") as f:
1678
+ with output_path.open("w") as f:
1679
1679
  json.dump(config_data, f, indent=2)
1680
1680
 
1681
1681
  return CommandResult.success_result(
@@ -1692,7 +1692,7 @@ Directory: {self.project_dir}
1692
1692
  if not input_path.exists():
1693
1693
  return CommandResult.error_result(f"File not found: {file_path}")
1694
1694
 
1695
- with open(input_path) as f:
1695
+ with input_path.open() as f:
1696
1696
  config_data = json.load(f)
1697
1697
 
1698
1698
  # Apply agent states
@@ -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}"
@@ -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,11 @@ 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(
721
+ trigger_vector_search_indexing(project_root)
722
+ )
723
+ # Fire-and-forget: task will complete in background
722
724
  except RuntimeError:
723
725
  # No event loop running - use subprocess directly to avoid event loop lifecycle issues
724
726
  # 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:
@@ -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
@@ -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__}")