claude-mpm 4.5.8__py3-none-any.whl → 4.5.12__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +20 -5
  3. claude_mpm/agents/agent_loader.py +19 -2
  4. claude_mpm/agents/base_agent_loader.py +5 -5
  5. claude_mpm/agents/frontmatter_validator.py +4 -4
  6. claude_mpm/agents/templates/agent-manager.json +3 -3
  7. claude_mpm/agents/templates/agentic-coder-optimizer.json +3 -3
  8. claude_mpm/agents/templates/api_qa.json +1 -1
  9. claude_mpm/agents/templates/clerk-ops.json +3 -3
  10. claude_mpm/agents/templates/code_analyzer.json +3 -3
  11. claude_mpm/agents/templates/dart_engineer.json +294 -0
  12. claude_mpm/agents/templates/data_engineer.json +3 -3
  13. claude_mpm/agents/templates/documentation.json +2 -2
  14. claude_mpm/agents/templates/engineer.json +2 -2
  15. claude_mpm/agents/templates/gcp_ops_agent.json +2 -2
  16. claude_mpm/agents/templates/imagemagick.json +1 -1
  17. claude_mpm/agents/templates/local_ops_agent.json +319 -41
  18. claude_mpm/agents/templates/memory_manager.json +2 -2
  19. claude_mpm/agents/templates/nextjs_engineer.json +2 -2
  20. claude_mpm/agents/templates/ops.json +2 -2
  21. claude_mpm/agents/templates/php-engineer.json +1 -1
  22. claude_mpm/agents/templates/project_organizer.json +1 -1
  23. claude_mpm/agents/templates/prompt-engineer.json +6 -4
  24. claude_mpm/agents/templates/python_engineer.json +2 -2
  25. claude_mpm/agents/templates/qa.json +1 -1
  26. claude_mpm/agents/templates/react_engineer.json +3 -3
  27. claude_mpm/agents/templates/refactoring_engineer.json +3 -3
  28. claude_mpm/agents/templates/research.json +2 -2
  29. claude_mpm/agents/templates/security.json +2 -2
  30. claude_mpm/agents/templates/ticketing.json +2 -2
  31. claude_mpm/agents/templates/typescript_engineer.json +2 -2
  32. claude_mpm/agents/templates/vercel_ops_agent.json +2 -2
  33. claude_mpm/agents/templates/version_control.json +2 -2
  34. claude_mpm/agents/templates/web_qa.json +6 -6
  35. claude_mpm/agents/templates/web_ui.json +3 -3
  36. claude_mpm/cli/__init__.py +49 -19
  37. claude_mpm/cli/commands/agent_manager.py +3 -3
  38. claude_mpm/cli/commands/agents.py +6 -6
  39. claude_mpm/cli/commands/aggregate.py +4 -4
  40. claude_mpm/cli/commands/analyze.py +2 -2
  41. claude_mpm/cli/commands/analyze_code.py +1 -1
  42. claude_mpm/cli/commands/cleanup.py +3 -3
  43. claude_mpm/cli/commands/config.py +2 -2
  44. claude_mpm/cli/commands/configure.py +605 -21
  45. claude_mpm/cli/commands/dashboard.py +1 -1
  46. claude_mpm/cli/commands/debug.py +3 -3
  47. claude_mpm/cli/commands/doctor.py +1 -1
  48. claude_mpm/cli/commands/mcp.py +7 -7
  49. claude_mpm/cli/commands/mcp_command_router.py +1 -1
  50. claude_mpm/cli/commands/mcp_config.py +2 -2
  51. claude_mpm/cli/commands/mcp_external_commands.py +2 -2
  52. claude_mpm/cli/commands/mcp_install_commands.py +3 -3
  53. claude_mpm/cli/commands/mcp_pipx_config.py +2 -2
  54. claude_mpm/cli/commands/mcp_setup_external.py +3 -3
  55. claude_mpm/cli/commands/monitor.py +1 -1
  56. claude_mpm/cli/commands/mpm_init_handler.py +1 -1
  57. claude_mpm/cli/interactive/agent_wizard.py +1 -1
  58. claude_mpm/cli/parsers/configure_parser.py +5 -0
  59. claude_mpm/cli/parsers/search_parser.py +1 -1
  60. claude_mpm/cli/shared/argument_patterns.py +2 -2
  61. claude_mpm/cli/shared/base_command.py +1 -1
  62. claude_mpm/cli/startup_logging.py +4 -4
  63. claude_mpm/config/experimental_features.py +4 -4
  64. claude_mpm/config/socketio_config.py +2 -2
  65. claude_mpm/core/__init__.py +53 -17
  66. claude_mpm/core/agent_session_manager.py +2 -2
  67. claude_mpm/core/api_validator.py +3 -3
  68. claude_mpm/core/base_service.py +10 -1
  69. claude_mpm/core/cache.py +2 -2
  70. claude_mpm/core/config.py +5 -5
  71. claude_mpm/core/config_aliases.py +4 -4
  72. claude_mpm/core/config_constants.py +1 -1
  73. claude_mpm/core/error_handler.py +1 -1
  74. claude_mpm/core/file_utils.py +5 -5
  75. claude_mpm/core/framework/formatters/capability_generator.py +5 -5
  76. claude_mpm/core/framework/loaders/agent_loader.py +1 -1
  77. claude_mpm/core/framework/processors/metadata_processor.py +1 -1
  78. claude_mpm/core/framework/processors/template_processor.py +3 -3
  79. claude_mpm/core/framework_loader.py +2 -2
  80. claude_mpm/core/log_manager.py +11 -4
  81. claude_mpm/core/logger.py +2 -2
  82. claude_mpm/core/optimized_startup.py +1 -1
  83. claude_mpm/core/output_style_manager.py +1 -1
  84. claude_mpm/core/service_registry.py +2 -2
  85. claude_mpm/core/session_manager.py +3 -3
  86. claude_mpm/core/shared/config_loader.py +1 -1
  87. claude_mpm/core/socketio_pool.py +2 -2
  88. claude_mpm/core/unified_agent_registry.py +2 -2
  89. claude_mpm/core/unified_config.py +6 -6
  90. claude_mpm/core/unified_paths.py +2 -2
  91. claude_mpm/dashboard/api/simple_directory.py +1 -1
  92. claude_mpm/generators/agent_profile_generator.py +1 -1
  93. claude_mpm/hooks/claude_hooks/event_handlers.py +2 -2
  94. claude_mpm/hooks/claude_hooks/installer.py +9 -9
  95. claude_mpm/hooks/claude_hooks/response_tracking.py +16 -11
  96. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +16 -13
  97. claude_mpm/hooks/claude_hooks/tool_analysis.py +2 -2
  98. claude_mpm/hooks/memory_integration_hook.py +1 -1
  99. claude_mpm/hooks/validation_hooks.py +1 -1
  100. claude_mpm/init.py +4 -4
  101. claude_mpm/models/agent_session.py +1 -1
  102. claude_mpm/scripts/socketio_daemon.py +5 -5
  103. claude_mpm/services/__init__.py +145 -161
  104. claude_mpm/services/agent_capabilities_service.py +1 -1
  105. claude_mpm/services/agents/agent_builder.py +4 -4
  106. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +1 -1
  107. claude_mpm/services/agents/deployment/agent_metrics_collector.py +1 -1
  108. claude_mpm/services/agents/deployment/agent_record_service.py +3 -3
  109. claude_mpm/services/agents/deployment/deployment_config_loader.py +21 -0
  110. claude_mpm/services/agents/deployment/deployment_wrapper.py +1 -1
  111. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +2 -2
  112. claude_mpm/services/agents/loading/agent_profile_loader.py +2 -2
  113. claude_mpm/services/agents/loading/base_agent_manager.py +12 -2
  114. claude_mpm/services/agents/local_template_manager.py +5 -5
  115. claude_mpm/services/agents/registry/deployed_agent_discovery.py +1 -1
  116. claude_mpm/services/agents/registry/modification_tracker.py +19 -11
  117. claude_mpm/services/async_session_logger.py +3 -3
  118. claude_mpm/services/claude_session_logger.py +4 -4
  119. claude_mpm/services/cli/agent_listing_service.py +3 -3
  120. claude_mpm/services/cli/agent_validation_service.py +1 -1
  121. claude_mpm/services/cli/session_manager.py +2 -2
  122. claude_mpm/services/core/path_resolver.py +1 -1
  123. claude_mpm/services/diagnostics/checks/agent_check.py +1 -1
  124. claude_mpm/services/diagnostics/checks/claude_code_check.py +2 -2
  125. claude_mpm/services/diagnostics/checks/common_issues_check.py +3 -3
  126. claude_mpm/services/diagnostics/checks/configuration_check.py +2 -2
  127. claude_mpm/services/diagnostics/checks/installation_check.py +1 -1
  128. claude_mpm/services/diagnostics/checks/mcp_check.py +1 -1
  129. claude_mpm/services/diagnostics/checks/mcp_services_check.py +9 -9
  130. claude_mpm/services/diagnostics/checks/monitor_check.py +1 -1
  131. claude_mpm/services/diagnostics/doctor_reporter.py +1 -1
  132. claude_mpm/services/event_aggregator.py +1 -1
  133. claude_mpm/services/event_bus/event_bus.py +7 -2
  134. claude_mpm/services/events/consumers/dead_letter.py +2 -2
  135. claude_mpm/services/framework_claude_md_generator/__init__.py +1 -1
  136. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +3 -3
  137. claude_mpm/services/framework_claude_md_generator/version_manager.py +1 -1
  138. claude_mpm/services/hook_installer_service.py +7 -7
  139. claude_mpm/services/infrastructure/context_preservation.py +7 -7
  140. claude_mpm/services/infrastructure/daemon_manager.py +5 -5
  141. claude_mpm/services/mcp_config_manager.py +169 -48
  142. claude_mpm/services/mcp_gateway/__init__.py +98 -94
  143. claude_mpm/services/mcp_gateway/auto_configure.py +5 -5
  144. claude_mpm/services/mcp_gateway/config/config_loader.py +2 -2
  145. claude_mpm/services/mcp_gateway/config/configuration.py +3 -3
  146. claude_mpm/services/mcp_gateway/core/process_pool.py +3 -3
  147. claude_mpm/services/mcp_gateway/core/singleton_manager.py +2 -2
  148. claude_mpm/services/mcp_gateway/core/startup_verification.py +1 -1
  149. claude_mpm/services/mcp_gateway/main.py +1 -1
  150. claude_mpm/services/mcp_gateway/registry/service_registry.py +4 -2
  151. claude_mpm/services/mcp_gateway/registry/tool_registry.py +2 -1
  152. claude_mpm/services/mcp_gateway/server/stdio_handler.py +1 -1
  153. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +1 -1
  154. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +1 -1
  155. claude_mpm/services/mcp_gateway/tools/hello_world.py +1 -1
  156. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +5 -5
  157. claude_mpm/services/mcp_gateway/utils/update_preferences.py +2 -2
  158. claude_mpm/services/mcp_service_verifier.py +1 -1
  159. claude_mpm/services/memory/builder.py +1 -1
  160. claude_mpm/services/memory/cache/shared_prompt_cache.py +2 -1
  161. claude_mpm/services/memory/indexed_memory.py +3 -3
  162. claude_mpm/services/monitor/daemon.py +1 -1
  163. claude_mpm/services/monitor/daemon_manager.py +9 -9
  164. claude_mpm/services/monitor/event_emitter.py +1 -1
  165. claude_mpm/services/monitor/handlers/file.py +1 -1
  166. claude_mpm/services/monitor/handlers/hooks.py +3 -3
  167. claude_mpm/services/monitor/management/lifecycle.py +7 -7
  168. claude_mpm/services/monitor/server.py +2 -2
  169. claude_mpm/services/orphan_detection.py +788 -0
  170. claude_mpm/services/port_manager.py +2 -2
  171. claude_mpm/services/project/analyzer.py +3 -3
  172. claude_mpm/services/project/archive_manager.py +13 -13
  173. claude_mpm/services/project/dependency_analyzer.py +4 -4
  174. claude_mpm/services/project/documentation_manager.py +4 -4
  175. claude_mpm/services/project/enhanced_analyzer.py +8 -8
  176. claude_mpm/services/project/registry.py +4 -4
  177. claude_mpm/services/project_port_allocator.py +597 -0
  178. claude_mpm/services/response_tracker.py +1 -1
  179. claude_mpm/services/session_management_service.py +1 -1
  180. claude_mpm/services/session_manager.py +6 -4
  181. claude_mpm/services/socketio/event_normalizer.py +1 -1
  182. claude_mpm/services/socketio/handlers/code_analysis.py +14 -12
  183. claude_mpm/services/socketio/handlers/file.py +1 -1
  184. claude_mpm/services/socketio/migration_utils.py +1 -1
  185. claude_mpm/services/socketio/server/core.py +1 -1
  186. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +1 -1
  187. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +4 -4
  188. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +1 -1
  189. claude_mpm/services/unified/config_strategies/config_schema.py +4 -4
  190. claude_mpm/services/unified/config_strategies/context_strategy.py +6 -6
  191. claude_mpm/services/unified/config_strategies/error_handling_strategy.py +10 -10
  192. claude_mpm/services/unified/config_strategies/file_loader_strategy.py +5 -5
  193. claude_mpm/services/unified/config_strategies/unified_config_service.py +8 -8
  194. claude_mpm/services/unified/config_strategies/validation_strategy.py +15 -15
  195. claude_mpm/services/unified/deployment_strategies/base.py +4 -4
  196. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +15 -15
  197. claude_mpm/services/unified/deployment_strategies/local.py +9 -9
  198. claude_mpm/services/unified/deployment_strategies/utils.py +9 -9
  199. claude_mpm/services/unified/deployment_strategies/vercel.py +7 -7
  200. claude_mpm/services/unified/unified_config.py +5 -5
  201. claude_mpm/services/unified/unified_deployment.py +2 -2
  202. claude_mpm/services/utility_service.py +1 -1
  203. claude_mpm/services/version_control/conflict_resolution.py +2 -2
  204. claude_mpm/services/version_control/git_operations.py +3 -3
  205. claude_mpm/services/version_control/semantic_versioning.py +13 -13
  206. claude_mpm/services/version_control/version_parser.py +1 -1
  207. claude_mpm/storage/state_storage.py +12 -13
  208. claude_mpm/tools/code_tree_analyzer.py +5 -5
  209. claude_mpm/tools/code_tree_builder.py +4 -4
  210. claude_mpm/tools/socketio_debug.py +1 -1
  211. claude_mpm/utils/agent_dependency_loader.py +4 -4
  212. claude_mpm/utils/common.py +2 -2
  213. claude_mpm/utils/config_manager.py +3 -3
  214. claude_mpm/utils/dependency_cache.py +2 -2
  215. claude_mpm/utils/dependency_strategies.py +6 -6
  216. claude_mpm/utils/file_utils.py +11 -11
  217. claude_mpm/utils/log_cleanup.py +1 -1
  218. claude_mpm/utils/path_operations.py +1 -1
  219. claude_mpm/validation/agent_validator.py +2 -2
  220. claude_mpm/validation/frontmatter_validator.py +1 -1
  221. {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/METADATA +1 -1
  222. {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/RECORD +226 -223
  223. {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/WHEEL +0 -0
  224. {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/entry_points.txt +0 -0
  225. {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/licenses/LICENSE +0 -0
  226. {claude_mpm-4.5.8.dist-info → claude_mpm-4.5.12.dist-info}/top_level.txt +0 -0
@@ -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(
@@ -63,7 +63,7 @@ except ImportError:
63
63
  try:
64
64
  from claude_mpm.services.mcp_gateway.server.mcp_gateway import MCPGateway
65
65
  except ImportError as e:
66
- raise ImportError(f"Critical: Cannot import MCPGateway server: {e}")
66
+ raise ImportError(f"Critical: Cannot import MCPGateway server: {e}") from e
67
67
 
68
68
  try:
69
69
  from claude_mpm.services.mcp_gateway.server.stdio_handler import StdioHandler
@@ -224,7 +224,8 @@ class MCPServiceRegistry(ManagerBase):
224
224
  try:
225
225
  import asyncio
226
226
 
227
- asyncio.create_task(instance.stop())
227
+ _task = asyncio.create_task(instance.stop()) # noqa: RUF006
228
+ # Fire-and-forget shutdown during unregister
228
229
  except Exception as e:
229
230
  self.logger.warning(
230
231
  f"Error stopping service {interface.__name__}: {e}"
@@ -335,7 +336,8 @@ class MCPServiceRegistry(ManagerBase):
335
336
  try:
336
337
  import asyncio
337
338
 
338
- asyncio.create_task(instance.stop())
339
+ _task = asyncio.create_task(instance.stop()) # noqa: RUF006
340
+ # Fire-and-forget shutdown during clear
339
341
  except Exception as e:
340
342
  self.logger.warning(f"Error stopping service: {e}")
341
343
 
@@ -216,7 +216,8 @@ class ToolRegistry(BaseMCPService, IMCPToolRegistry):
216
216
 
217
217
  # Shutdown adapter (outside lock to avoid deadlock)
218
218
  try:
219
- asyncio.create_task(adapter.shutdown())
219
+ _task = asyncio.create_task(adapter.shutdown()) # noqa: RUF006
220
+ # Fire-and-forget shutdown during tool unregister
220
221
  except Exception as e:
221
222
  self.log_warning(f"Error shutting down tool adapter {tool_name}: {e}")
222
223
 
@@ -139,7 +139,7 @@ class StdioHandler(BaseMCPService, IMCPCommunication):
139
139
  self._metrics["errors"] += 1
140
140
  raise
141
141
 
142
- async def receive_message(self) -> Optional[Dict[str, Any]]: # noqa: PLR0911
142
+ async def receive_message(self) -> Optional[Dict[str, Any]]:
143
143
  """
144
144
  Receive a message from the MCP client via stdin.
145
145
 
@@ -306,7 +306,7 @@ class DocumentSummarizerTool(BaseToolAdapter):
306
306
  continue
307
307
 
308
308
  # If all fail, read as binary and decode with errors='ignore'
309
- with open(file_path, "rb") as f:
309
+ with file_path.open("rb") as f:
310
310
  content = f.read()
311
311
  return content.decode("utf-8", errors="ignore")
312
312
 
@@ -383,7 +383,7 @@ class HealthCheckTool(BaseToolAdapter):
383
383
  # Try to load configuration
384
384
  import json
385
385
 
386
- with open(config_file) as f:
386
+ with config_file.open() as f:
387
387
  config_data = json.load(f)
388
388
 
389
389
  check_result["checks"]["config_valid"] = True
@@ -204,7 +204,7 @@ class HelloWorldTool(BaseToolAdapter):
204
204
  self.greeting_history: List[Dict[str, Any]] = []
205
205
  self.max_history_size = 100
206
206
 
207
- def validate_parameters(self, parameters: Dict[str, Any]) -> bool: # noqa: PLR0911
207
+ def validate_parameters(self, parameters: Dict[str, Any]) -> bool:
208
208
  """
209
209
  Enhanced parameter validation with detailed error messages.
210
210
 
@@ -8,7 +8,7 @@ Provides non-blocking version checking for MCP tools like kuzu-memory.
8
8
 
9
9
  import asyncio
10
10
  import json
11
- from datetime import datetime, timedelta
11
+ from datetime import datetime, timedelta, timezone
12
12
  from pathlib import Path
13
13
  from typing import Any, Dict, Optional
14
14
 
@@ -83,7 +83,7 @@ class PackageVersionChecker:
83
83
  "latest": latest,
84
84
  "update_available": version.parse(latest)
85
85
  > version.parse(current_version),
86
- "checked_at": datetime.now().isoformat(),
86
+ "checked_at": datetime.now(timezone.utc).isoformat(),
87
87
  }
88
88
  self._write_cache(cache_file, result)
89
89
  return result
@@ -133,12 +133,12 @@ class PackageVersionChecker:
133
133
  return None
134
134
 
135
135
  try:
136
- with open(cache_file) as f:
136
+ with cache_file.open() as f:
137
137
  data = json.load(f)
138
138
 
139
139
  # Check TTL
140
140
  checked_at = datetime.fromisoformat(data["checked_at"])
141
- if datetime.now() - checked_at < timedelta(seconds=ttl):
141
+ if datetime.now(timezone.utc) - checked_at < timedelta(seconds=ttl):
142
142
  return data
143
143
  except Exception as e:
144
144
  self.logger.debug(f"Cache read error: {e}")
@@ -154,7 +154,7 @@ class PackageVersionChecker:
154
154
  data: Data to cache
155
155
  """
156
156
  try:
157
- with open(cache_file, "w") as f:
157
+ with cache_file.open("w") as f:
158
158
  json.dump(data, f, indent=2)
159
159
  except Exception as e:
160
160
  self.logger.debug(f"Cache write failed: {e}")
@@ -36,7 +36,7 @@ class UpdatePreferences:
36
36
  """
37
37
  if cls.PREFS_FILE.exists():
38
38
  try:
39
- with open(cls.PREFS_FILE) as f:
39
+ with cls.PREFS_FILE.open() as f:
40
40
  return json.load(f)
41
41
  except (OSError, json.JSONDecodeError):
42
42
  # Return empty dict if file is corrupted or unreadable
@@ -53,7 +53,7 @@ class UpdatePreferences:
53
53
  """
54
54
  cls.PREFS_FILE.parent.mkdir(parents=True, exist_ok=True)
55
55
  try:
56
- with open(cls.PREFS_FILE, "w") as f:
56
+ with cls.PREFS_FILE.open("w") as f:
57
57
  json.dump(prefs, f, indent=2)
58
58
  except OSError:
59
59
  # Silently fail if we can't write preferences
@@ -429,7 +429,7 @@ class MCPServiceVerifier:
429
429
  return {"configured": False, "correct": False}
430
430
 
431
431
  try:
432
- with open(self.claude_config_path) as f:
432
+ with self.claude_config_path.open() as f:
433
433
  config = json.load(f)
434
434
 
435
435
  # Check if project is configured
@@ -458,7 +458,7 @@ class MemoryBuilder(LoggerMixin):
458
458
  import tomllib
459
459
  except ImportError:
460
460
  import tomli as tomllib
461
- with open(file_path, "rb") as f:
461
+ with file_path.open("rb") as f:
462
462
  config_data = tomllib.load(f)
463
463
  items = self._extract_from_toml_config(config_data, source)
464
464
  extracted_items.extend(items)
@@ -194,7 +194,8 @@ class SharedPromptCache(BaseService):
194
194
  with cls._lock:
195
195
  if cls._instance is not None:
196
196
  if cls._instance.running:
197
- asyncio.create_task(cls._instance.stop())
197
+ _task = asyncio.create_task(cls._instance.stop()) # noqa: RUF006
198
+ # Fire-and-forget cleanup task during test reset
198
199
  cls._instance = None
199
200
 
200
201
  async def _initialize(self) -> None:
@@ -213,7 +213,7 @@ class InvertedIndex:
213
213
  "doc_freqs": dict(self.doc_freqs),
214
214
  "doc_count": self.doc_count,
215
215
  }
216
- with open(path, "wb") as f:
216
+ with path.open("wb") as f:
217
217
  pickle.dump(data, f)
218
218
 
219
219
  def load(self, path: Path):
@@ -221,7 +221,7 @@ class InvertedIndex:
221
221
  if not path.exists():
222
222
  return
223
223
 
224
- with open(path, "rb") as f:
224
+ with path.open("rb") as f:
225
225
  data = pickle.load(f)
226
226
 
227
227
  self.index = defaultdict(set, {k: set(v) for k, v in data["index"].items()})
@@ -597,7 +597,7 @@ class IndexedMemoryService:
597
597
  # Load other indexes
598
598
  indexes_path = self.data_dir / "indexes.pkl"
599
599
  if indexes_path.exists():
600
- with open(indexes_path, "rb") as f:
600
+ with indexes_path.open("rb") as f:
601
601
  data = pickle.load(f)
602
602
 
603
603
  self.memories = data.get("memories", {})
@@ -121,7 +121,7 @@ class UnifiedMonitorDaemon:
121
121
  """
122
122
  return self.daemon_manager.cleanup_port_conflicts()
123
123
 
124
- def _start_daemon(self, force_restart: bool = False) -> bool: # noqa: PLR0911
124
+ def _start_daemon(self, force_restart: bool = False) -> bool:
125
125
  """Start as background daemon process.
126
126
 
127
127
  Args:
@@ -302,7 +302,7 @@ class DaemonManager:
302
302
  if not self.pid_file.exists():
303
303
  return True
304
304
 
305
- with open(self.pid_file) as f:
305
+ with self.pid_file.open() as f:
306
306
  pid = int(f.read().strip())
307
307
 
308
308
  self.logger.info(f"Found PID {pid} in PID file")
@@ -397,7 +397,7 @@ class DaemonManager:
397
397
  # First check PID file
398
398
  if self.pid_file.exists():
399
399
  try:
400
- with open(self.pid_file) as f:
400
+ with self.pid_file.open() as f:
401
401
  pid = int(f.read().strip())
402
402
 
403
403
  # Verify process exists
@@ -604,7 +604,7 @@ class DaemonManager:
604
604
  # Check if PID file was written
605
605
  if self.pid_file.exists():
606
606
  try:
607
- with open(self.pid_file) as f:
607
+ with self.pid_file.open() as f:
608
608
  written_pid = int(f.read().strip())
609
609
  if written_pid == pid:
610
610
  # PID file written correctly, check port
@@ -798,7 +798,7 @@ class DaemonManager:
798
798
  if not self.pid_file.exists():
799
799
  return None
800
800
 
801
- with open(self.pid_file) as f:
801
+ with self.pid_file.open() as f:
802
802
  return int(f.read().strip())
803
803
 
804
804
  except Exception as e:
@@ -809,7 +809,7 @@ class DaemonManager:
809
809
  """Write current PID to PID file."""
810
810
  try:
811
811
  self.pid_file.parent.mkdir(parents=True, exist_ok=True)
812
- with open(self.pid_file, "w") as f:
812
+ with self.pid_file.open("w") as f:
813
813
  f.write(str(os.getpid()))
814
814
  self.logger.debug(f"PID file written: {self.pid_file}")
815
815
  except Exception as e:
@@ -863,7 +863,7 @@ class DaemonManager:
863
863
 
864
864
  # Redirect stdout and stderr to log file
865
865
  self.log_file.parent.mkdir(parents=True, exist_ok=True)
866
- with open(self.log_file, "a") as log_out:
866
+ with self.log_file.open("a") as log_out:
867
867
  os.dup2(log_out.fileno(), sys.stdout.fileno())
868
868
  os.dup2(log_out.fileno(), sys.stderr.fileno())
869
869
 
@@ -903,7 +903,7 @@ class DaemonManager:
903
903
  continue
904
904
 
905
905
  try:
906
- with open(self.startup_status_file) as f:
906
+ with self.startup_status_file.open() as f:
907
907
  status = f.read().strip()
908
908
 
909
909
  if status == "success":
@@ -935,7 +935,7 @@ class DaemonManager:
935
935
  try:
936
936
  # Don't check if file exists - we need to write to it regardless
937
937
  # The parent created it and is waiting for us to update it
938
- with open(self.startup_status_file, "w") as f:
938
+ with self.startup_status_file.open("w") as f:
939
939
  f.write("success")
940
940
  f.flush() # Ensure it's written immediately
941
941
  os.fsync(f.fileno()) # Force write to disk
@@ -948,7 +948,7 @@ class DaemonManager:
948
948
  if self.startup_status_file:
949
949
  try:
950
950
  # Don't check if file exists - we need to write to it regardless
951
- with open(self.startup_status_file, "w") as f:
951
+ with self.startup_status_file.open("w") as f:
952
952
  f.write(f"error:{error}")
953
953
  f.flush() # Ensure it's written immediately
954
954
  os.fsync(f.fileno()) # Force write to disk
@@ -85,7 +85,7 @@ class AsyncEventEmitter:
85
85
  },
86
86
  )
87
87
 
88
- self.logger.info("AsyncEventEmitter initialized with connection pooling")
88
+ self.logger.debug("AsyncEventEmitter initialized with connection pooling")
89
89
 
90
90
  except Exception as e:
91
91
  self.logger.error(f"Error initializing AsyncEventEmitter: {e}")
@@ -225,7 +225,7 @@ class FileHandler:
225
225
  be displayed as text.
226
226
  """
227
227
  try:
228
- with open(real_path, "rb") as f:
228
+ with real_path.open("rb") as f:
229
229
  binary_content = f.read()
230
230
 
231
231
  # Check if it's a text file by looking for common text patterns
@@ -236,10 +236,10 @@ class HookHandler:
236
236
  },
237
237
  )
238
238
 
239
- # Remove from active sessions after a delay
240
- asyncio.create_task(
239
+ # Remove from active sessions after a delay (5 minutes)
240
+ _task = asyncio.create_task( # noqa: RUF006
241
241
  self._cleanup_session(session_id, delay=300)
242
- ) # 5 minutes
242
+ ) # Fire-and-forget cleanup task
243
243
 
244
244
  self.logger.info(f"Claude Code session ended: {session_id}")
245
245
  else:
@@ -127,14 +127,14 @@ class DaemonLifecycle:
127
127
  # Redirect stdout and stderr
128
128
  if self.log_file:
129
129
  # Redirect to log file
130
- with open(self.log_file, "a") as log_out:
130
+ with self.log_file.open("a") as log_out:
131
131
  os.dup2(log_out.fileno(), sys.stdout.fileno())
132
132
  os.dup2(log_out.fileno(), sys.stderr.fileno())
133
133
  else:
134
134
  # Default to a daemon log file instead of /dev/null for errors
135
135
  default_log = Path.home() / ".claude-mpm" / "monitor-daemon.log"
136
136
  default_log.parent.mkdir(parents=True, exist_ok=True)
137
- with open(default_log, "a") as log_out:
137
+ with default_log.open("a") as log_out:
138
138
  os.dup2(log_out.fileno(), sys.stdout.fileno())
139
139
  os.dup2(log_out.fileno(), sys.stderr.fileno())
140
140
 
@@ -149,7 +149,7 @@ class DaemonLifecycle:
149
149
  self.pid_file.parent.mkdir(parents=True, exist_ok=True)
150
150
 
151
151
  # Write PID
152
- with open(self.pid_file, "w") as f:
152
+ with self.pid_file.open("w") as f:
153
153
  f.write(str(os.getpid()))
154
154
 
155
155
  self.logger.debug(f"PID file written: {self.pid_file}")
@@ -199,7 +199,7 @@ class DaemonLifecycle:
199
199
  if not self.pid_file.exists():
200
200
  return None
201
201
 
202
- with open(self.pid_file) as f:
202
+ with self.pid_file.open() as f:
203
203
  pid_str = f.read().strip()
204
204
  return int(pid_str) if pid_str else None
205
205
 
@@ -385,7 +385,7 @@ class DaemonLifecycle:
385
385
  try:
386
386
  # Check if status file exists and read it
387
387
  if self.startup_status_file and Path(self.startup_status_file).exists():
388
- with open(self.startup_status_file) as f:
388
+ with self.startup_status_file.open() as f:
389
389
  status = f.read().strip()
390
390
 
391
391
  if status == "success":
@@ -437,7 +437,7 @@ class DaemonLifecycle:
437
437
  """Report successful startup to parent process."""
438
438
  if self.startup_status_file:
439
439
  try:
440
- with open(self.startup_status_file, "w") as f:
440
+ with self.startup_status_file.open("w") as f:
441
441
  f.write("success")
442
442
  except Exception as e:
443
443
  self.logger.error(f"Failed to report startup success: {e}")
@@ -450,7 +450,7 @@ class DaemonLifecycle:
450
450
  """
451
451
  if self.startup_status_file:
452
452
  try:
453
- with open(self.startup_status_file, "w") as f:
453
+ with self.startup_status_file.open("w") as f:
454
454
  f.write(f"error:{error_msg}")
455
455
  except Exception:
456
456
  pass # Can't report if file write fails
@@ -313,7 +313,7 @@ class UnifiedMonitorServer:
313
313
  async def dashboard_index(request):
314
314
  template_path = dashboard_dir / "templates" / "index.html"
315
315
  if template_path.exists():
316
- with open(template_path) as f:
316
+ with template_path.open() as f:
317
317
  content = f.read()
318
318
  return web.Response(text=content, content_type="text/html")
319
319
  return web.Response(text="Dashboard not found", status=404)
@@ -365,7 +365,7 @@ class UnifiedMonitorServer:
365
365
  return web.Response(text=f"Error: {e!s}", status=500)
366
366
 
367
367
  # File content endpoint for file viewer
368
- async def api_file_handler(request): # noqa: PLR0911
368
+ async def api_file_handler(request):
369
369
  """Handle file content requests."""
370
370
  import json
371
371
  import os