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
@@ -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__}")
@@ -80,8 +80,8 @@ def safe_path_join(*parts: Union[str, Path]) -> Path:
80
80
  # Ensure the resolved path is under the base path
81
81
  try:
82
82
  resolved.relative_to(base)
83
- except ValueError:
84
- raise ValueError(f"Path traversal detected: {path}")
83
+ except ValueError as e:
84
+ raise ValueError(f"Path traversal detected: {path}") from e
85
85
 
86
86
  return resolved
87
87
 
@@ -142,7 +142,7 @@ def safe_read(
142
142
  """Safely read a file with error handling.
143
143
 
144
144
  Replaces the common pattern:
145
- with open(file, 'r') as f:
145
+ with file.open('r') as f:
146
146
  content = f.read()
147
147
 
148
148
  Args:
@@ -641,7 +641,7 @@ def file_lock(filepath: Union[str, Path], timeout: float = 5.0):
641
641
  if e.errno != errno.EAGAIN:
642
642
  raise
643
643
  if time.time() - start_time > timeout:
644
- raise TimeoutError(f"Could not acquire lock for {filepath}")
644
+ raise TimeoutError(f"Could not acquire lock for {filepath}") from e
645
645
  time.sleep(0.1)
646
646
 
647
647
  yield lock_handle
@@ -724,7 +724,7 @@ def get_file_hash(
724
724
 
725
725
  try:
726
726
  hasher = hashlib.new(algorithm)
727
- with open(filepath, "rb") as f:
727
+ with filepath.open("rb") as f:
728
728
  for chunk in iter(lambda: f.read(8192), b""):
729
729
  hasher.update(chunk)
730
730
  return hasher.hexdigest()
@@ -154,7 +154,7 @@ class CapabilityGenerator:
154
154
  Dictionary with agent metadata or None
155
155
  """
156
156
  try:
157
- with open(agent_file) as f:
157
+ with agent_file.open() as f:
158
158
  content = f.read()
159
159
 
160
160
  # Default values
@@ -244,7 +244,7 @@ class CapabilityGenerator:
244
244
  template_file = templates_dir / f"{agent_name}.json"
245
245
 
246
246
  if template_file.exists():
247
- with open(template_file) as f:
247
+ with template_file.open() as f:
248
248
  template_data = json.load(f)
249
249
  return template_data.get("routing")
250
250
 
@@ -262,7 +262,7 @@ class CapabilityGenerator:
262
262
  if alt_name != agent_name:
263
263
  alt_file = templates_dir / f"{alt_name}.json"
264
264
  if alt_file.exists():
265
- with open(alt_file) as f:
265
+ with alt_file.open() as f:
266
266
  template_data = json.load(f)
267
267
  return template_data.get("routing")
268
268
 
@@ -310,7 +310,7 @@ class CapabilityGenerator:
310
310
  template_file = templates_dir / f"{agent_name}.json"
311
311
 
312
312
  if template_file.exists():
313
- with open(template_file) as f:
313
+ with template_file.open() as f:
314
314
  template_data = json.load(f)
315
315
  return template_data.get("memory_routing")
316
316
 
@@ -330,7 +330,7 @@ class CapabilityGenerator:
330
330
  if alt_name != agent_name:
331
331
  alt_file = templates_dir / f"{alt_name}.json"
332
332
  if alt_file.exists():
333
- with open(alt_file) as f:
333
+ with alt_file.open() as f:
334
334
  template_data = json.load(f)
335
335
  return template_data.get("memory_routing")
336
336
 
@@ -134,7 +134,7 @@ class AgentLoader:
134
134
 
135
135
  for json_file in template_dir.glob("*.json"):
136
136
  try:
137
- with open(json_file) as f:
137
+ with json_file.open() as f:
138
138
  template_data = json.load(f)
139
139
 
140
140
  # Extract agent metadata
@@ -55,7 +55,7 @@ class MetadataProcessor:
55
55
  Dictionary with agent metadata or None
56
56
  """
57
57
  try:
58
- with open(agent_file) as f:
58
+ with agent_file.open() as f:
59
59
  content = f.read()
60
60
 
61
61
  # Default values
@@ -89,7 +89,7 @@ class TemplateProcessor:
89
89
  # Try exact match first
90
90
  template_file = templates_dir / f"{agent_name}.json"
91
91
  if template_file.exists():
92
- with open(template_file) as f:
92
+ with template_file.open() as f:
93
93
  return json.load(f)
94
94
 
95
95
  # Try alternative naming variations
@@ -97,7 +97,7 @@ class TemplateProcessor:
97
97
  for alt_name in alternative_names:
98
98
  alt_file = templates_dir / f"{alt_name}.json"
99
99
  if alt_file.exists():
100
- with open(alt_file) as f:
100
+ with alt_file.open() as f:
101
101
  return json.load(f)
102
102
 
103
103
  return None
@@ -218,7 +218,7 @@ class TemplateProcessor:
218
218
 
219
219
  for json_file in template_dir.glob("*.json"):
220
220
  try:
221
- with open(json_file) as f:
221
+ with json_file.open() as f:
222
222
  template_data = json.load(f)
223
223
 
224
224
  agent_metadata = self.extract_metadata(template_data)
@@ -504,9 +504,9 @@ class FrameworkLoader:
504
504
  metadata["instructions_length"] = len(instructions)
505
505
 
506
506
  if loop.is_running():
507
- asyncio.create_task(
507
+ _task = asyncio.create_task( # noqa: RUF006
508
508
  log_manager.log_prompt("system_prompt", instructions, metadata)
509
- )
509
+ ) # Fire-and-forget logging
510
510
  else:
511
511
  loop.run_until_complete(
512
512
  log_manager.log_prompt("system_prompt", instructions, metadata)
@@ -542,12 +542,12 @@ class LogManager:
542
542
 
543
543
  if extension == ".json":
544
544
  # JSON files also get structured metadata for consistency
545
- with open(file_path, "w", encoding="utf-8") as f:
545
+ with file_path.open("w", encoding="utf-8") as f:
546
546
  json.dump(data, f, indent=2, ensure_ascii=False)
547
547
  # For markdown or text files
548
548
  elif isinstance(data, dict):
549
549
  # Write as formatted markdown with metadata
550
- with open(file_path, "w", encoding="utf-8") as f:
550
+ with file_path.open("w", encoding="utf-8") as f:
551
551
  f.write("---\n")
552
552
  f.write(f"timestamp: {data.get('timestamp', 'unknown')}\n")
553
553
  f.write(f"type: {data.get('type', 'unknown')}\n")
@@ -559,7 +559,7 @@ class LogManager:
559
559
  f.write(data.get("content", ""))
560
560
  else:
561
561
  # Write content directly
562
- with open(file_path, "w", encoding="utf-8") as f:
562
+ with file_path.open("w", encoding="utf-8") as f:
563
563
  f.write(str(data))
564
564
  except Exception as e:
565
565
  logger.error(f"Failed to write {file_path}: {e}")
@@ -588,7 +588,7 @@ class LogManager:
588
588
 
589
589
  def write_task():
590
590
  try:
591
- with open(log_file, "a", encoding="utf-8") as f:
591
+ with log_file.open("a", encoding="utf-8") as f:
592
592
  f.write(log_entry)
593
593
  except Exception as e:
594
594
  logger.error(f"Failed to write log: {e}")
claude_mpm/core/logger.py CHANGED
@@ -526,7 +526,7 @@ class ProjectLogger:
526
526
  self.dirs["logs_system"]
527
527
  / f"{datetime.now(timezone.utc).strftime('%Y%m%d')}.jsonl"
528
528
  )
529
- with open(log_file, "a") as f:
529
+ with log_file.open("a") as f:
530
530
  f.write(json.dumps(log_entry) + "\n")
531
531
 
532
532
  def log_agent_invocation(
@@ -580,7 +580,7 @@ class ProjectLogger:
580
580
  daily_log = (
581
581
  agent_log_dir / f"{datetime.now(timezone.utc).strftime('%Y%m%d')}.jsonl"
582
582
  )
583
- with open(daily_log, "a") as f:
583
+ with daily_log.open("a") as f:
584
584
  f.write(json.dumps(log_entry) + "\n")
585
585
 
586
586
  def get_session_summary(self) -> Dict[str, Any]:
@@ -227,7 +227,7 @@ class OptimizedStartup:
227
227
  """Parse configuration file."""
228
228
  import yaml
229
229
 
230
- with open(path) as f:
230
+ with path.open() as f:
231
231
  return yaml.safe_load(f) or {}
232
232
 
233
233
  def _store_config(self, config: Dict[str, Any]):
@@ -42,7 +42,7 @@ class OutputStyleManager:
42
42
  Path(__file__).parent.parent / "agents" / "OUTPUT_STYLE.md"
43
43
  )
44
44
 
45
- def _detect_claude_version(self) -> Optional[str]: # noqa: PLR0911
45
+ def _detect_claude_version(self) -> Optional[str]:
46
46
  """
47
47
  Detect Claude Code version by running 'claude --version'.
48
48
  Uses global cache to avoid duplicate detection and logging.
@@ -160,10 +160,10 @@ class ServiceRegistry:
160
160
  try:
161
161
  # Use the enhanced container's named resolution
162
162
  return self.container.get(BaseService, name=service_type)
163
- except Exception:
163
+ except Exception as e:
164
164
  # Fall back to looking up class and resolving
165
165
  if service_type not in self._services:
166
- raise KeyError(f"Service '{service_type}' not registered")
166
+ raise KeyError(f"Service '{service_type}' not registered") from e
167
167
  service_class = self._services[service_type]
168
168
  return self.container.get(service_class)
169
169
  else:
@@ -187,7 +187,7 @@ class SessionManager:
187
187
  """Save sessions to disk."""
188
188
  session_file = self.session_dir / "active_sessions.json"
189
189
  try:
190
- with open(session_file, "w") as f:
190
+ with session_file.open("w") as f:
191
191
  json.dump(self.active_sessions, f, indent=2)
192
192
  except Exception as e:
193
193
  logger.error(f"Failed to save sessions: {e}")
@@ -197,7 +197,7 @@ class SessionManager:
197
197
  session_file = self.session_dir / "active_sessions.json"
198
198
  if session_file.exists():
199
199
  try:
200
- with open(session_file) as f:
200
+ with session_file.open() as f:
201
201
  self.active_sessions = json.load(f)
202
202
 
203
203
  # Clean up old sessions on load (archive by default)
@@ -286,7 +286,7 @@ class SessionManager:
286
286
  backup_path = archive_dir / backup_name
287
287
 
288
288
  # Compress and backup current file
289
- with open(claude_json_path, "rb") as f_in:
289
+ with claude_json_path.open("rb") as f_in:
290
290
  with gzip.open(backup_path, "wb") as f_out:
291
291
  shutil.copyfileobj(f_in, f_out)
292
292
 
@@ -228,7 +228,7 @@ class ConfigLoader:
228
228
  try:
229
229
  import yaml
230
230
 
231
- with open(config_file) as f:
231
+ with config_file.open() as f:
232
232
  if config_file.suffix.lower() in (".yaml", ".yml"):
233
233
  return yaml.safe_load(f) or {}
234
234
  # Try JSON as fallback
@@ -594,9 +594,9 @@ class SocketIOConnectionPool:
594
594
  timeout=2.0,
595
595
  )
