claude-mpm 4.5.11__py3-none-any.whl → 4.5.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_ENGINEER.md +47 -0
  3. claude_mpm/agents/BASE_QA.md +60 -0
  4. claude_mpm/agents/frontmatter_validator.py +4 -4
  5. claude_mpm/agents/templates/nextjs_engineer.json +2 -2
  6. claude_mpm/agents/templates/qa.json +13 -3
  7. claude_mpm/agents/templates/react_engineer.json +2 -2
  8. claude_mpm/agents/templates/typescript_engineer.json +2 -2
  9. claude_mpm/agents/templates/web_qa.json +14 -3
  10. claude_mpm/cli/commands/agent_manager.py +3 -3
  11. claude_mpm/cli/commands/agents.py +6 -6
  12. claude_mpm/cli/commands/aggregate.py +4 -4
  13. claude_mpm/cli/commands/analyze.py +2 -2
  14. claude_mpm/cli/commands/analyze_code.py +1 -1
  15. claude_mpm/cli/commands/cleanup.py +3 -3
  16. claude_mpm/cli/commands/config.py +2 -2
  17. claude_mpm/cli/commands/configure.py +14 -14
  18. claude_mpm/cli/commands/dashboard.py +1 -1
  19. claude_mpm/cli/commands/debug.py +3 -3
  20. claude_mpm/cli/commands/doctor.py +1 -1
  21. claude_mpm/cli/commands/mcp.py +7 -7
  22. claude_mpm/cli/commands/mcp_command_router.py +1 -1
  23. claude_mpm/cli/commands/mcp_config.py +2 -2
  24. claude_mpm/cli/commands/mcp_external_commands.py +2 -2
  25. claude_mpm/cli/commands/mcp_install_commands.py +3 -3
  26. claude_mpm/cli/commands/mcp_pipx_config.py +2 -2
  27. claude_mpm/cli/commands/mcp_setup_external.py +3 -3
  28. claude_mpm/cli/commands/monitor.py +1 -1
  29. claude_mpm/cli/commands/mpm_init_handler.py +1 -1
  30. claude_mpm/cli/interactive/agent_wizard.py +1 -1
  31. claude_mpm/cli/parsers/search_parser.py +1 -1
  32. claude_mpm/cli/shared/argument_patterns.py +2 -2
  33. claude_mpm/cli/shared/base_command.py +1 -1
  34. claude_mpm/cli/startup_logging.py +6 -4
  35. claude_mpm/config/experimental_features.py +4 -4
  36. claude_mpm/config/socketio_config.py +2 -2
  37. claude_mpm/core/agent_session_manager.py +2 -2
  38. claude_mpm/core/api_validator.py +3 -3
  39. claude_mpm/core/base_service.py +10 -1
  40. claude_mpm/core/cache.py +2 -2
  41. claude_mpm/core/config.py +4 -4
  42. claude_mpm/core/config_aliases.py +4 -4
  43. claude_mpm/core/config_constants.py +1 -1
  44. claude_mpm/core/error_handler.py +1 -1
  45. claude_mpm/core/file_utils.py +5 -5
  46. claude_mpm/core/framework/formatters/capability_generator.py +5 -5
  47. claude_mpm/core/framework/loaders/agent_loader.py +1 -1
  48. claude_mpm/core/framework/processors/metadata_processor.py +1 -1
  49. claude_mpm/core/framework/processors/template_processor.py +3 -3
  50. claude_mpm/core/framework_loader.py +2 -2
  51. claude_mpm/core/log_manager.py +4 -4
  52. claude_mpm/core/logger.py +2 -2
  53. claude_mpm/core/optimized_startup.py +1 -1
  54. claude_mpm/core/output_style_manager.py +1 -1
  55. claude_mpm/core/service_registry.py +2 -2
  56. claude_mpm/core/session_manager.py +3 -3
  57. claude_mpm/core/shared/config_loader.py +1 -1
  58. claude_mpm/core/socketio_pool.py +2 -2
  59. claude_mpm/core/unified_agent_registry.py +2 -2
  60. claude_mpm/core/unified_config.py +6 -6
  61. claude_mpm/core/unified_paths.py +2 -2
  62. claude_mpm/dashboard/api/simple_directory.py +1 -1
  63. claude_mpm/generators/agent_profile_generator.py +1 -1
  64. claude_mpm/hooks/claude_hooks/event_handlers.py +2 -2
  65. claude_mpm/hooks/claude_hooks/installer.py +9 -9
  66. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +7 -2
  67. claude_mpm/hooks/claude_hooks/tool_analysis.py +2 -2
  68. claude_mpm/hooks/memory_integration_hook.py +1 -1
  69. claude_mpm/hooks/validation_hooks.py +1 -1
  70. claude_mpm/init.py +4 -4
  71. claude_mpm/models/agent_session.py +1 -1
  72. claude_mpm/scripts/socketio_daemon.py +5 -5
  73. claude_mpm/services/__init__.py +2 -2
  74. claude_mpm/services/agent_capabilities_service.py +1 -1
  75. claude_mpm/services/agents/agent_builder.py +6 -4
  76. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +1 -1
  77. claude_mpm/services/agents/deployment/agent_metrics_collector.py +1 -1
  78. claude_mpm/services/agents/deployment/agent_record_service.py +3 -3
  79. claude_mpm/services/agents/deployment/deployment_wrapper.py +1 -1
  80. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +2 -2
  81. claude_mpm/services/agents/loading/agent_profile_loader.py +2 -2
  82. claude_mpm/services/agents/local_template_manager.py +5 -5
  83. claude_mpm/services/agents/registry/deployed_agent_discovery.py +1 -1
  84. claude_mpm/services/agents/registry/modification_tracker.py +19 -11
  85. claude_mpm/services/async_session_logger.py +1 -1
  86. claude_mpm/services/claude_session_logger.py +1 -1
  87. claude_mpm/services/cli/agent_listing_service.py +3 -3
  88. claude_mpm/services/cli/agent_validation_service.py +1 -1
  89. claude_mpm/services/cli/session_manager.py +2 -2
  90. claude_mpm/services/core/path_resolver.py +1 -1
  91. claude_mpm/services/diagnostics/checks/agent_check.py +1 -1
  92. claude_mpm/services/diagnostics/checks/claude_code_check.py +2 -2
  93. claude_mpm/services/diagnostics/checks/common_issues_check.py +3 -3
  94. claude_mpm/services/diagnostics/checks/configuration_check.py +2 -2
  95. claude_mpm/services/diagnostics/checks/installation_check.py +1 -1
  96. claude_mpm/services/diagnostics/checks/mcp_check.py +1 -1
  97. claude_mpm/services/diagnostics/checks/mcp_services_check.py +9 -9
  98. claude_mpm/services/diagnostics/checks/monitor_check.py +1 -1
  99. claude_mpm/services/diagnostics/doctor_reporter.py +1 -1
  100. claude_mpm/services/event_aggregator.py +1 -1
  101. claude_mpm/services/event_bus/event_bus.py +9 -2
  102. claude_mpm/services/events/consumers/dead_letter.py +2 -2
  103. claude_mpm/services/framework_claude_md_generator/__init__.py +1 -1
  104. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +3 -3
  105. claude_mpm/services/framework_claude_md_generator/version_manager.py +1 -1
  106. claude_mpm/services/hook_installer_service.py +7 -7
  107. claude_mpm/services/infrastructure/context_preservation.py +7 -7
  108. claude_mpm/services/infrastructure/daemon_manager.py +5 -5
  109. claude_mpm/services/mcp_config_manager.py +10 -10
  110. claude_mpm/services/mcp_gateway/auto_configure.py +5 -5
  111. claude_mpm/services/mcp_gateway/config/config_loader.py +2 -2
  112. claude_mpm/services/mcp_gateway/config/configuration.py +5 -3
  113. claude_mpm/services/mcp_gateway/core/process_pool.py +3 -3
  114. claude_mpm/services/mcp_gateway/core/singleton_manager.py +2 -2
  115. claude_mpm/services/mcp_gateway/core/startup_verification.py +1 -1
  116. claude_mpm/services/mcp_gateway/main.py +1 -1
  117. claude_mpm/services/mcp_gateway/registry/service_registry.py +4 -2
  118. claude_mpm/services/mcp_gateway/registry/tool_registry.py +2 -1
  119. claude_mpm/services/mcp_gateway/server/stdio_handler.py +1 -1
  120. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +1 -1
  121. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +1 -1
  122. claude_mpm/services/mcp_gateway/tools/hello_world.py +1 -1
  123. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +5 -5
  124. claude_mpm/services/mcp_gateway/utils/update_preferences.py +2 -2
  125. claude_mpm/services/mcp_service_verifier.py +1 -1
  126. claude_mpm/services/memory/builder.py +1 -1
  127. claude_mpm/services/memory/cache/shared_prompt_cache.py +2 -1
  128. claude_mpm/services/memory/indexed_memory.py +3 -3
  129. claude_mpm/services/monitor/daemon.py +1 -1
  130. claude_mpm/services/monitor/daemon_manager.py +9 -9
  131. claude_mpm/services/monitor/handlers/file.py +1 -1
  132. claude_mpm/services/monitor/handlers/hooks.py +3 -3
  133. claude_mpm/services/monitor/management/lifecycle.py +7 -7
  134. claude_mpm/services/monitor/server.py +2 -2
  135. claude_mpm/services/orphan_detection.py +11 -16
  136. claude_mpm/services/port_manager.py +2 -2
  137. claude_mpm/services/project/analyzer.py +3 -3
  138. claude_mpm/services/project/archive_manager.py +17 -13
  139. claude_mpm/services/project/dependency_analyzer.py +4 -4
  140. claude_mpm/services/project/documentation_manager.py +4 -4
  141. claude_mpm/services/project/enhanced_analyzer.py +19 -8
  142. claude_mpm/services/project/registry.py +4 -4
  143. claude_mpm/services/project_port_allocator.py +7 -12
  144. claude_mpm/services/session_management_service.py +1 -1
  145. claude_mpm/services/socketio/event_normalizer.py +1 -1
  146. claude_mpm/services/socketio/handlers/code_analysis.py +14 -12
  147. claude_mpm/services/socketio/handlers/file.py +1 -1
  148. claude_mpm/services/socketio/migration_utils.py +1 -1
  149. claude_mpm/services/socketio/server/core.py +1 -1
  150. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +1 -1
  151. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +4 -4
  152. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +1 -1
  153. claude_mpm/services/unified/config_strategies/config_schema.py +4 -4
  154. claude_mpm/services/unified/config_strategies/context_strategy.py +8 -6
  155. claude_mpm/services/unified/config_strategies/error_handling_strategy.py +10 -10
  156. claude_mpm/services/unified/config_strategies/file_loader_strategy.py +5 -5
  157. claude_mpm/services/unified/config_strategies/unified_config_service.py +8 -8
  158. claude_mpm/services/unified/config_strategies/validation_strategy.py +15 -15
  159. claude_mpm/services/unified/deployment_strategies/base.py +4 -4
  160. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +15 -15
  161. claude_mpm/services/unified/deployment_strategies/local.py +11 -11
  162. claude_mpm/services/unified/deployment_strategies/utils.py +11 -9
  163. claude_mpm/services/unified/deployment_strategies/vercel.py +7 -9
  164. claude_mpm/services/unified/unified_config.py +5 -5
  165. claude_mpm/services/unified/unified_deployment.py +2 -2
  166. claude_mpm/services/utility_service.py +1 -1
  167. claude_mpm/services/version_control/conflict_resolution.py +2 -2
  168. claude_mpm/services/version_control/git_operations.py +3 -3
  169. claude_mpm/services/version_control/semantic_versioning.py +13 -13
  170. claude_mpm/services/version_control/version_parser.py +1 -1
  171. claude_mpm/storage/state_storage.py +12 -13
  172. claude_mpm/tools/code_tree_analyzer.py +5 -5
  173. claude_mpm/tools/code_tree_builder.py +4 -4
  174. claude_mpm/tools/socketio_debug.py +1 -1
  175. claude_mpm/utils/agent_dependency_loader.py +4 -4
  176. claude_mpm/utils/common.py +2 -2
  177. claude_mpm/utils/config_manager.py +3 -3
  178. claude_mpm/utils/dependency_cache.py +2 -2
  179. claude_mpm/utils/dependency_strategies.py +6 -6
  180. claude_mpm/utils/file_utils.py +11 -11
  181. claude_mpm/utils/log_cleanup.py +1 -1
  182. claude_mpm/utils/path_operations.py +1 -1
  183. claude_mpm/validation/agent_validator.py +2 -2
  184. claude_mpm/validation/frontmatter_validator.py +1 -1
  185. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/METADATA +1 -1
  186. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/RECORD +190 -190
  187. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/WHEEL +0 -0
  188. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/entry_points.txt +0 -0
  189. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/licenses/LICENSE +0 -0
  190. {claude_mpm-4.5.11.dist-info → claude_mpm-4.5.13.dist-info}/top_level.txt +0 -0
