claude-mpm 4.5.11__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.

Potentially problematic release.


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

Files changed (183) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/frontmatter_validator.py +4 -4
  3. claude_mpm/cli/commands/agent_manager.py +3 -3
  4. claude_mpm/cli/commands/agents.py +6 -6
  5. claude_mpm/cli/commands/aggregate.py +4 -4
  6. claude_mpm/cli/commands/analyze.py +2 -2
  7. claude_mpm/cli/commands/analyze_code.py +1 -1
  8. claude_mpm/cli/commands/cleanup.py +3 -3
  9. claude_mpm/cli/commands/config.py +2 -2
  10. claude_mpm/cli/commands/configure.py +14 -14
  11. claude_mpm/cli/commands/dashboard.py +1 -1
  12. claude_mpm/cli/commands/debug.py +3 -3
  13. claude_mpm/cli/commands/doctor.py +1 -1
  14. claude_mpm/cli/commands/mcp.py +7 -7
  15. claude_mpm/cli/commands/mcp_command_router.py +1 -1
  16. claude_mpm/cli/commands/mcp_config.py +2 -2
  17. claude_mpm/cli/commands/mcp_external_commands.py +2 -2
  18. claude_mpm/cli/commands/mcp_install_commands.py +3 -3
  19. claude_mpm/cli/commands/mcp_pipx_config.py +2 -2
  20. claude_mpm/cli/commands/mcp_setup_external.py +3 -3
  21. claude_mpm/cli/commands/monitor.py +1 -1
  22. claude_mpm/cli/commands/mpm_init_handler.py +1 -1
  23. claude_mpm/cli/interactive/agent_wizard.py +1 -1
  24. claude_mpm/cli/parsers/search_parser.py +1 -1
  25. claude_mpm/cli/shared/argument_patterns.py +2 -2
  26. claude_mpm/cli/shared/base_command.py +1 -1
  27. claude_mpm/cli/startup_logging.py +4 -4
  28. claude_mpm/config/experimental_features.py +4 -4
  29. claude_mpm/config/socketio_config.py +2 -2
  30. claude_mpm/core/agent_session_manager.py +2 -2
  31. claude_mpm/core/api_validator.py +3 -3
  32. claude_mpm/core/base_service.py +10 -1
  33. claude_mpm/core/cache.py +2 -2
  34. claude_mpm/core/config.py +4 -4
  35. claude_mpm/core/config_aliases.py +4 -4
  36. claude_mpm/core/config_constants.py +1 -1
  37. claude_mpm/core/error_handler.py +1 -1
  38. claude_mpm/core/file_utils.py +5 -5
  39. claude_mpm/core/framework/formatters/capability_generator.py +5 -5
  40. claude_mpm/core/framework/loaders/agent_loader.py +1 -1
  41. claude_mpm/core/framework/processors/metadata_processor.py +1 -1
  42. claude_mpm/core/framework/processors/template_processor.py +3 -3
  43. claude_mpm/core/framework_loader.py +2 -2
  44. claude_mpm/core/log_manager.py +4 -4
  45. claude_mpm/core/logger.py +2 -2
  46. claude_mpm/core/optimized_startup.py +1 -1
  47. claude_mpm/core/output_style_manager.py +1 -1
  48. claude_mpm/core/service_registry.py +2 -2
  49. claude_mpm/core/session_manager.py +3 -3
  50. claude_mpm/core/shared/config_loader.py +1 -1
  51. claude_mpm/core/socketio_pool.py +2 -2
  52. claude_mpm/core/unified_agent_registry.py +2 -2
  53. claude_mpm/core/unified_config.py +6 -6
  54. claude_mpm/core/unified_paths.py +2 -2
  55. claude_mpm/dashboard/api/simple_directory.py +1 -1
  56. claude_mpm/generators/agent_profile_generator.py +1 -1
  57. claude_mpm/hooks/claude_hooks/event_handlers.py +2 -2
  58. claude_mpm/hooks/claude_hooks/installer.py +9 -9
  59. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +7 -2
  60. claude_mpm/hooks/claude_hooks/tool_analysis.py +2 -2
  61. claude_mpm/hooks/memory_integration_hook.py +1 -1
  62. claude_mpm/hooks/validation_hooks.py +1 -1
  63. claude_mpm/init.py +4 -4
  64. claude_mpm/models/agent_session.py +1 -1
  65. claude_mpm/scripts/socketio_daemon.py +5 -5
  66. claude_mpm/services/__init__.py +2 -2
  67. claude_mpm/services/agent_capabilities_service.py +1 -1
  68. claude_mpm/services/agents/agent_builder.py +4 -4
  69. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +1 -1
  70. claude_mpm/services/agents/deployment/agent_metrics_collector.py +1 -1
  71. claude_mpm/services/agents/deployment/agent_record_service.py +3 -3
  72. claude_mpm/services/agents/deployment/deployment_wrapper.py +1 -1
  73. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +2 -2
  74. claude_mpm/services/agents/loading/agent_profile_loader.py +2 -2
  75. claude_mpm/services/agents/local_template_manager.py +5 -5
  76. claude_mpm/services/agents/registry/deployed_agent_discovery.py +1 -1
  77. claude_mpm/services/agents/registry/modification_tracker.py +19 -11
  78. claude_mpm/services/async_session_logger.py +1 -1
  79. claude_mpm/services/claude_session_logger.py +1 -1
  80. claude_mpm/services/cli/agent_listing_service.py +3 -3
  81. claude_mpm/services/cli/agent_validation_service.py +1 -1
  82. claude_mpm/services/cli/session_manager.py +2 -2
  83. claude_mpm/services/core/path_resolver.py +1 -1
  84. claude_mpm/services/diagnostics/checks/agent_check.py +1 -1
  85. claude_mpm/services/diagnostics/checks/claude_code_check.py +2 -2
  86. claude_mpm/services/diagnostics/checks/common_issues_check.py +3 -3
  87. claude_mpm/services/diagnostics/checks/configuration_check.py +2 -2
  88. claude_mpm/services/diagnostics/checks/installation_check.py +1 -1
  89. claude_mpm/services/diagnostics/checks/mcp_check.py +1 -1
  90. claude_mpm/services/diagnostics/checks/mcp_services_check.py +9 -9
  91. claude_mpm/services/diagnostics/checks/monitor_check.py +1 -1
  92. claude_mpm/services/diagnostics/doctor_reporter.py +1 -1
  93. claude_mpm/services/event_aggregator.py +1 -1
  94. claude_mpm/services/event_bus/event_bus.py +7 -2
  95. claude_mpm/services/events/consumers/dead_letter.py +2 -2
  96. claude_mpm/services/framework_claude_md_generator/__init__.py +1 -1
  97. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +3 -3
  98. claude_mpm/services/framework_claude_md_generator/version_manager.py +1 -1
  99. claude_mpm/services/hook_installer_service.py +7 -7
  100. claude_mpm/services/infrastructure/context_preservation.py +7 -7
  101. claude_mpm/services/infrastructure/daemon_manager.py +5 -5
  102. claude_mpm/services/mcp_config_manager.py +10 -10
  103. claude_mpm/services/mcp_gateway/auto_configure.py +5 -5
  104. claude_mpm/services/mcp_gateway/config/config_loader.py +2 -2
  105. claude_mpm/services/mcp_gateway/config/configuration.py +3 -3
  106. claude_mpm/services/mcp_gateway/core/process_pool.py +3 -3
  107. claude_mpm/services/mcp_gateway/core/singleton_manager.py +2 -2
  108. claude_mpm/services/mcp_gateway/core/startup_verification.py +1 -1
  109. claude_mpm/services/mcp_gateway/main.py +1 -1
  110. claude_mpm/services/mcp_gateway/registry/service_registry.py +4 -2
  111. claude_mpm/services/mcp_gateway/registry/tool_registry.py +2 -1
  112. claude_mpm/services/mcp_gateway/server/stdio_handler.py +1 -1
  113. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +1 -1
  114. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +1 -1
  115. claude_mpm/services/mcp_gateway/tools/hello_world.py +1 -1
  116. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +5 -5
  117. claude_mpm/services/mcp_gateway/utils/update_preferences.py +2 -2
  118. claude_mpm/services/mcp_service_verifier.py +1 -1
  119. claude_mpm/services/memory/builder.py +1 -1
  120. claude_mpm/services/memory/cache/shared_prompt_cache.py +2 -1
  121. claude_mpm/services/memory/indexed_memory.py +3 -3
  122. claude_mpm/services/monitor/daemon.py +1 -1
  123. claude_mpm/services/monitor/daemon_manager.py +9 -9
  124. claude_mpm/services/monitor/handlers/file.py +1 -1
  125. claude_mpm/services/monitor/handlers/hooks.py +3 -3
  126. claude_mpm/services/monitor/management/lifecycle.py +7 -7
  127. claude_mpm/services/monitor/server.py +2 -2
  128. claude_mpm/services/orphan_detection.py +13 -16
  129. claude_mpm/services/port_manager.py +2 -2
  130. claude_mpm/services/project/analyzer.py +3 -3
  131. claude_mpm/services/project/archive_manager.py +13 -13
  132. claude_mpm/services/project/dependency_analyzer.py +4 -4
  133. claude_mpm/services/project/documentation_manager.py +4 -4
  134. claude_mpm/services/project/enhanced_analyzer.py +8 -8
  135. claude_mpm/services/project/registry.py +4 -4
  136. claude_mpm/services/project_port_allocator.py +7 -11
  137. claude_mpm/services/session_management_service.py +1 -1
  138. claude_mpm/services/socketio/event_normalizer.py +1 -1
  139. claude_mpm/services/socketio/handlers/code_analysis.py +14 -12
  140. claude_mpm/services/socketio/handlers/file.py +1 -1
  141. claude_mpm/services/socketio/migration_utils.py +1 -1
  142. claude_mpm/services/socketio/server/core.py +1 -1
  143. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +1 -1
  144. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +4 -4
  145. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +1 -1
  146. claude_mpm/services/unified/config_strategies/config_schema.py +4 -4
  147. claude_mpm/services/unified/config_strategies/context_strategy.py +6 -6
  148. claude_mpm/services/unified/config_strategies/error_handling_strategy.py +10 -10
  149. claude_mpm/services/unified/config_strategies/file_loader_strategy.py +5 -5
  150. claude_mpm/services/unified/config_strategies/unified_config_service.py +8 -8
  151. claude_mpm/services/unified/config_strategies/validation_strategy.py +15 -15
  152. claude_mpm/services/unified/deployment_strategies/base.py +4 -4
  153. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +15 -15
  154. claude_mpm/services/unified/deployment_strategies/local.py +9 -9
  155. claude_mpm/services/unified/deployment_strategies/utils.py +9 -9
  156. claude_mpm/services/unified/deployment_strategies/vercel.py +7 -7
  157. claude_mpm/services/unified/unified_config.py +5 -5
  158. claude_mpm/services/unified/unified_deployment.py +2 -2
  159. claude_mpm/services/utility_service.py +1 -1
  160. claude_mpm/services/version_control/conflict_resolution.py +2 -2
  161. claude_mpm/services/version_control/git_operations.py +3 -3
  162. claude_mpm/services/version_control/semantic_versioning.py +13 -13
  163. claude_mpm/services/version_control/version_parser.py +1 -1
  164. claude_mpm/storage/state_storage.py +12 -13
  165. claude_mpm/tools/code_tree_analyzer.py +5 -5
  166. claude_mpm/tools/code_tree_builder.py +4 -4
  167. claude_mpm/tools/socketio_debug.py +1 -1
  168. claude_mpm/utils/agent_dependency_loader.py +4 -4
  169. claude_mpm/utils/common.py +2 -2
  170. claude_mpm/utils/config_manager.py +3 -3
  171. claude_mpm/utils/dependency_cache.py +2 -2
  172. claude_mpm/utils/dependency_strategies.py +6 -6
  173. claude_mpm/utils/file_utils.py +11 -11
  174. claude_mpm/utils/log_cleanup.py +1 -1
  175. claude_mpm/utils/path_operations.py +1 -1
  176. claude_mpm/validation/agent_validator.py +2 -2
  177. claude_mpm/validation/frontmatter_validator.py +1 -1
  178. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.12.dist-info}/METADATA +1 -1
  179. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.12.dist-info}/RECORD +183 -183
  180. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.12.dist-info}/WHEEL +0 -0
  181. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.12.dist-info}/entry_points.txt +0 -0
  182. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.12.dist-info}/licenses/LICENSE +0 -0
  183. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.12.dist-info}/top_level.txt +0 -0