596
596
 
597
- except asyncio.TimeoutError:
597
+ except asyncio.TimeoutError as e:
598
598
  self.logger.debug("Socket.IO connection timeout")
599
- raise TimeoutError("Socket.IO connection timeout")
599
+ raise TimeoutError("Socket.IO connection timeout") from e
600
600
  except Exception as e:
601
601
  self.logger.debug(f"Client connection failed: {e}")
602
602
  raise
@@ -728,7 +728,7 @@ class UnifiedAgentRegistry:
728
728
  },
729
729
  }
730
730
 
731
- with open(output_path, "w") as f:
731
+ with output_path.open("w") as f:
732
732
  json.dump(export_data, f, indent=2)
733
733
 
734
734
  logger.info(f"Exported {len(self.registry)} agents to {output_path}")
@@ -737,7 +737,7 @@ class UnifiedAgentRegistry:
737
737
  """Import registry from JSON file."""
738
738
  input_path = Path(input_path)
739
739
 
740
- with open(input_path) as f:
740
+ with input_path.open() as f:
741
741
  data = json.load(f)
742
742
 
743
743
  # Clear current registry
@@ -436,7 +436,7 @@ class ConfigurationService:
436
436
  if config_path.exists():
437
437
  import yaml
438
438
 
439
- with open(config_path) as f:
439
+ with config_path.open() as f:
440
440
  file_config = yaml.safe_load(f) or {}