@@ -287,7 +287,7 @@ class AgentBuilderService:
287
287
 
288
288
  for template_file in self.templates_dir.glob("*.json"):
289
289
  try:
290
- with open(template_file) as f:
290
+ with template_file.open() as f:
291
291
  config = json.load(f)
292
292
 
293
293
  # Use filename stem as ID if not specified in config
@@ -377,12 +377,14 @@ class AgentBuilderService:
377
377
  raise AgentDeploymentError(f"Template '{template_id}' not found")
378
378
 
379
379
  try:
380
- with open(template_file) as f:
380
+ with template_file.open() as f:
381
381
  config = json.load(f)
382
382
  self._template_cache[template_id] = config
383
383
  return config.copy()
384
384
  except Exception as e:
385
- raise AgentDeploymentError(f"Failed to load template '{template_id}': {e}")
385
+ raise AgentDeploymentError(
386
+ f"Failed to load template '{template_id}': {e}"
387
+ ) from e
386
388
 
387
389
  def _load_instructions(self, agent_id: str) -> str:
388
390
  """Load agent instructions.
@@ -406,7 +408,7 @@ class AgentBuilderService:
406
408
  for instructions_file in possible_files:
407
409
  if instructions_file.exists():
408
410
  try:
409
- with open(instructions_file) as f:
411
+ with instructions_file.open() as f:
410
412
  return f.read()
411
413
  except Exception as e:
412
414
  self.logger.warning(
@@ -272,7 +272,7 @@ class AgentLifecycleManager(BaseService):
272
272
  # First convert to JSON string with custom encoder, then save
273
273
  json_str = json.dumps(data, indent=2, default=str)
274
274
  records_file.parent.mkdir(parents=True, exist_ok=True)
275
- with open(records_file, "w", encoding="utf-8") as f:
275
+ with records_file.open("w", encoding="utf-8") as f:
276
276
  f.write(json_str)
277
277
 
278
278
  self.logger.debug(f"Saved {len(self.agent_records)} agent records")
@@ -227,7 +227,7 @@ class AgentMetricsCollector:
227
227
  return "validation_error"
228
228
  return "other_error"
229
229
 
230
- def _extract_agent_type(self, agent_name: str) -> str: # noqa: PLR0911
230
+ def _extract_agent_type(self, agent_name: str) -> str:
231
231
  """