@@ -114,7 +114,7 @@ class CommonIssuesCheck(BaseDiagnosticCheck):
114
114
  # Try to count conversations
115
115
  conversation_count = 0
116
116
  try:
117
- with open(claude_json_path) as f:
117
+ with claude_json_path.open() as f:
118
118
  data = json.load(f)
119
119
  if isinstance(data, dict) and "conversations" in data:
120
120
  conversation_count = len(data["conversations"])
@@ -283,10 +283,10 @@ class CommonIssuesCheck(BaseDiagnosticCheck):
283
283
  try:
284
284
  import yaml
285
285
 
286
- with open(user_config) as f:
286
+ with user_config.open() as f:
287
287
  user_data = yaml.safe_load(f) or {}
288
288
 
289
- with open(project_config) as f:
289
+ with project_config.open() as f:
290
290
  project_data = yaml.safe_load(f) or {}
291
291
 
292
292
  # Check for conflicting keys
@@ -91,7 +91,7 @@ class ConfigurationCheck(BaseDiagnosticCheck):
91
91
  )
92
92
 
93
93
  try:
94
- with open(config_path) as f:
94
+ with config_path.open() as f:
95
95
  config = yaml.safe_load(f)
96
96
 
97
97
  issues = self._validate_config_structure(config)
@@ -143,7 +143,7 @@ class ConfigurationCheck(BaseDiagnosticCheck):
143
143
  )