441
441
  config_data.update(file_config)
442
442
  break
@@ -448,7 +448,7 @@ class ConfigurationService:
448
448
  raise ConfigurationError(
449
449
  f"Failed to load configuration: {e}",
450
450
  context={"error_type": type(e).__name__},
451
- )
451
+ ) from e
452
452
 
453
453
  @property
454
454
  def config(self) -> UnifiedConfig:
@@ -515,7 +515,7 @@ class ConfigurationService:
515
515
  return True
516
516
  raise ConfigurationError("Invalid SocketIO port range")
517
517
  except Exception as e:
518
- raise ConfigurationError(f"Configuration validation failed: {e}")
518
+ raise ConfigurationError(f"Configuration validation failed: {e}") from e
519
519
 
520
520
  def export_to_file(self, file_path: Union[str, Path], format: str = "yaml") -> None:
521
521
  """
@@ -531,12 +531,12 @@ class ConfigurationService:
531
531
  if format.lower() == "yaml":
532
532
  import yaml
533
533
 
534
- with open(file_path, "w") as f:
534
+ with file_path.open("w") as f:
535
535
  yaml.dump(self._config.dict(), f, default_flow_style=False)
536
536
  elif format.lower() == "json":
537
537
  import json
538
538
 
539
- with open(file_path, "w") as f:
539
+ with file_path.open("w") as f:
540
540
  json.dump(self._config.dict(), f, indent=2)
541
541
  else:
542
542
  raise ConfigurationError(f"Unsupported export format: {format}")
@@ -545,4 +545,4 @@ class ConfigurationService:
545
545
  raise ConfigurationError(
546
546
  f"Failed to export configuration: {e}",
547
547
  context={"file_path": str(file_path), "format": format},
548
- )
548
+ ) from e
@@ -167,7 +167,7 @@ class PathContext:
167
167
 
168
168
  @staticmethod
169
169
  @lru_cache(maxsize=1)
170
- def detect_deployment_context() -> DeploymentContext: # noqa: PLR0911
170
+ def detect_deployment_context() -> DeploymentContext:
171
171
  """Detect the current deployment context.