232
232
  Extract agent type from agent name for categorization.
233
233
 
@@ -80,7 +80,7 @@ class AgentRecordService(BaseService):
80
80
  json_str = json.dumps(data, indent=2, default=str)
81
81
  self.records_file.parent.mkdir(parents=True, exist_ok=True)
82
82
 
83
- with open(self.records_file, "w", encoding="utf-8") as f:
83
+ with self.records_file.open("w", encoding="utf-8") as f:
84
84
  f.write(json_str)
85
85
 
86
86
  self.logger.debug(f"Saved {len(records)} agent records")
@@ -149,7 +149,7 @@ class AgentRecordService(BaseService):
149
149
  json_str = json.dumps(data, indent=2, default=str)
150
150
  self.history_file.parent.mkdir(parents=True, exist_ok=True)
151
151
 
152
- with open(self.history_file, "w", encoding="utf-8") as f:
152
+ with self.history_file.open("w", encoding="utf-8") as f:
153
153
  f.write(json_str)
154
154
 
155
155
  self.logger.debug(f"Saved {len(history)} operation history entries")
@@ -255,7 +255,7 @@ class AgentRecordService(BaseService):
255
255
 
256
256
  # Write to output path
257
257
  json_str = json.dumps(data, indent=2, default=str)
258
- with open(output_path, "w", encoding="utf-8") as f:
258
+ with output_path.open("w", encoding="utf-8") as f:
259
259
  f.write(json_str)