144
144
 
145
145
  try:
146
- with open(config_path) as f:
146
+ with config_path.open() as f:
147
147
  config = yaml.safe_load(f)
148
148
 
149
149
  issues = self._validate_config_structure(config)
@@ -138,7 +138,7 @@ class InstallationCheck(BaseDiagnosticCheck):
138
138
  details={"error": str(e)},
139
139
  )
140
140
 
141
- def _check_installation_method(self) -> DiagnosticResult: # noqa: PLR0911
141
+ def _check_installation_method(self) -> DiagnosticResult:
142
142
  """Detect how claude-mpm was installed."""
143
143
  methods_found = []
144
144
  details = {}
@@ -154,7 +154,7 @@ class MCPCheck(BaseDiagnosticCheck):
154
154
  )
155
155
 
156
156
  try:
157
- with open(config_path) as f:
157
+ with config_path.open() as f:
158
158
  config = json.load(f)
159
159
 
160
160
  mcp_servers = config.get("mcpServers", {})
@@ -369,7 +369,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
369
369
  stderr_output = stderr_data.decode(
370
370
  "utf-8", errors="ignore"
371
371
  )[:200]
372
- except:
372
+ except (asyncio.TimeoutError, OSError):
373
373
  pass
374
374
 
375
375
  if stderr_output:
@@ -391,7 +391,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
391
391
  stderr_output = stderr_data.decode(
392
392
  "utf-8", errors="ignore"
393
393
  )[:200]
394
- except:
394
+ except (asyncio.TimeoutError, OSError):
395
395
  pass
396
396
 
397
397
  if stderr_output:
@@ -778,7 +778,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
778
778
  return None
779
779
 
780
780
  try:
781
- with open(claude_config_path) as f:
781
+ with claude_config_path.open() as f:
782
782
  config = json.load(f)
783
783
 
784
784
  mcp_servers = config.get("mcpServers", {})
@@ -869,7 +869,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
869
869
 
870
870
  # Create backup
871
871
  backup_path = config_path.with_suffix(".json.backup")
872
- with open(backup_path, "w") as f:
872
+ with backup_path.open("w") as f:
873
873
  json.dump(config, f, indent=2)
874
874
 
875
875
  # Update the configuration - ensure we're setting the exact new_args
@@ -884,11 +884,11 @@ class MCPServicesCheck(BaseDiagnosticCheck):
884
884
  return False
885
885
 
886
886
  # Write updated configuration
887
- with open(config_path, "w") as f:
887
+ with config_path.open("w") as f:
888
888
  json.dump(config, f, indent=2)
889
889
 
890
890
  # Verify the file was written correctly
891
- with open(config_path) as f:
891
+ with config_path.open() as f:
892
892
  verify_config = json.load(f)
893
893
  verify_args = (
894
894
  verify_config.get("mcpServers", {})
@@ -902,9 +902,9 @@ class MCPServicesCheck(BaseDiagnosticCheck):
902
902
  f"Expected {new_args}, got {verify_args}"
903
903
  )
904
904
  # Restore backup
905
- with open(backup_path) as bf:
905
+ with backup_path.open() as bf:
906
906
  backup_config = json.load(bf)
907
- with open(config_path, "w") as f:
907
+ with config_path.open("w") as f:
908
908
  json.dump(backup_config, f, indent=2)
909
909
  return False
910
910
 
@@ -936,7 +936,7 @@ class MCPServicesCheck(BaseDiagnosticCheck):
936
936
  fix_description="Initialize Claude configuration",
937
937
  )
938
938
 
939
- with open(config_file) as f:
939
+ with config_file.open() as f:
940
940
  config = json.load(f)
941
941
 
942
942
  # Get the current project configuration
@@ -193,7 +193,7 @@ class MonitorCheck(BaseDiagnosticCheck):
193
193
  for config_path in config_paths:
194
194
  if config_path.exists():
195
195
  try:
196
- with open(config_path) as f:
196
+ with config_path.open() as f:
197
197
  config = yaml.safe_load(f)
198
198
  if config and isinstance(config, dict):
199
199
  response_config = config.get("response_logging", {})
@@ -248,7 +248,7 @@ class DoctorReporter:
248
248
  # Header with timestamp
249
249
  print("# Claude MPM Doctor Report")
250
250
  print(
251
- f"\n**Generated:** {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
251
+ f"\n**Generated:** {datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d %H:%M:%S')}"
252
252
  )
253
253
  print(f"**Version:** {self._get_version()}\n")
254
254
  print("---\n")
@@ -491,7 +491,7 @@ class EventAggregator:
491
491
  for filepath in session_files:
492
492
  try:
493
493
  # Load just the metadata, not the full session
494
- with open(filepath) as f:
494
+ with filepath.open() as f:
495
495
  data = json.load(f)
496
496
 