172
172
 
173
173
  Priority order:
@@ -396,7 +396,7 @@ class UnifiedPathManager:
396
396
  return current
397
397
  current = current.parent
398
398
 
399
- raise FileNotFoundError("Could not determine framework root")
399
+ raise FileNotFoundError("Could not determine framework root") from None
400
400
 
401
401
  @property
402
402
  @lru_cache(maxsize=1)
@@ -129,7 +129,7 @@ def has_code_files(directory_path, max_depth=5, current_depth=0):
129
129
  return False
130
130
 
131
131
 
132
- def should_show_item(item_name, item_path, is_directory): # noqa: PLR0911
132
+ def should_show_item(item_name, item_path, is_directory):
133
133
  """Determine if an item should be shown based on filtering rules"""
134
134
  # Always hide system files
135
135
  if item_name in {".DS_Store", "Thumbs.db", "desktop.ini"}:
@@ -29,7 +29,7 @@ class AgentProfileGenerator:
29
29
  if not self.template_path.exists():
30
30
  raise FileNotFoundError(f"Template not found: {self.template_path}")
31
31
 
32
- with open(self.template_path) as f:
32
+ with self.template_path.open() as f:
33
33
  return yaml.safe_load(f)
34
34
 
35
35
  def generate_profile(self, config: Dict[str, Any]) -> str:
@@ -284,11 +284,11 @@ class EventHandlers:
284
284
  # Log the agent prompt asynchronously
285
285
  try:
286
286
  loop = asyncio.get_running_loop()
287
- asyncio.create_task(
287
+ _task = asyncio.create_task( # noqa: RUF006
288
288
  log_manager.log_prompt(
289
289
  f"agent_{agent_type}", prompt_content, metadata
290
290
  )
291
- )
291
+ ) # Fire-and-forget logging (ephemeral hook process)
292
292
  except RuntimeError:
293
293
  # No running loop, create one
294
294
  loop = asyncio.new_event_loop()