260
260
 
261
261
  elif format == "csv":
@@ -91,7 +91,7 @@ class DeploymentServiceWrapper:
91
91
 
92
92
  # Read agent content if file exists
93
93
  if agent_path.exists():
94
- with open(agent_path) as f:
94
+ with agent_path.open() as f:
95
95
  content = f.read()
96
96
 
97
97
  # Parse metadata from content
@@ -48,10 +48,10 @@ class TargetDirectorySetupStep(BaseDeploymentStep):
48
48
  try:
49
49
  test_file.write_text("test")
50
50
  test_file.unlink()
51
- except Exception:
51
+ except Exception as e:
52
52
  raise PermissionError(
53
53
  f"Target directory is not writable: {context.actual_target_dir}"
54
- )
54
+ ) from e
55
55
 
56
56
  self.logger.info(f"Target directory set up: {context.actual_target_dir}")
57
57
 
@@ -457,7 +457,7 @@ class AgentProfileLoader(BaseService):
457
457
 
458
458
  if prompt_file.exists():
459
459
  try:
460
- with open(prompt_file) as f:
460
+ with prompt_file.open() as f:
461
461
  data = json.load(f)
462
462
  for prompt_data in data:
463
463
  prompt = ImprovedPrompt(
@@ -500,7 +500,7 @@ class AgentProfileLoader(BaseService):
500
500
 
501
501
  # Save to file
502
502
  prompt_file = self.improved_prompts_path / f"{agent_name}_prompts.json"
503
- with open(prompt_file, "w") as f:
503
+ with prompt_file.open("w") as f:
504
504
  json.dump(
505
505
  [
506
506
  {
@@ -195,7 +195,7 @@ class LocalAgentTemplateManager:
195
195
  """
196
196
  for template_file in directory.glob("*.json"):
197
197
  try:
198
- with open(template_file) as f:
198
+ with template_file.open() as f:
199
199
  data = json.load(f)
200
200
 
201
201
  # Create LocalAgentTemplate
@@ -307,7 +307,7 @@ class LocalAgentTemplateManager:
307
307
 
308
308
  # Save to JSON file
309
309
  template_file = target_dir / f"{template.agent_id}.json"
310
- with open(template_file, "w") as f:
310
+ with template_file.open("w") as f:
311
311
  json.dump(template.to_json(), f, indent=2)
312
312
 
313
313
  # Invalidate cache
@@ -628,7 +628,7 @@ class LocalAgentTemplateManager:
628
628
 
629
629
  # Save current version
630
630
  old_version_file = versions_dir / f"{template.agent_version}.json"
631
- with open(old_version_file, "w") as f:
631
+ with old_version_file.open("w") as f:
632
632
  json.dump(template.to_json(), f, indent=2)
633
633
 
634
634
  # Update template version
@@ -691,7 +691,7 @@ class LocalAgentTemplateManager:
691
691
  count = 0
692
692
  for agent_id, template in templates.items():
693
693
  output_file = output_dir / f"{agent_id}.json"
694
- with open(output_file, "w") as f:
694
+ with output_file.open("w") as f:
695
695
  json.dump(template.to_json(), f, indent=2)
696
696
  count += 1
697
697
 
@@ -715,7 +715,7 @@ class LocalAgentTemplateManager:
715
715
  count = 0
716
716
  for template_file in input_dir.glob("*.json"):
717
717
  try:
718
- with open(template_file) as f:
718
+ with template_file.open() as f:
719
719
  data = json.load(f)
720
720
 
721
721
  template = LocalAgentTemplate.from_json(data)
@@ -166,7 +166,7 @@ class DeployedAgentDiscovery(ConfigServiceBase):
166
166
  try:
167
167
  path = Path(agent_path)
168
168
  if path.exists() and path.suffix == ".json":
169
- with open(path) as f:
169
+ with path.open() as f:
170
170
  return json.load(f)
171
171
  except Exception as e:
172
172
  logger.warning(f"Failed to load full agent data from {agent_path}: {e}")
@@ -156,12 +156,19 @@ class AgentFileSystemHandler(FileSystemEventHandler):
156
156
  def __init__(self, tracker: "AgentModificationTracker"):
157
157
  self.tracker = tracker
158
158
 
159
+ def _create_tracked_task(self, coro):
160
+ """Create a task with automatic tracking and cleanup."""
161
+ task = asyncio.create_task(coro)
162
+ self.tracker._file_event_tasks.add(task)
163
+ task.add_done_callback(self.tracker._file_event_tasks.discard)
164
+ return task
165
+
159
166
  def on_created(self, event: FileSystemEvent) -> None:
160
167
  """Handle file creation events."""
161
168
  if not event.is_directory and event.src_path.endswith(
162
169
  (".md", ".json", ".yaml")
163
170
  ):
164
- asyncio.create_task(
171
+ self._create_tracked_task(
165
172
  self.tracker._handle_file_modification(
166
173
  event.src_path, ModificationType.CREATE
167
174
  )
@@ -172,7 +179,7 @@ class AgentFileSystemHandler(FileSystemEventHandler):
172
179
  if not event.is_directory and event.src_path.endswith(
173
180
  (".md", ".json", ".yaml")
174
181
  ):
175
- asyncio.create_task(
182
+ self._create_tracked_task(
176
183
  self.tracker._handle_file_modification(
177
184
  event.src_path, ModificationType.MODIFY
178
185
  )
@@ -183,7 +190,7 @@ class AgentFileSystemHandler(FileSystemEventHandler):
183
190
  if not event.is_directory and event.src_path.endswith(
184
191
  (".md", ".json", ".yaml")
185
192
  ):
186
- asyncio.create_task(
193
+ self._create_tracked_task(
187
194
  self.tracker._handle_file_modification(
188
195
  event.src_path, ModificationType.DELETE
189
196
  )
@@ -194,7 +201,7 @@ class AgentFileSystemHandler(FileSystemEventHandler):
194
201
  if not event.is_directory and event.src_path.endswith(
195
202
  (".md", ".json", ".yaml")
196
203
  ):
197
- asyncio.create_task(
204
+ self._create_tracked_task(
198
205
  self.tracker._handle_file_move(event.src_path, event.dest_path)
199
206
  )
200
207
 
@@ -248,6 +255,7 @@ class AgentModificationTracker(BaseService):
248
255
  # Background tasks
249
256
  self._persistence_task: Optional[asyncio.Task] = None
250
257
  self._cleanup_task: Optional[asyncio.Task] = None
258
+ self._file_event_tasks: Set[asyncio.Task] = set() # Track file event tasks
251
259
 
252
260
  # Callbacks
253
261
  self.modification_callbacks: List[Callable[[AgentModification], None]] = []
@@ -473,7 +481,7 @@ class AgentModificationTracker(BaseService):
473
481
  metadata["file_size_after"] = path.stat().st_size
474
482
 
475
483
  # File hash
476
- with open(path, "rb") as f:
484
+ with path.open("rb") as f:
477
485
  metadata["file_hash_after"] = hashlib.sha256(f.read()).hexdigest()
478
486
 
479
487
  # File type
@@ -512,7 +520,7 @@ class AgentModificationTracker(BaseService):
512
520
  }
513
521
 
514
522
  metadata_path = backup_dir / "metadata.json"
515
- with open(metadata_path, "w") as f:
523
+ with metadata_path.open("w") as f:
516
524
  json.dump(metadata, f, indent=2)
517
525
 
518
526
  return str(backup_path)
@@ -661,7 +669,7 @@ class AgentModificationTracker(BaseService):
661
669
  # Load active modifications
662
670
  active_path = self.persistence_root / "active_modifications.json"
663
671
  if active_path.exists():
664
- with open(active_path) as f:
672
+ with active_path.open() as f:
665
673
  data = json.load(f)
666
674
  self.active_modifications = {
667
675
  k: AgentModification.from_dict(v) for k, v in data.items()
@@ -669,7 +677,7 @@ class AgentModificationTracker(BaseService):
669
677
 
670
678
  # Load modification history
671
679
  for history_file in self.history_root.glob("*.json"):
672
- with open(history_file) as f:
680
+ with history_file.open() as f:
673
681
  data = json.load(f)
674
682
  agent_name = data["agent_name"]
675
683
  history = ModificationHistory(agent_name=agent_name)
@@ -699,7 +707,7 @@ class AgentModificationTracker(BaseService):
699
707
  active_data = {k: v.to_dict() for k, v in self.active_modifications.items()}
700
708
  active_path = self.persistence_root / "active_modifications.json"
701
709
 
702
- with open(active_path, "w") as f:
710
+ with active_path.open("w") as f:
703
711
  json.dump(active_data, f, indent=2)
704
712
 
705
713
  # Save modification history
@@ -714,7 +722,7 @@ class AgentModificationTracker(BaseService):
714
722
  }
715
723
 
716
724
  history_path = self.history_root / f"{agent_name}_history.json"
717
- with open(history_path, "w") as f:
725
+ with history_path.open("w") as f:
718
726
  json.dump(history_data, f, indent=2)
719
727
 
720
728
  self.logger.debug("Saved modification history to disk")
@@ -767,7 +775,7 @@ class AgentModificationTracker(BaseService):
767
775
  if backup_dir.is_dir():
768
776
  metadata_path = backup_dir / "metadata.json"
769
777
  if metadata_path.exists():
770
- with open(metadata_path) as f:
778
+ with metadata_path.open() as f:
771
779
  metadata = json.load(f)
772
780
  if metadata.get("backup_time", 0) < cutoff_time:
773
781
  shutil.rmtree(backup_dir)
@@ -336,7 +336,7 @@ class AsyncSessionLogger:
336
336
  with gzip.open(file_path, "wt", encoding="utf-8") as f:
337
337
  json.dump(data, f, indent=2, ensure_ascii=False)
338
338
  else:
339
- with open(file_path, "w", encoding="utf-8") as f:
339
+ with file_path.open("w", encoding="utf-8") as f:
340
340
  json.dump(data, f, indent=2, ensure_ascii=False)
341
341
 
342
342
  logger.debug(f"Wrote log entry to {file_path}")
@@ -224,7 +224,7 @@ class ClaudeSessionLogger:
224
224
 
225
225
  # Save response
226
226
  try:
227
- with open(file_path, "w", encoding="utf-8") as f:
227
+ with file_path.open("w", encoding="utf-8") as f:
228
228
  json.dump(response_data, f, indent=2, ensure_ascii=False)
229
229
 
230
230
  logger.debug(f"Logged response to {filename} for session {self.session_id}")
@@ -169,8 +169,8 @@ class AgentListingService(IAgentListingService):
169
169
 
170
170
  base_service = AgentDeploymentService()
171
171
  self._deployment_service = DeploymentServiceWrapper(base_service)
172
- except ImportError:
173
- raise ImportError("Agent deployment service not available")
172
+ except ImportError as e:
173
+ raise ImportError("Agent deployment service not available") from e
174
174
  return self._deployment_service
175
175
 
176
176
  @property
@@ -364,7 +364,7 @@ class AgentListingService(IAgentListingService):
364
364
  if not agent_path.exists():
365
365
  return None
366
366
 
367
- with open(agent_path) as f:
367
+ with agent_path.open() as f:
368
368
  content = f.read()
369
369
 
370
370
  details = {
@@ -72,7 +72,7 @@ class AgentValidationService(IAgentValidationService):
72
72
  self._registry = adapter.registry
73
73
  except Exception as e:
74
74
  self.logger.error(f"Failed to initialize agent registry: {e}")
75
- raise RuntimeError(f"Could not initialize agent registry: {e}")
75
+ raise RuntimeError(f"Could not initialize agent registry: {e}") from e
76
76
  return self._registry
77
77
 
78
78
  def validate_agent(self, agent_name: str) -> Dict[str, Any]:
@@ -449,7 +449,7 @@ class SessionManager(ISessionManager):
449
449
  sessions_dict = {
450
450
  sid: session.to_dict() for sid, session in self._sessions_cache.items()
451
451
  }
452
- with open(session_file, "w") as f:
452
+ with session_file.open("w") as f:
453
453
  json.dump(sessions_dict, f, indent=2)
454
454
  except Exception as e:
455
455
  self.logger.error(f"Failed to save sessions: {e}")
@@ -464,7 +464,7 @@ class SessionManager(ISessionManager):
464
464
  return
465
465
 
466
466
  try:
467
- with open(session_file) as f:
467
+ with session_file.open() as f:
468
468
  sessions_dict = json.load(f)
469
469
 
470
470
  self._sessions_cache = {
@@ -166,7 +166,7 @@ class PathResolver(IPathResolver):
166
166
  self.logger.debug(f"No project root found from {start_path}")
167
167
  return None
168
168
 
169
- def detect_framework_path(self) -> Optional[Path]: # noqa: PLR0911
169
+ def detect_framework_path(self) -> Optional[Path]:
170
170
  """
171
171
  Auto-detect claude-mpm framework using unified path management.
172
172
 
@@ -289,7 +289,7 @@ class AgentCheck(BaseDiagnosticCheck):
289
289
 
290
290
  # Basic validation
291
291
  try:
292
- with open(agent_file) as f:
292
+ with agent_file.open() as f:
293
293
  content = f.read()
294
294
 
295
295
  # Check for required sections
@@ -187,7 +187,7 @@ class ClaudeCodeCheck(BaseDiagnosticCheck):
187
187
 
188
188
  # Check if it's up to date
189
189
  try:
190
- with open(style_path) as f:
190
+ with style_path.open() as f:
191
191
  content = f.read()
192
192
  if "Claude MPM Output Style" in content:
193
193
  return DiagnosticResult(
@@ -232,7 +232,7 @@ class ClaudeCodeCheck(BaseDiagnosticCheck):
232
232
  )
233
233
 
234
234
  try:
235
- with open(config_path) as f:
235
+ with config_path.open() as f:
236
236
  config = json.load(f)
237
237
 
238
238
  mcp_servers = config.get("mcpServers", {})
@@ -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,17 @@ 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(
205
+ self._handler_tasks.discard
206
+ )
200
207
  else:
201
208
  loop.run_until_complete(
202
209
  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