497
497
  sessions.append(
@@ -65,6 +65,9 @@ class EventBus:
65
65
  self._event_history: List[Dict[str, Any]] = []
66
66
  self._max_history_size = 100
67
67
 
68
+ # Track async handler tasks to prevent garbage collection
69
+ self._handler_tasks: Set[asyncio.Task] = set()
70
+
68
71
  logger.info("EventBus initialized")
69
72
 
70
73
  @classmethod
@@ -190,13 +193,15 @@ class EventBus:
190
193
  try:
191
194
  # Call with event_type and data for wildcard handlers
192
195
  if asyncio.iscoroutinefunction(handler):
193
- # Schedule async handlers
196
+ # Schedule async handlers with tracking
194
197
  try:
195
198
  loop = asyncio.get_event_loop()
196
199
  if loop.is_running():
197
- asyncio.create_task(
200
+ task = asyncio.create_task(
198
201
  handler(event_type, data)
199
202
  )
203
+ self._handler_tasks.add(task)
204
+ task.add_done_callback(self._handler_tasks.discard)
200
205
  else:
201
206
  loop.run_until_complete(
202
207
  handler(event_type, data)
@@ -109,7 +109,7 @@ class DeadLetterConsumer(IEventConsumer):
109
109
  self._rotate_file()
110
110
 
111
111
  # Write to file
112
- with open(self._current_file, "a") as f:
112
+ with self._current_file.open("a") as f:
113
113
  f.write(event_json)
114
114
 
115
115
  # Update metrics
@@ -165,7 +165,7 @@ class DeadLetterConsumer(IEventConsumer):
165
165
  # Find files in time range
166
166
  for file_path in sorted(self.output_dir.glob("dead-letter-*.jsonl")):
167
167
  try:
168
- with open(file_path) as f:
168
+ with file_path.open() as f:
169
169
  for line in f:
170
170
  event_data = json.loads(line)
171
171
 
@@ -141,7 +141,7 @@ class FrameworkClaudeMdGenerator:
141
141
  current_content = None
142
142
 
143
143
  if target_file.exists():
144
- with open(target_file) as f:
144
+ with target_file.open() as f:
145
145
  current_content = f.read()
146
146
 
147
147
  # Generate new content
@@ -95,7 +95,7 @@ class DeploymentManager:
95
95
 
96
96
  # Check if file exists and compare versions
97
97
  if target_file.exists() and not force:
98
- with open(target_file) as f:
98
+ with target_file.open() as f:
99
99
  existing_content = f.read()
100
100
  existing_fw_ver = self.version_manager.parse_current_version(
101
101
  existing_content
@@ -110,7 +110,7 @@ class DeploymentManager:
110
110
  parent_path.mkdir(parents=True, exist_ok=True)
111
111
 
112
112
  # Write content
113
- with open(target_file, "w") as f:
113
+ with target_file.open("w") as f:
114
114
  f.write(content)
115
115
 
116
116
  # Get version info for success message
@@ -137,7 +137,7 @@ class DeploymentManager:
137
137
  return True, f"{self.target_filename} does not exist"
138
138
 
139
139
  try:
140
- with open(target_file) as f:
140
+ with target_file.open() as f:
141
141
  existing_content = f.read()
142
142
  existing_fw_ver = self.version_manager.parse_current_version(
143
143
  existing_content
@@ -38,7 +38,7 @@ class VersionManager:
38
38
  version_path = package_path.parent / "framework" / "VERSION"
39
39
 
40
40
  if version_path.exists():
41
- with open(version_path) as f:
41
+ with version_path.open() as f:
42
42
  version_content = f.read().strip()
43
43
  # Framework VERSION file contains just the framework version number
44
44
  try:
@@ -24,7 +24,7 @@ class HookInstallerService:
24
24
  self.claude_dir = Path.home() / ".claude"
25
25
  self.settings_file = self.claude_dir / "settings.json"
26
26
 
27
- def is_hooks_configured(self) -> bool: # noqa: PLR0911
27
+ def is_hooks_configured(self) -> bool:
28
28
  """Check if hooks are configured in Claude settings.
29
29
 
30
30
  Returns:
@@ -35,7 +35,7 @@ class HookInstallerService:
35
35
  self.logger.debug("Claude settings file does not exist")
36
36
  return False
37
37
 
38
- with open(self.settings_file) as f:
38
+ with self.settings_file.open() as f:
39
39
  settings = json.load(f)
40
40
 
41
41
  # Check if hooks section exists
@@ -292,7 +292,7 @@ class HookInstallerService:
292
292
 
293
293
  # Load existing settings or create new
294
294
  if self.settings_file.exists():
295
- with open(self.settings_file) as f:
295
+ with self.settings_file.open() as f:
296
296
  settings = json.load(f)
297
297
  self.logger.debug("Found existing Claude settings")
298
298
  else:
@@ -320,7 +320,7 @@ class HookInstallerService:
320
320
  settings["hooks"][event_type] = [hook_config]
321
321
 
322
322
  # Write settings
323
- with open(self.settings_file, "w") as f:
323
+ with self.settings_file.open("w") as f:
324
324
  json.dump(settings, f, indent=2)
325
325
 
326
326
  self.logger.info(f"Updated Claude settings at: {self.settings_file}")
@@ -380,7 +380,7 @@ class HookInstallerService:
380
380
 
381
381
  self.logger.info("Removing Claude Code hooks...")
382
382
 
383
- with open(self.settings_file) as f:
383
+ with self.settings_file.open() as f:
384
384
  settings = json.load(f)
385
385
 
386
386
  hooks_removed = 0
@@ -422,7 +422,7 @@ class HookInstallerService:
422
422
  del settings["hooks"]
423
423
 
424
424
  # Write updated settings
425
- with open(self.settings_file, "w") as f:
425
+ with self.settings_file.open("w") as f:
426
426
  json.dump(settings, f, indent=2)
427
427
 
428
428
  if hooks_removed > 0:
@@ -469,7 +469,7 @@ class HookInstallerService:
469
469
 
470
470
  try:
471
471
  if self.settings_file.exists():
472
- with open(self.settings_file) as f:
472
+ with self.settings_file.open() as f:
473
473
  settings = json.load(f)
474
474
 
475
475
  if "hooks" in settings:
@@ -151,7 +151,7 @@ class ContextPreservationService(BaseService):
151
151
  return None
152
152
 
153
153
  # Use streaming to find active conversation
154
- with open(self.claude_json_path, "rb") as f:
154
+ with self.claude_json_path.open("rb") as f:
155
155
  parser = ijson.parse(f)
156
156
 
157
157
  active_conv_id = None
@@ -207,7 +207,7 @@ class ContextPreservationService(BaseService):
207
207
  keep_recent_days * 86400
208
208
  )
209
209
 
210
- with open(self.claude_json_path) as f:
210
+ with self.claude_json_path.open() as f:
211
211
  data = json.load(f)
212
212
 
213
213
  original_count = len(data.get("conversations", []))
@@ -239,7 +239,7 @@ class ContextPreservationService(BaseService):
239
239
 
240
240
  # Write compressed version
241
241
  temp_path = self.claude_json_path.with_suffix(".tmp")
242
- with open(temp_path, "w") as f:
242
+ with temp_path.open("w") as f:
243
243
  json.dump(data, f, separators=(",", ":")) # Compact format
244
244
 
245
245
  # Replace original
@@ -316,7 +316,7 @@ class ContextPreservationService(BaseService):
316
316
  # Use streaming to extract preferences
317
317
  preferences = {}
318
318
 
319
- with open(self.claude_json_path, "rb") as f:
319
+ with self.claude_json_path.open("rb") as f:
320
320
  parser = ijson.parse(f)
321
321
 
322
322
  for prefix, event, value in parser:
@@ -339,7 +339,7 @@ class ContextPreservationService(BaseService):
339
339
  ) -> ConversationState:
340
340
  """Parse Claude JSON using standard JSON parser."""
341
341
  try:
342
- with open(self.claude_json_path) as f:
342
+ with self.claude_json_path.open() as f:
343
343
  data = json.load(f)
344
344
 
345
345
  return await self._extract_conversation_state(data, extract_full)
@@ -357,7 +357,7 @@ class ContextPreservationService(BaseService):
357
357
  preferences = {}
358
358
  open_files = []
359
359
 
360
- with open(self.claude_json_path, "rb") as f:
360
+ with self.claude_json_path.open("rb") as f:
361
361
  parser = ijson.parse(f)
362
362
 
363
363
  conversation_count = 0
@@ -527,7 +527,7 @@ class ContextPreservationService(BaseService):
527
527
  backup_name += ".gz"
528
528
  backup_path = self.claude_backup_dir / backup_name
529
529
 
530
- with open(self.claude_json_path, "rb") as f_in:
530
+ with self.claude_json_path.open("rb") as f_in:
531
531
  with gzip.open(backup_path, "wb") as f_out:
532
532
  shutil.copyfileobj(f_in, f_out)
533
533
  else:
@@ -80,7 +80,7 @@ class SocketIODaemonManager:
80
80
  return False
81
81
 
82
82
  try:
83
- with open(self.pid_file) as f:
83
+ with self.pid_file.open() as f:
84
84
  pid = int(f.read().strip())
85
85
 
86
86
  # Check if process exists and is running
@@ -135,7 +135,7 @@ class SocketIODaemonManager:
135
135
  pid = os.fork()
136
136
  if pid > 0:
137
137
  # Parent process - save PID and exit
138
- with open(self.pid_file, "w") as f:
138
+ with self.pid_file.open("w") as f:
139
139
  f.write(str(pid))
140
140
  logger.info(f"Socket.IO server started as daemon (PID: {pid})")
141
141
  return True
@@ -155,7 +155,7 @@ class SocketIODaemonManager:
155
155
  os.umask(0)
156
156
 
157
157
  # Redirect stdout/stderr to log file
158
- with open(self.log_file, "a") as log:
158
+ with self.log_file.open("a") as log:
159
159
  os.dup2(log.fileno(), sys.stdout.fileno())
160
160
  os.dup2(log.fileno(), sys.stderr.fileno())
161
161
 
@@ -199,7 +199,7 @@ class SocketIODaemonManager:
199
199
  return False
200
200
 
201
201
  try:
202
- with open(self.pid_file) as f:
202
+ with self.pid_file.open() as f:
203
203
  pid = int(f.read().strip())
204
204
 
205
205
  logger.info(f"Stopping Socket.IO server (PID: {pid})")
@@ -254,7 +254,7 @@ class SocketIODaemonManager:
254
254
  }
255
255
 
256
256
  if status_info["running"]:
257
- with open(self.pid_file) as f:
257
+ with self.pid_file.open() as f:
258
258
  status_info["pid"] = int(f.read().strip())
259
259
 
260
260
  # Check port accessibility
@@ -199,7 +199,7 @@ class MCPConfigManager:
199
199
  f"Found kuzu-memory with MCP support at {path}"
200
200
  )
201
201
  return path
202
- except:
202
+ except (subprocess.SubprocessError, subprocess.TimeoutExpired, OSError):
203
203
  pass
204
204
 
205
205
  # If no MCP-capable version found, log warning but return None
@@ -571,7 +571,7 @@ class MCPConfigManager:
571
571
  if result.returncode == 0 or "version" in result.stdout.lower():
572
572
  use_pipx_run = True
573
573
  self.logger.debug(f"Will use 'pipx run' for {service_name}")
574
- except:
574
+ except (subprocess.SubprocessError, subprocess.TimeoutExpired, OSError):
575
575
  pass
576
576
 
577
577
  # Try uvx if pipx run not available
@@ -587,7 +587,7 @@ class MCPConfigManager:
587
587
  if result.returncode == 0 or "version" in result.stdout.lower():
588
588
  use_uvx = True
589
589
  self.logger.debug(f"Will use 'uvx' for {service_name}")
590
- except:
590
+ except (subprocess.SubprocessError, subprocess.TimeoutExpired, OSError):
591
591
  pass
592
592
 
593
593
  # If neither work, try to find direct path
@@ -719,7 +719,7 @@ class MCPConfigManager:
719
719
  claude_config = {}
720
720
  if self.claude_config_path.exists():
721
721
  try:
722
- with open(self.claude_config_path) as f:
722
+ with self.claude_config_path.open() as f:
723
723
  claude_config = json.load(f)
724
724
  except Exception as e:
725
725
  self.logger.error(f"Error reading {self.claude_config_path}: {e}")
@@ -832,7 +832,7 @@ class MCPConfigManager:
832
832
  self.logger.debug(f"Created backup: {backup_path}")
833
833
 
834
834
  # Write updated config
835
- with open(self.claude_config_path, "w") as f:
835
+ with self.claude_config_path.open("w") as f:
836
836
  json.dump(claude_config, f, indent=2)
837
837
 
838
838
  messages = []
@@ -884,7 +884,7 @@ class MCPConfigManager:
884
884
  existing_config = {}
885
885
  if mcp_config_path.exists():
886
886
  try:
887
- with open(mcp_config_path) as f:
887
+ with mcp_config_path.open() as f:
888
888
  existing_config = json.load(f)
889
889
  except Exception as e:
890
890
  self.logger.error(f"Error reading existing config: {e}")
@@ -912,7 +912,7 @@ class MCPConfigManager:
912
912
 
913
913
  # Write the updated configuration
914
914
  try:
915
- with open(mcp_config_path, "w") as f:
915
+ with mcp_config_path.open("w") as f:
916
916
  json.dump(new_config, f, indent=2)
917
917
 
918
918
  if missing_services:
@@ -937,7 +937,7 @@ class MCPConfigManager:
937
937
  mcp_config_path = self.project_root / ConfigLocation.PROJECT_MCP.value
938
938
  if mcp_config_path.exists():
939
939
  try:
940
- with open(mcp_config_path) as f:
940
+ with mcp_config_path.open() as f:
941
941
  config = json.load(f)
942
942
  results = {}
943
943
  for service_name, service_config in config.get(
@@ -951,7 +951,7 @@ class MCPConfigManager:
951
951
  return {}
952
952
 
953
953
  try:
954
- with open(self.claude_config_path) as f:
954
+ with self.claude_config_path.open() as f:
955
955
  claude_config = json.load(f)
956
956
 
957
957
  # Get project's MCP servers
@@ -1638,7 +1638,7 @@ class MCPConfigManager:
1638
1638
  if result.returncode == 0 or "version" in result.stdout.lower():
1639
1639
  self.logger.debug(f"{service_name} accessible via 'pipx run'")
1640
1640
  return True
1641
- except:
1641
+ except (subprocess.SubprocessError, subprocess.TimeoutExpired, OSError):
1642
1642
  pass
1643
1643
  return False
1644
1644
 
@@ -78,7 +78,7 @@ class MCPAutoConfigurator:
78
78
  return False
79
79
 
80
80
  try:
81
- with open(self.claude_config_path) as f:
81
+ with self.claude_config_path.open() as f:
82
82
  config = json.load(f)
83
83
 
84
84
  # Check if claude-mpm-gateway is configured
@@ -131,7 +131,7 @@ class MCPAutoConfigurator:
131
131
  return False
132
132
 
133
133
  try:
134
- with open(self.preference_file) as f:
134
+ with self.preference_file.open() as f:
135
135
  prefs = json.load(f)
136
136
  return prefs.get("asked", False)
137
137
  except (OSError, json.JSONDecodeError):
@@ -148,7 +148,7 @@ class MCPAutoConfigurator:
148
148
  }
149
149
 
150
150
  try:
151
- with open(self.preference_file, "w") as f:
151
+ with self.preference_file.open("w") as f:
152
152
  json.dump(prefs, f, indent=2)
153
153
  except Exception as e:
154
154
  self.logger.debug(f"Could not save preference: {e}")
@@ -244,7 +244,7 @@ class MCPAutoConfigurator:
244
244
  }
245
245
 
246
246
  # Save configuration
247
- with open(self.claude_config_path, "w") as f:
247
+ with self.claude_config_path.open("w") as f:
248
248
  json.dump(config, f, indent=2)
249
249
 
250
250
  print(f"✅ Configuration saved to: {self.claude_config_path}")
@@ -284,7 +284,7 @@ class MCPAutoConfigurator:
284
284
  """Load existing config or create new one."""
285
285
  if self.claude_config_path.exists():
286
286
  try:
287
- with open(self.claude_config_path) as f:
287
+ with self.claude_config_path.open() as f:
288
288
  return json.load(f)
289
289
  except json.JSONDecodeError:
290
290
  self.logger.warning("Existing config is invalid JSON, creating new")
@@ -97,7 +97,7 @@ class MCPConfigLoader:
97
97
  self.logger.error(f"Configuration file not found: {expanded_path}")
98
98
  return None
99
99
 
100
- with open(expanded_path) as f:
100
+ with expanded_path.open() as f:
101
101
  config = yaml.safe_load(f)
102
102
 
103
103
  self.logger.info(f"Configuration loaded from {expanded_path}")
@@ -271,7 +271,7 @@ class MCPConfigLoader:
271
271
  expanded_path = path.expanduser()
272
272
  expanded_path.parent.mkdir(parents=True, exist_ok=True)
273
273
 
274
- with open(expanded_path, "w") as f:
274
+ with expanded_path.open("w") as f:
275
275
  yaml.dump(
276
276
  MCPConfiguration.DEFAULT_CONFIG,
277
277
  f,
@@ -155,7 +155,7 @@ class MCPConfiguration(BaseMCPService, IMCPConfiguration):
155
155
  self.log_warning(f"Configuration file not found: {config_path}")
156
156
  return True # Not an error, use defaults
157
157
 
158
- with open(config_path) as f:
158
+ with config_path.open() as f:
159
159
  if config_path.suffix in [".yaml", ".yml"]:
160
160
  loaded_config = yaml.safe_load(f) or {}
161
161
  else:
@@ -171,7 +171,7 @@ class MCPConfiguration(BaseMCPService, IMCPConfiguration):
171
171
  return True
172
172
 
173
173
  except yaml.YAMLError as e:
174
- raise MCPConfigurationError(f"Failed to parse YAML configuration: {e}")
174
+ raise MCPConfigurationError(f"Failed to parse YAML configuration: {e}") from e
175
175
  except Exception as e:
176
176
  self.log_error(f"Failed to load configuration: {e}")
177
177
  return False
@@ -364,7 +364,7 @@ class MCPConfiguration(BaseMCPService, IMCPConfiguration):
364
364
  save_path = Path(save_path).expanduser()
365
365
  save_path.parent.mkdir(parents=True, exist_ok=True)
366
366
 
367
- with open(save_path, "w") as f:
367
+ with save_path.open("w") as f:
368
368
  yaml.dump(
369
369
  self._config_data, f, default_flow_style=False, sort_keys=True
370
370
  )
@@ -190,7 +190,7 @@ class MCPProcessPool:
190
190
 
191
191
  # Write process info to file for debugging
192
192
  info_file = self.pool_dir / f"{server_name}_{process.pid}.json"
193
- with open(info_file, "w") as f:
193
+ with info_file.open("w") as f:
194
194
  json.dump(self._process_info[server_name], f, indent=2)
195
195
 
196
196
  return process
@@ -934,7 +934,7 @@ async def pre_warm_mcp_servers():
934
934
  #
935
935
  # if claude_config_path.exists():
936
936
  # try:
937
- # with open(claude_config_path) as f:
937
+ # with claude_config_path.open() as f:
938
938
  # config_data = json.load(f)
939
939
  # mcp_servers = config_data.get("mcpServers", {})
940
940
  # configs.update(mcp_servers)
@@ -945,7 +945,7 @@ async def pre_warm_mcp_servers():
945
945
  # mcp_config_path = Path.cwd() / ".mcp.json"
946
946
  # if mcp_config_path.exists():
947
947
  # try:
948
- # with open(mcp_config_path) as f:
948
+ # with mcp_config_path.open() as f:
949
949
  # config_data = json.load(f)
950
950
  # mcp_servers = config_data.get("mcpServers", {})
951
951
  # configs.update(mcp_servers)
@@ -149,7 +149,7 @@ class MCPGatewayManager:
149
149
  return None
150
150
 
151
151
  try:
152
- with open(self.instance_file) as f:
152
+ with self.instance_file.open() as f:
153
153
  instance_info = json.load(f)
154
154
 
155
155
  # Validate PID is still running
@@ -195,7 +195,7 @@ class MCPGatewayManager:
195
195
  "lock_file": str(self.lock_file),
196
196
  }
197
197
 
198
- with open(self.instance_file, "w") as f:
198
+ with self.instance_file.open("w") as f:
199
199
  json.dump(instance_info, f, indent=2)
200
200
 
201
201
  self._current_instance = instance_info
@@ -125,7 +125,7 @@ class MCPGatewayStartupVerifier:
125
125
  try:
126
126
  import json
127
127
 
128
- with open(self.config_file, "w") as f:
128
+ with self.config_file.open("w") as f:
129
129
  json.dump(default_config, f, indent=2)
130
130
 
131
131
  self.logger.info(